diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md index e48f5c41..651d26f7 100644 --- a/.github/CODE_OF_CONDUCT.md +++ b/.github/CODE_OF_CONDUCT.md @@ -6,8 +6,8 @@ As such, the Architect project adheres to the [OpenJS Foundation Code of Conduct Lack of familiarity with this or the OpenJS Foundation Codes of Conduct, or the Contributor covenant, is not an excuse for non-adherence. +## Reporting -# Reporting If you are the subject of any behavior prohibited by this Code of Conduct, or observe someone who is, please contact an Architect team member immediately. If you know an Architect team member, you may wish to contact them personally (and you should); but if not, please [contact us via email](mailto:conduct@arc.codes). @@ -16,6 +16,6 @@ If possible, please attempt to collect any relevant information and evidence, in Reports will be handled with the utmost care, confidence, and sensitivity towards the individual(s) reporting. +## Enforcement -# Enforcement Should incidents arise, upon adjudication those found to be in violation of this Code of Conduct may be immediately expelled from the Architect community, including events, forums, chat workspaces, code repositories, and any other place where Architect community members collaborate. diff --git a/.github/contributing.md b/.github/contributing.md index 08966f86..ad3ac569 100644 --- a/.github/contributing.md +++ b/.github/contributing.md @@ -3,6 +3,6 @@ ## First: go read the [Architect Code of Conduct](/.github/CODE_OF_CONDUCT.md) ### Agreement to the Architect Code of Conduct -By participating in and contributing to the Architect community — including, but not limited to its open source projects, any related online venues such as GitHub, Slack, and in-person events, etc. — you agree to the [Architect Code of Conduct](/.github/CODE_OF_CONDUCT.md). +By participating in and contributing to the Architect community — including, but not limited to its open source projects, any related online venues such as GitHub, Discord, and in-person events, etc. — you agree to the [Architect Code of Conduct](/.github/CODE_OF_CONDUCT.md). Lack of familiarity with this Code of Conduct is not an excuse for not adhering to it. diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 7e6d1f89..78ccaaf2 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -4,13 +4,13 @@ To maintain a high standard of quality in our releases, before merging every pull request we ask that you've completed the following: -- [ ] Forked the repo and created your branch from `master` +- [ ] Forked the repo and created your branch from `main` - [ ] Made sure tests pass (run `npm it` from the repo root) - [ ] Updated relevant documentation internal to this repo (e.g. `readme.md`, help docs, inline docs & comments, etc.) - [ ] Linked to any related issues, PRs, etc. below that may relate to, consume, or necessitate these changes Please also be sure to completed the CLA as it's required for your PR to be merged. A github comment will prompt you if you haven't already. -Learn more about [contributing to Architect here](https://arc.codes/intro/community). +Learn more about [contributing to Architect here](/docs/en/about/contribute). Thanks again! diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4b8ffcfd..8cbb86e5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -3,97 +3,60 @@ name: Node CI # Push tests pushes; PR tests merges on: [ push, pull_request ] -defaults: - run: - shell: bash - jobs: - # Test the build build: # Setup - runs-on: ${{ matrix.os }} - strategy: - matrix: - node-version: [ 12.x, 14.x ] - os: [ ubuntu-latest ] + runs-on: ubuntu-latest # Go steps: - - name: Check out repo - uses: actions/checkout@v2 - - - name: Set up Node.js - uses: actions/setup-node@v1 + - name: Build App + uses: architect/action-build@v3 with: - node-version: ${{ matrix.node-version }} + node-version: lts/* - - name: Env - run: | - echo "Event name: ${{ github.event_name }}" - echo "Git ref: ${{ github.ref }}" - echo "GH actor: ${{ github.actor }}" - echo "SHA: ${{ github.sha }}" - VER=`node --version`; echo "Node ver: $VER" - VER=`npm --version`; echo "npm ver: $VER" - - - name: Install - run: npm install - - - name: Hydrate - run: npx arc hydrate - env: - CI: true - - - name: Test - run: npm test - env: - CI: true - - - name: Notify - uses: homoluctus/slatify@master - # Only fire alert once - if: github.ref == 'refs/heads/main' && failure() && matrix.node-version == '12.x' && matrix.os == 'ubuntu-latest' - with: - type: ${{ job.status }} - job_name: '*Build*' - url: ${{ secrets.SLACK_WEBHOOK }} - commit: true - token: ${{ secrets.GITHUB_TOKEN }} - - # Assuming all that went fine (and it's master): deploy! + # Assuming all that went fine (and it's main): deploy! deploy: # Setup needs: build - if: github.ref == 'refs/heads/main' + if: github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v') runs-on: ubuntu-latest # Go steps: - name: Check out repo - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Set up Node.js - uses: actions/setup-node@v1 + uses: actions/setup-node@v4 with: - node-version: 12 + node-version: lts/* - name: Install - run: npm i --production + run: npm i + + - name: Staging Deploy + if: github.ref == 'refs/heads/main' + run: npx arc deploy --staging + env: + CI: true + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - - name: Deploy - run: npm run deploy + - name: Production Deploy + if: startsWith(github.ref, 'refs/tags/v') + run: npx arc deploy --production env: CI: true - AWS_ACCESS_KEY_ID: ${{ secrets.V8_AWS_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.V8_AWS_SECRET_ACCESS_KEY }} + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - name: Notify - uses: homoluctus/slatify@master + uses: sarisia/actions-status-discord@v1 if: always() with: - type: ${{ job.status }} - job_name: '*Deploy*' - url: ${{ secrets.SLACK_WEBHOOK }} - commit: true - token: ${{ secrets.GITHUB_TOKEN }} + webhook: ${{ secrets.DISCORD_WEBHOOK }} + title: "deploy build" + color: 0x222222 + username: GitHub Actions diff --git a/.github/workflows/link-checker.yaml b/.github/workflows/link-checker.yaml new file mode 100644 index 00000000..75f5609e --- /dev/null +++ b/.github/workflows/link-checker.yaml @@ -0,0 +1,16 @@ +name: "Arc.codes Link Checker" +on: [ pull_request ] +defaults: + run: + shell: bash +jobs: + linkchecker: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: lts/* + - run: npm i + - run: npx arc hydrate + - run: npm run link-checker diff --git a/.github/workflows/woke.yaml b/.github/workflows/woke.yaml new file mode 100644 index 00000000..7463e156 --- /dev/null +++ b/.github/workflows/woke.yaml @@ -0,0 +1,30 @@ +name: 'woke' +on: + push: + branches: + - main + pull_request: + branches: + - main +jobs: + woke: + name: 'woke' + runs-on: ubuntu-latest + steps: + - name: 'Checkout' + uses: actions/checkout@v3 + + - name: 'Changed files' + uses: jitterbit/get-changed-files@v1 + id: files + continue-on-error: true + + - name: 'woke' + uses: get-woke/woke-action@v0 + with: + github-token: ${{ github.token }} + # Cause the check to fail on any broke rules + fail-on-error: true + # See https://github.com/marketplace/actions/get-all-changed-files + # for more options + woke-args: ${{ steps.files.outputs.added_modified }} diff --git a/.gitignore b/.gitignore index fc24aab5..8d4f5e51 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,10 @@ -.arc-env .DS_Store -**/static.json node_modules -package-lock.json +scratch sam.json sam.yaml +public/static.json +**/static.json +src/http/get-docs-000lang-catchall/highlight/ +.enhance +src/shared/enhance-styles diff --git a/.npmrc b/.npmrc new file mode 100644 index 00000000..b6f27f13 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +engine-strict=true diff --git a/app.arc b/app.arc new file mode 100644 index 00000000..e13b5261 --- /dev/null +++ b/app.arc @@ -0,0 +1,23 @@ +@app +arc-codes + +@aws +region us-west-2 +profile openjsf + +@static +fingerprint true + +@http +get / +get /docs/:lang/* +get /api/package +any /* + +@plugins +spellcheck +architect/plugin-node-prune +enhance/arc-plugin-styles + +@enhance-styles +config theme.json diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 00000000..9421567d --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,26 @@ +import arc from '@architect/eslint-config' +import importPlugin from 'eslint-plugin-import' + +export default [ + ...arc, + { + languageOptions: { + parserOptions: { + ecmaVersion: 2022, + sourceType: 'module' + } + }, + plugins: { + import: importPlugin.flatConfigs.recommended.plugins.import + }, + rules: { + 'import/no-commonjs': 'error', + 'import/extensions': [ + 'error', + 'ignorePackages' + ], + // Additive to our old `import` config, but everything seems quite sane! + ...importPlugin.flatConfigs.recommended.rules, + } + }, +] diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..bc61ff47 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,11256 @@ +{ + "name": "@architect/arc.codes", + "version": "3.13.16", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@architect/arc.codes", + "version": "3.13.16", + "license": "Apache-2.0", + "dependencies": { + "@architect/asap": "^7.0.10", + "@architect/functions": "^8.1.6", + "@architect/inventory": "^4.0.5", + "@architect/package": "^9.0.3", + "@architect/plugin-node-prune": "^2.0.2", + "@enhance/arc-plugin-styles": "^5.0.6", + "@enhance/enhance-style-transform": "^0.1.2", + "@enhance/ssr": "^4.0.3", + "arcdown": "^2.3.0", + "markdown-it-arc-static-img": "^2.1.0", + "slugify": "^1.6.6" + }, + "devDependencies": { + "@architect/architect": "^11.1.0", + "@architect/eslint-config": "^3.0.0", + "@architect/spellcheck-dictionary": "github:architect/spellcheck-dictionary", + "eslint": "^9.17.0", + "eslint-plugin-import": "^2.31.0", + "linkinator": "^6.1.2", + "spellchecker-cli": "^7.0.0", + "tap-arc": "^1.3.2", + "tape": "^5.9.0", + "tiny-json-http": "^7.5.1" + }, + "engines": { + "node": ">=16", + "npm": ">=8" + } + }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@architect/architect": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/@architect/architect/-/architect-11.2.0.tgz", + "integrity": "sha512-sBLZOa6vTG1F0fHULc3CmrK7458aGS0Zxqbt8inB1yVtqiSP2lDM2+Dizgqreh9gI92D8JHc0j1jP6q659ZYAA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@architect/create": "5.0.3", + "@architect/deploy": "5.0.8", + "@architect/destroy": "4.0.5", + "@architect/env": "4.0.5", + "@architect/hydrate": "4.0.8", + "@architect/inventory": "~4.0.6", + "@architect/logs": "5.0.5", + "@architect/sandbox": "6.0.5", + "@architect/utils": "~4.0.6", + "@aws-lite/client": "^0.22.2", + "chalk": "4.1.2", + "update-notifier-cjs": "5.1.6" + }, + "bin": { + "arc": "src/index.js" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@architect/asap": { + "version": "7.0.10", + "resolved": "https://registry.npmjs.org/@architect/asap/-/asap-7.0.10.tgz", + "integrity": "sha512-oJjYDranGTCkp21bziF/fIxJfLTucitqg/ar5mmLPHyroNG3XF3SUIMvuNd1GNIe4oy40wvGEXvTToKYvUeOLA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-lite/client": "~0.21.1", + "@aws-lite/s3": "^0.1.21" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@architect/asap/node_modules/@aws-lite/client": { + "version": "0.21.10", + "resolved": "https://registry.npmjs.org/@aws-lite/client/-/client-0.21.10.tgz", + "integrity": "sha512-fOn3lg1ynBAxqcELRf084bNJ6gu+GGoNyC+hwitW/hg3Vc1z1ZbK5HWWTrDw8HdM/fEQ0UN++g7GXVN1GVctdQ==", + "license": "Apache-2.0", + "workspaces": [ + "plugins/acm", + "plugins/apigateway", + "plugins/apigatewaymanagementapi", + "plugins/apigatewayv2", + "plugins/cloudformation", + "plugins/cloudfront", + "plugins/cloudwatch-logs", + "plugins/dynamodb", + "plugins/iam", + "plugins/lambda", + "plugins/organizations", + "plugins/rds-data", + "plugins/route53", + "plugins/s3", + "plugins/sns", + "plugins/sqs", + "plugins/ssm", + "plugins/sts" + ], + "dependencies": { + "aws4": "^1.13.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@architect/create": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@architect/create/-/create-5.0.3.tgz", + "integrity": "sha512-wLHLdZnArRR08HoEFCUtnvxSDjWexkhqrzK7NgeswjMMr5mK4y0zagarte0oaXSIhjCN+x79GWCkH49bKdsVBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@architect/inventory": "~4.0.5", + "@architect/utils": "~4.0.6", + "chalk": "4.1.2", + "lambda-runtimes": "~2.0.2", + "minimist": "~1.2.8" + }, + "bin": { + "arc-create": "src/cli.js" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@architect/deploy": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/@architect/deploy/-/deploy-5.0.8.tgz", + "integrity": "sha512-D6NAgjm6/2rQsR1qKPYxH/xwhOIrEaFWhX4wAgMVq5Yh7W6ahJC6nuPw/RA/aYr657uZ4ofMfVj+pNbZd8meXw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@architect/create": "~5.0.3", + "@architect/hydrate": "~4.0.6", + "@architect/inventory": "~4.0.5", + "@architect/package": "~9.0.3", + "@architect/utils": "~4.0.6", + "@aws-lite/apigatewayv2": "^0.0.4", + "@aws-lite/client": "^0.21.1", + "@aws-lite/cloudformation": "^0.0.5", + "@aws-lite/cloudfront": "^0.0.8", + "@aws-lite/lambda": "^0.0.5", + "@aws-lite/s3": "^0.1.21", + "@aws-lite/ssm": "^0.2.3", + "chalk": "4.1.2", + "fs-extra": "~11.3.0", + "get-folder-size": "2.0.1", + "glob": "~10.3.12", + "mime-types": "~2.1.35", + "minimist": "~1.2.8", + "path-sort": "~0.1.0", + "run-parallel": "~1.2.0", + "run-series": "~1.1.9", + "run-waterfall": "~1.1.7", + "zip-dir": "~2.0.0", + "zipit": "~2.0.0" + }, + "bin": { + "arc-deploy": "src/cli/index.js" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@architect/deploy/node_modules/@aws-lite/client": { + "version": "0.21.10", + "resolved": "https://registry.npmjs.org/@aws-lite/client/-/client-0.21.10.tgz", + "integrity": "sha512-fOn3lg1ynBAxqcELRf084bNJ6gu+GGoNyC+hwitW/hg3Vc1z1ZbK5HWWTrDw8HdM/fEQ0UN++g7GXVN1GVctdQ==", + "dev": true, + "license": "Apache-2.0", + "workspaces": [ + "plugins/acm", + "plugins/apigateway", + "plugins/apigatewaymanagementapi", + "plugins/apigatewayv2", + "plugins/cloudformation", + "plugins/cloudfront", + "plugins/cloudwatch-logs", + "plugins/dynamodb", + "plugins/iam", + "plugins/lambda", + "plugins/organizations", + "plugins/rds-data", + "plugins/route53", + "plugins/s3", + "plugins/sns", + "plugins/sqs", + "plugins/ssm", + "plugins/sts" + ], + "dependencies": { + "aws4": "^1.13.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@architect/destroy": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@architect/destroy/-/destroy-4.0.5.tgz", + "integrity": "sha512-qZtH6NZmNb1iUTdLq1ayCkOaPUf6PjvGoMAGrorhmf7VxGc8M43sSGE4v+OOfUrKdhKxrcY+lEdcdP2KZXSfSQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@architect/inventory": "~4.0.5", + "@architect/utils": "~4.0.6", + "@aws-lite/client": "^0.21.1", + "@aws-lite/cloudformation": "^0.0.5", + "@aws-lite/cloudwatch-logs": "^0.0.4", + "@aws-lite/s3": "^0.1.21", + "@aws-lite/ssm": "^0.2.3", + "minimist": "~1.2.8", + "run-parallel": "~1.2.0", + "run-waterfall": "~1.1.7" + }, + "bin": { + "arc-destroy": "src/cli.js" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@architect/destroy/node_modules/@aws-lite/client": { + "version": "0.21.10", + "resolved": "https://registry.npmjs.org/@aws-lite/client/-/client-0.21.10.tgz", + "integrity": "sha512-fOn3lg1ynBAxqcELRf084bNJ6gu+GGoNyC+hwitW/hg3Vc1z1ZbK5HWWTrDw8HdM/fEQ0UN++g7GXVN1GVctdQ==", + "dev": true, + "license": "Apache-2.0", + "workspaces": [ + "plugins/acm", + "plugins/apigateway", + "plugins/apigatewaymanagementapi", + "plugins/apigatewayv2", + "plugins/cloudformation", + "plugins/cloudfront", + "plugins/cloudwatch-logs", + "plugins/dynamodb", + "plugins/iam", + "plugins/lambda", + "plugins/organizations", + "plugins/rds-data", + "plugins/route53", + "plugins/s3", + "plugins/sns", + "plugins/sqs", + "plugins/ssm", + "plugins/sts" + ], + "dependencies": { + "aws4": "^1.13.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@architect/env": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@architect/env/-/env-4.0.5.tgz", + "integrity": "sha512-WltaHMwT6/At9kqtYgIEkN+RpHTYo6vWsdOQP/shr80m9pCStHNZW0YaVAjHbbBOPMOMyaAGizE1gcwaDXzT6Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@architect/inventory": "~4.0.5", + "@architect/parser": "~7.0.1", + "@architect/utils": "~4.0.6", + "@aws-lite/client": "^0.21.1", + "@aws-lite/ssm": "^0.2.3", + "chalk": "4.1.2", + "dotenv": "~16.4.5", + "minimist": "~1.2.8", + "run-series": "~1.1.9", + "run-waterfall": "~1.1.7", + "yesno": "~0.4.0" + }, + "bin": { + "arc-env": "src/cli.js" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@architect/env/node_modules/@aws-lite/client": { + "version": "0.21.10", + "resolved": "https://registry.npmjs.org/@aws-lite/client/-/client-0.21.10.tgz", + "integrity": "sha512-fOn3lg1ynBAxqcELRf084bNJ6gu+GGoNyC+hwitW/hg3Vc1z1ZbK5HWWTrDw8HdM/fEQ0UN++g7GXVN1GVctdQ==", + "dev": true, + "license": "Apache-2.0", + "workspaces": [ + "plugins/acm", + "plugins/apigateway", + "plugins/apigatewaymanagementapi", + "plugins/apigatewayv2", + "plugins/cloudformation", + "plugins/cloudfront", + "plugins/cloudwatch-logs", + "plugins/dynamodb", + "plugins/iam", + "plugins/lambda", + "plugins/organizations", + "plugins/rds-data", + "plugins/route53", + "plugins/s3", + "plugins/sns", + "plugins/sqs", + "plugins/ssm", + "plugins/sts" + ], + "dependencies": { + "aws4": "^1.13.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@architect/eslint-config": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@architect/eslint-config/-/eslint-config-3.0.0.tgz", + "integrity": "sha512-S0Tq8HdB4zmPMQjsBzpu6gSGunBXdwveMccMluTezS6NQA4xUqkRM6wjr3vKQSuUftqo2HCtvSocnBcaCjxZ5Q==", + "dev": true, + "dependencies": { + "@eslint/js": "^9.0.0", + "@stylistic/eslint-plugin-js": "^1.7.2", + "eslint-plugin-fp": "^2.3.0" + }, + "peerDependencies": { + "eslint": ">= 9" + } + }, + "node_modules/@architect/functions": { + "version": "8.1.7", + "resolved": "https://registry.npmjs.org/@architect/functions/-/functions-8.1.7.tgz", + "integrity": "sha512-+2+houdMC3FXROgY4i46VbzQwHq4ryLdWrPbO2VEuegBp0KdhQLgD/4W7aYGQ31qx58yQr2hYgZ/ZhdLYEwHSA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-lite/apigatewaymanagementapi": "^0.0.10", + "@aws-lite/client": "^0.22.4", + "@aws-lite/dynamodb": "^0.3.9", + "@aws-lite/sns": "^0.0.8", + "@aws-lite/sqs": "^0.2.4", + "@aws-lite/ssm": "^0.2.5", + "cookie": "^1.0.2", + "cookie-signature": "^1.2.2", + "csrf": "^3.1.0", + "node-webtokens": "^1.0.4", + "run-parallel": "^1.2.0", + "run-waterfall": "^1.1.7", + "uid-safe": "^2.1.5" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@architect/hydrate": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/@architect/hydrate/-/hydrate-4.0.8.tgz", + "integrity": "sha512-rf5xA9ZHWj69CPbwuQB3P0gscX+1ast0IilrqbGglZKi0DS4Xkoo6kJpUbdGfRsNAV3Of+z2GM0XLfEnuigVWg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@architect/inventory": "~4.0.5", + "@architect/utils": "~4.0.6", + "acorn-loose": "~8.4.0", + "chalk": "4.1.2", + "esquery": "~1.6.0", + "glob": "10.4.5", + "minimist": "~1.2.8", + "run-series": "~1.1.9", + "symlink-or-copy": "~1.3.1" + }, + "bin": { + "arc-hydrate": "src/cli.js" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@architect/hydrate/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@architect/hydrate/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@architect/hydrate/node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/@architect/hydrate/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@architect/inventory": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@architect/inventory/-/inventory-4.0.6.tgz", + "integrity": "sha512-vCKfoogAQmHPXDgCU76lvHsmmMdMoLDOEi21f6kq31G++lpqhY6+68fsOxnV1iakDY6tXV5YeZ0WGLOBa37/fA==", + "license": "Apache-2.0", + "dependencies": { + "@architect/asap": "~7.0.10", + "@architect/parser": "~7.0.1", + "@architect/utils": "~4.0.6", + "@aws-lite/client": "^0.21.1", + "@aws-lite/ssm": "^0.2.3", + "lambda-runtimes": "~2.0.5" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@architect/inventory/node_modules/@aws-lite/client": { + "version": "0.21.10", + "resolved": "https://registry.npmjs.org/@aws-lite/client/-/client-0.21.10.tgz", + "integrity": "sha512-fOn3lg1ynBAxqcELRf084bNJ6gu+GGoNyC+hwitW/hg3Vc1z1ZbK5HWWTrDw8HdM/fEQ0UN++g7GXVN1GVctdQ==", + "license": "Apache-2.0", + "workspaces": [ + "plugins/acm", + "plugins/apigateway", + "plugins/apigatewaymanagementapi", + "plugins/apigatewayv2", + "plugins/cloudformation", + "plugins/cloudfront", + "plugins/cloudwatch-logs", + "plugins/dynamodb", + "plugins/iam", + "plugins/lambda", + "plugins/organizations", + "plugins/rds-data", + "plugins/route53", + "plugins/s3", + "plugins/sns", + "plugins/sqs", + "plugins/ssm", + "plugins/sts" + ], + "dependencies": { + "aws4": "^1.13.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@architect/logs": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/@architect/logs/-/logs-5.0.5.tgz", + "integrity": "sha512-NEro6KnvKIRvavvcGHKw6ziJ5SrP0FpTXhiDAYnLLGgh6qJ1zXqxyKrgCYAu5oI5lNoccZomVoYv3ooaDUDqMQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@architect/inventory": "~4.0.5", + "@architect/utils": "~4.0.6", + "@aws-lite/client": "^0.21.1", + "@aws-lite/cloudformation": "^0.0.5", + "@aws-lite/cloudwatch-logs": "^0.0.4", + "chalk": "4.1.2", + "minimist": "~1.2.8", + "run-parallel": "~1.2.0", + "run-waterfall": "~1.1.7", + "strftime": "~0.10.2" + }, + "bin": { + "arc-logs": "src/cli.js" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@architect/logs/node_modules/@aws-lite/client": { + "version": "0.21.10", + "resolved": "https://registry.npmjs.org/@aws-lite/client/-/client-0.21.10.tgz", + "integrity": "sha512-fOn3lg1ynBAxqcELRf084bNJ6gu+GGoNyC+hwitW/hg3Vc1z1ZbK5HWWTrDw8HdM/fEQ0UN++g7GXVN1GVctdQ==", + "dev": true, + "license": "Apache-2.0", + "workspaces": [ + "plugins/acm", + "plugins/apigateway", + "plugins/apigatewaymanagementapi", + "plugins/apigatewayv2", + "plugins/cloudformation", + "plugins/cloudfront", + "plugins/cloudwatch-logs", + "plugins/dynamodb", + "plugins/iam", + "plugins/lambda", + "plugins/organizations", + "plugins/rds-data", + "plugins/route53", + "plugins/s3", + "plugins/sns", + "plugins/sqs", + "plugins/ssm", + "plugins/sts" + ], + "dependencies": { + "aws4": "^1.13.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@architect/package": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/@architect/package/-/package-9.0.3.tgz", + "integrity": "sha512-k21bX5xmGfGLr9wPy+TweN8OaMscf4nPYeVotf35rzdhCfnEBax3Tyi3GY8v0WyJ2jgxPzu7CrsgnhG8goIxag==", + "license": "Apache-2.0", + "dependencies": { + "@architect/inventory": "~4.0.5", + "@architect/utils": "~4.0.6" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@architect/parser": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@architect/parser/-/parser-7.0.1.tgz", + "integrity": "sha512-T4Rr/eQbtg/gPvS4HcXR7zYxLJ3gEh6pSKj0s/Y1IrvJY9QG4BDAVZgE7AYGfzqymwIF0pUI2mQ91CLi2CTnQw==", + "license": "Apache-2.0", + "engines": { + "node": ">=16" + } + }, + "node_modules/@architect/plugin-node-prune": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@architect/plugin-node-prune/-/plugin-node-prune-2.0.2.tgz", + "integrity": "sha512-H0U4oAxF9NdMWYYvEB6XSGKrfU7qFzrgykWculu+19lvZHatZxUv3EYHFXiNswCd1yf/1IpOVoXGSKvVb5Xveg==", + "dependencies": { + "@architect/utils": "~3.1.0" + } + }, + "node_modules/@architect/plugin-node-prune/node_modules/@architect/utils": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/@architect/utils/-/utils-3.1.9.tgz", + "integrity": "sha512-VSjIM3+JGHqjryrURd3bwBfPhc6K0znljjQOsxEOOUFEdB8XA2cZGS2kpfhIIz3ApovoEfrCvo6y1D8qhqKHNg==", + "dependencies": { + "chalk": "4.1.2", + "glob": "~10.2.2", + "path-sort": "~0.1.0", + "restore-cursor": "3.1.0", + "run-series": "~1.1.9", + "run-waterfall": "~1.1.7", + "sha": "~3.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@architect/plugin-node-prune/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@architect/plugin-node-prune/node_modules/glob": { + "version": "10.2.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.2.7.tgz", + "integrity": "sha512-jTKehsravOJo8IJxUGfZILnkvVJM/MOfHRs8QcXolVef2zNI9Tqyy5+SeuOAZd3upViEZQLyFpQhYiHLrMUNmA==", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.0.3", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2", + "path-scurry": "^1.7.0" + }, + "bin": { + "glob": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@architect/plugin-node-prune/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@architect/plugin-node-prune/node_modules/minipass": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-6.0.2.tgz", + "integrity": "sha512-MzWSV5nYVT7mVyWCwn2o7JH13w2TBRmmSqSRCKzTw+lmft9X4z+3wjvs06Tzijo5z4W/kahUCDpRXTF+ZrmF/w==", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/@architect/sandbox": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/@architect/sandbox/-/sandbox-6.0.5.tgz", + "integrity": "sha512-VJcFxVhSbrhisTMqq2ukbj4NlN18eJgYYqEMnCk9UAdIyHBBoEAP4YmroyFCHv7CsutFUMhjDQP9+9deQje+6w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@architect/asap": "~7.0.10", + "@architect/create": "~5.0.3", + "@architect/hydrate": "~4.0.6", + "@architect/inventory": "~4.0.5", + "@architect/utils": "~4.0.6", + "@aws-lite/client": "^0.21.1", + "@aws-lite/dynamodb": "^0.3.4", + "@begin/hashid": "~1.0.0", + "chalk": "4.1.2", + "chokidar": "~3.6.0", + "depstatus": "~1.1.1", + "dynalite": "~3.2.2", + "finalhandler": "~1.2.0", + "glob": "~10.3.12", + "http-proxy": "~1.18.1", + "lambda-runtimes": "~2.0.2", + "minimist": "~1.2.8", + "router": "~1.3.8", + "run-parallel": "~1.2.0", + "run-series": "~1.1.9", + "send": "~0.18.0", + "server-destroy": "~1.0.1", + "tmp": "~0.2.3", + "tree-kill": "~1.2.2", + "update-notifier-cjs": "~5.1.6", + "ws": "~8.17.0" + }, + "bin": { + "sandbox": "src/cli/cli.js" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@architect/sandbox/node_modules/@aws-lite/client": { + "version": "0.21.10", + "resolved": "https://registry.npmjs.org/@aws-lite/client/-/client-0.21.10.tgz", + "integrity": "sha512-fOn3lg1ynBAxqcELRf084bNJ6gu+GGoNyC+hwitW/hg3Vc1z1ZbK5HWWTrDw8HdM/fEQ0UN++g7GXVN1GVctdQ==", + "dev": true, + "license": "Apache-2.0", + "workspaces": [ + "plugins/acm", + "plugins/apigateway", + "plugins/apigatewaymanagementapi", + "plugins/apigatewayv2", + "plugins/cloudformation", + "plugins/cloudfront", + "plugins/cloudwatch-logs", + "plugins/dynamodb", + "plugins/iam", + "plugins/lambda", + "plugins/organizations", + "plugins/rds-data", + "plugins/route53", + "plugins/s3", + "plugins/sns", + "plugins/sqs", + "plugins/ssm", + "plugins/sts" + ], + "dependencies": { + "aws4": "^1.13.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@architect/spellcheck-dictionary": { + "version": "0.1.18", + "resolved": "git+ssh://git@github.com/architect/spellcheck-dictionary.git#e4892db85c01ce9f375a74da8e59a340f1b10dbc", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@architect/syntaxes": { + "version": "1.2.1", + "resolved": "git+ssh://git@github.com/architect/syntaxes.git#ca61027ef8295e2bb012a8df9ac9240052f43b66", + "license": "Apache-2.0" + }, + "node_modules/@architect/utils": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@architect/utils/-/utils-4.0.6.tgz", + "integrity": "sha512-aa6gNNoHxgKpQrIFOa5zNW5fD10v46AE2VZNcjToxAvm//8itbIBoGw2wj8oF3gqHMKKkeLAtdO8K8tlKVN8ZA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-lite/client": "^0.21.1", + "chalk": "4.1.2", + "glob": "~10.3.12", + "path-sort": "~0.1.0", + "restore-cursor": "3.1.0", + "run-series": "~1.1.9", + "run-waterfall": "~1.1.7", + "sha": "~3.0.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@architect/utils/node_modules/@aws-lite/client": { + "version": "0.21.10", + "resolved": "https://registry.npmjs.org/@aws-lite/client/-/client-0.21.10.tgz", + "integrity": "sha512-fOn3lg1ynBAxqcELRf084bNJ6gu+GGoNyC+hwitW/hg3Vc1z1ZbK5HWWTrDw8HdM/fEQ0UN++g7GXVN1GVctdQ==", + "license": "Apache-2.0", + "workspaces": [ + "plugins/acm", + "plugins/apigateway", + "plugins/apigatewaymanagementapi", + "plugins/apigatewayv2", + "plugins/cloudformation", + "plugins/cloudfront", + "plugins/cloudwatch-logs", + "plugins/dynamodb", + "plugins/iam", + "plugins/lambda", + "plugins/organizations", + "plugins/rds-data", + "plugins/route53", + "plugins/s3", + "plugins/sns", + "plugins/sqs", + "plugins/ssm", + "plugins/sts" + ], + "dependencies": { + "aws4": "^1.13.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@aws-lite/apigatewaymanagementapi": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/@aws-lite/apigatewaymanagementapi/-/apigatewaymanagementapi-0.0.10.tgz", + "integrity": "sha512-fIkUYTV4TF0wnNwzvbqWou/I0bxGsgJbbawBmeKoJIc+3yc2PzOIP6RqNPmWjlUHsuI7QnymPgWljWfvExlaVg==", + "license": "Apache-2.0", + "engines": { + "node": ">=16" + } + }, + "node_modules/@aws-lite/apigatewayv2": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/@aws-lite/apigatewayv2/-/apigatewayv2-0.0.4.tgz", + "integrity": "sha512-e6kzA7oIJcEN6RiSlfWTunS6RpK3DadCLNPpBZCdFfH1Bz1GYr15rkkWItIaEO6e21VWZPYAzTRSqBRcZfgbfw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=16" + } + }, + "node_modules/@aws-lite/client": { + "version": "0.22.4", + "resolved": "https://registry.npmjs.org/@aws-lite/client/-/client-0.22.4.tgz", + "integrity": "sha512-52ua/U3+JXecuTtrTZ1XjSbDL2S+iyfOD/1daYRnPQ83YPNzo4BJe1iIVwtZbJB6goLyeVolxSlPmnuqF6JFvw==", + "license": "Apache-2.0", + "workspaces": [ + "plugins/acm", + "plugins/apigateway", + "plugins/apigatewaymanagementapi", + "plugins/apigatewayv2", + "plugins/cloudformation", + "plugins/cloudfront", + "plugins/cloudwatch-logs", + "plugins/dynamodb", + "plugins/iam", + "plugins/lambda", + "plugins/organizations", + "plugins/rds-data", + "plugins/route53", + "plugins/s3", + "plugins/sns", + "plugins/sqs", + "plugins/ssm", + "plugins/sts" + ], + "dependencies": { + "aws4": "^1.13.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@aws-lite/cloudformation": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/@aws-lite/cloudformation/-/cloudformation-0.0.5.tgz", + "integrity": "sha512-x0//MSCl3ntV0RKQcyYLIe6dzzCbjdAKEh1ftzkXp5dK7f+li7iZXhP3YhrVUaVWmEyjX8FLiIsDX0dRIODEYA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=16" + } + }, + "node_modules/@aws-lite/cloudfront": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/@aws-lite/cloudfront/-/cloudfront-0.0.8.tgz", + "integrity": "sha512-RdBFr7sHoGnLf/QLp8j1AlgAnLNtj9rAnmbkkvUwqrmaTZ0iLnWekeLe44YnknaQQjzscsGdAOjiNuPLds/BFw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=16" + } + }, + "node_modules/@aws-lite/cloudwatch-logs": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/@aws-lite/cloudwatch-logs/-/cloudwatch-logs-0.0.4.tgz", + "integrity": "sha512-4tQx0WBKhXrSCtpCcqsDWf+lr5Q6g+Xc2wepiuTKYtd3MXmfSTfbEr1AokaXHgHomGg9z08sAlNdGwnxYePspg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=16" + } + }, + "node_modules/@aws-lite/dynamodb": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@aws-lite/dynamodb/-/dynamodb-0.3.9.tgz", + "integrity": "sha512-jrMAWwxoAMVJ3z0/mI/GzPM5AfGmH+xzTpNIbjg3+2WdYJRqvIf8025XJdCDtS9/4x8zogdchEp3ZelXnwYyqw==", + "license": "Apache-2.0", + "engines": { + "node": ">=16" + } + }, + "node_modules/@aws-lite/lambda": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/@aws-lite/lambda/-/lambda-0.0.5.tgz", + "integrity": "sha512-2mNibCwhGU9qAhhqpDIqXdJRR8yKEspoYpycZN/BoOZolHaFZnItqomcF6ZifeZPF6bDfBHVuEu9cX9OU1IXVg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=16" + } + }, + "node_modules/@aws-lite/s3": { + "version": "0.1.22", + "resolved": "https://registry.npmjs.org/@aws-lite/s3/-/s3-0.1.22.tgz", + "integrity": "sha512-9OL95fTvHV80JvFTxLx8hhWQ6DgwHUts02KpXITA8syCDnYgua2rNcpwQ5b6GZzpL7yNXU0dud/Y6edThbffig==", + "license": "Apache-2.0", + "engines": { + "node": ">=16" + } + }, + "node_modules/@aws-lite/sns": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/@aws-lite/sns/-/sns-0.0.8.tgz", + "integrity": "sha512-MIzHe66kLNyzPFY/DX30uN7DlVQsnBiHPYbq/7syNWuoYSG8bkWuUX2CVIuSL7Ji5jaLpQ4lf8/VQ+SiAeoIZA==", + "license": "Apache-2.0", + "engines": { + "node": ">=16" + } + }, + "node_modules/@aws-lite/sqs": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@aws-lite/sqs/-/sqs-0.2.4.tgz", + "integrity": "sha512-a1M3HDdkNE/xJfASlfisAaZ8XF6FpvoJbJsH/gr6pogEFWgNQyvmPVNRElnDY7JW3ee82sEOkMukYRdAbjytNQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=16" + } + }, + "node_modules/@aws-lite/ssm": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/@aws-lite/ssm/-/ssm-0.2.5.tgz", + "integrity": "sha512-1B8mZ79ySqlTEfSQ87KZ0XkmTOKQFMO3lUYUGUtwNTUncJINr6nhRWEjk128oBWwEQnpJ7NfpDPjdfg1ICe3xw==", + "license": "Apache-2.0", + "engines": { + "node": ">=16" + } + }, + "node_modules/@begin/hashid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@begin/hashid/-/hashid-1.0.0.tgz", + "integrity": "sha512-w+U9klEtRkt7hyW/f+/SvwPgJ4CTMO2ENddisX9dGgLUZKu+iKpb/IhYnQWIt9/Nnm/5DkApmiHGimPs621wwA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@begin/parse5": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/@begin/parse5/-/parse5-0.0.4.tgz", + "integrity": "sha512-1H5S1AcFqpQfBMG0z4z7URFb5jWCrILIH9MkXSmTsyYd4hj4kgJnpXHGELJE9HnXH1maI/WfHi5j+udDVkvZ9g==", + "dependencies": { + "parse5": "^7.0.0" + } + }, + "node_modules/@enhance/arc-plugin-styles": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@enhance/arc-plugin-styles/-/arc-plugin-styles-5.0.6.tgz", + "integrity": "sha512-LHTUDlz1KxcvB1ZZ2azLdixkCnxcAyYfS6XhUnzP+Nr3au331T75q4k73liCbPOaS0TEtYRPylcSGJwMTVH6HQ==", + "dependencies": { + "@architect/functions": "^8.0.4", + "@enhance/styles": "^6.4.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@enhance/css-parser": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@enhance/css-parser/-/css-parser-1.0.0.tgz", + "integrity": "sha512-DG18Jwxvf0WvUtA8VEA+axaIq0aoJzGTd25kdeLDPQcNnq3bWG+TUMPi8oa4btVXwHVyCR7Xsy0jW80m1ZwPow==", + "dependencies": { + "inherits": "^2.0.4", + "source-map": "^0.6.1", + "source-map-resolve": "^0.6.0" + } + }, + "node_modules/@enhance/enhance-style-transform": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@enhance/enhance-style-transform/-/enhance-style-transform-0.1.2.tgz", + "integrity": "sha512-Hx/c5PfJl4XYToZaKL0b3o54rq4JX5zKD6Z+Pz6BSygnX0xOOHdWriaXxFKDUl1X/VVVlJM3FWjYj93/a7WSPg==", + "dependencies": { + "@enhance/css-parser": "^1.0.0" + } + }, + "node_modules/@enhance/ssr": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@enhance/ssr/-/ssr-4.0.3.tgz", + "integrity": "sha512-WJoF8Bv9EXovJXpB0JyXavqkz5KTwPKSQG8nHxtT++F+Pm6iIdtUQjNG+VVSBmOryC0pq/sZNtBxiC0g9xzksQ==", + "license": "Apache-2.0", + "dependencies": { + "@begin/parse5": "^0.0.4", + "nanoid": "^4.0.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@enhance/styles": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@enhance/styles/-/styles-6.4.0.tgz", + "integrity": "sha512-Op/QqOIwJ4d5MqPPGUKdlerz+zgrmA7Id8Yy1+auAHyBa/GStzqqKveN4ui18xpt7OI//zoLNlr4Apw2XyHu7g==", + "dependencies": { + "color-to-hsla": "^0.1.1" + }, + "bin": { + "enhance-styles": "cli.mjs" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.1.tgz", + "integrity": "sha512-fo6Mtm5mWyKjA/Chy1BYTdn5mGJoDNjC7C64ug20ADsRDGrA85bN3uK3MaKbeRkRuuIEAR5N33Jr1pbm411/PA==", + "dev": true, + "dependencies": { + "@eslint/object-schema": "^2.1.5", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.9.1.tgz", + "integrity": "sha512-GuUdqkyyzQI5RMIWkHhvTWLCyLo1jNK3vzkSyaExH5kHPDHcuL2VOpHjmMY+y3+NC69qAKToBqldTBgYeLSr9Q==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.2.0.tgz", + "integrity": "sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/espree": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", + "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", + "dev": true, + "dependencies": { + "acorn": "^8.14.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "9.17.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.17.0.tgz", + "integrity": "sha512-Sxc4hqcs1kTu0iID3kcZDW3JHq2a77HO9P8CP6YEA/FpH3Ll8UXE2r/86Rz9YJLKme39S9vU5OWNjC6Xl0Cr3w==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.5.tgz", + "integrity": "sha512-o0bhxnL89h5Bae5T318nFoFzGy+YE5i/gGkoPAgkmTVdRKTiv3p8JHevPiPaMwoloKfEiiaHlawCqaZMqRm+XQ==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.4.tgz", + "integrity": "sha512-zSkKow6H5Kdm0ZUQUB2kV5JIXqoG0+uH5YADhaEHswm664N9Db8dXSi0nMJpacpMf+MyyglF1vnZohpEg5yUtg==", + "dev": true, + "dependencies": { + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "dev": true, + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.1.tgz", + "integrity": "sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==", + "dev": true, + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@ljharb/resumer": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@ljharb/resumer/-/resumer-0.1.3.tgz", + "integrity": "sha512-d+tsDgfkj9X5QTriqM4lKesCkMMJC3IrbPKHvayP00ELx2axdXvDfWkqjxrLXIzGcQzmj7VAUT1wopqARTvafw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ljharb/through": "^2.3.13", + "call-bind": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/@ljharb/through": { + "version": "2.3.13", + "resolved": "https://registry.npmjs.org/@ljharb/through/-/through-2.3.13.tgz", + "integrity": "sha512-/gKJun8NNiWGZJkGzI/Ragc53cOdcLNdzjLaIa+GEjguQs0ulsurx8WN0jijdK9yPqDvziX995sMRLyLt1uZMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@pnpm/config.env-replace": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz", + "integrity": "sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.22.0" + } + }, + "node_modules/@pnpm/network.ca-file": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@pnpm/network.ca-file/-/network.ca-file-1.0.2.tgz", + "integrity": "sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "4.2.10" + }, + "engines": { + "node": ">=12.22.0" + } + }, + "node_modules/@pnpm/network.ca-file/node_modules/graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true, + "license": "ISC" + }, + "node_modules/@pnpm/npm-conf": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@pnpm/npm-conf/-/npm-conf-2.3.1.tgz", + "integrity": "sha512-c83qWb22rNRuB0UaVCI0uRPNRr8Z0FWnEIvT47jiHAmOIUHbBOg5XvV7pM5x+rKn9HRpjxquDbXYSXr3fAKFcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@pnpm/config.env-replace": "^1.1.0", + "@pnpm/network.ca-file": "^1.0.1", + "config-chain": "^1.1.11" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@rtsao/scc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", + "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", + "dev": true + }, + "node_modules/@stylistic/eslint-plugin-js": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-1.8.1.tgz", + "integrity": "sha512-c5c2C8Mos5tTQd+NWpqwEu7VT6SSRooAguFPMj1cp2RkTYl1ynKoXo8MWy3k4rkbzoeYHrqC2UlUzsroAN7wtQ==", + "dev": true, + "dependencies": { + "@types/eslint": "^8.56.10", + "acorn": "^8.11.3", + "escape-string-regexp": "^4.0.0", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "peerDependencies": { + "eslint": ">=8.40.0" + } + }, + "node_modules/@types/debug": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "dev": true, + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/eslint": { + "version": "8.56.12", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.12.tgz", + "integrity": "sha512-03ruubjWyOHlmljCVoxSuNDdmfZDzsrrz0P2LeJsOXr+ZwFQ+0yQIwNCwt/GYhV7Z31fgtXJTAEs+FYlEL851g==", + "dev": true, + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "dev": true + }, + "node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dev": true, + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true + }, + "node_modules/@types/linkify-it": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-3.0.5.tgz", + "integrity": "sha512-yg6E+u0/+Zjva+buc3EIb+29XEg4wltq7cSmd4Uc2EE/1nUVmxyzpX6gUXD0V8jIrG0r7YeOGVIbYRkxeooCtw==", + "peer": true + }, + "node_modules/@types/markdown-it": { + "version": "13.0.7", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-13.0.7.tgz", + "integrity": "sha512-U/CBi2YUUcTHBt5tjO2r5QV/x0Po6nsYwQU4Y04fBS6vfoImaiZ6f8bi3CjTCxBPQSO1LMyUqkByzi8AidyxfA==", + "peer": true, + "dependencies": { + "@types/linkify-it": "*", + "@types/mdurl": "*" + } + }, + "node_modules/@types/mdast": { + "version": "3.0.15", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.15.tgz", + "integrity": "sha512-LnwD+mUEfxWMa1QpDraczIn6k0Ee3SMicuYSSzS6ZYl2gKS09EClnJYGd8Du6rfc5r/GZEk5o1mRb8TaTj03sQ==", + "dev": true, + "dependencies": { + "@types/unist": "^2" + } + }, + "node_modules/@types/mdurl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-1.0.5.tgz", + "integrity": "sha512-6L6VymKTzYSrEf4Nev4Xa1LCHKrlTlYCBMTlQKFuddo1CvQcE52I0mwfOJayueUC7MJuXOeHTcIU683lzd0cUA==", + "peer": true + }, + "node_modules/@types/ms": { + "version": "0.7.34", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz", + "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==", + "dev": true + }, + "node_modules/@types/nlcst": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@types/nlcst/-/nlcst-1.0.4.tgz", + "integrity": "sha512-ABoYdNQ/kBSsLvZAekMhIPMQ3YUZvavStpKYs7BjLLuKVmIMA0LUgZ7b54zzuWJRbHF80v1cNf4r90Vd6eMQDg==", + "dev": true, + "dependencies": { + "@types/unist": "^2" + } + }, + "node_modules/@types/unist": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.10.tgz", + "integrity": "sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==", + "dev": true + }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.1.tgz", + "integrity": "sha512-fEzPV3hSkSMltkw152tJKNARhOupqbH96MZWyRjNaYZOMIzbrTeQDG+MTc6Mr2pgzFQzFxAfmhGDNP5QK++2ZA==", + "dev": true + }, + "node_modules/abstract-leveldown": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-6.2.3.tgz", + "integrity": "sha512-BsLm5vFMRUrrLeCcRc+G0t2qOaTzpoJQLOubq2XM72eNpjF5UdU5o/5NvlNhx95XHcAvcl8OMXr4mlg/fRgUXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "immediate": "^3.2.3", + "level-concat-iterator": "~2.0.0", + "level-supports": "~1.0.0", + "xtend": "~4.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/acorn": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-loose": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/acorn-loose/-/acorn-loose-8.4.0.tgz", + "integrity": "sha512-M0EUka6rb+QC4l9Z3T0nJEzNOO7JcoJlYMrBlyBCiFSXRyxjLKayd4TbQs2FDRWQU1h9FR7QVNHt+PEaoNL5rQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", + "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", + "dev": true, + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-align": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", + "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.1.0" + } + }, + "node_modules/ansi-align/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/ansi-align/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arcdown": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/arcdown/-/arcdown-2.3.0.tgz", + "integrity": "sha512-9JEt4bvvclAJKv4XIuBJNAqh74zlLcAJMW9jREFYKA5Uj++BmdSV3I5ZTACbhA4LmI4Ty6y8vdEJOJAJvzTsSw==", + "dependencies": { + "@architect/syntaxes": "git+https://github.com/architect/syntaxes.git#v1.2.1", + "gray-matter": "^4.0.3", + "markdown-it": "^14.0.0", + "markdown-it-anchor": "^8.6.7", + "markdown-it-class": "^1.0.0", + "markdown-it-external-anchor": "^1.0.0", + "markdown-it-toc-done-right": "^4.2.0" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "node_modules/array-back": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz", + "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-flatten": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-3.0.0.tgz", + "integrity": "sha512-zPMVc3ZYlGLNk4mpK1NzP2wg0ml9t7fUgDsayR5Y5rSzxQilzR9FGu/EH2jQOcKSAeAfWeylyW8juy3OkWRvNA==", + "dev": true, + "license": "MIT" + }, + "node_modules/array-includes": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", + "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.4", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-iterate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/array-iterate/-/array-iterate-2.0.1.tgz", + "integrity": "sha512-I1jXZMjAgCMmxT4qxXfPXa6SthSoE8h6gkSI9BGGNv8mP8G/v0blc+qFnZu6K42vTOiuME596QaLO0TP3Lk0xg==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/array.prototype.every": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/array.prototype.every/-/array.prototype.every-1.1.6.tgz", + "integrity": "sha512-gNEqZD97w6bfQRNmHkFv7rNnGM+VWyHZT+h/rf9C+22owcXuENr66Lfo0phItpU5KoXW6Owb34q2+8MnSIZ57w==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.0", + "es-object-atoms": "^1.0.0", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.findlastindex": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz", + "integrity": "sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", + "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", + "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/async": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash": "^4.17.14" + } + }, + "node_modules/atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "bin": { + "atob": "bin/atob.js" + }, + "engines": { + "node": ">= 4.5.0" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/aws4": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.13.1.tgz", + "integrity": "sha512-u5w79Rd7SU4JaIlA/zFqG+gOiuq25q5VLyZ8E+ijJeILuTxVzZgp2CaGw/UTw6pXYN9XMO9yiqj/nEHmhTG5CA==", + "license": "MIT" + }, + "node_modules/bail": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", + "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/boxen": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-5.1.2.tgz", + "integrity": "sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-align": "^3.0.0", + "camelcase": "^6.2.0", + "chalk": "^4.1.0", + "cli-boxes": "^2.2.1", + "string-width": "^4.2.2", + "type-fest": "^0.20.2", + "widest-line": "^3.1.0", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/boxen/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/boxen/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/boxen/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "dev": true, + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz", + "integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz", + "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==", + "dev": true, + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/character-entities": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-html4": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/cli-boxes": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", + "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/color-to-hsla": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/color-to-hsla/-/color-to-hsla-0.1.1.tgz", + "integrity": "sha512-lXqaKMk8lY5R9PK0R/fqSZygEKLIJvBrLc20Y/7CpPdoKe5pNvJQYs2olo5BbuJK9gOVr6za4mndjZHHVLZvEg==" + }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/command-line-args": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/command-line-args/-/command-line-args-5.2.1.tgz", + "integrity": "sha512-H4UfQhZyakIjC74I9d34fGYDwk3XpSr17QhEd0Q3I9Xq1CETHo4Hcuo87WyWHpAF1aSLjLRf5lD9ZGX2qStUvg==", + "dev": true, + "dependencies": { + "array-back": "^3.1.0", + "find-replace": "^3.0.0", + "lodash.camelcase": "^4.3.0", + "typical": "^4.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/command-line-usage": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-5.0.5.tgz", + "integrity": "sha512-d8NrGylA5oCXSbGoKz05FkehDAzSmIm4K03S5VDh4d5lZAtTWfc3D1RuETtuQCn8129nYfJfDdF7P/lwcz1BlA==", + "dev": true, + "dependencies": { + "array-back": "^2.0.0", + "chalk": "^2.4.1", + "table-layout": "^0.4.3", + "typical": "^2.6.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/command-line-usage/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/command-line-usage/node_modules/array-back": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-2.0.0.tgz", + "integrity": "sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw==", + "dev": true, + "dependencies": { + "typical": "^2.6.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/command-line-usage/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/command-line-usage/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/command-line-usage/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/command-line-usage/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/command-line-usage/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/command-line-usage/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/command-line-usage/node_modules/typical": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/typical/-/typical-2.6.1.tgz", + "integrity": "sha512-ofhi8kjIje6npGozTip9Fr8iecmYfEbS06i0JnIg+rh51KakryWF4+jX8lLKZVhy6N+ID45WYSFCxPOdTWCzNg==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/config-chain": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", + "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, + "node_modules/config-chain/node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true, + "license": "ISC" + }, + "node_modules/configstore": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", + "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "dot-prop": "^5.2.0", + "graceful-fs": "^4.1.2", + "make-dir": "^3.0.0", + "unique-string": "^2.0.0", + "write-file-atomic": "^3.0.0", + "xdg-basedir": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cookie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", + "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/create-eslint-index": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/create-eslint-index/-/create-eslint-index-1.0.0.tgz", + "integrity": "sha512-nXvJjnfDytOOaPOonX0h0a1ggMoqrhdekGeZkD6hkcWYvlCWhU719tKFVh8eU04CnMwu3uwe1JjwuUF2C3k2qg==", + "dev": true, + "dependencies": { + "lodash.get": "^4.3.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crypto-random-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", + "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/csrf": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/csrf/-/csrf-3.1.0.tgz", + "integrity": "sha512-uTqEnCvWRk042asU6JtapDTcJeeailFy4ydOQS28bj1hcLnYRiqi8SsD2jS412AY1I/4qdOwWZun774iqywf9w==", + "dependencies": { + "rndm": "1.2.0", + "tsscmp": "1.0.6", + "uid-safe": "2.1.5" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/inspect-js" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/date-format": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.3.tgz", + "integrity": "sha512-7P3FyqDcfeznLZp2b+OMitV9Sz2lUnsT87WaTat9nVwqsBkTzPG3lPLNwW3en6F4pHUiWzr6vb8CLhjdK9bcxQ==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decode-named-character-reference": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz", + "integrity": "sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==", + "dev": true, + "dependencies": { + "character-entities": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/decode-uri-component": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", + "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/deep-equal": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.3.tgz", + "integrity": "sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "call-bind": "^1.0.5", + "es-get-iterator": "^1.1.3", + "get-intrinsic": "^1.2.2", + "is-arguments": "^1.1.1", + "is-array-buffer": "^3.0.2", + "is-date-object": "^1.0.5", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "isarray": "^2.0.5", + "object-is": "^1.1.5", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.5.1", + "side-channel": "^1.0.4", + "which-boxed-primitive": "^1.0.2", + "which-collection": "^1.0.1", + "which-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/deferred-leveldown": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/deferred-leveldown/-/deferred-leveldown-5.3.0.tgz", + "integrity": "sha512-a59VOT+oDy7vtAbLRCZwWgxu2BaCfd5Hk7wxJd48ei7I+nsg8Orlb9CLG0PMZienk9BSUKgeAqkO2+Lw+1+Ukw==", + "dev": true, + "license": "MIT", + "dependencies": { + "abstract-leveldown": "~6.2.1", + "inherits": "^2.0.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/defined": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/defined/-/defined-0.0.0.tgz", + "integrity": "sha512-zpqiCT8bODLu3QSmLLic8xJnYWBFjOSu/fBCm189oAiTtPq/PSanNACKZDS7kgSyCJY7P+IcODzlIogBK/9RBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/depstatus": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/depstatus/-/depstatus-1.1.1.tgz", + "integrity": "sha512-QT4i2Ql8RS1ttcj7zo4RzYvKz+/eOOIh6N7CXHqfDqC5ZX1hfx5KX6T88gy/j2UWP2x2ytjSoFBl+XW0gVZMug==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "semver": "^7.3.2" + } + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "dev": true, + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/dictionary-en": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/dictionary-en/-/dictionary-en-3.2.0.tgz", + "integrity": "sha512-oujbJhuplUTlDntmOoultEcNyk8ge7cFI6yXNn7eJvpbBJOhGNhWtK0XjOJsiwl4EfIeyvDKwGB95vJXv1d+lQ==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/dictionary-en-au": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/dictionary-en-au/-/dictionary-en-au-2.4.0.tgz", + "integrity": "sha512-SEETr3rqt26/Umc43gemwaH/ez4gPv7I4alifu/QLi8uxiCm6a7cn3wKb22HNB5l6j/R7/Sfkq9NTKk/QRVUHw==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/dictionary-en-ca": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/dictionary-en-ca/-/dictionary-en-ca-2.4.0.tgz", + "integrity": "sha512-uDoOe7/dz1XD3niVpOroSo3Pas7cdsAp+NSwfan30MGzzeXaD04lQuTRDqHpsXuP8fq3U7f9v8Iaiyk5q/kgzw==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/dictionary-en-gb": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/dictionary-en-gb/-/dictionary-en-gb-2.4.0.tgz", + "integrity": "sha512-zwc7e6CM/+Em+v74a5/ia6663nwR9vvtEayaX5WkgvHD99al1qm/RbcdLludqX3mggYxXXFN1zN5hzyREW/DXg==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/dictionary-en-za": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/dictionary-en-za/-/dictionary-en-za-2.2.0.tgz", + "integrity": "sha512-YFod3pshfaecqCe221rczW5lVWn4ZhfFH6E395DN1bEbL4dUndGm9Bn/zxMOU2w9imYKwptYalO23hetwUx+OA==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/dictionary-vi": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/dictionary-vi/-/dictionary-vi-2.2.0.tgz", + "integrity": "sha512-WDCaSzHMtayCIEa2hPq0rL8ds603SKNzMzNXU0kbLM7kzXoaXaKeHgPXJHKjKMxxnozKLMsg+rES7hC6Lxe7iA==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/diff": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", + "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dev": true, + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dev": true, + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", + "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", + "dev": true, + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dotenv": { + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dotignore": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/dotignore/-/dotignore-0.1.2.tgz", + "integrity": "sha512-UGGGWfSauusaVJC+8fgV+NVvBXkCTmVv7sk6nojDZZvuOUNGUy0Zk4UpHQD6EDjS0jpBwcACvH4eofvyzBcRDw==", + "dev": true, + "dependencies": { + "minimatch": "^3.0.4" + }, + "bin": { + "ignored": "bin/ignored" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/dynalite": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/dynalite/-/dynalite-3.2.2.tgz", + "integrity": "sha512-sx9ZjTgMs/D4gHnba4rnBkw29648dHwHmywJet132KAbiq1ZyWx9W1fMd/eP9cPwTKDXyCBuTYOChE0qMDjaXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "async": "^2.6.3", + "big.js": "^5.2.2", + "buffer-crc32": "^0.2.13", + "lazy": "^1.0.11", + "levelup": "^4.4.0", + "lock": "^1.1.0", + "memdown": "^5.1.0", + "minimist": "^1.2.5", + "once": "^1.4.0", + "subleveldown": "^5.0.1" + }, + "bin": { + "dynalite": "cli.js" + }, + "optionalDependencies": { + "leveldown": "^5.6.0" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "dev": true, + "license": "MIT" + }, + "node_modules/emoji-regex": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz", + "integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==", + "dev": true + }, + "node_modules/emoticon": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/emoticon/-/emoticon-4.0.1.tgz", + "integrity": "sha512-dqx7eA9YaqyvYtUhJwT4rC1HIp82j5ybS1/vQ42ur+jBe17dJMwZE4+gvL1XadSFfxaPFFGt3Xsw+Y8akThDlw==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/encoding-down": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/encoding-down/-/encoding-down-6.3.0.tgz", + "integrity": "sha512-QKrV0iKR6MZVJV08QY0wp1e7vF6QbhnbQhb07bwpEyuz4uZiZgPlEGdkCROuFkUwdxlFaiPIhjyarH1ee/3vhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "abstract-leveldown": "^6.2.1", + "inherits": "^2.0.3", + "level-codec": "^9.0.0", + "level-errors": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/errno": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", + "dev": true, + "license": "MIT", + "dependencies": { + "prr": "~1.0.1" + }, + "bin": { + "errno": "cli.js" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-abstract": { + "version": "1.23.8", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.8.tgz", + "integrity": "sha512-lfab8IzDn6EpI1ibZakcgS6WsfEBiB+43cuJo+wgylx1xKXf+Sp+YR3vFuQwC/u3sxYwV8Cxe3B0DpVUu/WiJQ==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-set-tostringtag": "^2.0.3", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.2.6", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-regex": "^1.2.1", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.0", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.3", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "own-keys": "^1.0.0", + "regexp.prototype.flags": "^1.5.3", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.18" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-get-iterator": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", + "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "has-symbols": "^1.0.3", + "is-arguments": "^1.1.1", + "is-map": "^2.0.2", + "is-set": "^2.0.2", + "is-string": "^1.0.7", + "isarray": "^2.0.5", + "stop-iteration-iterator": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-object-atoms": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", + "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", + "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.4", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", + "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", + "dev": true, + "dependencies": { + "hasown": "^2.0.0" + } + }, + "node_modules/es-to-primitive": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", + "dev": true, + "dependencies": { + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/escape-goat": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz", + "integrity": "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.17.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.17.0.tgz", + "integrity": "sha512-evtlNcpJg+cZLcnVKwsai8fExnqjGPicK7gnUtlNuzu+Fv9bI0aLpND5T44VLQtoMEnI57LoXO9XAkIXwohKrA==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.19.0", + "@eslint/core": "^0.9.0", + "@eslint/eslintrc": "^3.2.0", + "@eslint/js": "9.17.0", + "@eslint/plugin-kit": "^0.2.3", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.1", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.2.0", + "eslint-visitor-keys": "^4.2.0", + "espree": "^10.3.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-ast-utils": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/eslint-ast-utils/-/eslint-ast-utils-1.1.0.tgz", + "integrity": "sha512-otzzTim2/1+lVrlH19EfQQJEhVJSu0zOb9ygb3iapN6UlyaDtyRq4b5U1FuW0v1lRa9Fp/GJyHkSwm6NqABgCA==", + "dev": true, + "dependencies": { + "lodash.get": "^4.4.2", + "lodash.zip": "^4.2.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", + "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", + "dev": true, + "dependencies": { + "debug": "^3.2.7", + "is-core-module": "^2.13.0", + "resolve": "^1.22.4" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-module-utils": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.0.tgz", + "integrity": "sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==", + "dev": true, + "dependencies": { + "debug": "^3.2.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-fp": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-fp/-/eslint-plugin-fp-2.3.0.tgz", + "integrity": "sha512-3n2oHibwoIxAht9/+ZaTldhI6brXORgl8oNXqZd+d9xuAQt2SBJ2/aml0oQRMWvXrgsz2WG6wfC++NjzSG3prA==", + "dev": true, + "dependencies": { + "create-eslint-index": "^1.0.0", + "eslint-ast-utils": "^1.0.0", + "lodash": "^4.13.1", + "req-all": "^0.1.0" + }, + "engines": { + "node": ">=4.0.0" + }, + "peerDependencies": { + "eslint": ">=3" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.31.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz", + "integrity": "sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==", + "dev": true, + "dependencies": { + "@rtsao/scc": "^1.1.0", + "array-includes": "^3.1.8", + "array.prototype.findlastindex": "^1.2.5", + "array.prototype.flat": "^1.3.2", + "array.prototype.flatmap": "^1.3.2", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.9", + "eslint-module-utils": "^2.12.0", + "hasown": "^2.0.2", + "is-core-module": "^2.15.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "object.groupby": "^1.0.3", + "object.values": "^1.2.0", + "semver": "^6.3.1", + "string.prototype.trimend": "^1.0.8", + "tsconfig-paths": "^3.15.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-scope": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz", + "integrity": "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/espree": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", + "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", + "dev": true, + "dependencies": { + "acorn": "^8.14.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true, + "license": "MIT" + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fault": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fault/-/fault-2.0.1.tgz", + "integrity": "sha512-WtySTkS4OKev5JtpHXnib4Gxiurzh5NCGvWrFaZ34m6JehfTUhKZvn9njTfw48t6JumVQOmrKqpmGcdwxnhqBQ==", + "dev": true, + "dependencies": { + "format": "^0.2.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/find-replace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-3.0.0.tgz", + "integrity": "sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ==", + "dev": true, + "dependencies": { + "array-back": "^3.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz", + "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==", + "dev": true + }, + "node_modules/follow-redirects": { + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.3" + } + }, + "node_modules/foreground-child": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", + "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/format": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", + "integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==", + "dev": true, + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-extra": { + "version": "11.3.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz", + "integrity": "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", + "dev": true, + "license": "MIT" + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gar": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/gar/-/gar-1.0.4.tgz", + "integrity": "sha512-w4n9cPWyP7aHxKxYHFQMegj7WIAsL/YX/C4Bs5Rr8s1H9M1rNtRWRsw+ovYMkXDQ5S4ZbYHsHAPmevPjPgw44w==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "dev": true, + "license": "MIT" + }, + "node_modules/gaxios": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.2.0.tgz", + "integrity": "sha512-H6+bHeoEAU5D6XNc6mPKeN5dLZqEDs9Gpk6I+SZBEzK5So58JVrHPmevNi35fRl1J9Y5TaeLW0kYx3pCJ1U2mQ==", + "dev": true, + "dependencies": { + "extend": "^3.0.2", + "https-proxy-agent": "^7.0.1", + "is-stream": "^2.0.0", + "node-fetch": "^2.6.9" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/gemoji": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/gemoji/-/gemoji-7.1.0.tgz", + "integrity": "sha512-wI0YWDIfQraQMDs0yXAVQiVBZeMm/rIYssf8LZlMDdssKF19YqJKOHkv4zvwtVQTBJ0LNmErv1S+DqlVUudz8g==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/get-folder-size": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/get-folder-size/-/get-folder-size-2.0.1.tgz", + "integrity": "sha512-+CEb+GDCM7tkOS2wdMKTn9vU7DgnKUTuDlehkNJKNSovdCOVxs14OfKCk4cvSaR3za4gj+OBdl9opPN9xrJ0zA==", + "dev": true, + "license": "MIT", + "dependencies": { + "gar": "^1.0.4", + "tiny-each-async": "2.0.3" + }, + "bin": { + "get-folder-size": "bin/get-folder-size" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.6.tgz", + "integrity": "sha512-qxsEs+9A+u85HhllWJJFicJfPDhRmjzoYdl64aMWW9yRIJmSyxdn8IEkuIM530/7T+lv0TIHd8L6Q/ra0tEoeA==", + "dev": true, + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "dunder-proto": "^1.0.0", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "function-bind": "^1.1.2", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-symbol-description": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/glob": { + "version": "10.3.16", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.16.tgz", + "integrity": "sha512-JDKXl1DiuuHJ6fVS2FXjownaavciiHNUU4mOvV/B793RLh05vZL1rcPnCSaOgv1hDT6RDlY7AB7ZUvFYAtPgAw==", + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.1", + "minipass": "^7.0.4", + "path-scurry": "^1.11.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/glob/node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/global-dirs": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.1.tgz", + "integrity": "sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ini": "2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + }, + "node_modules/gray-matter": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.3.tgz", + "integrity": "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==", + "dependencies": { + "js-yaml": "^3.13.1", + "kind-of": "^6.0.2", + "section-matter": "^1.0.0", + "strip-bom-string": "^1.0.0" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/gray-matter/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/gray-matter/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-dynamic-import": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/has-dynamic-import/-/has-dynamic-import-2.1.0.tgz", + "integrity": "sha512-su0anMkNEnJKZ/rB99jn3y6lV/J8Ro96hBJ28YAeVzj5rWxH+YL/AdCyiYYA1HDLV9YhmvqpWSJJj2KLo1MX6g==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.5", + "get-intrinsic": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "dev": true, + "dependencies": { + "dunder-proto": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-yarn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", + "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hast-util-embedded": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-embedded/-/hast-util-embedded-3.0.0.tgz", + "integrity": "sha512-naH8sld4Pe2ep03qqULEtvYr7EjrLK2QHY8KJR6RJkTUjPGObe1vnx585uzem2hGra+s1q08DZZpfgDVYRbaXA==", + "dev": true, + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-is-element": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-html": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hast-util-from-html/-/hast-util-from-html-2.0.3.tgz", + "integrity": "sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==", + "dev": true, + "dependencies": { + "@types/hast": "^3.0.0", + "devlop": "^1.1.0", + "hast-util-from-parse5": "^8.0.0", + "parse5": "^7.0.0", + "vfile": "^6.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-html/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "dev": true + }, + "node_modules/hast-util-from-html/node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-html/node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-html/node_modules/vfile-message": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz", + "integrity": "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-parse5": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-8.0.2.tgz", + "integrity": "sha512-SfMzfdAi/zAoZ1KkFEyyeXBn7u/ShQrfd675ZEE9M3qj+PMFX05xubzRyF76CCSJu8au9jgVxDV1+okFvgZU4A==", + "dev": true, + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "devlop": "^1.0.0", + "hastscript": "^9.0.0", + "property-information": "^6.0.0", + "vfile": "^6.0.0", + "vfile-location": "^5.0.0", + "web-namespaces": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-parse5/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "dev": true + }, + "node_modules/hast-util-from-parse5/node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-parse5/node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-parse5/node_modules/vfile-location": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-5.0.3.tgz", + "integrity": "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-parse5/node_modules/vfile-message": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz", + "integrity": "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-has-property": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-has-property/-/hast-util-has-property-3.0.0.tgz", + "integrity": "sha512-MNilsvEKLFpV604hwfhVStK0usFY/QmM5zX16bo7EjnAEGofr5YyI37kzopBlZJkHD4t887i+q/C8/tr5Q94cA==", + "dev": true, + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-is-body-ok-link": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/hast-util-is-body-ok-link/-/hast-util-is-body-ok-link-3.0.1.tgz", + "integrity": "sha512-0qpnzOBLztXHbHQenVB8uNuxTnm/QBFUOmdOSsEn7GnBtyY07+ENTWVFBAnXd/zEgd9/SUG3lRY7hSIBWRgGpQ==", + "dev": true, + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-is-element": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-3.0.0.tgz", + "integrity": "sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==", + "dev": true, + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-parse-selector": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz", + "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==", + "dev": true, + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-phrasing": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/hast-util-phrasing/-/hast-util-phrasing-3.0.1.tgz", + "integrity": "sha512-6h60VfI3uBQUxHqTyMymMZnEbNl1XmEGtOxxKYL7stY2o601COo62AWAYBQR9lZbYXYSBoxag8UpPRXK+9fqSQ==", + "dev": true, + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-embedded": "^3.0.0", + "hast-util-has-property": "^3.0.0", + "hast-util-is-body-ok-link": "^3.0.0", + "hast-util-is-element": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-html": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.4.tgz", + "integrity": "sha512-wxQzXtdbhiwGAUKrnQJXlOPmHnEehzphwkK7aluUPQ+lEc1xefC8pblMgpp2w5ldBTEfveRIrADcrhGIWrlTDA==", + "dev": true, + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-whitespace": "^3.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0", + "stringify-entities": "^4.0.0", + "zwitch": "^2.0.4" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-html/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "dev": true + }, + "node_modules/hast-util-to-nlcst": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/hast-util-to-nlcst/-/hast-util-to-nlcst-4.0.0.tgz", + "integrity": "sha512-+YxIJMLJe+2AEhJeJHXZu1VuhApzjD9dLlWOLTS9kUusHfxKsyqRSnSF/YEu4h0uvzwSC0wPqFwIb9GSnK/chQ==", + "dev": true, + "dependencies": { + "@types/hast": "^3.0.0", + "@types/nlcst": "^2.0.0", + "@types/unist": "^3.0.0", + "hast-util-embedded": "^3.0.0", + "hast-util-is-element": "^3.0.0", + "hast-util-phrasing": "^3.0.0", + "hast-util-to-string": "^2.0.0", + "hast-util-whitespace": "^3.0.0", + "nlcst-to-string": "^4.0.0", + "unist-util-position": "^5.0.0", + "vfile": "^6.0.0", + "vfile-location": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-nlcst/node_modules/@types/nlcst": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/nlcst/-/nlcst-2.0.3.tgz", + "integrity": "sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA==", + "dev": true, + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/hast-util-to-nlcst/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "dev": true + }, + "node_modules/hast-util-to-nlcst/node_modules/nlcst-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/nlcst-to-string/-/nlcst-to-string-4.0.0.tgz", + "integrity": "sha512-YKLBCcUYKAg0FNlOBT6aI91qFmSiFKiluk655WzPF+DDMA02qIyy8uiRqI8QXtcFpEvll12LpL5MXqEmAZ+dcA==", + "dev": true, + "dependencies": { + "@types/nlcst": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-nlcst/node_modules/unist-util-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-nlcst/node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-nlcst/node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-nlcst/node_modules/vfile-location": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-5.0.3.tgz", + "integrity": "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-nlcst/node_modules/vfile-message": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz", + "integrity": "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hast-util-to-string/-/hast-util-to-string-2.0.0.tgz", + "integrity": "sha512-02AQ3vLhuH3FisaMM+i/9sm4OXGSq1UhOOCpTLLQtHdL3tZt7qil69r8M8iDkZYyC0HCFylcYoP+8IO7ddta1A==", + "dev": true, + "dependencies": { + "@types/hast": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-string/node_modules/@types/hast": { + "version": "2.3.10", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.10.tgz", + "integrity": "sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==", + "dev": true, + "dependencies": { + "@types/unist": "^2" + } + }, + "node_modules/hast-util-whitespace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", + "dev": true, + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hastscript": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-9.0.0.tgz", + "integrity": "sha512-jzaLBGavEDKHrc5EfFImKN7nZKKBdSLIdGvCwDZ9TfzbF2ffXiov8CKE445L2Z1Ek2t/m4SKQ2j6Ipv7NyUolw==", + "dev": true, + "dependencies": { + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-parse-selector": "^4.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/html-void-elements": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", + "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/htmlparser2": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-9.1.0.tgz", + "integrity": "sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==", + "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.1.0", + "entities": "^4.5.0" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz", + "integrity": "sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==", + "dev": true, + "dependencies": { + "agent-base": "^7.0.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/ignore": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/immediate": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.3.0.tgz", + "integrity": "sha512-HR7EVodfFUdQCTIeySw+WDRFJlPcLOJbXfwwZ7Oom6tjsvZ3bOkCDJHehQC3nxJrv7+f9XecwazynjU8e4Vw3Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-lazy": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", + "integrity": "sha512-m7ZEHgtw69qOGw+jwxXkHlrlIPdTGkyh66zXZ1ajZbxkDBNjSY/LGbmjc7h0s2ELsUDTAhFr55TrPSSqJGPG0A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ini": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", + "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/insync": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/insync/-/insync-2.1.1.tgz", + "integrity": "sha512-UzUhOZFpCMM22Xlig9iUPqalf8n7c4eYScamce1C+jN3ad8FtmVm42ryMwVq0hAxHbwUhWFhPvTFQQpFdDUKkw==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "node_modules/is-async-function": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz", + "integrity": "sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "dev": true, + "dependencies": { + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-boolean-object": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.1.tgz", + "integrity": "sha512-l9qO6eFlUETHtuihLcYOaLKByJ1f+N4kthcU9YjHy3N+B3hWv0y/2Nd0mu/7lTFnRQHTrSdXF50HQ3bl5fEnng==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "engines": { + "node": ">=4" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ci-info": "^2.0.0" + }, + "bin": { + "is-ci": "bin.js" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-installed-globally": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz", + "integrity": "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "global-dirs": "^3.0.0", + "is-path-inside": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-npm": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-5.0.0.tgz", + "integrity": "sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "dev": true, + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.0.tgz", + "integrity": "sha512-SXM8Nwyys6nT5WP6pltOwKytLV7FqQ4UiibxVmW+EIosHcmCqkkjViTb5SNssDlkCiEYRP1/pdWUKVvZBmsR2Q==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-yarn-global": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz", + "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==", + "dev": true, + "license": "MIT" + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "node_modules/isomorphic-fetch": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz", + "integrity": "sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA==", + "dev": true, + "license": "MIT", + "dependencies": { + "node-fetch": "^2.6.1", + "whatwg-fetch": "^3.4.1" + } + }, + "node_modules/jackspeak": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", + "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonc": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/jsonc/-/jsonc-2.0.0.tgz", + "integrity": "sha512-B281bLCT2TRMQa+AQUQY5AGcqSOXBOKaYGP4wDzoA/+QswUfN8sODektbPEs9Baq7LGKun5jQbNFpzwGuVYKhw==", + "dev": true, + "dependencies": { + "fast-safe-stringify": "^2.0.6", + "graceful-fs": "^4.1.15", + "mkdirp": "^0.5.1", + "parse-json": "^4.0.0", + "strip-bom": "^4.0.0", + "strip-json-comments": "^3.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "dev": true, + "license": "(MIT OR GPL-3.0-or-later)", + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, + "node_modules/jszip/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jszip/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/jszip/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/jszip/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/junit-report-builder": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/junit-report-builder/-/junit-report-builder-3.2.1.tgz", + "integrity": "sha512-IMCp5XyDQ4YESDE4Za7im3buM0/7cMnRfe17k2X8B05FnUl9vqnaliX6cgOEmPIeWKfJrEe/gANRq/XgqttCqQ==", + "dev": true, + "dependencies": { + "date-format": "4.0.3", + "lodash": "^4.17.21", + "make-dir": "^3.1.0", + "xmlbuilder": "^15.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/lambda-runtimes": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/lambda-runtimes/-/lambda-runtimes-2.0.5.tgz", + "integrity": "sha512-6BoLX9xuvr+B/f05MOhJnzRdF8Za5YYh82n45ndun9EU3uhJv9kIwnYrOrvuA7MoGwZgCMI7RUhBRzfw/l63SQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/lazy": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/lazy/-/lazy-1.0.11.tgz", + "integrity": "sha512-Y+CjUfLmIpoUCCRl0ub4smrYtGGr5AOa2AKOaWelGHOGz33X/Y/KizefGqbkwfz44+cnq/+9habclf8vOmu2LA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.2.0" + } + }, + "node_modules/level-codec": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/level-codec/-/level-codec-9.0.2.tgz", + "integrity": "sha512-UyIwNb1lJBChJnGfjmO0OR+ezh2iVu1Kas3nvBS/BzGnx79dv6g7unpKIDNPMhfdTEGoc7mC8uAu51XEtX+FHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer": "^5.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/level-concat-iterator": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/level-concat-iterator/-/level-concat-iterator-2.0.1.tgz", + "integrity": "sha512-OTKKOqeav2QWcERMJR7IS9CUo1sHnke2C0gkSmcR7QuEtFNLLzHQAvnMw8ykvEcv0Qtkg0p7FOwP1v9e5Smdcw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/level-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/level-errors/-/level-errors-2.0.1.tgz", + "integrity": "sha512-UVprBJXite4gPS+3VznfgDSU8PTRuVX0NXwoWW50KLxd2yw4Y1t2JUR5In1itQnudZqRMT9DlAM3Q//9NCjCFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "errno": "~0.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/level-iterator-stream": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/level-iterator-stream/-/level-iterator-stream-4.0.2.tgz", + "integrity": "sha512-ZSthfEqzGSOMWoUGhTXdX9jv26d32XJuHz/5YnuHZzH6wldfWMOVwI9TBtKcya4BKTyTt3XVA0A3cF3q5CY30Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.4", + "readable-stream": "^3.4.0", + "xtend": "^4.0.2" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/level-option-wrap": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/level-option-wrap/-/level-option-wrap-1.1.0.tgz", + "integrity": "sha512-gQouC22iCqHuBLNl4BHxEZUxLvUKALAtT/Q0c6ziOxZQ8c02G/gyxHWNbLbxUzRNfMrRnbt6TZT3gNe8VBqQeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "defined": "~0.0.0" + } + }, + "node_modules/level-supports": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/level-supports/-/level-supports-1.0.1.tgz", + "integrity": "sha512-rXM7GYnW8gsl1vedTJIbzOrRv85c/2uCMpiiCzO2fndd06U/kUXEEU9evYn4zFggBOg36IsBW8LzqIpETwwQzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "xtend": "^4.0.2" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/leveldown": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/leveldown/-/leveldown-5.6.0.tgz", + "integrity": "sha512-iB8O/7Db9lPaITU1aA2txU/cBEXAt4vWwKQRrrWuS6XDgbP4QZGj9BL2aNbwb002atoQ/lIotJkfyzz+ygQnUQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "abstract-leveldown": "~6.2.1", + "napi-macros": "~2.0.0", + "node-gyp-build": "~4.1.0" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/levelup": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/levelup/-/levelup-4.4.0.tgz", + "integrity": "sha512-94++VFO3qN95cM/d6eBXvd894oJE0w3cInq9USsyQzzoJxmiYzPAocNcuGCPGGjoXqDVJcr3C1jzt1TSjyaiLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "deferred-leveldown": "~5.3.0", + "level-errors": "~2.0.0", + "level-iterator-stream": "~4.0.0", + "level-supports": "~1.0.0", + "xtend": "~4.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "immediate": "~3.0.5" + } + }, + "node_modules/lie/node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "dependencies": { + "uc.micro": "^2.0.0" + } + }, + "node_modules/linkinator": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/linkinator/-/linkinator-6.1.2.tgz", + "integrity": "sha512-PndSrQe21Hf4sn2vZldEzJmD0EUJbIsEy4jcZLcHd6IZfQ6rC6iv+Fwo666TWM9DcXjbCrHpxnVX6xaGrcJ/eA==", + "dev": true, + "dependencies": { + "chalk": "^5.0.0", + "escape-html": "^1.0.3", + "gaxios": "^6.0.0", + "glob": "^10.3.10", + "htmlparser2": "^9.0.0", + "marked": "^13.0.0", + "meow": "^13.0.0", + "mime": "^4.0.0", + "server-destroy": "^1.0.1", + "srcset": "^5.0.0" + }, + "bin": { + "linkinator": "build/src/cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/linkinator/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "dev": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lock": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/lock/-/lock-1.1.0.tgz", + "integrity": "sha512-NZQIJJL5Rb9lMJ0Yl1JoVr9GSdo4HTPsUEWsSFzB8dE8DSoiLCVavWZPi7Rnlv/o73u6I24S/XYc/NmG4l8EKA==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "dev": true + }, + "node_modules/lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", + "dev": true + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/lodash.padend": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/lodash.padend/-/lodash.padend-4.6.1.tgz", + "integrity": "sha512-sOQs2aqGpbl27tmCS1QNZA09Uqp01ZzWfDUoD+xzTii0E7dSQfRKcRetFwa+uXaxaqL+TKm7CgD2JdKP7aZBSw==", + "dev": true + }, + "node_modules/lodash.zip": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.zip/-/lodash.zip-4.2.0.tgz", + "integrity": "sha512-C7IOaBBK/0gMORRBd8OETNx3kmOkgIWIPvyDpZSCTwUrpYmgZwJkjZeOD8ww4xbOUOs4/attY+pciKvadNfFbg==", + "dev": true + }, + "node_modules/longest-streak": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", + "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/lru-cache": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", + "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", + "engines": { + "node": "14 || >=16.14" + } + }, + "node_modules/ltgt": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ltgt/-/ltgt-2.2.1.tgz", + "integrity": "sha512-AI2r85+4MquTw9ZYqabu4nMwy9Oftlfa/e/52t9IjtfG+mGBbTNdAoZ3RQKLHR6r0wQnwZnPIEh/Ya6XTWAKNA==", + "dev": true, + "license": "MIT" + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/markdown-it": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.0.0.tgz", + "integrity": "sha512-seFjF0FIcPt4P9U39Bq1JYblX0KZCjDLFFQPHpL5AzHpqPEKtosxmdq/LTVZnjfH7tjt9BxStm+wXcDBNuYmzw==", + "dependencies": { + "argparse": "^2.0.1", + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.0.0" + }, + "bin": { + "markdown-it": "bin/markdown-it.mjs" + } + }, + "node_modules/markdown-it-anchor": { + "version": "8.6.7", + "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.7.tgz", + "integrity": "sha512-FlCHFwNnutLgVTflOYHPW2pPcl2AACqVzExlkGQNsi4CJgqOHN7YTgDd4LuhgN1BFO3TS0vLAruV1Td6dwWPJA==", + "peerDependencies": { + "@types/markdown-it": "*", + "markdown-it": "*" + } + }, + "node_modules/markdown-it-arc-static-img": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/markdown-it-arc-static-img/-/markdown-it-arc-static-img-2.1.0.tgz", + "integrity": "sha512-EYa/5n91d4hyHJmUhk60DQwDnlriwboZgiXJQTdmA79Llnnft1PguZp1kpzMZLJ54NyZvY7rHxrGKTUzDIpg5g==", + "license": "Apache-2.0", + "dependencies": { + "@architect/functions": "^5.0.2", + "markdown-it": "^12.3.2" + } + }, + "node_modules/markdown-it-arc-static-img/node_modules/@architect/functions": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/@architect/functions/-/functions-5.4.1.tgz", + "integrity": "sha512-F13FBUvVHjerUaSdnXIC3pZUnI10lxyVZ7HsrU8vM2qB5pFNqw5EgqcnVgORz/eqCcqevpIKayQ9yOUf/HuBAA==", + "license": "Apache-2.0", + "dependencies": { + "cookie": "^0.5.0", + "cookie-signature": "^1.2.0", + "csrf": "^3.1.0", + "node-webtokens": "^1.0.4", + "run-parallel": "^1.2.0", + "run-waterfall": "^1.1.7", + "uid-safe": "^2.1.5" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/markdown-it-arc-static-img/node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/markdown-it-arc-static-img/node_modules/entities": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", + "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", + "license": "BSD-2-Clause", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/markdown-it-arc-static-img/node_modules/linkify-it": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", + "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==", + "license": "MIT", + "dependencies": { + "uc.micro": "^1.0.1" + } + }, + "node_modules/markdown-it-arc-static-img/node_modules/markdown-it": { + "version": "12.3.2", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz", + "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==", + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1", + "entities": "~2.1.0", + "linkify-it": "^3.0.1", + "mdurl": "^1.0.1", + "uc.micro": "^1.0.5" + }, + "bin": { + "markdown-it": "bin/markdown-it.js" + } + }, + "node_modules/markdown-it-arc-static-img/node_modules/mdurl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", + "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", + "license": "MIT" + }, + "node_modules/markdown-it-arc-static-img/node_modules/uc.micro": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", + "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", + "license": "MIT" + }, + "node_modules/markdown-it-class": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/markdown-it-class/-/markdown-it-class-1.0.0.tgz", + "integrity": "sha512-CVDYqSgmErLAqInwWu8WmAR2nX6MMIBIt8LB6qg8DNldca9+aoC6ZyuY0lvBMsaTSHNFJRkcHVR1XjLw9nr9qQ==" + }, + "node_modules/markdown-it-external-anchor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/markdown-it-external-anchor/-/markdown-it-external-anchor-1.0.0.tgz", + "integrity": "sha512-d6Jb+kLciiv7uueM8q46k6szwr+B8QYg5ppiHVvfaS6hTx1KHpKwYDivmPAcNJYx0wUPEmRre9RgEnM5oHcdMw==" + }, + "node_modules/markdown-it-toc-done-right": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/markdown-it-toc-done-right/-/markdown-it-toc-done-right-4.2.0.tgz", + "integrity": "sha512-UB/IbzjWazwTlNAX0pvWNlJS8NKsOQ4syrXZQ/C72j+jirrsjVRT627lCaylrKJFBQWfRsPmIVQie8x38DEhAQ==" + }, + "node_modules/marked": { + "version": "13.0.3", + "resolved": "https://registry.npmjs.org/marked/-/marked-13.0.3.tgz", + "integrity": "sha512-rqRix3/TWzE9rIoFGIn8JmsVfhiuC8VIQ8IdX5TfzmeBucdY05/0UlzKaw0eVtpcN/OdVFpBk7CjKGo9iHJ/zA==", + "dev": true, + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mdast-util-from-markdown": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-1.3.1.tgz", + "integrity": "sha512-4xTO/M8c82qBcnQc1tgpNtubGUW/Y1tBQ1B0i5CtSoelOLKFYlElIr3bvgREYYO5iRqbMY1YuqZng0GVOI8Qww==", + "dev": true, + "dependencies": { + "@types/mdast": "^3.0.0", + "@types/unist": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "mdast-util-to-string": "^3.1.0", + "micromark": "^3.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-decode-string": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "unist-util-stringify-position": "^3.0.0", + "uvu": "^0.5.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-frontmatter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-frontmatter/-/mdast-util-frontmatter-1.0.1.tgz", + "integrity": "sha512-JjA2OjxRqAa8wEG8hloD0uTU0kdn8kbtOWpPP94NBkfAlbxn4S8gCGf/9DwFtEeGPXrDcNXdiDjVaRdUFqYokw==", + "dev": true, + "dependencies": { + "@types/mdast": "^3.0.0", + "mdast-util-to-markdown": "^1.3.0", + "micromark-extension-frontmatter": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-phrasing": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-3.0.1.tgz", + "integrity": "sha512-WmI1gTXUBJo4/ZmSk79Wcb2HcjPJBzM1nlI/OUWA8yk2X9ik3ffNbBGsU+09BFmXaL1IBb9fiuvq6/KMiNycSg==", + "dev": true, + "dependencies": { + "@types/mdast": "^3.0.0", + "unist-util-is": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-hast": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz", + "integrity": "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==", + "dev": true, + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-hast/node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "dev": true, + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/mdast-util-to-hast/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "dev": true + }, + "node_modules/mdast-util-to-hast/node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-to-hast/node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/mdast-util-to-hast/node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/mdast-util-to-hast/node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/mdast-util-to-hast/node_modules/micromark-util-types": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.1.tgz", + "integrity": "sha512-534m2WhVTddrcKVepwmVEVnUAmtrx9bfIjNoQHRqfnvdaHQiFytEhJoTgpWJvDEXCO5gLTQh3wYC1PgOJA4NSQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/mdast-util-to-hast/node_modules/unist-util-is": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", + "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-hast/node_modules/unist-util-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-hast/node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-hast/node_modules/unist-util-visit": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", + "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-hast/node_modules/unist-util-visit-parents": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", + "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-hast/node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-hast/node_modules/vfile-message": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz", + "integrity": "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-markdown": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-1.5.0.tgz", + "integrity": "sha512-bbv7TPv/WC49thZPg3jXuqzuvI45IL2EVAr/KxF0BSdHsU0ceFHOmwQn6evxAh1GaoK/6GQ1wp4R4oW2+LFL/A==", + "dev": true, + "dependencies": { + "@types/mdast": "^3.0.0", + "@types/unist": "^2.0.0", + "longest-streak": "^3.0.0", + "mdast-util-phrasing": "^3.0.0", + "mdast-util-to-string": "^3.0.0", + "micromark-util-decode-string": "^1.0.0", + "unist-util-visit": "^4.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-markdown/node_modules/unist-util-visit": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-4.1.2.tgz", + "integrity": "sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-is": "^5.0.0", + "unist-util-visit-parents": "^5.1.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-markdown/node_modules/unist-util-visit-parents": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-5.1.3.tgz", + "integrity": "sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-is": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-nlcst": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/mdast-util-to-nlcst/-/mdast-util-to-nlcst-5.2.1.tgz", + "integrity": "sha512-Xznpj85MsJnLQjBboajOovT2fAAvbbbmYutpFgzLi9pjZEOkgGzjq+t6fHcge8uzZ5uEkj5pigzw2QrnIVq/kw==", + "dev": true, + "dependencies": { + "@types/mdast": "^3.0.0", + "@types/nlcst": "^1.0.0", + "@types/unist": "^2.0.0", + "nlcst-to-string": "^3.0.0", + "unist-util-position": "^4.0.0", + "vfile": "^5.0.0", + "vfile-location": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-nlcst/node_modules/vfile": { + "version": "5.3.7", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-5.3.7.tgz", + "integrity": "sha512-r7qlzkgErKjobAmyNIkkSpizsFPYiUPuJb5pNW1RB4JcYVZhs4lIbVqk8XPk033CV/1z8ss5pkax8SuhGpcG8g==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.0", + "is-buffer": "^2.0.0", + "unist-util-stringify-position": "^3.0.0", + "vfile-message": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-nlcst/node_modules/vfile-message": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.4.tgz", + "integrity": "sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-stringify-position": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-string": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-3.2.0.tgz", + "integrity": "sha512-V4Zn/ncyN1QNSqSBxTrMOLpjr+IKdHl2v3KVLoWmDPscP4r9GcCi71gjgvUV1SFSKh92AjAG4peFuBl2/YgCJg==", + "dev": true, + "dependencies": { + "@types/mdast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==" + }, + "node_modules/memdown": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/memdown/-/memdown-5.1.0.tgz", + "integrity": "sha512-B3J+UizMRAlEArDjWHTMmadet+UKwHd3UjMgGBkZcKAxAYVPS9o0Yeiha4qvz7iGiL2Sb3igUft6p7nbFWctpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "abstract-leveldown": "~6.2.1", + "functional-red-black-tree": "~1.0.1", + "immediate": "~3.2.3", + "inherits": "~2.0.1", + "ltgt": "~2.2.0", + "safe-buffer": "~5.2.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/memdown/node_modules/immediate": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.2.3.tgz", + "integrity": "sha512-RrGCXRm/fRVqMIhqXrGEX9rRADavPiDFSoMb/k64i9XMk8uH4r/Omi5Ctierj6XzNecwDbO4WuFbDD1zmpl3Tg==", + "dev": true, + "license": "MIT" + }, + "node_modules/meow": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-13.1.0.tgz", + "integrity": "sha512-o5R/R3Tzxq0PJ3v3qcQJtSvSE9nKOLSAaDuuoMzDVuGTwHdccMWcYomh9Xolng2tjT6O/Y83d+0coVGof6tqmA==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromark": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-3.2.0.tgz", + "integrity": "sha512-uD66tJj54JLYq0De10AhWycZWGQNUvDI55xPgk2sQM5kn1JYlhbCMTtEeT27+vAhW2FBQxLlOmS3pmA7/2z4aA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "micromark-core-commonmark": "^1.0.1", + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-chunked": "^1.0.0", + "micromark-util-combine-extensions": "^1.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-encode": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-resolve-all": "^1.0.0", + "micromark-util-sanitize-uri": "^1.0.0", + "micromark-util-subtokenize": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.1", + "uvu": "^0.5.0" + } + }, + "node_modules/micromark-core-commonmark": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-1.1.0.tgz", + "integrity": "sha512-BgHO1aRbolh2hcrzL2d1La37V0Aoz73ymF8rAcKnohLy93titmv62E0gP8Hrx9PKcKrqCZ1BbLGbP3bEhoXYlw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-factory-destination": "^1.0.0", + "micromark-factory-label": "^1.0.0", + "micromark-factory-space": "^1.0.0", + "micromark-factory-title": "^1.0.0", + "micromark-factory-whitespace": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-chunked": "^1.0.0", + "micromark-util-classify-character": "^1.0.0", + "micromark-util-html-tag-name": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-resolve-all": "^1.0.0", + "micromark-util-subtokenize": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.1", + "uvu": "^0.5.0" + } + }, + "node_modules/micromark-extension-frontmatter": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/micromark-extension-frontmatter/-/micromark-extension-frontmatter-1.1.1.tgz", + "integrity": "sha512-m2UH9a7n3W8VAH9JO9y01APpPKmNNNs71P0RbknEmYSaZU5Ghogv38BYO94AI5Xw6OYfxZRdHZZ2nYjs/Z+SZQ==", + "dev": true, + "dependencies": { + "fault": "^2.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-factory-destination": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-1.1.0.tgz", + "integrity": "sha512-XaNDROBgx9SgSChd69pjiGKbV+nfHGDPVYFs5dOoDd7ZnMAE+Cuu91BCpsY8RT2NP9vo/B8pds2VQNCLiu0zhg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-factory-label": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-1.1.0.tgz", + "integrity": "sha512-OLtyez4vZo/1NjxGhcpDSbHQ+m0IIGnT8BoPamh+7jVlzLJBH98zzuCoUeMxvM6WsNeh8wx8cKvqLiPHEACn0w==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + } + }, + "node_modules/micromark-factory-space": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-1.1.0.tgz", + "integrity": "sha512-cRzEj7c0OL4Mw2v6nwzttyOZe8XY/Z8G0rzmWQZTBi/jjwyw/U4uqKtUORXQrR5bAZZnbTI/feRV/R7hc4jQYQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-factory-title": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-1.1.0.tgz", + "integrity": "sha512-J7n9R3vMmgjDOCY8NPw55jiyaQnH5kBdV2/UXCtZIpnHH3P6nHUKaH7XXEYuWwx/xUJcawa8plLBEjMPU24HzQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-factory-whitespace": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-1.1.0.tgz", + "integrity": "sha512-v2WlmiymVSp5oMg+1Q0N1Lxmt6pMhIHD457whWM7/GUlEks1hI9xj5w3zbc4uuMKXGisksZk8DzP2UyGbGqNsQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-util-character": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-1.2.0.tgz", + "integrity": "sha512-lXraTwcX3yH/vMDaFWCQJP1uIszLVebzUa3ZHdrgxr7KEU/9mL4mVgCpGbyhvNLNlauROiNUq7WN5u7ndbY6xg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-util-chunked": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-1.1.0.tgz", + "integrity": "sha512-Ye01HXpkZPNcV6FiyoW2fGZDUw4Yc7vT0E9Sad83+bEDiCJ1uXu0S3mr8WLpsz3HaG3x2q0HM6CTuPdcZcluFQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/micromark-util-classify-character": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-1.1.0.tgz", + "integrity": "sha512-SL0wLxtKSnklKSUplok1WQFoGhUdWYKggKUiqhX+Swala+BtptGCu5iPRc+xvzJ4PXE/hwM3FNXsfEVgoZsWbw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-util-combine-extensions": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-1.1.0.tgz", + "integrity": "sha512-Q20sp4mfNf9yEqDL50WwuWZHUrCO4fEyeDCnMGmG5Pr0Cz15Uo7KBs6jq+dq0EgX4DPwwrh9m0X+zPV1ypFvUA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-chunked": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-util-decode-numeric-character-reference": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-1.1.0.tgz", + "integrity": "sha512-m9V0ExGv0jB1OT21mrWcuf4QhP46pH1KkfWy9ZEezqHKAxkj4mPCy3nIH1rkbdMlChLHX531eOrymlwyZIf2iw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/micromark-util-decode-string": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-1.1.0.tgz", + "integrity": "sha512-YphLGCK8gM1tG1bd54azwyrQRjCFcmgj2S2GoJDNnh4vYtnL38JS8M4gpxzOPNyHdNEpheyWXCTnnTDY3N+NVQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-1.1.0.tgz", + "integrity": "sha512-EuEzTWSTAj9PA5GOAs992GzNh2dGQO52UvAbtSOMvXTxv3Criqb6IOzJUBCmEqrrXSblJIJBbFFv6zPxpreiJw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-html-tag-name": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-1.2.0.tgz", + "integrity": "sha512-VTQzcuQgFUD7yYztuQFKXT49KghjtETQ+Wv/zUjGSGBioZnkA4P1XXZPT1FHeJA6RwRXSF47yvJ1tsJdoxwO+Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-normalize-identifier": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-1.1.0.tgz", + "integrity": "sha512-N+w5vhqrBihhjdpM8+5Xsxy71QWqGn7HYNUvch71iV2PM7+E3uWGox1Qp90loa1ephtCxG2ftRV/Conitc6P2Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/micromark-util-resolve-all": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-1.1.0.tgz", + "integrity": "sha512-b/G6BTMSg+bX+xVCshPTPyAu2tmA0E4X98NSR7eIbeC6ycCqCeE7wjfDIgzEbkzdEVJXRtOG4FbEm/uGbCRouA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-1.2.0.tgz", + "integrity": "sha512-QO4GXv0XZfWey4pYFndLUKEAktKkG5kZTdUNaTAkzbuJxn2tNBOr+QtxR2XpWaMhbImT2dPzyLrPXLlPhph34A==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-encode": "^1.0.0", + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/micromark-util-subtokenize": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-1.1.0.tgz", + "integrity": "sha512-kUQHyzRoxvZO2PuLzMt2P/dwVsTiivCK8icYTeR+3WgbuPqfHgPPy7nFKbeqRivBvn/3N3GBiNC+JRTMSxEC7A==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-chunked": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-1.1.0.tgz", + "integrity": "sha512-uEjpEYY6KMs1g7QfJ2eX1SQEV+ZT4rUD3UcF6l57acZvLNK7PBZL+ty82Z1qhK1/yXIY4bdx04FKMgR0g4IAag==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-1.1.0.tgz", + "integrity": "sha512-ukRBgie8TIAcacscVHSiddHjO4k/q3pnedmzMQ4iwDcK0FtFCohKOlFbaOL/mPgfnPsL3C1ZyxJa4sbWrBl3jg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/mime/-/mime-4.0.1.tgz", + "integrity": "sha512-5lZ5tyrIfliMXzFtkYyekWbtRXObT9OWa8IwQ5uxTBDHucNNwniRqo0yInflj+iYi5CBa6qxadGzGarDfuEOxA==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa" + ], + "bin": { + "mime": "bin/cli.js" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/mock-property": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/mock-property/-/mock-property-1.1.0.tgz", + "integrity": "sha512-1/JjbLoGwv87xVsutkX0XJc0M0W4kb40cZl/K41xtTViBOD9JuFPKfyMNTrLJ/ivYAd0aPqu/vduamXO0emTFQ==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.4", + "functions-have-names": "^1.2.3", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2", + "hasown": "^2.0.2", + "isarray": "^2.0.5", + "object-inspect": "^1.13.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/nanoid": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-4.0.2.tgz", + "integrity": "sha512-7ZtY5KTCNheRGfEFxnedV5zFiORN1+Y1N6zvPTnHQd8ENUvfaDBeuJDZb2bN/oXwXxu3qkTXDzy57W5vAmDTBw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.js" + }, + "engines": { + "node": "^14 || ^16 || >=18" + } + }, + "node_modules/napi-macros": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/napi-macros/-/napi-macros-2.0.0.tgz", + "integrity": "sha512-A0xLykHtARfueITVDernsAWdtIMbOJgKgcluwENp3AlsKN/PloyO10HtmoqnFAQAcxPkgZN7wdfPfEd0zNGxbg==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/nlcst-affix-emoticon-modifier": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nlcst-affix-emoticon-modifier/-/nlcst-affix-emoticon-modifier-2.1.1.tgz", + "integrity": "sha512-0kOpSNwB6pFMoe5tWFZ3KrvW6ftVqvnXW+jQw3EJnXkzXdAmdhbcoG9r+NMvJ0nc37BLYlEy5A+FGlB8IOfq5A==", + "dev": true, + "dependencies": { + "@types/nlcst": "^1.0.0", + "@types/unist": "^2.0.0", + "nlcst-emoticon-modifier": "^2.0.0", + "unist-util-modify-children": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/nlcst-emoji-modifier": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/nlcst-emoji-modifier/-/nlcst-emoji-modifier-5.2.0.tgz", + "integrity": "sha512-bxgOEDWN2hz6/JN0uiIww+28Ssktq9FRctHq3bxiBi/8G/mb72cQ99vhzrZMWZe8tKD3YYckVH1bEkzcxTJFxg==", + "dev": true, + "dependencies": { + "@types/nlcst": "^1.0.0", + "emoji-regex": "^10.0.0", + "gemoji": "^8.0.0", + "nlcst-emoticon-modifier": "^2.0.0", + "nlcst-to-string": "^3.0.0", + "unist-util-generated": "^2.0.0", + "unist-util-position": "^4.0.0", + "unist-util-visit": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/nlcst-emoji-modifier/node_modules/gemoji": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/gemoji/-/gemoji-8.1.0.tgz", + "integrity": "sha512-HA4Gx59dw2+tn+UAa7XEV4ufUKI4fH1KgcbenVA9YKSj1QJTT0xh5Mwv5HMFNN3l2OtUe3ZIfuRwSyZS5pLIWw==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/nlcst-emoji-modifier/node_modules/unist-util-visit": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-4.1.2.tgz", + "integrity": "sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-is": "^5.0.0", + "unist-util-visit-parents": "^5.1.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/nlcst-emoji-modifier/node_modules/unist-util-visit-parents": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-5.1.3.tgz", + "integrity": "sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-is": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/nlcst-emoticon-modifier": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nlcst-emoticon-modifier/-/nlcst-emoticon-modifier-2.1.1.tgz", + "integrity": "sha512-fDfvvmA6ziUQC+I3BYLNZ7lq0rIG3Uz6IsmhTAkdB8d9UyI5LPz1uvmk+W7fKkX1mVWGJw0PeOT9VtXISrPFbg==", + "dev": true, + "dependencies": { + "@types/nlcst": "^1.0.0", + "@types/unist": "^2.0.0", + "emoticon": "^4.0.0", + "nlcst-to-string": "^3.0.0", + "unist-util-modify-children": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/nlcst-is-literal": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nlcst-is-literal/-/nlcst-is-literal-2.1.1.tgz", + "integrity": "sha512-/PyEKNHN+SrcrmnZRwszzZYbvZSN2AVD506+rfMUzyFHB0PtUmqZOdUuXmQxQeZXv6o29pT5chLjQJdC9weOCQ==", + "dev": true, + "dependencies": { + "@types/nlcst": "^1.0.0", + "@types/unist": "^2.0.0", + "nlcst-to-string": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/nlcst-to-string": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/nlcst-to-string/-/nlcst-to-string-3.1.1.tgz", + "integrity": "sha512-63mVyqaqt0cmn2VcI2aH6kxe1rLAmSROqHMA0i4qqg1tidkfExgpb0FGMikMCn86mw5dFtBtEANfmSSK7TjNHw==", + "dev": true, + "dependencies": { + "@types/nlcst": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dev": true, + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-gyp-build": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.1.1.tgz", + "integrity": "sha512-dSq1xmcPDKPZ2EED2S6zw/b9NKsqzXRE6dVr8TVQnI3FJOTteUMuqF3Qqs6LZg+mLGYJWqQzMbIjMtJqTv87nQ==", + "dev": true, + "license": "MIT", + "optional": true, + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/node-webtokens": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/node-webtokens/-/node-webtokens-1.0.4.tgz", + "integrity": "sha512-Sla56CeSLWvPbwud2kogqf5edQtKNXZBtXDDpmOzAgNZjwETbK/Am6PXfs54iZPLBm8K8amZ9XWaCQwGqZmKyQ==", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nspell": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/nspell/-/nspell-2.1.5.tgz", + "integrity": "sha512-PSStyugKMiD9mHmqI/CR5xXrSIGejUXPlo88FBRq5Og1kO5QwQ5Ilu8D8O5I/SHpoS+mibpw6uKA8rd3vXd2Sg==", + "dev": true, + "dependencies": { + "is-buffer": "^2.0.0" + } + }, + "node_modules/number-to-words": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/number-to-words/-/number-to-words-1.2.4.tgz", + "integrity": "sha512-/fYevVkXRcyBiZDg6yzZbm0RuaD6i0qRfn8yr+6D0KgBMOndFPxuW10qCHpzs50nN8qKuv78k8MuotZhcVX6Pw==", + "dev": true + }, + "node_modules/object-inspect": { + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", + "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-is": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", + "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.groupby": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", + "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.values": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", + "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "dev": true, + "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true, + "license": "(MIT AND Zlib)" + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "dev": true, + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/parse-latin": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/parse-latin/-/parse-latin-5.0.1.tgz", + "integrity": "sha512-b/K8ExXaWC9t34kKeDV8kGXBkXZ1HCSAZRYE7HR14eA1GlXX5L8iWhs8USJNhQU9q5ci413jCKF0gOyovvyRBg==", + "dev": true, + "dependencies": { + "nlcst-to-string": "^3.0.0", + "unist-util-modify-children": "^3.0.0", + "unist-util-visit-children": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/parse5": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", + "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", + "dependencies": { + "entities": "^4.4.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-sort": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/path-sort/-/path-sort-0.1.0.tgz", + "integrity": "sha512-70MSq7edKtbODYKkqXYzSMQxtYMjDgP3K6D15Fu4KUvpyBPlxDWPvv8JI9GjNDF2K5baPHFEtlg818dOmf2ifg==" + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pkg-dir": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-6.0.1.tgz", + "integrity": "sha512-C9R+PTCKGA32HG0n5I4JMYkdLL58ZpayVuncQHQrGeKa8o26A4o2x0u6BKekHG+Au0jv5ZW7Xfq1Cj6lm9Ag4w==", + "dev": true, + "dependencies": { + "find-up": "^6.1.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/find-up": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz", + "integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==", + "dev": true, + "dependencies": { + "locate-path": "^7.1.0", + "path-exists": "^5.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", + "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", + "dev": true, + "dependencies": { + "p-locate": "^6.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", + "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^1.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", + "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", + "dev": true, + "dependencies": { + "p-limit": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/path-exists": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", + "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/pkg-dir/node_modules/yocto-queue": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", + "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", + "dev": true, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", + "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true, + "license": "MIT" + }, + "node_modules/property-information": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz", + "integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/proto-list": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", + "dev": true, + "license": "ISC" + }, + "node_modules/prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", + "dev": true, + "license": "MIT" + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/pupa": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz", + "integrity": "sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-goat": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/quotation": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/quotation/-/quotation-2.0.3.tgz", + "integrity": "sha512-yEc24TEgCFLXx7D4JHJJkK4JFVtatO8fziwUxY4nB/Jbea9o9CVS3gt22mA0W7rPYAGW2fWzYDSOtD94PwOyqA==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/random-bytes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", + "integrity": "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/rc/node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true, + "license": "ISC" + }, + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/reachdown": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reachdown/-/reachdown-1.1.0.tgz", + "integrity": "sha512-6LsdRe4cZyOjw4NnvbhUd/rGG7WQ9HMopPr+kyL018Uci4kijtxcGR5kVb5Ln13k4PEE+fEFQbjfOvNw7cnXmA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/reduce-flatten": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/reduce-flatten/-/reduce-flatten-1.0.1.tgz", + "integrity": "sha512-j5WfFJfc9CoXv/WbwVLHq74i/hdTUpy+iNC534LxczMRP67vJeK3V9JOdnL0N1cIRbn9mYhE2yVjvvKXDxvNXQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/reflect.getprototypeof": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.9.tgz", + "integrity": "sha512-r0Ay04Snci87djAsI4U+WNRcSw5S4pOH7qFjd/veA5gC7TbqESR3tcj28ia95L/fYUDw11JKP7uqUKUAfVvV5Q==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "dunder-proto": "^1.0.1", + "es-abstract": "^1.23.6", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "gopd": "^1.2.0", + "which-builtin-type": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.3.tgz", + "integrity": "sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/registry-auth-token": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-5.0.2.tgz", + "integrity": "sha512-o/3ikDxtXaA59BmZuZrJZDJv8NMDGSj+6j6XaeBmHw8eY1i1qd9+6H+LjVvQXx3HN6aRCGa1cUdJ9RaJZUugnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@pnpm/npm-conf": "^2.1.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/registry-url": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz", + "integrity": "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==", + "dev": true, + "license": "MIT", + "dependencies": { + "rc": "^1.2.8" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/rehype": { + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/rehype/-/rehype-13.0.2.tgz", + "integrity": "sha512-j31mdaRFrwFRUIlxGeuPXXKWQxet52RBQRvCmzl5eCefn/KGbomK5GMHNMsOJf55fgo3qw5tST5neDuarDYR2A==", + "dev": true, + "dependencies": { + "@types/hast": "^3.0.0", + "rehype-parse": "^9.0.0", + "rehype-stringify": "^10.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-parse": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/rehype-parse/-/rehype-parse-9.0.1.tgz", + "integrity": "sha512-ksCzCD0Fgfh7trPDxr2rSylbwq9iYDkSn8TCDmEJ49ljEUBxDVCzCHv7QNzZOfODanX4+bWQ4WZqLCRWYLfhag==", + "dev": true, + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-from-html": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-parse/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "dev": true + }, + "node_modules/rehype-parse/node_modules/unified": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", + "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0", + "bail": "^2.0.0", + "devlop": "^1.0.0", + "extend": "^3.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-parse/node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-parse/node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-parse/node_modules/vfile-message": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz", + "integrity": "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-retext": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/rehype-retext/-/rehype-retext-5.0.0.tgz", + "integrity": "sha512-amCBMrCItZzu2tLIfL+m0S8mE6+nwD+H88hesbxCgZ9AdMZ42aGPqunMh+bbkb/v1kY3bwppSnfPMDmxIz42Nw==", + "dev": true, + "dependencies": { + "@types/hast": "^3.0.0", + "@types/nlcst": "^2.0.0", + "hast-util-to-nlcst": "^4.0.0", + "parse-latin": "^7.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-retext/node_modules/@types/nlcst": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/nlcst/-/nlcst-2.0.3.tgz", + "integrity": "sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA==", + "dev": true, + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/rehype-retext/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "dev": true + }, + "node_modules/rehype-retext/node_modules/nlcst-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/nlcst-to-string/-/nlcst-to-string-4.0.0.tgz", + "integrity": "sha512-YKLBCcUYKAg0FNlOBT6aI91qFmSiFKiluk655WzPF+DDMA02qIyy8uiRqI8QXtcFpEvll12LpL5MXqEmAZ+dcA==", + "dev": true, + "dependencies": { + "@types/nlcst": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-retext/node_modules/parse-latin": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/parse-latin/-/parse-latin-7.0.0.tgz", + "integrity": "sha512-mhHgobPPua5kZ98EF4HWiH167JWBfl4pvAIXXdbaVohtK7a6YBOy56kvhCqduqyo/f3yrHFWmqmiMg/BkBkYYQ==", + "dev": true, + "dependencies": { + "@types/nlcst": "^2.0.0", + "@types/unist": "^3.0.0", + "nlcst-to-string": "^4.0.0", + "unist-util-modify-children": "^4.0.0", + "unist-util-visit-children": "^3.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/rehype-retext/node_modules/unified": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", + "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0", + "bail": "^2.0.0", + "devlop": "^1.0.0", + "extend": "^3.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-retext/node_modules/unist-util-modify-children": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-modify-children/-/unist-util-modify-children-4.0.0.tgz", + "integrity": "sha512-+tdN5fGNddvsQdIzUF3Xx82CU9sMM+fA0dLgR9vOmT0oPT2jH+P1nd5lSqfCfXAw+93NhcXNY2qqvTUtE4cQkw==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0", + "array-iterate": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-retext/node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-retext/node_modules/unist-util-visit-children": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unist-util-visit-children/-/unist-util-visit-children-3.0.0.tgz", + "integrity": "sha512-RgmdTfSBOg04sdPcpTSD1jzoNBjt9a80/ZCzp5cI9n1qPzLZWF9YdvWGN2zmTumP1HWhXKdUWexjy/Wy/lJ7tA==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-retext/node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-retext/node_modules/vfile-message": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz", + "integrity": "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-stringify": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/rehype-stringify/-/rehype-stringify-10.0.1.tgz", + "integrity": "sha512-k9ecfXHmIPuFVI61B9DeLPN0qFHfawM6RsuX48hoqlaKSF61RskNjSm1lI8PhBEM0MRdLxVVm4WmTqJQccH9mA==", + "dev": true, + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-to-html": "^9.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-stringify/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "dev": true + }, + "node_modules/rehype-stringify/node_modules/unified": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", + "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0", + "bail": "^2.0.0", + "devlop": "^1.0.0", + "extend": "^3.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-stringify/node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-stringify/node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-stringify/node_modules/vfile-message": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz", + "integrity": "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "dev": true + }, + "node_modules/rehype/node_modules/unified": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", + "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0", + "bail": "^2.0.0", + "devlop": "^1.0.0", + "extend": "^3.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype/node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype/node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype/node_modules/vfile-message": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz", + "integrity": "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark": { + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/remark/-/remark-14.0.3.tgz", + "integrity": "sha512-bfmJW1dmR2LvaMJuAnE88pZP9DktIFYXazkTfOIKZzi3Knk9lT0roItIA24ydOucI3bV/g/tXBA6hzqq3FV9Ew==", + "dev": true, + "dependencies": { + "@types/mdast": "^3.0.0", + "remark-parse": "^10.0.0", + "remark-stringify": "^10.0.0", + "unified": "^10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-frontmatter": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/remark-frontmatter/-/remark-frontmatter-4.0.1.tgz", + "integrity": "sha512-38fJrB0KnmD3E33a5jZC/5+gGAC2WKNiPw1/fdXJvijBlhA7RCsvJklrYJakS0HedninvaCYW8lQGf9C918GfA==", + "dev": true, + "dependencies": { + "@types/mdast": "^3.0.0", + "mdast-util-frontmatter": "^1.0.0", + "micromark-extension-frontmatter": "^1.0.0", + "unified": "^10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-parse": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-10.0.2.tgz", + "integrity": "sha512-3ydxgHa/ZQzG8LvC7jTXccARYDcRld3VfcgIIFs7bI6vbRSxJJmzgLEIIoYKyrfhaY+ujuWaf/PJiMZXoiCXgw==", + "dev": true, + "dependencies": { + "@types/mdast": "^3.0.0", + "mdast-util-from-markdown": "^1.0.0", + "unified": "^10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-retext": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/remark-retext/-/remark-retext-5.0.1.tgz", + "integrity": "sha512-h3kOjKNy7oJfohqXlKp+W4YDigHD3rw01x91qvQP/cUkK5nJrDl6yEYwTujQCAXSLZrsBxywlK3ntzIX6c29aA==", + "dev": true, + "dependencies": { + "@types/mdast": "^3.0.0", + "@types/unist": "^2.0.0", + "mdast-util-to-nlcst": "^5.0.0", + "unified": "^10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-stringify": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-10.0.3.tgz", + "integrity": "sha512-koyOzCMYoUHudypbj4XpnAKFbkddRMYZHwghnxd7ue5210WzGw6kOBwauJTRUMq16jsovXx8dYNvSSWP89kZ3A==", + "dev": true, + "dependencies": { + "@types/mdast": "^3.0.0", + "mdast-util-to-markdown": "^1.0.0", + "unified": "^10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/replace-ext": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", + "integrity": "sha512-vuNYXC7gG7IeVNBC1xUllqCcZKRbJoSPOBhnTEcAIiKCsbuef6zO3F0Rve3isPMMoNoQRWjQwbAgAjHUHniyEA==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/req-all": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/req-all/-/req-all-0.1.0.tgz", + "integrity": "sha512-ZdvPr8uXy9ujX3KujwE2P1HWkMYgogIhqeAeyb47MqWjSfyxERSm0TNbN/IapCCmWDufXab04AYrRgObaJCJ6Q==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "dev": true, + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/restore-cursor/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + }, + "node_modules/retext": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/retext/-/retext-8.1.0.tgz", + "integrity": "sha512-N9/Kq7YTn6ZpzfiGW45WfEGJqFf1IM1q8OsRa1CGzIebCJBNCANDRmOrholiDRGKo/We7ofKR4SEvcGAWEMD3Q==", + "dev": true, + "dependencies": { + "@types/nlcst": "^1.0.0", + "retext-latin": "^3.0.0", + "retext-stringify": "^3.0.0", + "unified": "^10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/retext-emoji": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/retext-emoji/-/retext-emoji-8.1.0.tgz", + "integrity": "sha512-cvT53Ttn6XXgNprhmI8p4qtwujxNwoHW8hHe2B0JLEMxrMzGTbxwKqnwGslLk/FDsH4dSYStmqnpE20auNSrNg==", + "dev": true, + "dependencies": { + "@types/nlcst": "^1.0.0", + "emoticon": "^4.0.0", + "gemoji": "^7.0.0", + "nlcst-affix-emoticon-modifier": "^2.0.0", + "nlcst-emoji-modifier": "^5.0.0", + "nlcst-emoticon-modifier": "^2.0.0", + "nlcst-to-string": "^3.0.0", + "unified": "^10.0.0", + "unist-util-visit": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/retext-emoji/node_modules/unist-util-visit": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-4.1.2.tgz", + "integrity": "sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-is": "^5.0.0", + "unist-util-visit-parents": "^5.1.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/retext-emoji/node_modules/unist-util-visit-parents": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-5.1.3.tgz", + "integrity": "sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-is": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/retext-indefinite-article": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/retext-indefinite-article/-/retext-indefinite-article-4.3.0.tgz", + "integrity": "sha512-NdVOT0pR68ZhC/Eph/eg2z92zwC9H3DzXkEaiaIs0aDAnNBVzJT/DeRFYDTEMDWINnEVTApuGoCl9mqciUUsAw==", + "dev": true, + "dependencies": { + "@types/nlcst": "^1.0.0", + "format": "^0.2.0", + "nlcst-to-string": "^3.0.0", + "number-to-words": "^1.0.0", + "unified": "^10.0.0", + "unist-util-visit": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/retext-indefinite-article/node_modules/unist-util-visit": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-4.1.2.tgz", + "integrity": "sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-is": "^5.0.0", + "unist-util-visit-parents": "^5.1.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/retext-indefinite-article/node_modules/unist-util-visit-parents": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-5.1.3.tgz", + "integrity": "sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-is": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/retext-latin": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/retext-latin/-/retext-latin-3.1.0.tgz", + "integrity": "sha512-5MrD1tuebzO8ppsja5eEu+ZbBeUNCjoEarn70tkXOS7Bdsdf6tNahsv2bY0Z8VooFF6cw7/6S+d3yI/TMlMVVQ==", + "dev": true, + "dependencies": { + "@types/nlcst": "^1.0.0", + "parse-latin": "^5.0.0", + "unherit": "^3.0.0", + "unified": "^10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/retext-repeated-words": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/retext-repeated-words/-/retext-repeated-words-4.2.0.tgz", + "integrity": "sha512-Tle40/5Xy6KxI94s4XRqGXcj6aWUeCoQZUGQto8OjZP98t4tKVDRVG3QGodF633hVOEiN1vYqj0Zegqoe8XOaw==", + "dev": true, + "dependencies": { + "@types/nlcst": "^1.0.0", + "nlcst-to-string": "^3.0.0", + "unified": "^10.0.0", + "unist-util-position": "^4.0.0", + "unist-util-visit": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/retext-repeated-words/node_modules/unist-util-visit": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-4.1.2.tgz", + "integrity": "sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-is": "^5.0.0", + "unist-util-visit-parents": "^5.1.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/retext-repeated-words/node_modules/unist-util-visit-parents": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-5.1.3.tgz", + "integrity": "sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-is": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/retext-spell": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/retext-spell/-/retext-spell-5.3.0.tgz", + "integrity": "sha512-b4LLp5S7ScmE+qJ2gu7FKfJICcfIK/UYIn1L84gJNRjDJyVIXWgdqQ7kgoqduP1mH3fFiKz3YVyNS4hD3XTlWw==", + "dev": true, + "dependencies": { + "@types/nlcst": "^1.0.0", + "nlcst-is-literal": "^2.0.0", + "nlcst-to-string": "^3.0.0", + "nspell": "^2.0.0", + "quotation": "^2.0.0", + "unified": "^10.0.0", + "unist-util-visit": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/retext-spell/node_modules/unist-util-visit": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-4.1.2.tgz", + "integrity": "sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-is": "^5.0.0", + "unist-util-visit-parents": "^5.1.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/retext-spell/node_modules/unist-util-visit-parents": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-5.1.3.tgz", + "integrity": "sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-is": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/retext-stringify": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/retext-stringify/-/retext-stringify-3.1.0.tgz", + "integrity": "sha512-767TLOaoXFXyOnjx/EggXlb37ZD2u4P1n0GJqVdpipqACsQP+20W+BNpMYrlJkq7hxffnFk+jc6mAK9qrbuB8w==", + "dev": true, + "dependencies": { + "@types/nlcst": "^1.0.0", + "nlcst-to-string": "^3.0.0", + "unified": "^10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/retext-syntax-mentions": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/retext-syntax-mentions/-/retext-syntax-mentions-3.1.0.tgz", + "integrity": "sha512-xcHHHOm3coErzrxtXN3S75UJ1S+WUJXKes5Mpu8WyVmMMYVC5qjSoBlYLHx1+jztq+KmYYkG3rbM3it2J4y7jA==", + "dev": true, + "dependencies": { + "@types/nlcst": "^1.0.0", + "nlcst-to-string": "^3.0.0", + "unified": "^10.0.0", + "unist-util-position": "^4.0.0", + "unist-util-visit": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/retext-syntax-mentions/node_modules/unist-util-visit": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-4.1.2.tgz", + "integrity": "sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-is": "^5.0.0", + "unist-util-visit-parents": "^5.1.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/retext-syntax-mentions/node_modules/unist-util-visit-parents": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-5.1.3.tgz", + "integrity": "sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-is": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/retext-syntax-urls": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/retext-syntax-urls/-/retext-syntax-urls-3.1.2.tgz", + "integrity": "sha512-CFuqX1x7GPFQRMPTA88bjrD0l8jTN7Y5Zp8QVr9ToWyJRChIMqUlPq4HfOJbKdDfcwhTVZvh/jRyxLc/K6Tc4g==", + "dev": true, + "dependencies": { + "@types/nlcst": "^1.0.0", + "@types/unist": "^2.0.0", + "ccount": "^2.0.0", + "nlcst-to-string": "^3.0.0", + "unified": "^10.0.0", + "unist-util-modify-children": "^3.0.0", + "unist-util-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rndm": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/rndm/-/rndm-1.2.0.tgz", + "integrity": "sha512-fJhQQI5tLrQvYIYFpOnFinzv9dwmR7hRnUz1XqP3OJ1jIweTNOd6aTO4jwQSgcBSFUB+/KHJxuGneime+FdzOw==" + }, + "node_modules/router": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/router/-/router-1.3.8.tgz", + "integrity": "sha512-461UFH44NtSfIlS83PUg2N7OZo86BC/kB3dY77gJdsODsBhhw7+2uE0tzTINxrY9CahCUVk1VhpWCA5i1yoIEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-flatten": "3.0.0", + "debug": "2.6.9", + "methods": "~1.1.2", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "setprototypeof": "1.2.0", + "utils-merge": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/router/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/router/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/run-series": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/run-series/-/run-series-1.1.9.tgz", + "integrity": "sha512-Arc4hUN896vjkqCYrUXquBFtRZdv1PfLbTYP71efP6butxyQ0kWpiNJyAgsxscmQg1cqvHY32/UCBzXedTpU2g==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/run-waterfall": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/run-waterfall/-/run-waterfall-1.1.7.tgz", + "integrity": "sha512-iFPgh7SatHXOG1ClcpdwHI63geV3Hc/iL6crGSyBlH2PY7Rm/za+zoKz6FfY/Qlw5K7JwSol8pseO8fN6CMhhQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/sade": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", + "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", + "dev": true, + "dependencies": { + "mri": "^1.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/safe-array-concat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", + "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/section-matter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz", + "integrity": "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==", + "dependencies": { + "extend-shallow": "^2.0.1", + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver-diff": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz", + "integrity": "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/semver-diff/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/send/node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/server-destroy": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/server-destroy/-/server-destroy-1.0.1.tgz", + "integrity": "sha512-rb+9B5YBIEzYcD6x2VKidaa+cqYBJQKnU4oe4E3ANwRRN56yk/ua1YCJT1n21NTS8w6CcOclAKNP3PhdCXKYtQ==", + "dev": true + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "dev": true, + "license": "MIT" + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true, + "license": "ISC" + }, + "node_modules/sha": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/sha/-/sha-3.0.0.tgz", + "integrity": "sha512-DOYnM37cNsLNSGIG/zZWch5CKIRNoLdYUQTQlcgkRkoYIUwDYjqDyye16YcDZg/OPdcbUgTKMjc4SY6TB7ZAPw==", + "dependencies": { + "graceful-fs": "^4.1.2" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/slugify": { + "version": "1.6.6", + "resolved": "https://registry.npmjs.org/slugify/-/slugify-1.6.6.tgz", + "integrity": "sha512-h+z7HKHYXj6wJU+AnS/+IH8Uh9fdcX1Lrhg1/VMdf9PwoBQXFcXiAdsy2tSK0P6gKwJLXp02r90ahUCqHk9rrw==", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-resolve": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.6.0.tgz", + "integrity": "sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w==", + "deprecated": "See https://github.com/lydell/source-map-resolve#deprecated", + "dependencies": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0" + } + }, + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/spellchecker-cli": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/spellchecker-cli/-/spellchecker-cli-7.0.0.tgz", + "integrity": "sha512-v6ArsUnURxEVld9ZiLOAYZkT2NWeIMSWnO5j81hdBrMDAjPIZrxtz/GR6+uwxNA/OrG53FFJyAgLYD7Vg0dWNw==", + "dev": true, + "dependencies": { + "chalk": "^2.4.2", + "command-line-args": "^5.1.1", + "command-line-usage": "^5.0.4", + "dictionary-en": "^3", + "dictionary-en-au": "^2.3.0", + "dictionary-en-ca": "^2.3.0", + "dictionary-en-gb": "^2.3.0", + "dictionary-en-za": "^2.0.4", + "dictionary-vi": "^2.1.3", + "fs-extra": "^6.0.1", + "globby": "^11.0.0", + "js-yaml": "^3.14.1", + "jsonc": "^2.0.0", + "junit-report-builder": "^3.0.1", + "lodash": "^4.17.21", + "pkg-dir": "^6.0.1", + "rehype": "^13.0.2", + "rehype-retext": "^5.0.0", + "remark": "^14.0.0", + "remark-frontmatter": "^4.0.0", + "remark-retext": "^5.0.1", + "retext": "^8.1.0", + "retext-emoji": "^8.1.0", + "retext-indefinite-article": "^4.1.0", + "retext-repeated-words": "^4.2.0", + "retext-spell": "^5.1.0", + "retext-syntax-mentions": "^3.1.0", + "retext-syntax-urls": "^3.1.2", + "toml": "^3.0.0", + "unist-util-visit": "^1.4.1", + "vfile": "^3.0.1", + "vfile-reporter": "^6.0.0" + }, + "bin": { + "spellchecker": "build/index.js" + } + }, + "node_modules/spellchecker-cli/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/spellchecker-cli/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/spellchecker-cli/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/spellchecker-cli/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/spellchecker-cli/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/spellchecker-cli/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/spellchecker-cli/node_modules/fs-extra": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-6.0.1.tgz", + "integrity": "sha512-GnyIkKhhzXZUWFCaJzvyDLEEgDkPfb4/TPvJCJVuS8MWZgoSsErf++QpiAlDnKFcqhRlm+tIOcencCjyJE6ZCA==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "node_modules/spellchecker-cli/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/spellchecker-cli/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/spellchecker-cli/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/spellchecker-cli/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/spellchecker-cli/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" + }, + "node_modules/srcset": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/srcset/-/srcset-5.0.1.tgz", + "integrity": "sha512-/P1UYbGfJVlxZag7aABNRrulEXAwCSDo7fklafOQrantuPTDmYgijJMks2zusPCVzgW9+4P69mq7w6pYuZpgxw==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/stop-iteration-iterator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz", + "integrity": "sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==", + "dev": true, + "dependencies": { + "internal-slot": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/strftime": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/strftime/-/strftime-0.10.3.tgz", + "integrity": "sha512-DZrDUeIF73eKJ4/GgGuv8UHWcUQPYDYfDeQFj3jrx+JZl6GQE656MbHIpvbo4mEG9a5DgS8GRCc5DxJXD2udDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.2.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/string-width/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/stringify-entities": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", + "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", + "dev": true, + "dependencies": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom-string": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz", + "integrity": "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/subleveldown": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/subleveldown/-/subleveldown-5.0.1.tgz", + "integrity": "sha512-cVqd/URpp7si1HWu5YqQ3vqQkjuolAwHypY1B4itPlS71/lsf6TQPZ2Y0ijT22EYVkvH5ove9JFJf4u7VGPuZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "abstract-leveldown": "^6.3.0", + "encoding-down": "^6.2.0", + "inherits": "^2.0.3", + "level-option-wrap": "^1.1.0", + "levelup": "^4.4.0", + "reachdown": "^1.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/subleveldown/node_modules/abstract-leveldown": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-6.3.0.tgz", + "integrity": "sha512-TU5nlYgta8YrBMNpc9FwQzRbiXsj49gsALsXadbGHt9CROPzX5fB0rWDR5mtdpOOKa5XqRFpbj1QroPAoPzVjQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "immediate": "^3.2.3", + "level-concat-iterator": "~2.0.0", + "level-supports": "~1.0.0", + "xtend": "~4.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/symlink-or-copy": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/symlink-or-copy/-/symlink-or-copy-1.3.1.tgz", + "integrity": "sha512-0K91MEXFpBUaywiwSSkmKjnGcasG/rVBXFLJz5DrgGabpYD6N+3yZrfD6uUIfpuTu65DZLHi7N8CizHc07BPZA==", + "dev": true, + "license": "MIT" + }, + "node_modules/table-layout": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-0.4.5.tgz", + "integrity": "sha512-zTvf0mcggrGeTe/2jJ6ECkJHAQPIYEwDoqsiqBjI24mvRmQbInK5jq33fyypaCBxX08hMkfmdOqj6haT33EqWw==", + "dev": true, + "dependencies": { + "array-back": "^2.0.0", + "deep-extend": "~0.6.0", + "lodash.padend": "^4.6.1", + "typical": "^2.6.1", + "wordwrapjs": "^3.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/table-layout/node_modules/array-back": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-2.0.0.tgz", + "integrity": "sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw==", + "dev": true, + "dependencies": { + "typical": "^2.6.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/table-layout/node_modules/typical": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/typical/-/typical-2.6.1.tgz", + "integrity": "sha512-ofhi8kjIje6npGozTip9Fr8iecmYfEbS06i0JnIg+rh51KakryWF4+jX8lLKZVhy6N+ID45WYSFCxPOdTWCzNg==", + "dev": true + }, + "node_modules/tap-arc": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/tap-arc/-/tap-arc-1.3.2.tgz", + "integrity": "sha512-JjHVZ4v+8tdA+t4rKvT7qBTa3CXyFjAfpOqjyZLTBR17JloVRdJP0jh7tctf93VxpUbrTgi4zybjlBL1oAdNDQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "chalk": "^5.3.0", + "diff": "^5.2.0", + "json5": "^2.2.3", + "minimist": "^1.2.8", + "strip-ansi": "^7.1.0", + "tap-reader": "^0.2.1" + }, + "bin": { + "tap-arc": "src/index.js" + } + }, + "node_modules/tap-arc/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/tap-arc/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "dev": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/tap-arc/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/tap-reader": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tap-reader/-/tap-reader-0.2.1.tgz", + "integrity": "sha512-X12Rlo1/eKxexylBs2tASjtXUXCgHL2tKfPM0LeJLku3Kf93DXw6eOheYmAEk+0RB9K5ZfemEC75Tc7w7zxlqA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tap-reader": "src/index.js" + } + }, + "node_modules/tape": { + "version": "5.9.0", + "resolved": "https://registry.npmjs.org/tape/-/tape-5.9.0.tgz", + "integrity": "sha512-czbGgxSVwRlbB3Ly/aqQrNwrDAzKHDW/kVXegp4hSFmR2c8qqm3hCgZbUy1+3QAQFGhPDG7J56UsV1uNilBFCA==", + "dev": true, + "dependencies": { + "@ljharb/resumer": "^0.1.3", + "@ljharb/through": "^2.3.13", + "array.prototype.every": "^1.1.6", + "call-bind": "^1.0.7", + "deep-equal": "^2.2.3", + "defined": "^1.0.1", + "dotignore": "^0.1.2", + "for-each": "^0.3.3", + "get-package-type": "^0.1.0", + "glob": "^7.2.3", + "has-dynamic-import": "^2.1.0", + "hasown": "^2.0.2", + "inherits": "^2.0.4", + "is-regex": "^1.1.4", + "minimist": "^1.2.8", + "mock-property": "^1.1.0", + "object-inspect": "^1.13.2", + "object-is": "^1.1.6", + "object-keys": "^1.1.1", + "object.assign": "^4.1.5", + "resolve": "^2.0.0-next.5", + "string.prototype.trim": "^1.2.9" + }, + "bin": { + "tape": "bin/tape" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tape/node_modules/defined": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.1.tgz", + "integrity": "sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tape/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/tape/node_modules/resolve": { + "version": "2.0.0-next.5", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", + "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", + "dev": true, + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tiny-each-async": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tiny-each-async/-/tiny-each-async-2.0.3.tgz", + "integrity": "sha512-5ROII7nElnAirvFn8g7H7MtpfV1daMcyfTGQwsn/x2VtyV+VPiO5CjReCJtWLvoKTDEDmZocf3cNPraiMnBXLA==", + "dev": true, + "license": "MIT" + }, + "node_modules/tiny-json-http": { + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/tiny-json-http/-/tiny-json-http-7.5.1.tgz", + "integrity": "sha512-lB7qkBGpL3HR/8gidBu3MMfgfnDj2mlvK/eYXgSbO06gKphemLKGp/TgRTy/BKVD7nCbgIeCm41lMNayXO1f2w==", + "dev": true + }, + "node_modules/tmp": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", + "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.14" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/toml": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/toml/-/toml-3.0.0.tgz", + "integrity": "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==", + "dev": true + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "license": "MIT", + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/trough": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", + "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/tsconfig-paths": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", + "dev": true, + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/tsconfig-paths/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/tsconfig-paths/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/tsscmp": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.6.tgz", + "integrity": "sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==", + "engines": { + "node": ">=0.6.x" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-typedarray": "^1.0.0" + } + }, + "node_modules/typical": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/typical/-/typical-4.0.0.tgz", + "integrity": "sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/uc.micro": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.0.0.tgz", + "integrity": "sha512-DffL94LsNOccVn4hyfRe5rdKa273swqeA5DJpMOeFmEn1wCDc7nAbbB0gXlgBCL7TNzeTv6G7XVWzan7iJtfig==" + }, + "node_modules/uid-safe": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", + "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", + "dependencies": { + "random-bytes": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/unbox-primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "has-bigints": "^1.0.2", + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/unherit": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/unherit/-/unherit-3.0.1.tgz", + "integrity": "sha512-akOOQ/Yln8a2sgcLj4U0Jmx0R5jpIg2IUyRrWOzmEbjBtGzBdHtSeFKgoEcoH4KYIG/Pb8GQ/BwtYm0GCq1Sqg==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/unified": { + "version": "10.1.2", + "resolved": "https://registry.npmjs.org/unified/-/unified-10.1.2.tgz", + "integrity": "sha512-pUSWAi/RAnVy1Pif2kAoeWNBa3JVrx0MId2LASj8G+7AiHWoKZNTomq6LG326T68U7/e263X6fTdcXIy7XnF7Q==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.0", + "bail": "^2.0.0", + "extend": "^3.0.0", + "is-buffer": "^2.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unified/node_modules/vfile": { + "version": "5.3.7", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-5.3.7.tgz", + "integrity": "sha512-r7qlzkgErKjobAmyNIkkSpizsFPYiUPuJb5pNW1RB4JcYVZhs4lIbVqk8XPk033CV/1z8ss5pkax8SuhGpcG8g==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.0", + "is-buffer": "^2.0.0", + "unist-util-stringify-position": "^3.0.0", + "vfile-message": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unified/node_modules/vfile-message": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.4.tgz", + "integrity": "sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-stringify-position": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unique-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", + "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "crypto-random-string": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/unist-util-generated": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unist-util-generated/-/unist-util-generated-2.0.1.tgz", + "integrity": "sha512-qF72kLmPxAw0oN2fwpWIqbXAVyEqUzDHMsbtPvOudIlUzXYFIeQIuxXQCRCFh22B7cixvU0MG7m3MW8FTq/S+A==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-is": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-5.2.1.tgz", + "integrity": "sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-modify-children": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/unist-util-modify-children/-/unist-util-modify-children-3.1.1.tgz", + "integrity": "sha512-yXi4Lm+TG5VG+qvokP6tpnk+r1EPwyYL04JWDxLvgvPV40jANh7nm3udk65OOWquvbMDe+PL9+LmkxDpTv/7BA==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.0", + "array-iterate": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-4.0.4.tgz", + "integrity": "sha512-kUBE91efOWfIVBo8xzh/uZQ7p9ffYRtUbMRZBNFYwf0RK8koUMx6dGUfwylLOKmaT2cs4wSW96QoYUSXAyEtpg==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-3.0.3.tgz", + "integrity": "sha512-k5GzIBZ/QatR8N5X2y+drfpWG8IDBzdnVj6OInRNWm1oXrzydiaAT2OQiA8DPRRZyAKb9b6I2a6PxYklZD0gKg==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-1.4.1.tgz", + "integrity": "sha512-AvGNk7Bb//EmJZyhtRUnNMEpId/AZ5Ph/KUpTI09WHQuDZHKovQ1oEv3mfmKpWKtoMzyMC4GLBm1Zy5k12fjIw==", + "dev": true, + "dependencies": { + "unist-util-visit-parents": "^2.0.0" + } + }, + "node_modules/unist-util-visit-children": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unist-util-visit-children/-/unist-util-visit-children-2.0.2.tgz", + "integrity": "sha512-+LWpMFqyUwLGpsQxpumsQ9o9DG2VGLFrpz+rpVXYIEdPy57GSy5HioC0g3bg/8WP9oCLlapQtklOzQ8uLS496Q==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-2.1.2.tgz", + "integrity": "sha512-DyN5vD4NE3aSeB+PXYNKxzGsfocxp6asDc2XXE3b0ekO2BaRUpBicbbUygfSvYfUz1IkmjFR1YF7dPklraMZ2g==", + "dev": true, + "dependencies": { + "unist-util-is": "^3.0.0" + } + }, + "node_modules/unist-util-visit-parents/node_modules/unist-util-is": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-3.0.0.tgz", + "integrity": "sha512-sVZZX3+kspVNmLWBPAB6r+7D9ZgAFPNWm66f7YNb420RlQSbn+n8rG8dGZSkrER7ZIXGQYNm5pqC3v3HopH24A==", + "dev": true + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/update-notifier-cjs": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/update-notifier-cjs/-/update-notifier-cjs-5.1.6.tgz", + "integrity": "sha512-wgxdSBWv3x/YpMzsWz5G4p4ec7JWD0HCl8W6bmNB6E5Gwo+1ym5oN4hiXpLf0mPySVEJEIsYlkshnplkg2OP9A==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boxen": "^5.0.0", + "chalk": "^4.1.0", + "configstore": "^5.0.1", + "has-yarn": "^2.1.0", + "import-lazy": "^2.1.0", + "is-ci": "^2.0.0", + "is-installed-globally": "^0.4.0", + "is-npm": "^5.0.0", + "is-yarn-global": "^0.3.0", + "isomorphic-fetch": "^3.0.0", + "pupa": "^2.1.1", + "registry-auth-token": "^5.0.1", + "registry-url": "^5.1.0", + "semver": "^7.3.7", + "semver-diff": "^3.1.1", + "xdg-basedir": "^4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uvu": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/uvu/-/uvu-0.5.6.tgz", + "integrity": "sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==", + "dev": true, + "dependencies": { + "dequal": "^2.0.0", + "diff": "^5.0.0", + "kleur": "^4.0.3", + "sade": "^1.7.3" + }, + "bin": { + "uvu": "bin.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/vfile": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-3.0.1.tgz", + "integrity": "sha512-y7Y3gH9BsUSdD4KzHsuMaCzRjglXN0W2EcMf0gpvu6+SbsGhMje7xDc8AEoeXy6mIwCKMI6BkjMsRjzQbhMEjQ==", + "dev": true, + "dependencies": { + "is-buffer": "^2.0.0", + "replace-ext": "1.0.0", + "unist-util-stringify-position": "^1.0.0", + "vfile-message": "^1.0.0" + } + }, + "node_modules/vfile-location": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-4.1.0.tgz", + "integrity": "sha512-YF23YMyASIIJXpktBa4vIGLJ5Gs88UB/XePgqPmTa7cDA+JeO3yclbpheQYCHjVHBn/yePzrXuygIL+xbvRYHw==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.0", + "vfile": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-location/node_modules/vfile": { + "version": "5.3.7", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-5.3.7.tgz", + "integrity": "sha512-r7qlzkgErKjobAmyNIkkSpizsFPYiUPuJb5pNW1RB4JcYVZhs4lIbVqk8XPk033CV/1z8ss5pkax8SuhGpcG8g==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.0", + "is-buffer": "^2.0.0", + "unist-util-stringify-position": "^3.0.0", + "vfile-message": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-location/node_modules/vfile-message": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.4.tgz", + "integrity": "sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-stringify-position": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-1.1.1.tgz", + "integrity": "sha512-1WmsopSGhWt5laNir+633LszXvZ+Z/lxveBf6yhGsqnQIhlhzooZae7zV6YVM1Sdkw68dtAW3ow0pOdPANugvA==", + "dev": true, + "dependencies": { + "unist-util-stringify-position": "^1.1.1" + } + }, + "node_modules/vfile-message/node_modules/unist-util-stringify-position": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-1.1.2.tgz", + "integrity": "sha512-pNCVrk64LZv1kElr0N1wPiHEUoXNVFERp+mlTg/s9R5Lwg87f9bM/3sQB99w+N9D/qnM9ar3+AKDBwo/gm/iQQ==", + "dev": true + }, + "node_modules/vfile-reporter": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/vfile-reporter/-/vfile-reporter-6.0.2.tgz", + "integrity": "sha512-GN2bH2gs4eLnw/4jPSgfBjo+XCuvnX9elHICJZjVD4+NM0nsUrMTvdjGY5Sc/XG69XVTgLwj7hknQVc6M9FukA==", + "dev": true, + "dependencies": { + "repeat-string": "^1.5.0", + "string-width": "^4.0.0", + "supports-color": "^6.0.0", + "unist-util-stringify-position": "^2.0.0", + "vfile-sort": "^2.1.2", + "vfile-statistics": "^1.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-reporter/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/vfile-reporter/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/vfile-reporter/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/vfile-reporter/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/vfile-reporter/node_modules/unist-util-stringify-position": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz", + "integrity": "sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-sort": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/vfile-sort/-/vfile-sort-2.2.2.tgz", + "integrity": "sha512-tAyUqD2R1l/7Rn7ixdGkhXLD3zsg+XLAeUDUhXearjfIcpL1Hcsj5hHpCoy/gvfK/Ws61+e972fm0F7up7hfYA==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-statistics": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/vfile-statistics/-/vfile-statistics-1.1.4.tgz", + "integrity": "sha512-lXhElVO0Rq3frgPvFBwahmed3X03vjPF8OcjKMy8+F1xU/3Q3QU3tKEDp743SFtb74PdF0UWpxPvtOP0GCLheA==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile/node_modules/unist-util-stringify-position": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-1.1.2.tgz", + "integrity": "sha512-pNCVrk64LZv1kElr0N1wPiHEUoXNVFERp+mlTg/s9R5Lwg87f9bM/3sQB99w+N9D/qnM9ar3+AKDBwo/gm/iQQ==", + "dev": true + }, + "node_modules/web-namespaces": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", + "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true + }, + "node_modules/whatwg-fetch": { + "version": "3.6.20", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz", + "integrity": "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==", + "dev": true, + "license": "MIT" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "dev": true, + "dependencies": { + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "dev": true, + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.18.tgz", + "integrity": "sha512-qEcY+KJYlWyLH9vNbsr6/5j59AXk5ni5aakf8ldzBvGde6Iz4sxZGkJyWSAueTG7QhOvNRYb1lDdFmL5Td0QKA==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/widest-line": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", + "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", + "dev": true, + "license": "MIT", + "dependencies": { + "string-width": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/widest-line/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/widest-line/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wordwrapjs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-3.0.0.tgz", + "integrity": "sha512-mO8XtqyPvykVCsrwj5MlOVWvSnCdT+C+QVbm6blradR7JExAhbkZ7hZ9A+9NUtwzSqrlUo9a67ws0EiILrvRpw==", + "dev": true, + "dependencies": { + "reduce-flatten": "^1.0.1", + "typical": "^2.6.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/wordwrapjs/node_modules/typical": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/typical/-/typical-2.6.1.tgz", + "integrity": "sha512-ofhi8kjIje6npGozTip9Fr8iecmYfEbS06i0JnIg+rh51KakryWF4+jX8lLKZVhy6N+ID45WYSFCxPOdTWCzNg==", + "dev": true + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "node_modules/write-file-atomic/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xdg-basedir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", + "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/xmlbuilder": { + "version": "15.1.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", + "integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==", + "dev": true, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/yazl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/yazl/-/yazl-2.5.1.tgz", + "integrity": "sha512-phENi2PLiHnHb6QBVot+dJnaAZ0xosj7p3fWl+znIjBDlnMI2PsZCJZ306BPTFOaHf5qdDEI8x5qFrSOBN5vrw==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-crc32": "~0.2.3" + } + }, + "node_modules/yesno": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/yesno/-/yesno-0.4.0.tgz", + "integrity": "sha512-tdBxmHvbXPBKYIg81bMCB7bVeDmHkRzk5rVJyYYXurwKkHq/MCd8rz4HSJUP7hW0H2NlXiq8IFiWvYKEHhlotA==", + "dev": true, + "license": "BSD" + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zip-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/zip-dir/-/zip-dir-2.0.0.tgz", + "integrity": "sha512-uhlsJZWz26FLYXOD6WVuq+fIcZ3aBPGo/cFdiLlv3KNwpa52IF3ISV8fLhQLiqVu5No3VhlqlgthN6gehil1Dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "async": "^3.2.0", + "jszip": "^3.2.2" + } + }, + "node_modules/zip-dir/node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true, + "license": "MIT" + }, + "node_modules/zipit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/zipit/-/zipit-2.0.0.tgz", + "integrity": "sha512-e/y9Xf1eYElSSo1s1e1+0QraquqaCmfnCvxd2IlvILtKK+F93kpV+/TIRUDYW7UnYtDMeYijG5kyEA7B+66wgg==", + "dev": true, + "license": "MIT", + "dependencies": { + "insync": "2.1.1", + "yazl": "2.5.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/zwitch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + } + } +} diff --git a/package.json b/package.json index 1be58176..1516cf54 100644 --- a/package.json +++ b/package.json @@ -1,60 +1,46 @@ { "name": "@architect/arc.codes", - "version": "3.1.9", - "architect": { - "app": "v8-arc-codes", - "aws": { - "region": "us-west-1", - "profile": "openjsf", - "bucket": "arc.codes-deploy", - "apigateway": "http" - }, - "static": { - "folder": "public", - "fingerprint": true - }, - "http": [ - [ - "get", - "/docs/:lang/*" - ], - [ - "get", - "/api/package" - ], - [ - "any", - "/*" - ] - ] + "version": "3.13.16", + "repository": { + "type": "git", + "url": "https://github.com/architect/arc.codes" + }, + "license": "Apache-2.0", + "engines": { + "node": ">=16", + "npm": ">=8" }, "scripts": { - "deploy": "./scripts/deploy", - "spellcheck": "cross-env npx spellchecker './src/views/docs/en/**/*.md' --no-suggestions -q -d ./scripts/dictionary.js --plugins spell indefinite-article repeated-words syntax-mentions syntax-urls", - "lint": "eslint src --fix", - "start": "sandbox", - "test": "tape -r esm test/*-test.js test/**/*.js | tap-spec" + "spellcheck": "npx spellchecker --config ./scripts/spellcheckerrc.json", + "link-checker": "node ./test/link-checker.mjs | tap-arc", + "lint": "npx eslint src --fix", + "start": "npx arc sandbox", + "tape": "npx tape 'test/**/*-test.mjs' | tap-arc", + "test": "npm run spellcheck && npm run lint && npm run tape" }, "dependencies": { - "@architect/architect": "^8.4.2", - "@architect/eslint-config": "^1.0.0", - "@architect/functions": "^3.13.8", - "@architect/inventory": "^1.2.0", - "@architect/package": "^6.0.1", - "@toycode/markdown-it-class": "^1.2.4", - "eslint": "^7.15.0", - "esm": "^3.2.25", - "highlight.js": "^10.4.1", - "js-yaml": "^3.14.1", - "markdown-it": "^12.0.3", - "markdown-it-anchor": "^6.0.1", - "markdown-it-front-matter": "^0.2.3", - "slugify": "^1.4.6", - "spellchecker-cli": "^4.4.0", - "tap-spec": "^5.0.0", - "tape": "^5.0.1" + "@architect/asap": "^7.0.10", + "@architect/functions": "^8.1.6", + "@architect/inventory": "^4.0.5", + "@architect/package": "^9.0.3", + "@architect/plugin-node-prune": "^2.0.2", + "@enhance/arc-plugin-styles": "^5.0.6", + "@enhance/enhance-style-transform": "^0.1.2", + "@enhance/ssr": "^4.0.3", + "arcdown": "^2.3.0", + "markdown-it-arc-static-img": "^2.1.0", + "slugify": "^1.6.6" }, - "eslintConfig": { - "extends": "@architect/eslint-config" + "devDependencies": { + "@architect/architect": "^11.1.0", + "@architect/eslint-config": "^3.0.0", + "@architect/spellcheck-dictionary": "github:architect/spellcheck-dictionary", + "eslint": "^9.17.0", + "eslint-plugin-import": "^2.31.0", + "linkinator": "^6.1.2", + "spellchecker-cli": "^7.0.0", + "tap-arc": "^1.3.2", + "tape": "^5.9.0", + "tiny-json-http": "^7.5.1" } } diff --git a/public/arc.codes.png b/public/arc.codes.png index 48ab9664..81b193b8 100644 Binary files a/public/arc.codes.png and b/public/arc.codes.png differ diff --git a/public/components/arc-tab.js b/public/components/arc-tab.js index 1eb0fb13..b12a523d 100644 --- a/public/components/arc-tab.js +++ b/public/components/arc-tab.js @@ -1,5 +1,7 @@ +/* eslint-env browser */ +// eslint-disable-next-line fp/no-class class ArcTab extends HTMLElement { - constructor() { + constructor () { super() const template = document.createElement('template') this.template = this.template.bind(this) @@ -10,18 +12,18 @@ class ArcTab extends HTMLElement { this.content = this.querySelector('[slot=content]') } - template() { + template () { return ` - + ` } - connectedCallback() { + connectedCallback () { this.updateStyles() } - set active(value) { + set active (value) { const isActive = Boolean(value) if (isActive) { this.setAttribute('active', '') @@ -31,34 +33,34 @@ class ArcTab extends HTMLElement { } } - set label(value) { + set label (value) { this.setAttribute('label', value) } - get label() { + get label () { return this.getAttribute('label') } - get active() { + get active () { return this.hasAttribute('active') } - static get observedAttributes() { + static get observedAttributes () { return [ 'active' ] } - attributeChangedCallback(name, o, n) { - if(name === 'active') { + attributeChangedCallback (name, o, n) { + if (name === 'active') { if (o !== n) { this.updateStyles() } } } - updateStyles() { - if(this.hasAttribute('active')) { + updateStyles () { + if (this.hasAttribute('active')) { this.content.classList.remove('hidden') } else { diff --git a/public/components/arc-viewer.js b/public/components/arc-viewer.js index 35e9532b..44c51798 100644 --- a/public/components/arc-viewer.js +++ b/public/components/arc-viewer.js @@ -1,5 +1,7 @@ +/* eslint-env browser */ +// eslint-disable-next-line fp/no-class class ArcViewer extends HTMLElement { - constructor() { + constructor () { super() this.labels = [] this.tabs @@ -18,28 +20,28 @@ class ArcViewer extends HTMLElement { this.createTabBar() } - template() { + template () { return ` ` } - clickHandler(e) { + clickHandler (e) { let target = e.target let label = target.dataset.label - //remove active from all buttons + // remove active from all buttons let btns = this.shadowRoot.querySelectorAll('.arc--tab-btn') btns.forEach(b => b.classList.remove('active')) - //add active to the target btn + // add active to the target btn target.classList.add('active') this.updateTabs(label) } - connectedCallback() { + connectedCallback () { } - createTabBar() { + createTabBar () { let fragment = new DocumentFragment() this.labels.forEach(l => { let btn = document.createElement('button') @@ -57,6 +59,7 @@ class ArcViewer extends HTMLElement { 'bg-h6', 'bg-a0', 'text-1', + 'text-g10', 'text-center', 'font-semibold', 'cursor-pointer' @@ -69,9 +72,9 @@ class ArcViewer extends HTMLElement { this.shadowRoot.prepend(fragment) } - initTabs(str='') { + initTabs (str = '') { this.tabs = this.getElementsByTagName('arc-tab') - for( let tab of this.tabs ) { + for ( let tab of this.tabs ) { let label = tab.getAttribute('label') if (str === label) { tab.setAttribute('active', '') @@ -80,8 +83,8 @@ class ArcViewer extends HTMLElement { } } - updateTabs(str='') { - for( let tab of this.tabs ) { + updateTabs (str = '') { + for ( let tab of this.tabs ) { let label = tab.label tab.active = str === label } diff --git a/public/css/docsearch.css b/public/css/docsearch.css new file mode 100644 index 00000000..e36d0cc4 --- /dev/null +++ b/public/css/docsearch.css @@ -0,0 +1,32 @@ +input#docsearch { + background: #fbfbfb url("data:image/svg+xml,%3C%3Fxml version='1.0' %3F%3E%3Csvg fill='%23636363' viewBox='0 0 96 96' xmlns='http://www.w3.org/2000/svg'%3E%3Ctitle/%3E%3Cpath d='M54,0A42.051,42.051,0,0,0,12,42a41.5989,41.5989,0,0,0,8.48,25.0356L1.7578,85.7578a5.9994,5.9994,0,1,0,8.4844,8.4844L28.9644,75.52A41.5989,41.5989,0,0,0,54,84,42,42,0,0,0,54,0Zm0,72A30,30,0,1,1,84,42,30.0353,30.0353,0,0,1,54,72Z'/%3E%3C/svg%3E"); + background-position: 0.5rem center; + background-repeat: no-repeat; + background-size: 1rem; + box-sizing: content-box; + width: 5rem; + transition: all .5s; +} +input#docsearch[type=search]:focus { + width: 12rem; + background-color: #fff; +} + +input#docsearch::-webkit-search-decoration, +input#docsearch::-webkit-search-cancel-button { + display: none; +} + +.algolia-autocomplete .ds-dropdown-menu .ds-suggestions a { + text-decoration: none; +} + +.algolia-autocomplete .algolia-docsearch-suggestion--category-header { + background-color: var(--g1); + border-left: 8px solid var(--g3); + padding: .8rem !important; +} + +.algolia-autocomplete .algolia-docsearch-suggestion--category-header span { + color: var(--g8); +} diff --git a/public/css/index.css b/public/css/index.css index 8440be2a..2d168a31 100644 --- a/public/css/index.css +++ b/public/css/index.css @@ -1,81 +1,224 @@ :root { --sidebar: 22rem; } + html { scroll-behavior: smooth; } + +body { + scrollbar-color: var(--g2) transparent; +} + +::-webkit-scrollbar { + height: 8px; + width: 8px; +} + +::-webkit-scrollbar-track { + background-color: transparent; +} + +::-webkit-scrollbar-thumb { + border-radius: 8px; + background-color: var(--g2); +} + .font-semibold-active.active { font-weight: 600; } + .underline-h:hover { text-decoration: underline; } + .indicator { height: 4px; } + .max-width-content { - max-width: 60rem; + max-width: 53.125rem; } + .two-column { /* Height: Header | Content */ grid-template-rows: 3rem 1fr; /* Width: Sidebar | Content */ - grid-template-columns: 17.666rem 1fr; + grid-template-columns: 18.554rem 1fr; } + .icon { - width: 1.25rem; - height: 1.25rem; + width: 1.1rem; + height: 1.1rem; } -.star-icon { - width: 1rem; - height: 1rem; -} -.slack-icon { - width: 2rem; - height: 2rem; + +.discord-icon { + width: 1.3rem; + height: 1.3rem; } + .logo { width: 8rem; } + .logo, .h-logo { - height: 1.5rem; + height: 1.75rem; } + .left-sidebar { /* Hide Sidebar off screen in mobile */ left: calc(var(--sidebar) * -1); + z-index: 1; } + .table-fixed { table-layout: fixed; } + .border-collapse { border-collapse: collapse; } + .resize-vertical { resize: vertical; } + .open, .transition-x { transition: transform 100ms cubic-bezier(0.23, 1, 0.320, 1); } + .open, .translate-x-sidebar { transform: translateX(var(--sidebar)); } + .transition-fill { transition: fill 2s ease-out; } +/* Sidebar group toggling */ +label.sidebar-group-title { + color: var(--p1); +} + +label.sidebar-group-title:before { + font-size: .8rem; + color: var(--g6); + content: "\25B6"; +} + +label.sidebar-group-title+ul { + height: 0; + overflow: hidden; + padding-left: .7rem; +} + @media only screen and (min-width:48em) { + .open, .transition-x { transition: none; - } + } + .open, .translate-x-sidebar { transform: translateX(0); } + .resize-none-lg { resize: none; } } + +li>ul.mb1 { + margin-bottom: 0; +} + +li>ol.mb1 { + margin-bottom: 0; +} + +details>summary { + list-style: none; +} + +details>summary::-webkit-details-marker { + display: none; +} + +details summary .plus-icon, +details[open] summary .minus-icon { + display: inline; +} + +details[open] summary .plus-icon, +details summary .minus-icon { + display: none; +} + +main { + display: flex; +} + +.right-sidebar { + display: block; + font-size: .9rem; + min-width: 16rem; +} + +@media only screen and (max-width:80rem) { + main { + display: block; + } + + .right-sidebar { + display: none; + } +} + +.right-sidebar a { + text-decoration: none; +} + +.right-sidebar a.active, +.right-sidebar a.active code { + font-weight: 700; + color: var(--g10); +} + +nav.toc { + padding-left: 0.5rem; +} + +nav.toc ol { + list-style: none; +} + +nav.toc ol li { + margin-left: 0.5rem; +} + +nav.toc li a { + color: var(--p1); + font-weight: 500; +} + +nav.toc li a:hover { + color: var(--h1); +} + +.navButton { + border: 1px solid transparent; + border-radius: 99em; + padding-block: 0.25em; +} + +.navButton.active { + background-color: hsl(0deg 0% 40%); +} + +.navButton:not(.active):hover { + border-color: hsl(0deg 0% 80%); +} diff --git a/public/css/landing-syntax.min.css b/public/css/landing-syntax.min.css new file mode 100644 index 00000000..23cde0b5 --- /dev/null +++ b/public/css/landing-syntax.min.css @@ -0,0 +1,3 @@ +@media(prefers-color-scheme:no-preference) or (prefers-color-scheme:light){.hljs{color:#2a2c2d;background:#e6e6e6}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}.hljs-link{text-decoration:underline}.hljs-comment,.hljs-quote{color:#676b79;font-style:italic}.hljs-params{color:#676b79}.hljs-attr,.hljs-punctuation{color:#2a2c2d}.hljs-char.escape_,.hljs-meta,.hljs-name,.hljs-operator,.hljs-selector-tag{color:#c56200}.hljs-deletion,.hljs-keyword{color:#d92792}.hljs-regexp,.hljs-selector-attr,.hljs-selector-pseudo,.hljs-variable.language_{color:#cc5e91}.hljs-code,.hljs-formula,.hljs-property,.hljs-section,.hljs-subst,.hljs-title.function_{color:#3787c7}.hljs-addition,.hljs-bullet,.hljs-meta .hljs-string,.hljs-selector-class,.hljs-string,.hljs-symbol,.hljs-title.class_,.hljs-title.class_.inherited__{color:#0d7d6c}.hljs-attribute,.hljs-built_in,.hljs-doctag,.hljs-link,.hljs-literal,.hljs-meta .hljs-keyword,.hljs-number,.hljs-selector-id,.hljs-tag,.hljs-template-tag,.hljs-template-variable,.hljs-title,.hljs-type,.hljs-variable{color:#7641bb}} + +@media(prefers-color-scheme:dark){/*! Theme: nnfx dark Description: a theme inspired by Netscape Navigator/Firefox Author: (c) 2020-2021 Jim Mason Maintainer: @RocketMan License: https://creativecommons.org/licenses/by-sa/4.0 CC BY-SA 4.0 Updated: 2021-05-17 @version 1.1.0 */.hljs{background:#333;color:#fff}.language-xml .hljs-meta,.language-xml .hljs-meta-string{font-weight:700;font-style:italic;color:#69f}.hljs-comment,.hljs-quote{font-style:italic;color:#9c6}.hljs-built_in,.hljs-keyword,.hljs-name{color:#a7a}.hljs-attr,.hljs-doctag,.hljs-name,.hljs-strong{font-weight:700}.hljs-string{font-weight:400}.hljs-code,.hljs-link,.hljs-meta .hljs-string,.hljs-number,.hljs-regexp,.hljs-string{color:#bce}.hljs-bullet,.hljs-symbol,.hljs-template-variable,.hljs-title,.hljs-variable{color:#d40}.hljs-class .hljs-title,.hljs-title.class_,.hljs-type{font-weight:700;color:#96c}.hljs-attr,.hljs-function .hljs-title,.hljs-subst,.hljs-tag,.hljs-title.function_{color:#fff}.hljs-formula{background-color:#eee;font-style:italic}.hljs-addition{background-color:#797}.hljs-deletion{background-color:#c99}.hljs-meta{color:#69f}.hljs-section,.hljs-selector-class,.hljs-selector-id,.hljs-selector-pseudo,.hljs-selector-tag{font-weight:700;color:#69f}.hljs-emphasis,.hljs-selector-pseudo{font-style:italic}} diff --git a/public/css/styles.css b/public/css/styles.css index 1407d1a0..ccd2e1a3 100644 --- a/public/css/styles.css +++ b/public/css/styles.css @@ -1,12 +1,10 @@ /* ----- THEME ----- */ /* VARIABLES */ :root { - --p0:#2CDD93;/* Medium Aquamarine light */ - --p1:#2B7D9A;/* Bright Navy Blue light */ - --p2:#BBFFAA;/* brighter green */ + --p0:#2CDD93;/* clover green */ + --p1:#365c91;/* Arc blue */ + --p2:#BBFFAA;/* key lime */ --p3:#E21893;/* Barbie Pink light */ - --p4:#FF6263;/* Bittersweet Orange light */ - --p5:hsl(267, 75%, 45%);/* Royal purple */ --g0:#FBFBFB;/* #FBFBFB */ --g1:#E5E5E5;/* #E5E5E5 */ --g2:#CFCFCF;/* #CFCFCF */ @@ -19,20 +17,105 @@ --g9:#383838;/* #383838 */ --g10:#222222;/* #222222 */ --g11:#FFF; /* #FFF*/ - --h0:#3EE09C;/* Medium Aquamarine */ - --h1:#40B9E5;/* Bright Navy Blue */ - --h2:hsl(267, 90%, 55%);/* Royal Purple Hover */ - --h3:#DE1792;/* Barbie Pink */ - --h4:#FF4747;/* Bittersweet Orange */ - --h5:#FAE05D;/* Minion Yellow */ --h6:#CFCFCF;/* #CFCFCF */ --a0:#A4A4A4;/* #A4A4A4 */ --a1:#8E8E8E;/* #8E8E8E */ --a2:#1D5266;/* #8E8E8E */ --d0:#797979;/* #797979 */ --d1:#636363;/* #636363 */ + --text-color: #45413a;/* #45413a */ + --bkg-color: #FFF;/* #FFF */ } + +:root[data-theme="dark"] { + --p0:#6EE8B5;/* #6EE8B5 */ + --p1:#1496fa;/* #1496fa */ + --p2:#E4FFDD;/* #E4FFDD */ + --p3:#ED59B3;/* #ED59B3 */ + --g0:#222222;/* #222222 */ + --g1:#383838;/* #383838 */ + --g2:#4D4D4D;/* #4D4D4D */ + --g3:#636363;/* #636363 */ + --g4:#797979;/* #797979 */ + --g5:#8E8E8E;/* #8E8E8E */ + --g6:#A4A4A4;/* #A4A4A4 */ + --g7:#BABABA;/* #BABABA */ + --g8:#CFCFCF;/* #CFCFCF */ + --g9:#E5E5E5;/* #E5E5E5 */ + --g10:#FBFBFB;/* #FBFBFB */ + --g11:#000; /* #FFF*/ + --h6:#CFCFCF;/* #CFCFCF */ + --a0:#A4A4A4;/* #A4A4A4 */ + --a1:#8E8E8E;/* #8E8E8E */ + --a2:#2E82A2;/* #2E82A2 */ + --d0:#797979;/* #797979 */ + --d1:#636363;/* #636363 */ + --text-color: #EEE;/* #EEE */ + --bkg-color: #121212;/* #121212 */ +} + +@media (prefers-color-scheme: dark) { + :root { + --p0:#6EE8B5;/* #6EE8B5 */ + --p1:#46A9CB;/* #46A9CB */ + --p2:#E4FFDD;/* #E4FFDD */ + --p3:#ED59B3;/* #ED59B3 */ + --g0:#222222;/* #222222 */ + --g1:#383838;/* #383838 */ + --g2:#4D4D4D;/* #4D4D4D */ + --g3:#636363;/* #636363 */ + --g4:#797979;/* #797979 */ + --g5:#8E8E8E;/* #8E8E8E */ + --g6:#A4A4A4;/* #A4A4A4 */ + --g7:#BABABA;/* #BABABA */ + --g8:#CFCFCF;/* #CFCFCF */ + --g9:#E5E5E5;/* #E5E5E5 */ + --g10:#FBFBFB;/* #FBFBFB */ + --g11:#000; /* #FFF*/ + --h6:#CFCFCF;/* #CFCFCF */ + --a0:#A4A4A4;/* #A4A4A4 */ + --a1:#8E8E8E;/* #8E8E8E */ + --a2:#2E82A2;/* #2E82A2 */ + --d0:#797979;/* #797979 */ + --d1:#636363;/* #636363 */ + --text-color: #EEE;/* #EEE */ + --bkg-color: #121212;/* #121212 */ + } + :root[data-theme="light"] { + --p0:#2CDD93;/* clover green */ + --p1:#365c91;/* blue green */ + --p2:#BBFFAA;/* key lime */ + --p3:#E21893;/* Barbie Pink light */ + --g0:#FBFBFB;/* #FBFBFB */ + --g1:#E5E5E5;/* #E5E5E5 */ + --g2:#CFCFCF;/* #CFCFCF */ + --g3:#BABABA;/* #BABABA */ + --g4:#A4A4A4;/* #A4A4A4 */ + --g5:#8E8E8E;/* #8E8E8E */ + --g6:#797979;/* #797979 */ + --g7:#636363;/* #636363 */ + --g8:#4D4D4D;/* #4D4D4D */ + --g9:#383838;/* #383838 */ + --g10:#222222;/* #222222 */ + --g11:#FFF; /* #FFF*/ + --h6:#CFCFCF;/* #CFCFCF */ + --a0:#A4A4A4;/* #A4A4A4 */ + --a1:#8E8E8E;/* #8E8E8E */ + --a2:#1D5266;/* #8E8E8E */ + --d0:#797979;/* #797979 */ + --d1:#636363;/* #636363 */ + --text-color: #45413a;/* #45413a */ + --bkg-color: #FFF;/* #FFF */ + } +} + +[data-theme='light'] .moon-icon, +[data-theme='dark'] .sun-icon { + display: block !important; +} + + /* RESET */ *, *:before, @@ -55,13 +138,15 @@ input {font-family: inherit;} /* TYPEFACE */ -html {font-size: 18px;} +html {font-size: 16px;} body { font-weight: normal; line-height: 1.5; text-rendering: geometricPrecision; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; + background: var(--bkg-color); + color: var(--text-color); } @@ -156,9 +241,8 @@ body { .bg-a2.active{background-color:var(--a2);} .bg-d0:disabled{background-color:var(--d0);} .bg-d1:disabled{background-color:var(--d1);} - - - /* GRADIENT */.bg-image0{background-image:linear-gradient(0.4turn, var(--p1), var(--p0));} +/* GRADIENT */ +.bg-image0{background-image:linear-gradient(0.4turn, var(--p1), var(--p0));} /* BORDER */ @@ -218,11 +302,11 @@ body { .border-a1.active{border-color:var(--a1);}/* #8E8E8E */ .border-d0:disabled{border-color:var(--d0);}/* #797979 */ .border-d1:disabled{border-color:var(--d1);}/* #636363 */ - .border-current { border-color: currentColor; } + /* RADIUS */ .radius-none{border-radius:0;} .radius-100{border-radius:100%;} @@ -247,12 +331,10 @@ body { .stroke-current{stroke:currentColor;} - /* CONTAINER */ .container{max-width:100%;} - /* FAMILY */ .font-sans{font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";} .font-serif{font-family: Georgia, Cambria, "Times New Roman", Times, serif;} @@ -260,17 +342,15 @@ body { /* SIZES */ -.text5{font-size:4.209rem;}/* 75.757px */ -.text4{font-size:3.157rem;}/* 56.832px */ -.text3{font-size:2.369rem;}/* 42.635px */ -.text2{font-size:1.777rem;}/* 31.984px */ -.text1{font-size:1.333rem;}/* 23.994px */ -.text0{font-size:1rem;}/* 18px */ -.text-1{font-size:0.75rem;}/* 13.503px */ -.text-2{font-size:0.563rem;}/* 10.13px */ -.text-3{font-size:0.422rem;}/* 7.599px */ -.text-4{font-size:0.317rem;}/* 5.701px */ -.text-5{font-size:0.238rem;}/* 4.277px */ +.text5{font-size:1.802rem;}/* 30.63px */ +.text4{font-size:1.602rem;}/* 27.23px */ +.text3{font-size:1.424rem;}/* 24.21px */ +.text2{font-size:1.266rem;}/* 21.52px */ +.text1{font-size:1.125rem;}/* 19.13px */ +.text0{font-size:1rem;}/* 17.00px */ +.text-1{font-size:0.889rem;}/* 15.11px */ +.text-2{font-size:0.79rem;}/* 13.43px */ +.text-3{font-size:0.702rem;}/* 11.94px */ /* Style */ @@ -475,6 +555,7 @@ body { .col-span-2{grid-column: span 2 / span 2;} .col-start-2{grid-column-start: 2;} .row-start-2{grid-row-start: 2;} +.row-start-3{grid-row-start: 3;} .col-end-2{grid-column-end: 2;} .row-end-2{grid-row-end: 2;} .row-2{grid-template-rows: repeat(2, minmax(0, 1fr));} @@ -736,7 +817,6 @@ body { .container-lg{max-width:48em;} - /* FAMILY */ .font-sans-lg{font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";} .font-serif-lg{font-family: Georgia, Cambria, "Times New Roman", Times, serif;} @@ -744,17 +824,13 @@ body { /* SIZES */ -.text5-lg{font-size:4.209rem;}/* 75.757px */ -.text4-lg{font-size:3.157rem;}/* 56.832px */ -.text3-lg{font-size:2.369rem;}/* 42.635px */ -.text2-lg{font-size:1.777rem;}/* 31.984px */ -.text1-lg{font-size:1.333rem;}/* 23.994px */ -.text0-lg{font-size:1rem;}/* 18px */ -.text-1-lg{font-size:0.75rem;}/* 13.503px */ -.text-2-lg{font-size:0.563rem;}/* 10.13px */ -.text-3-lg{font-size:0.422rem;}/* 7.599px */ -.text-4-lg{font-size:0.317rem;}/* 5.701px */ -.text-5-lg{font-size:0.238rem;}/* 4.277px */ +.text3-lg{font-size:1.424rem;}/* 24.21px */ +.text2-lg{font-size:1.266rem;}/* 21.52px */ +.text1-lg{font-size:1.125rem;}/* 19.13px */ +.text0-lg{font-size:1rem;}/* 17.00px */ +.text-1-lg{font-size:0.889rem;}/* 15.11px */ +.text-2-lg{font-size:0.79rem;}/* 13.43px */ +.text-3-lg{font-size:0.702rem;}/* 11.94px */ /* Style */ @@ -837,7 +913,6 @@ body { .ellipsis-lg{text-overflow:ellipsis;} - /* ----- LAYOUT ----- */ @@ -1007,7 +1082,6 @@ body { .z-1-lg{z-index:-1;} - /* MARGIN */ .m-none-lg{margin:0;} .mt-none-lg{margin-top:0;} @@ -1210,4 +1284,3 @@ body { } - diff --git a/public/css/syntax.css b/public/css/syntax.css index 14bc8bc3..e064690d 100644 --- a/public/css/syntax.css +++ b/public/css/syntax.css @@ -1,59 +1,95 @@ -/* Hemisu Dark */ -.hljs { - display: block; - overflow-x: auto; - padding: 0.5em; - background: #282a36; -} - -.hljs-comment, -.hljs-meta { - color: #777777; -} +/* Atom One Dark */ +.hljs{display:block;overflow-x:auto;padding:.5em;color:#abb2bf;background:#282c34} +.hljs-keyword,.hljs-operator{color:#C678DD} +.hljs-pattern-match{color:#f92672} +.hljs-pattern-match .hljs-constructor{color:#61aeee} +.hljs-function{color:#61aeee} +.hljs-function .hljs-params{color:#a6e22e} +.hljs-function .hljs-params .hljs-typing{color:#fd971f} +.hljs-module-access .hljs-module{color:#7e57c2} +.hljs-constructor{color:#e2b93d} +.hljs-constructor .hljs-string{color:#9ccc65} +.hljs-comment,.hljs-quote{color:#5C6370;;font-style:italic} +.hljs-doctag,.hljs-formula{color:#c678dd} +.hljs-deletion,.hljs-name,.hljs-section,.hljs-selector-tag,.hljs-subst{color:#e06c75} +.hljs-literal{color:#D19A66} +.hljs-addition,.hljs-attribute,.hljs-meta-string,.hljs-regexp,.hljs-string{color:#98c379} +.hljs-built_in,.hljs-class{color:#56B6C2} +.hljs-number,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-pseudo,.hljs-template-variable,.hljs-type{color:#d19a66} +.hljs-variable{color:#ABB2BF} +.hljs-attr{color:#E06C75} +.hljs-bullet,.hljs-link,.hljs-meta,.hljs-selector-id,.hljs-symbol{color:#61aeee} +.hljs-title{color:#61AFEF;} +.hljs-emphasis{font-style:italic} +.hljs-strong{font-weight:700} +.hljs-link{text-decoration:underline} +.hljs-property{color:#E06C75} -.hljs-params, -.hljs-number, -.hljs-built_in { - color: #9fd3e6; +.docs blockquote > p { + margin: 0; } - -.hljs-string, -.hljs-regexp { - color: #b1d631; +.docs blockquote > p code { + background-color: var(--g2); } -.hljs-keyword, -.hljs-title, -.hljs-constant, -.hljs-tag, -.hljs-attr { - color: #bbffaa; +/* all code font family */ +.docs pre code, .docs :not(pre) > code { + font-family: Menlo, Monaco, Consolas, monospace; } - -.hljs-selector-tag, -.hljs-literal, -.hljs-section, -.hljs-link { - color: #ece1c8; +.docs pre code, .docs :not(h1 > a, h2 > a, h3 > a, h4 > a, h5 > a, h6 > a) > code { + font-size: 0.85rem; } - .docs pre code { - color: #eeeeee; - font-family: Menlo, Monaco, Consolas, monospace; + color: #ABB2BF; } -.hljs, -.hljs-string .hljs-subst, -.hljs-tag .hljs-name { - color: #eeeeee; -} -/* Code blocks */ +/* highlighted/hljs code blocks */ .docs pre { padding: 1rem; + margin-bottom: 1.33rem; overflow: auto; border-radius: 0.3rem; + white-space: pre; + word-break: break-word; } -.docs blockquote > p { + +/* fallback for when hljs encounters unknown syntax */ +:root[data-theme="light"] .docs pre { + color: var(--g2); + background-color: var(--g10); +} +:root[data-theme="dark"] .docs pre { + color: var(--g8); + background-color: var(--g0); +} + +.docs div[slot='content'] pre { + border-radius: 0 0.3rem 0.3rem 0.3rem; +} + +/* code copy button */ +.docs pre button.icon { + width: 1rem; + height: 1rem; + color: white; +} +.docs pre button.icon svg { + pointer-events: none; +} + +/* inline code, not highlighted blocks */ +.docs :not(pre) > code { + background-color: var(--g1); + padding: 0.1rem 0.2rem; margin: 0; + display: inline; + overflow-wrap: break-word; + min-width: auto; + border-radius: 0.25rem; } +@media only screen and (min-width:48em) { + .docs pre { + white-space: pre-wrap; + } +} diff --git a/public/fonts/montserrat-italic-subset-var.woff2 b/public/fonts/montserrat-italic-subset-var.woff2 new file mode 100644 index 00000000..06b48dd3 Binary files /dev/null and b/public/fonts/montserrat-italic-subset-var.woff2 differ diff --git a/public/fonts/montserrat-subset-var.woff2 b/public/fonts/montserrat-subset-var.woff2 new file mode 100644 index 00000000..d3d02d97 Binary files /dev/null and b/public/fonts/montserrat-subset-var.woff2 differ diff --git a/public/images/aws-credentials/1.png b/public/images/aws-credentials/1.png new file mode 100644 index 00000000..970aa62a Binary files /dev/null and b/public/images/aws-credentials/1.png differ diff --git a/public/images/aws-credentials/2.png b/public/images/aws-credentials/2.png new file mode 100644 index 00000000..f8debdff Binary files /dev/null and b/public/images/aws-credentials/2.png differ diff --git a/public/images/aws-credentials/3.png b/public/images/aws-credentials/3.png new file mode 100644 index 00000000..32c7e832 Binary files /dev/null and b/public/images/aws-credentials/3.png differ diff --git a/public/images/aws-credentials/4.png b/public/images/aws-credentials/4.png new file mode 100644 index 00000000..fcc450f6 Binary files /dev/null and b/public/images/aws-credentials/4.png differ diff --git a/public/images/aws-credentials/5.png b/public/images/aws-credentials/5.png new file mode 100644 index 00000000..1a790cc2 Binary files /dev/null and b/public/images/aws-credentials/5.png differ diff --git a/public/images/aws-credentials/6.png b/public/images/aws-credentials/6.png new file mode 100644 index 00000000..fa05ca46 Binary files /dev/null and b/public/images/aws-credentials/6.png differ diff --git a/public/images/aws-credentials/7.png b/public/images/aws-credentials/7.png new file mode 100644 index 00000000..9c31db26 Binary files /dev/null and b/public/images/aws-credentials/7.png differ diff --git a/public/images/aws-credentials/8.png b/public/images/aws-credentials/8.png new file mode 100644 index 00000000..2bdb0338 Binary files /dev/null and b/public/images/aws-credentials/8.png differ diff --git a/public/images/custom-domain/1.png b/public/images/custom-domain/1.png new file mode 100644 index 00000000..361f3987 Binary files /dev/null and b/public/images/custom-domain/1.png differ diff --git a/public/images/custom-domain/10.png b/public/images/custom-domain/10.png new file mode 100644 index 00000000..2e041e74 Binary files /dev/null and b/public/images/custom-domain/10.png differ diff --git a/public/images/custom-domain/11.png b/public/images/custom-domain/11.png new file mode 100644 index 00000000..bcfb315f Binary files /dev/null and b/public/images/custom-domain/11.png differ diff --git a/public/images/custom-domain/12.png b/public/images/custom-domain/12.png new file mode 100644 index 00000000..e9888b1a Binary files /dev/null and b/public/images/custom-domain/12.png differ diff --git a/public/images/custom-domain/13.png b/public/images/custom-domain/13.png new file mode 100644 index 00000000..cb20856b Binary files /dev/null and b/public/images/custom-domain/13.png differ diff --git a/public/images/custom-domain/14.png b/public/images/custom-domain/14.png new file mode 100644 index 00000000..37fa98c7 Binary files /dev/null and b/public/images/custom-domain/14.png differ diff --git a/public/images/custom-domain/15.png b/public/images/custom-domain/15.png new file mode 100644 index 00000000..f6c8280d Binary files /dev/null and b/public/images/custom-domain/15.png differ diff --git a/public/images/custom-domain/16.png b/public/images/custom-domain/16.png new file mode 100644 index 00000000..e4d407bb Binary files /dev/null and b/public/images/custom-domain/16.png differ diff --git a/public/images/custom-domain/2.png b/public/images/custom-domain/2.png new file mode 100644 index 00000000..285d516c Binary files /dev/null and b/public/images/custom-domain/2.png differ diff --git a/public/images/custom-domain/3.png b/public/images/custom-domain/3.png new file mode 100644 index 00000000..d6b46287 Binary files /dev/null and b/public/images/custom-domain/3.png differ diff --git a/public/images/custom-domain/4.png b/public/images/custom-domain/4.png new file mode 100644 index 00000000..d39a95e2 Binary files /dev/null and b/public/images/custom-domain/4.png differ diff --git a/public/images/custom-domain/5.png b/public/images/custom-domain/5.png new file mode 100644 index 00000000..9fcb9fc3 Binary files /dev/null and b/public/images/custom-domain/5.png differ diff --git a/public/images/custom-domain/6.png b/public/images/custom-domain/6.png new file mode 100644 index 00000000..9f3e6175 Binary files /dev/null and b/public/images/custom-domain/6.png differ diff --git a/public/images/custom-domain/7.png b/public/images/custom-domain/7.png new file mode 100644 index 00000000..ac5c63e8 Binary files /dev/null and b/public/images/custom-domain/7.png differ diff --git a/public/images/custom-domain/8.png b/public/images/custom-domain/8.png new file mode 100644 index 00000000..2ce9cedf Binary files /dev/null and b/public/images/custom-domain/8.png differ diff --git a/public/images/custom-domain/9.png b/public/images/custom-domain/9.png new file mode 100644 index 00000000..909fa993 Binary files /dev/null and b/public/images/custom-domain/9.png differ diff --git a/public/index.js b/public/index.js index 449954e1..8f250851 100644 --- a/public/index.js +++ b/public/index.js @@ -1,53 +1,125 @@ - (function(){ - // Scroll active sidebar link into view - document - .querySelector('a.active') - .scrollIntoView({ - behavior: 'smooth', - block: 'center' - }) - - // Toggle sidebar on mobile - let menuButton = document - .getElementById('menu-button') - let sidebar = document - .getElementById('sidebar') - let main = document - .getElementById('main') - - main.onclick = function hideSidebar() { - sidebar.classList.remove('open') - } +/* eslint-env browser */ +(function (){ + const activeLink = document.querySelector('a.active') + const main = document.getElementById('main') + const menuButton = document.getElementById('menu-button') + const sidebar = document.getElementById('sidebar') + const themeButton = document.getElementById('theme-button') + const codeBlocks = document.querySelectorAll('pre.hljs') - menuButton.onclick = function toggleSidebar() { - sidebar.classList.toggle('open') - } + // Scroll active sidebar link into view + if (activeLink) + activeLink.scrollIntoView({ + behavior: 'smooth', + block: 'center' + }) - // Show percentage of page read on desktop - let bar = document.querySelector('.indicator') - let isDesktop = window.innerWidth > 768 - - if (isDesktop) { - // If we want this to work on mobile - // we need to add a second function - // for handling window.onscroll - let el = main - bar.style.width = getScrollPercent(el) - el.onscroll = setReadPercent.bind(null, el) - } + // Toggle sidebar on mobile + main.onclick = () => sidebar.classList.remove('open') + menuButton.onclick = () => sidebar.classList.toggle('open') - function setReadPercent(el) { - bar.style.width = `${getScrollPercent(el)}%` - } + /* Light/Dark Mode */ + // Get the user's theme preference from local storage, otherwise check OS default + let currentTheme = localStorage.getItem('theme') || (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') + document.documentElement.setAttribute('data-theme', currentTheme) + + themeButton.onclick = function toggleTheme () { + let currentTheme = document.documentElement.getAttribute('data-theme') + let targetTheme = currentTheme === 'dark' ? 'light' : 'dark' + document.documentElement.setAttribute('data-theme', targetTheme) + localStorage.setItem('theme', targetTheme) + } + + // Copy-Paste function for code blocks + const buttonClassList = [ + 'icon', + 'invisible', + 'visible-lg', + 'absolute', + 'top0', + 'right0', + 'mt-2', + 'mr-2', + 'cursor-pointer', + 'text-h0', + 'text-a2', + 'bg-unset', + 'fill-current', + ] + const svgCopy = '' + const svgCheck = '' + for (const codeBlock of codeBlocks) { + codeBlock.classList.add('relative') + // create copy button + const button = document.createElement('button') + button.className = buttonClassList.join(' ') + button.innerHTML = svgCopy - function getScrollPercent(el) { - let body = document.body - let scrollTop = 'scrollTop' - let scrollHeight = 'scrollHeight' - let currentTop = el[scrollTop] || body[scrollTop] - let currentHeight = (el[scrollHeight] || body[scrollHeight]) - body.clientHeight - return Math.floor((currentTop / currentHeight) * 100) + button.onclick = (evt) => { + const target = evt.target + const parent = target.closest('pre') + const codeText = parent.querySelector('code').textContent.trim() + + navigator.clipboard.writeText(codeText).then( + () => { + target.innerHTML = svgCheck + setTimeout(() => target.innerHTML = svgCopy, 2000) + }, + () => target.innerHTML = 'Error copying!' + ) } + codeBlock.appendChild(button) + } + + // Document percent read progress bar + let bar = document.querySelector('.indicator') + bar.style.width = getScrollPercent(main) + main.onscroll = setReadPercent.bind(null, main) + + function setReadPercent (el) { + bar.style.width = `${getScrollPercent(el)}%` + } + + function getScrollPercent (el) { + let body = document.body + let scrollTop = 'scrollTop' + let scrollHeight = 'scrollHeight' + let currentTop = el[scrollTop] || body[scrollTop] + let currentHeight = (el[scrollHeight] || body[scrollHeight]) - body.clientHeight + return Math.floor((currentTop / currentHeight) * 100) + } + + /* Right sidebar behavior */ + // ↑ Top link fancy scroll enhancement + document.getElementById('top-link').onclick = function (e) { + e.preventDefault() + main + .querySelectorAll('div > h1')[0] + .scrollIntoView({ behavior: 'smooth' }) + history.replaceState(null, null, ' ') + } + + // highlight doc outline item per scroll position + window.addEventListener('DOMContentLoaded', () => { + const observer = new IntersectionObserver(entries => { + const allSectionLinks = document.querySelectorAll('.right-sidebar li a') + entries.forEach(entry => { + if (entry.isIntersecting) { + const id = entry.target.getAttribute('id') + const sectionLink = document.querySelector(`.right-sidebar a[href="#${id}"]`) + allSectionLinks.forEach(link => link.classList.remove('active')) + if (sectionLink) sectionLink.classList.add('active') + } + }) + }, { + root: null, + rootMargin: `0% 0% -80% 0%`, + threshold: [ 1 ], + }) - }()) + document.querySelectorAll('h2, h3, h4, h5, h6').forEach((header) => { + observer.observe(header) + }) + }) +}()) diff --git a/public/landing/begin-logo.svg b/public/landing/begin-logo.svg new file mode 100755 index 00000000..e56717d4 --- /dev/null +++ b/public/landing/begin-logo.svg @@ -0,0 +1 @@ + diff --git a/public/landing/openjs-foundation-logo.svg b/public/landing/openjs-foundation-logo.svg new file mode 100644 index 00000000..47a0f319 --- /dev/null +++ b/public/landing/openjs-foundation-logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/playground.html b/public/playground.html index 0b0f713f..5d70e6af 100644 --- a/public/playground.html +++ b/public/playground.html @@ -2,6 +2,9 @@ + + + Architect Playground @@ -55,7 +58,7 @@ text-g0 " > - + Playground @@ -154,4 +157,3 @@ - diff --git a/public/playground.js b/public/playground.js index 72e92649..1ba107b5 100644 --- a/public/playground.js +++ b/public/playground.js @@ -1,4 +1,4 @@ -(function() { +(function () { let btn = document.getElementById('pkg-submit') btn.remove() @@ -23,7 +23,7 @@ }()) -function copyShare(e) { +function copyShare (e) { e.preventDefault() let shareBtn = e.target let input = document.getElementById('pkg-input') @@ -31,12 +31,12 @@ function copyShare(e) { let shareUrl = `${window.location}?arc=${encodeURIComponent(arc)}` navigator.clipboard.writeText(shareUrl) shareBtn.innerHTML = 'Copied to clipboard' - setTimeout(function resetButton() { + setTimeout(function resetButton () { shareBtn.innerHTML = 'Share' }, 2000) } -async function getPreview() { +async function getPreview () { const input = document.getElementById('pkg-input') const arc = input.value const url = `/api/package?arc=${btoa(arc)}` @@ -44,17 +44,17 @@ async function getPreview() { let cfn = JSON.stringify(await (await fetch(url)).json(), null, 2) update(cfn) } - catch(e) { + catch (e) { console.error(e) } } -function submit(e) { +function submit (e) { e.preventDefault() getPreview() } -function update(cfn) { +function update (cfn) { const preview = document.getElementById('preview') preview.innerHTML = cfn } diff --git a/public/static.json b/public/static.json deleted file mode 100644 index 0d6688ff..00000000 --- a/public/static.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "arc.codes.png": "arc.codes-dffa3a9996.png", - "index.js": "index-9687ad7aae.js", - "playground.html": "playground-d3d4c75ee9.html", - "playground.js": "playground-f7ed4b7710.js", - "components/arc-tab.js": "components/arc-tab-efbcb40f74.js", - "components/arc-viewer.js": "components/arc-viewer-53669af1ac.js", - "css/index.css": "css/index-d0b9d5ab0b.css", - "css/styles.css": "css/styles-9dc95bf7f9.css", - "css/syntax.css": "css/syntax-605168cf6d.css" -} \ No newline at end of file diff --git a/readme.md b/readme.md index df2fe66d..ee0160f9 100644 --- a/readme.md +++ b/readme.md @@ -1,7 +1,51 @@ -[](https://www.npmjs.com/package/@architect/architect) + + + Architect Logo + -## [`https://arc.codes`](https://arc.codes) +

+ GitHub CI status + Apache-2.0 License +

-[![GitHub CI status](https://github.com/architect/arc.codes/workflows/Node%20CI/badge.svg)](https://github.com/architect/arc.codes/actions?query=workflow%3A%22Node+CI%22) +> Docs site for the OpenJS Architect framework! +[`https://arc.codes`](https://arc.codes) -> [Docs site for the Architect serverless framework](https://arc.codes)! +## Docs Development + +> ℹ️ We're actively improving Architect documentation, this repository, and this readme + +This docs site is its own Architect project 🎉 so it can be run locally with `npx arc sandbox` + +## Table of Contents & Sidebar + +The Sidebar is dynamically built from `table-of-contents.js` and the current state. + +The Table of Contents (TOC) is a simple set of objects containing arrays of strings that correspond with Markdown documents. + +> ⚠️ The fourth level of items will be grouped into a (closed by default) collapsible group. Use sparingly! + +## Markdown & Frontmatter + +All docs are written in Markdown. + +The [`arcdown` renderer](https://github.com/architect/arcdown) combines [markdown-it](https://www.npmjs.com/package/markdown-it) (+ plugins) and [frontmatter](https://www.npmjs.com/package/markdown-it-front-matter) to create HTML views on the fly. + +The frontmatter attributes are technically optional but highly encouraged as they aid the site's SEO. + +| attribute | type | effect | +|-----------------|------------|-----------------------| +| **title** | `string` | HTML title | +| **category** | `string` | HTML title prefix | +| **description** | `string` | HTML meta description | +| **sections** | `string[]` | currently unused | + +## Todo + +* dynamic category landing page +* writing style guide +* a homepage +* ~~dark mode~~ + +## Contributing +[Find out more about contributing to Architect](https://arc.codes/docs/en/about/contribute) diff --git a/scripts/deploy b/scripts/deploy deleted file mode 100755 index f9b0528e..00000000 --- a/scripts/deploy +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash -# Checks if last commit message was semver -# - If so, deploy to production -# - And always deploy to staging -TAG=`git log -1 --pretty=%B` -if echo "$TAG" | grep '^\([0-9]\{1,\}\.\?\)\{3\}'; then - npx arc deploy --production -fi -npx arc deploy --staging diff --git a/scripts/dictionary.js b/scripts/dictionary.js deleted file mode 100644 index 5b2f9f1e..00000000 --- a/scripts/dictionary.js +++ /dev/null @@ -1,290 +0,0 @@ -let dictionary = [ - '.vimrc', - '`build`ing', - 'ACM', - 'Alexa', - 'APIs', - 'arc-example-cors', - 'arc-repos', - 'arcana', - 'arcfile', - 'ARN', - 'ARNs', - 'async', - 'auth', - 'Auth', - 'authorizer', - 'authorizers', - 'authortime', - 'authortime', - 'automagical', - 'Automagical', - 'aws-sdk', - 'AWS', - 'AWSLambdaSQSQueueExecutionRole', - 'AZ', - 'backend', - 'Backends', - 'base64-encoded', - 'Base64', - 'bashrc', - 'birb', - 'blockchain', - 'Blockquoted', - 'Browserify', - 'BuddyBuild', - 'bundler', - 'bundlers', - 'bcrypt', - 'CA', - 'cacheControl', - 'Catalope', - 'cd', - 'CDN', - 'CDNs', - 'cheatsheet', - 'Chupacabra', - 'Changelog', - 'CLA', - 'CLI', - 'Cloudflare', - 'CloudFormation', - 'CloudFront', - 'ClouDNS', - 'CloudWatch', - 'CMK', - 'CNAME', - 'codebase', - 'Codeship', - 'coldstart', - 'config', - 'Config', - 'configs', - 'configurability', - 'congrats', - 'Congrats', - 'CORS', - 'cors', - 'CRON', - 'cron', - 'cross-env', - 'cruft', - 'cryptographically', - 'CSR', - 'CSRF', - 'css', - 'CSS', - 'Ctrl', - 'CVEs', - 'debuggable', - 'declaratively', - 'Declaratively', - 'Deno', - 'deployable', - 'deploytime', - 'deps', - 'deterministically', - 'dev', - 'devdep', - 'devs', - 'dir', - 'disambiguate', - 'disambiguates', - 'DNS-based', - 'DNS', - 'DNSimple', - 'DocumentClient', - 'DocumentDB', - 'Dyn', - 'DynamoDB.', - 'DynamoDB', - 'dynalite', - 'easyDNS', - 'EC2', - 'El', - 'endpoint', - 'endpoints', - 'env', - 'envs', - 'errback', - 'eslint', - 'esmodules', - 'EventBridge', - 'filesystem', - 'filetype', - 'FQDN', - 'frontend', - 'globals', - 'hashids', - 'hardcode', - 'hardcoding', - 'headless', - 'headlessly', - 'hostname', - 'hoc', - 'html', - 'HTTP', - 'httpOnly', - 'HTTPS', - 'IaC', - 'IAM', - 'ing', - 'init', - 'installable', - 'integrations', - 'internets', - 'isBase64Encoded', - 'js', - 'JS', - 'JSF', - 'JSON-like', - 'JSON-style', - 'JSON', - 'json', - 'jsx', - 'JSX', - 'LEANX', - 'learnable', - 'learnings', - 'linter', - 'LTS', - 'macOS', - 'Microservices', - 'middleware', - 'Middleware', - 'minify', - 'mjs', - 'MMO', - 'Mongo', - 'MongoDB', - 'NameCheap', - 'namespace', - 'namespaces', - 'nav', - 'NewOps', - 'Node', - 'NodeJS', - 'Nodemon', - 'non-serverless', - 'NPM', - 'NS1', - 'OAuth', - 'Ogopogo', - 'ok', - 'Ok', - 'OpenJS', - 'param', - 'params', - 'Params', - 'ParcelJS', - 'payload', - 'payloads', - 'PEM', - 'per-pragma', - 'plaintext', - 'powershell', - 'PowerShell', - 'pragma', - 'pragmas', - 'pre-compiled', - 'pre-installed', - 'pre-parsed', - 'pre-provision', - 'pre-wired', - 'Prerendering', - 'prepopulated', - 'privs', - 'protip', - 'Protip', - 'proxying', - 'Proxying', - 'PRs', - 'quickstart', - 'Quickstart', - 'readme', - 'Reference', - 'renderer', - 'repeatable', - 'REPL', - 'repo', - 'repos', - 'resiliance', - 'roadmap', - 'Roadmap', - 'Route53', - 'RSA', - 'Running', - 'runs', - 'runtime', - 'Runtime', - 'runtimes', - 'Runtimes', - 'S3.', - 'S3', - 'scalable', - 'SCSS', - 'SDK', - 'SemVer', - 'serverless', - 'Serverless', - 'serverlessly', - 'SHA', - 'signup', - 'Signup', - 'signups', - 'SNS', - 'SQS', - 'SSL', - 'SSM', - 'src', - 'stateful', - 'statusCode', - 'stderr', - 'stdout', - 'subdirectories', - 'subdomain', - 'subdomains', - 'symlinking', - 'svg', - 'teardown', - 'TestFlight', - 'tiny-json-http', - 'tldr', - 'transpilation', - 'transpile', - 'transpiled', - 'transpilers', - 'transpiling', - 'truthy', - 'tsx', - 'TSX', - 'TTL', - 'UI', - 'unauthenticated', - 'unbreak', - 'unformatted', - 'Unordered', - 'unparsed', - 'unsynced', - 'unthrottled', - 'userland', - 'v5', - 'vars', - 'vimrc', - 'vs', - 'webby', - 'webhooks', - 'Webpack', - 'WebSocket', - 'WebSockets', - 'whitespace', - 'XSS', - 'xml', - 'YAML', - 'ZoneEdit', -] - -dictionary.unshift(/[KMG]B/) // Data quantities, ostensibly - -module.exports = dictionary - diff --git a/scripts/dictionary.txt b/scripts/dictionary.txt new file mode 100644 index 00000000..5f8ab262 --- /dev/null +++ b/scripts/dictionary.txt @@ -0,0 +1,30 @@ +ℹ +&rarr +ACM +ApiGatewayManagementApi +arc-example-cors +Bundler +CJS +Cloudflare +CommonJS +csv|CSV|.csv +Dreamhost +ESM +FQDN +FWAs +getConnection +getter +GitLab +GoDaddy +Graviton2 +HostedZone +konsumer +LEANX +Namecheap +Omni +OmniLayer +Omniwallet +ParcelJS +PNC +treeshaking +scaffolded diff --git a/scripts/spellcheckerrc.json b/scripts/spellcheckerrc.json new file mode 100644 index 00000000..47a8c96d --- /dev/null +++ b/scripts/spellcheckerrc.json @@ -0,0 +1,27 @@ +{ + "files": ["./src/views/docs/en/**/*.md"], + "quiet": true, + "noSuggestions": true, + "dictionaries": [ + "./scripts/dictionary.txt", + "./node_modules/@architect/spellcheck-dictionary/index.js" + ], + "plugins": [ + "spell", + "indefinite-article", + "repeated-words", + "syntax-mentions", + "syntax-urls", + "frontmatter" + ], + "frontmatterKeys": [ + "title", + "category", + "description", + "sections" + ], + "ignore": [ + "v\\d?.\\d?", + ".*[\\u00B9\\u00B2\\u00B3\\u2070-\\u2079]" + ] +} diff --git a/src/http/any-catchall/index.js b/src/http/any-catchall/index.js deleted file mode 100644 index 39a08843..00000000 --- a/src/http/any-catchall/index.js +++ /dev/null @@ -1,14 +0,0 @@ -let { http } = require('@architect/functions') - -// middleware to preserve old urls -let redirect = require('./redirect') - -// middleware proxy s3 assets -let asap = http.proxy({ - spa: false, - alias: { - '/playground': '/playground.html' - } -}) - -exports.handler = http.async(redirect, asap) diff --git a/src/http/any-catchall/index.mjs b/src/http/any-catchall/index.mjs new file mode 100644 index 00000000..7b2a50b6 --- /dev/null +++ b/src/http/any-catchall/index.mjs @@ -0,0 +1,42 @@ +import arc from '@architect/functions' +import asap from '@architect/asap' +import { redirect as redirectMiddleware } from '@architect/shared/redirect-map.mjs' +import notFoundResponse from '@architect/shared/not-found-response.mjs' +import toc from '@architect/views/docs/table-of-contents.mjs' +import Html from '@architect/views/modules/document/html.mjs' +import NotFound from '@architect/views/modules/components/not-found.mjs' +import algolia from '@architect/views/modules/components/algolia.mjs' + +// middleware proxy s3 assets +const staticProxy = asap({ + alias: { '/playground': '/playground.html' }, + passthru: true, + spa: false, +}) + +async function robots (req) { + if (req.path === '/robots.txt') { + const headers = { 'content-type': 'text/plain; charset=utf8' } + const allow = 'User-agent: *\nDisallow: ' + const disallow = 'User-agent: *\nDisallow: /' + if (process.env.ARC_ENV === 'production') return { headers, body: allow } + return { headers, body: disallow } + } +} + +async function notFound (req) { + const term = req.path + + return { + ...notFoundResponse, + body: Html({ + active: term, + html: NotFound({ term }), + scripts: [ '/index.js' ], + thirdparty: algolia, + toc, + }), + } +} + +export const handler = arc.http.async(redirectMiddleware, robots, staticProxy, notFound) diff --git a/src/http/any-catchall/redirect.js b/src/http/any-catchall/redirect.js deleted file mode 100644 index 721add1e..00000000 --- a/src/http/any-catchall/redirect.js +++ /dev/null @@ -1,90 +0,0 @@ -// redirect v6 arc urls to v8 -let redirects = { - // tmp until marketing landing page work done - '/': '/docs/en/guides/get-started/quickstart', - - // Intro - '/intro/philosophy': '/docs/en/guides/get-started/why-architect', - '/intro/limits': '/docs/en/guides/get-started/detailed-aws-setup', - '/intro/community': '/docs/en/about/contribute', - - // Quickstart - '/quickstart': '/docs/en/guides/get-started/quickstart', - '/quickstart/install': '/docs/en/guides/get-started/quickstart', - '/quickstart/layout': '/docs/en/guides/get-started/project-layout', - - // Primitives - '/primitives/http': '/docs/en/reference/app.arc/http', - '/primitives/ws': '/docs/en/reference/app.arc/ws', - '/primitives/static': '/docs/en/reference/app.arc/static', - '/primitives/cdn': '/docs/en/reference/app.arc/static', - '/primitives/scheduled': '/docs/en/reference/app.arc/scheduled', - '/primitives/events': '/docs/en/reference/app.arc/events', - '/primitives/queues': '/docs/en/reference/app.arc/queues', - '/primitives/tables': '/docs/en/reference/app.arc/tables', - '/primitives/macros': '/docs/en/reference/app.arc/macros', - - // Guides - '/guides/upgrade': '/docs/en/about/upgrade', - '/guides/testing': '/docs/en/guides/developer-experience/local-development', - '/guides/project-manifest': '/docs/en/guides/get-started/project-layout', - '/guides/share-code': '/docs/en/guides/developer-experience/sharing-code', - '/guides/custom-file-paths': '/docs/en/guides/developer-experience/custom-source-paths', - - // CLI - '/reference/cli/deploy': '/docs/en/reference/cli/deploy', - '/reference/cli/destroy': '/docs/en/reference/cli/destroy', - '/reference/cli/env': '/docs/en/reference/cli/env', - '/reference/cli/hydrate': '/docs/en/reference/cli/sandbox', - '/reference/cli/init': '/docs/en/reference/cli/init', - '/reference/cli/logs': '/docs/en/reference/cli/logs', - '/reference/cli/package': '/docs/en/reference/cli/deploy', - '/reference/cli/sandbox': '/docs/en/reference/cli/sandbox', - - '/reference/arc/app': '/docs/en/reference/app.arc/app', - '/reference/arc/aws': '/docs/en/reference/app.arc/aws', - '/reference/arc/events': '/docs/en/reference/app.arc/events', - '/reference/arc/http': '/docs/en/reference/app.arc/http', - '/reference/arc/indexes': '/docs/en/reference/app.arc/indexes', - '/reference/arc/proxy': '/docs/en/reference/app.arc/proxy', - '/reference/arc/queues': '/docs/en/reference/app.arc/queues', - '/reference/arc/scheduled': '/docs/en/reference/app.arc/scheduled', - '/reference/arc/static': '/docs/en/reference/app.arc/static', - '/reference/arc/tables': '/docs/en/reference/app.arc/tables', - '/reference/arc/ws': '/docs/en/reference/app.arc/ws', - - '/reference/arc-config/aws': '/docs/en/reference/config.arc/aws', - '/reference/arc-config/runtime': '/docs/en/reference/config.arc/runtime', - '/reference/arc-config/memory': '/docs/en/reference/config.arc/memory', - '/reference/arc-config/timeout': '/docs/en/reference/config.arc/timeout', - '/reference/arc-config/concurrency': '/docs/en/reference/config.arc/concurrency', - '/reference/arc-config/layers': '/docs/en/reference/config.arc/layers', - '/reference/arc-config/policies': '/docs/en/reference/config.arc/policies', - - '/reference/preferences#create': '/docs/en/reference/prefs.arc/create', - '/reference/preferences#env': '/docs/en/reference/prefs.arc/env', - '/reference/preferences#.env': '/docs/en/reference/prefs.arc/.env', - '/reference/preferences#sandbox': '/docs/en/reference/prefs.arc/sandbox', - - '/reference/functions/events': '/docs/en/reference/runtime/node', - '/reference/functions/http': '/docs/en/reference/runtime/node', - '/reference/functions/http/node/async': '/docs/en/reference/runtime/node', - '/reference/functions/queues': '/docs/en/reference/runtime/node', - '/reference/functions/static': '/docs/en/reference/runtime/node', - '/reference/functions/tables': '/docs/en/reference/runtime/node', - '/reference/functions/ws': '/docs/en/reference/runtime/node', -} - -// eslint-disable-next-line -module.exports = async function redirect (req) { - let isGet = req.requestContext.http.method.toLowerCase() === 'get' - let isPath = Object.keys(redirects).includes(req.requestContext.http.path) - if (isGet && isPath) { - return { - statusCode: 301, - headers: { - location: redirects[req.requestContext.http.path] - } - } - } -} diff --git a/src/http/get-api-package/index.js b/src/http/get-api-package/index.js deleted file mode 100644 index 9b5c3354..00000000 --- a/src/http/get-api-package/index.js +++ /dev/null @@ -1,26 +0,0 @@ -const inventory = require('@architect/inventory') -const pkg = require('@architect/package') - -// serialize ?arc=base64arcfile into cloudformation -exports.handler = async function http (req) { - let statusCode = 200 - let body - - try { - let rawArc = Buffer.from(req.queryStringParameters.arc, 'base64').toString() - body = JSON.stringify(pkg(await inventory({ rawArc }))) - } - catch (e) { - statusCode = 500 - body = JSON.stringify({ - name: e.name, - message: e.message, - stack: e.stack - }) - } - - return { - statusCode, - body - } -} diff --git a/src/http/get-api-package/index.mjs b/src/http/get-api-package/index.mjs new file mode 100644 index 00000000..c731e163 --- /dev/null +++ b/src/http/get-api-package/index.mjs @@ -0,0 +1,26 @@ +import inventory from '@architect/inventory' +import pkg from '@architect/package' + +export async function handler (req) { + let statusCode = 200 + let body + + try { + const rawArc = Buffer.from(req.queryStringParameters.arc, 'base64').toString() + const inv = await inventory({ rawArc, deployStage: 'staging' }) + body = JSON.stringify(pkg(inv)) + } + catch (e) { + statusCode = 500 + body = JSON.stringify({ + name: e.name, + message: e.message, + stack: e.stack, + }) + } + + return { + statusCode, + body, + } +} diff --git a/src/http/get-docs-000lang-catchall/arc-grammar.js b/src/http/get-docs-000lang-catchall/arc-grammar.js deleted file mode 100644 index 8fe2e4a8..00000000 --- a/src/http/get-docs-000lang-catchall/arc-grammar.js +++ /dev/null @@ -1,55 +0,0 @@ -module.exports = function (hljs) { - const LITERALS = [ - 'true', - 'false', - 'null' - ].join(' ') - const PRAGMAS = { - className: 'built_in', - begin: '^@.*' - } - return { - aliases: [ - 'arc', - 'architect' - ], - case_insensitive: true, - keywords: { - literal: LITERALS, - keyword: [ - 'get', - 'post', - 'put', - 'patch', - 'delete', - 'options', - 'head', - 'any', - 'region', - 'profile', - 'runtime', - 'bucket', - 'httpv1', - 'rest', - 'apigateway', - 'fingerprint', - 'folder', - 'staging', - 'production', - 'session', - '_idx', - '_ttl' - ].join(' ') - }, - contains: [ - { - className: 'string', - begin: "'", end: "'" - }, - hljs.COMMENT( - '#.*' - ), - PRAGMAS - ] - } -} diff --git a/src/http/get-docs-000lang-catchall/highlighter.js b/src/http/get-docs-000lang-catchall/highlighter.js deleted file mode 100644 index 2b6481aa..00000000 --- a/src/http/get-docs-000lang-catchall/highlighter.js +++ /dev/null @@ -1,12 +0,0 @@ -module.exports = function (hljs, escapeHtml, str, lang) { - if (lang && hljs.getLanguage(lang)) { - try { - return `
${hljs.highlight(lang, str, true).value}
` - } - catch (err) { - console.error(err) - } - } - - return `
${escapeHtml(str)}
` -} diff --git a/src/http/get-docs-000lang-catchall/index.js b/src/http/get-docs-000lang-catchall/index.js deleted file mode 100644 index 4210b971..00000000 --- a/src/http/get-docs-000lang-catchall/index.js +++ /dev/null @@ -1,114 +0,0 @@ -require = require('esm')(module) // eslint-disable-line -const path = require('path') -const util = require('util') -const fs = require('fs') -const Markdown = require('markdown-it') -const markdownClass = require('@toycode/markdown-it-class') -const markdownAnchor = require('markdown-it-anchor') -const frontmatterParser = require('markdown-it-front-matter') -const classMapping = require('./markdown-class-mappings') -const hljs = require('highlight.js') -const { escapeHtml } = Markdown().utils -const highlight = require('./highlighter') - .bind(null, hljs, escapeHtml) -const arcGrammar = require('./arc-grammar') -hljs.registerLanguage('arc', arcGrammar) -const readFile = util.promisify(fs.readFile) -const Html = require('@architect/views/modules/document/html.js').default -const toc = require('@architect/views/docs/table-of-contents') -const yaml = require('js-yaml') -const EDIT_DOCS = `edit/main/src/views/docs/` -const cache = {} // cheap warm cache - -exports.handler = async function http (req) { - let { pathParameters } = req - let { lang, proxy } = pathParameters - let parts = proxy.split('/') - let docName = parts.pop() - - if (docName === 'playground') - return { statusCode: 303, headers: { location: '/playground' } } - - let doc = `${docName}.md` - let activePath = path.join( - 'docs', - lang, - ...parts, - docName - ) - let editURL = 'https://github.com/architect/arc.codes/' - editURL += path.join( - EDIT_DOCS, - lang, - ...parts, - doc - ) - // Add leading slash to match anchor href - let active = `/${activePath}` - - let filePath = path.join( - __dirname, - 'node_modules', - '@architect', - 'views', - 'docs', - lang, - ...parts, - doc - ) - let file - try { - if (!cache[filePath]) - cache[filePath] = await readFile(filePath, 'utf8') - file = cache[filePath] - } - catch (err) { - // TODO: Load next doc in section - console.error(err) - return { - statusCode: 404, - body: err.message - } - } - // Declare in outer scope for use later... sorry - let frontmatter = '' - const md = Markdown({ - highlight, - linkify: true, - html: true, - typography: true - }) - .use(markdownClass, classMapping) - .use(markdownAnchor, { - permalinkSymbol: ' ' - }) - .use(frontmatterParser, function (str) { - frontmatter = yaml.load(str) - }) - const children = md.render(file) - const { category, description, sections, title } = frontmatter - - return { - statusCode: 200, - headers: { - 'cache-control': 'no-cache, no-store, must-revalidate, max-age=0, s-maxage=0', - 'content-type': 'text/html; charset=utf8' - }, - body: Html({ - active, - category, - children, - description, - editURL, - lang, - sections, - thirdparty: ` - - - - `, - title, - toc - }) - } -} diff --git a/src/http/get-docs-000lang-catchall/index.mjs b/src/http/get-docs-000lang-catchall/index.mjs new file mode 100644 index 00000000..9856b3a8 --- /dev/null +++ b/src/http/get-docs-000lang-catchall/index.mjs @@ -0,0 +1,116 @@ +import { readFileSync } from 'fs' +import { join } from 'path' +import arc from '@architect/functions' +import { Arcdown } from 'arcdown' +import anchor from 'markdown-it-anchor' +import markdownItArcStaticImg from 'markdown-it-arc-static-img' +import { redirect as redirectMiddleware } from '@architect/shared/redirect-map.mjs' +import notFoundResponse from '@architect/shared/not-found-response.mjs' +import algolia from '@architect/views/modules/components/algolia.mjs' +import Html from '@architect/views/modules/document/html.mjs' +import NotFound from '@architect/views/modules/components/not-found.mjs' +import toc from '@architect/views/docs/table-of-contents.mjs' +import classMap from './markdown-class-mappings.mjs' + +const cache = {} // cheap warm cache + +async function handler (req) { + const { path, pathParameters } = req + const { lang, proxy } = pathParameters + const parts = proxy.split('/') + const docName = parts.pop() + + if (docName === 'playground') + return { statusCode: 303, headers: { location: '/playground' } } + + const doc = `${docName}.md` + const active = join( + '/docs', + lang, + ...parts, + docName, + ) + let editURL = 'https://github.com/architect/arc.codes/edit/main/src/views/docs/' + editURL += join(lang, ...parts, doc) + + const filePath = join( + + new URL('.', import.meta.url).pathname, + 'node_modules', + '@architect', + 'views', + 'docs', + lang, + ...parts, + doc, + ) + + try { + let body + + if (cache[filePath]) { + body = cache[filePath] + } + else { + const md = readFileSync(filePath, 'utf8') + const arcdown = new Arcdown({ + hljs: { classString: 'hljs mb0 mb1-lg relative' }, + plugins: { markdownItArcStaticImg }, + pluginOverrides: { + markdownItClass: classMap, + markdownItToc: { + containerClass: 'toc', + }, + markdownItAnchor: { + permalink: anchor.permalink.headerLink({ + class: 'text-p1 text-h1 text-a2 no-underline underline-h', + }), + }, + }, + }) + const result = await arcdown.render(md) + body = cache[filePath] = Html({ + ...result, + active, + editURL, + lang, + path, + scripts: [ + '/index.js', + '/components/arc-viewer.js', + '/components/arc-tab.js', + ], + thirdparty: algolia(lang), + toc, + }) + } + + return { + statusCode: 200, + headers: { + 'cache-control': 'no-cache, no-store, must-revalidate, max-age=0, s-maxage=0', + 'content-type': 'text/html; charset=utf8', + }, + body, + } + } + catch (error) { + // TODO: Load category "index" landing if available + console.error(error) + return { + ...notFoundResponse, + body: Html({ + active, + html: NotFound({ term: docName, error }), + lang, + scripts: [ '/index.js' ], + state: { notFoundTerm: docName }, + thirdparty: algolia(lang), + toc, + }), + } + } +} + +const _handler = arc.http.async(redirectMiddleware, handler) +export { _handler as handler } diff --git a/src/http/get-docs-000lang-catchall/markdown-class-mappings.js b/src/http/get-docs-000lang-catchall/markdown-class-mappings.mjs similarity index 57% rename from src/http/get-docs-000lang-catchall/markdown-class-mappings.js rename to src/http/get-docs-000lang-catchall/markdown-class-mappings.mjs index 0c17178b..d254bd62 100644 --- a/src/http/get-docs-000lang-catchall/markdown-class-mappings.js +++ b/src/http/get-docs-000lang-catchall/markdown-class-mappings.mjs @@ -1,84 +1,100 @@ -module.exports = { +export default { h1: [ - 'mb2', + 'mb3', 'font-semibold', - 'text2' + 'text5', ], h2: [ 'mt3', 'mb-1', 'font-semibold', - 'text1' + 'text4', ], h3: [ 'mt3', 'mb-1', 'font-semibold', - 'text0' + 'text3', ], h4: [ 'mt3', 'mb-1', 'font-semibold', - 'text0' + 'text2', + ], + h5: [ + 'mt3', + 'mb-1', + 'font-semibold', + 'text1', ], p: [ - 'mb-1' + 'mb-1', + ], + ol: [ + 'mb1', ], ul: [ - 'mb1' + 'mb1', ], li: [ - 'ml-1' + 'ml-1', ], blockquote: [ 'mb-1', 'p0', - 'text-g8', + 'text-g9', 'bg-g1', 'border-solid', 'border-t0', 'border-r0', 'border-b0', 'border-l4', - 'border-g3' + 'border-g3', ], a: [ 'font-medium', 'text-p1', - 'text-h1' + 'text-h1', + 'no-underline', + ], + hr: [ + 'mt3', + 'mb3', + 'border-solid', + 'border0', + 'border-b1', ], strong: [ 'font-semibold', - 'text-g9' + 'text-g9', ], table: [ - 'table-fixed', 'w-full', 'mb1', 'border-collapse', 'border-solid', 'border-g3', - 'border1' + 'border1', ], th: [ - 'pt0', - 'pr1', - 'pl1', - 'pb0', + 'pt-3', + 'pr-3', + 'pl-3', + 'pb-3', 'border-solid', 'border1', 'border-g3', - 'bg-g1' + 'bg-g1', ], td: [ - 'pt0', - 'pr1', - 'pl1', - 'pb0', + 'pt-3', + 'pr-3', + 'pl-3', + 'pb-3', 'border-solid', 'border-g1', 'border1', - 'bg-g0' - ] + 'bg-g0', + ], } diff --git a/src/http/get-index/index.mjs b/src/http/get-index/index.mjs new file mode 100644 index 00000000..a33d1380 --- /dev/null +++ b/src/http/get-index/index.mjs @@ -0,0 +1,86 @@ +import arc from '@architect/functions' +import enhance from '@enhance/ssr' +import styleTransform from '@enhance/enhance-style-transform' +import { getStyles } from '@enhance/arc-plugin-styles' + +import elements from '@architect/views/landing/elements.mjs' + +export async function handler () { + const html = enhance({ + styleTransforms: [ styleTransform ], + elements, + }) + + return { + statusCode: 200, + headers: { + 'cache-control': process.env.ARC_ENV === 'production' + ? 'max-age=86400' + : 'no-cache, no-store, must-revalidate, max-age=0, s-maxage=0', + 'content-type': 'text/html; charset=utf8', + }, + body: html` + + + + + + + + + + + + + + + + + + + + + + + + Architect + + ${getStyles.styleTag()} + + + + + + + + +`, + } +} diff --git a/src/plugins/spellcheck.js b/src/plugins/spellcheck.js new file mode 100644 index 00000000..5897ad33 --- /dev/null +++ b/src/plugins/spellcheck.js @@ -0,0 +1,39 @@ +/* eslint-disable import/no-commonjs */ +const { updater } = require('@architect/utils') +const { spawn } = require('child_process') +const pkg = require('../../package.json') +const cmd = pkg.scripts.spellcheck.split(' ') +const update = updater('Spelling') + +module.exports = { + sandbox: { + watcher: async ({ filename }) => { + if (!filename.endsWith('.md')) return + return new Promise((res) => { + update.start('Checking spelling') + const start = Date.now() + const done = () => update.done(`Checked spelling in ${(Date.now() - start) / 1000} seconds`) + const spell = spawn(cmd[0], cmd.slice(1)) + let found = false + const log = data => { + if (!found) { + done() + update.warn(`Found spelling or grammar error(s):`) + } + found = true + console.log(data.toString()) + } + spell.stdout.on('data', log) + spell.stderr.on('data', log) + spell.on('close', code => { + if (!code) { + done() + update.done('No spelling or grammar errors found!') + } + // Always resolve, errors should have been printed already + res() + }) + }) + }, + }, +} diff --git a/src/shared/not-found-response.mjs b/src/shared/not-found-response.mjs new file mode 100644 index 00000000..59be5242 --- /dev/null +++ b/src/shared/not-found-response.mjs @@ -0,0 +1,8 @@ +export default { + statusCode: 404, + headers: { + 'cache-control': 'no-cache, no-store, must-revalidate, max-age=0, s-maxage=0', + 'content-type': 'text/html; charset=utf8', + }, + body: '

404: Not found

', +} diff --git a/src/shared/redirect-map.mjs b/src/shared/redirect-map.mjs new file mode 100644 index 00000000..b803eeb4 --- /dev/null +++ b/src/shared/redirect-map.mjs @@ -0,0 +1,277 @@ +export const currentRoot = '/docs/en/get-started/quickstart' + +// these are soft redirects, not forever/canonical +export const tempRedirects = { + // Canonical pragma paths + '/app': '/docs/en/reference/project-manifest/app', + '/@app': '/docs/en/reference/project-manifest/app', + '/aws': '/docs/en/reference/project-manifest/aws', + '/@aws': '/docs/en/reference/project-manifest/aws', + '/events': '/docs/en/reference/project-manifest/events', + '/@events': '/docs/en/reference/project-manifest/events', + '/http': '/docs/en/reference/project-manifest/http', + '/@http': '/docs/en/reference/project-manifest/http', + '/indexes': '/docs/en/reference/project-manifest/tables-indexes', + '/@indexes': '/docs/en/reference/project-manifest/tables-indexes', + '/macros': '/docs/en/reference/project-manifest/macros', + '/@macros': '/docs/en/reference/project-manifest/macros', + '/plugins': '/docs/en/reference/project-manifest/plugins', + '/@plugins': '/docs/en/reference/project-manifest/plugins', + '/proxy': '/docs/en/reference/project-manifest/proxy', + '/@proxy': '/docs/en/reference/project-manifest/proxy', + '/queues': '/docs/en/reference/project-manifest/queues', + '/@queues': '/docs/en/reference/project-manifest/queues', + '/scheduled': '/docs/en/reference/project-manifest/scheduled', + '/@scheduled': '/docs/en/reference/project-manifest/scheduled', + '/shared': '/docs/en/reference/project-manifest/shared', + '/@shared': '/docs/en/reference/project-manifest/shared', + '/static': '/docs/en/reference/project-manifest/static', + '/@static': '/docs/en/reference/project-manifest/static', + '/tables': '/docs/en/reference/project-manifest/tables', + '/@tables': '/docs/en/reference/project-manifest/tables', + '/tables-indexes': '/docs/en/reference/project-manifest/tables-indexes', + '/@tables-indexes': '/docs/en/reference/project-manifest/tables-indexes', + '/tables-streams': '/docs/en/reference/project-manifest/tables-streams', + '/@tables-streams': '/docs/en/reference/project-manifest/tables-streams', + '/views': '/docs/en/reference/project-manifest/views', + '/@views': '/docs/en/reference/project-manifest/views', + '/ws': '/docs/en/reference/project-manifest/ws', + '/@ws': '/docs/en/reference/project-manifest/ws', + + // Runtimes + '/node': '/docs/en/reference/runtime-helpers/node.js', + '/ruby': '/docs/en/reference/runtime-helpers/ruby', + '/python': '/docs/en/reference/runtime-helpers/python', + '/deno': '/docs/en/reference/runtime-helpers/deno', + + // Other aliases + '/typescript': '/docs/en/guides/developer-experience/using-typescript', + '/esm': '/docs/en/guides/developer-experience/using-esm', + '/aws-sdk-versions': '/docs/en/get-started/detailed-aws-setup#aws-sdk', +} + +// redirect known v5/6 arc urls to v8 and then to v9 +export const permanentRedirects = { + '/examples': '/docs/en/guides/examples', + + // Intro + // round 1: Q1 2021 + '/intro/philosophy': '/docs/en/get-started/why-architect', + '/intro/limits': '/docs/en/get-started/detailed-aws-setup', + '/intro/community': '/docs/en/about/contribute', + + // Quickstart + // round 1: Q1 2021 + '/quickstart': '/docs/en/get-started/quickstart', + '/quickstart/install': '/docs/en/get-started/quickstart', + '/quickstart/layout': '/docs/en/get-started/project-manifest', + '/quickstart/what-next': '/docs/en/get-started/quickstart', + + // Primitives + // round 1: Q1 2021 + '/primitives/http': '/docs/en/reference/project-manifest/http', + '/primitives/ws': '/docs/en/reference/project-manifest/ws', + '/primitives/static': '/docs/en/reference/project-manifest/static', + '/primitives/cdn': '/docs/en/reference/project-manifest/static', + '/primitives/scheduled': '/docs/en/reference/project-manifest/scheduled', + '/primitives/events': '/docs/en/reference/project-manifest/events', + '/primitives/queues': '/docs/en/reference/project-manifest/queues', + '/primitives/tables': '/docs/en/reference/project-manifest/tables', + '/primitives/macros': '/docs/en/reference/project-manifest/macros', + + // Guides + // round 1: Q1 2021 + '/guides/upgrade': '/docs/en/about/upgrade-guide', + '/guides/testing': '/docs/en/guides/developer-experience/local-development', + '/guides/project-manifest': '/docs/en/get-started/project-manifest', + '/guides/share-code': '/docs/en/guides/developer-experience/sharing-code', + '/guides/sharing-common-code': '/docs/en/guides/developer-experience/sharing-code', + '/guides/custom-file-paths': '/docs/en/guides/developer-experience/custom-source-paths', + // round 2: Q4 2021 + '/docs/en/guides/get-started/why-architect': '/docs/en/get-started/why-architect', + '/docs/en/guides/get-started/quickstart': '/docs/en/get-started/quickstart', + '/docs/en/guides/get-started/project-layout': '/docs/en/get-started/project-manifest', + '/docs/en/guides/get-started/detailed-aws-setup': '/docs/en/get-started/detailed-aws-setup', + '/docs/en/guides/extend/custom-cloudformation': '/docs/en/guides/developer-experience/custom-cloudformation', + // mid-2022 + '/docs/en/guides/developer-experience/customizing-cloudformation': '/docs/en/guides/developer-experience/custom-cloudformation', + + // Reference > Project Manifest + // round 1: Q1 2021 + '/reference/arc/app': '/docs/en/reference/project-manifest/app', + '/reference/arc/aws': '/docs/en/reference/project-manifest/aws', + '/reference/arc/events': '/docs/en/reference/project-manifest/events', + '/reference/arc/http': '/docs/en/reference/project-manifest/http', + '/reference/arc/indexes': '/docs/en/reference/project-manifest/tables-indexes', + '/reference/arc/proxy': '/docs/en/reference/project-manifest/proxy', + '/reference/arc/queues': '/docs/en/reference/project-manifest/queues', + '/reference/arc/scheduled': '/docs/en/reference/project-manifest/scheduled', + '/reference/arc/static': '/docs/en/reference/project-manifest/static', + '/reference/arc/tables': '/docs/en/reference/project-manifest/tables', + '/reference/arc/ws': '/docs/en/reference/project-manifest/ws', + // round 2: Q4 2021 + '/docs/en/reference/app.arc/app': '/docs/en/reference/project-manifest/app', + '/docs/en/reference/app.arc/aws': '/docs/en/reference/project-manifest/aws', + '/docs/en/reference/app.arc/events': '/docs/en/reference/project-manifest/events', + '/docs/en/reference/app.arc/http': '/docs/en/reference/project-manifest/http', + '/docs/en/reference/app.arc/indexes': '/docs/en/reference/project-manifest/tables-indexes', + '/docs/en/reference/app.arc/proxy': '/docs/en/reference/project-manifest/proxy', + '/docs/en/reference/app.arc/queues': '/docs/en/reference/project-manifest/queues', + '/docs/en/reference/app.arc/scheduled': '/docs/en/reference/project-manifest/scheduled', + '/docs/en/reference/app.arc/shared': '/docs/en/reference/project-manifest/shared', + '/docs/en/reference/app.arc/static': '/docs/en/reference/project-manifest/static', + '/docs/en/reference/app.arc/tables': '/docs/en/reference/project-manifest/tables', + '/docs/en/reference/app.arc/views': '/docs/en/reference/project-manifest/views', + '/docs/en/reference/app.arc/ws': '/docs/en/reference/project-manifest/ws', + // rename @indexes => @tables-indexes + '/docs/en/reference/project-manifest/indexes': '/docs/en/reference/project-manifest/tables-indexes', + + // Reference > Configuration > Function config + // round 1: Q1 2021 + '/reference/arc-config/aws': '/docs/en/reference/configuration/function-config', + '/reference/arc-config/runtime': '/docs/en/reference/configuration/function-config#runtime', + '/reference/arc-config/memory': '/docs/en/reference/configuration/function-config#memory', + '/reference/arc-config/timeout': '/docs/en/reference/configuration/function-config#timeout', + '/reference/arc-config/concurrency': '/docs/en/reference/configuration/function-config#concurrency', + '/reference/arc-config/layers': '/docs/en/reference/configuration/function-config#layers', + '/reference/arc-config/policies': '/docs/en/reference/configuration/function-config#policies', + // round 2: Q4 2021 + '/docs/en/reference/config.arc/aws': '/docs/en/reference/configuration/function-config', + '/docs/en/reference/config.arc/runtime': '/docs/en/reference/configuration/function-config#runtime', + '/docs/en/reference/config.arc/memory': '/docs/en/reference/configuration/function-config#memory', + '/docs/en/reference/config.arc/timeout': '/docs/en/reference/configuration/function-config#timeout', + '/docs/en/reference/config.arc/concurrency': '/docs/en/reference/configuration/function-config#concurrency', + '/docs/en/reference/config.arc/layers': '/docs/en/reference/configuration/function-config#layers', + '/docs/en/reference/config.arc/policies': '/docs/en/reference/configuration/function-config#policies', + '/docs/en/reference/config.arc/architecture': '/docs/en/reference/configuration/function-config#architecture', + + // Reference > Configuration > Local preferences + // round 1: Q1 2021 + '/reference/preferences#create': '/docs/en/reference/configuration/local-preferences#@create', + '/reference/preferences#env': '/docs/en/reference/configuration/local-preferences#@env', + '/reference/preferences#.env': '/docs/en/reference/configuration/local-preferences#@env', + '/reference/preferences#sandbox': '/docs/en/reference/configuration/local-preferences#@sandbox', + // round 2: Q4 2021 + '/docs/en/reference/prefs.arc/create': '/docs/en/reference/configuration/local-preferences#@create', + '/docs/en/reference/prefs.arc/env': '/docs/en/reference/configuration/local-preferences#@env', + '/docs/en/reference/prefs.arc/.env': '/docs/en/reference/configuration/local-preferences#@env', + '/docs/en/reference/prefs.arc/sandbox': '/docs/en/reference/configuration/local-preferences#@sandbox', + '/docs/en/reference/prefs.arc/sandbox-startup': '/docs/en/reference/configuration/local-preferences#@sandbox-start', + + // Reference > CLI + // round 1: Q1 2021 + '/reference/cli/deploy': '/docs/en/reference/cli/deploy', + '/reference/cli/package': '/docs/en/reference/cli/deploy', + '/reference/cli/destroy': '/docs/en/reference/cli/destroy', + '/reference/cli/env': '/docs/en/reference/cli/env', + '/reference/cli/init': '/docs/en/reference/cli/init', + '/reference/cli/logs': '/docs/en/reference/cli/logs', + '/reference/cli/sandbox': '/docs/en/reference/cli/sandbox', + '/reference/cli/hydrate': '/docs/en/reference/cli/sandbox', + // round 2: Q4 2021 - no change + + // Reference > Runtime helpers + // round 1: Q1 2021 + '/reference/functions/events': '/docs/en/reference/runtime-helpers/node.js', + '/reference/functions/http': '/docs/en/reference/runtime-helpers/node.js', + '/reference/functions/http/node/async': '/docs/en/reference/runtime-helpers/node.js', + '/reference/functions/queues': '/docs/en/reference/runtime-helpers/node.js', + '/reference/functions/static': '/docs/en/reference/runtime-helpers/node.js', + '/reference/functions/tables': '/docs/en/reference/runtime-helpers/node.js', + '/reference/functions/ws': '/docs/en/reference/runtime-helpers/node.js', + // round 2: Q4 2021 + '/docs/en/reference/runtime/node': '/docs/en/reference/runtime-helpers/node.js', + '/docs/en/reference/runtime/node.js': '/docs/en/reference/runtime-helpers/node.js', + '/docs/en/reference/runtime/deno': '/docs/en/reference/runtime-helpers/deno', + '/docs/en/reference/runtime/ruby': '/docs/en/reference/runtime-helpers/ruby', + '/docs/en/reference/runtime/python': '/docs/en/reference/runtime-helpers/python', + + // v5 and v6 archive + // round 1 addendum + // TODO: revisit these as new guides and tutorials are added + '/guides/http': '/docs/en/reference/runtime-helpers/node.js#arc.http', + '/guides/offline': '/docs/en/guides/developer-experience/local-development', + '/guides/static-assets': '/docs/en/guides/frontend/static-assets', + '/guides/spa': '/docs/en/reference/runtime-helpers/node.js#@architect/asap', + '/guides/sessions': '/docs/en/reference/runtime-helpers/node.js#arc.http.session', + '/guides/middleware': '/docs/en/reference/runtime-helpers/node.js#arc.http', + '/guides/data': '/docs/en/reference/runtime-helpers/node.js#arc.tables', + '/guides/background-tasks': '/docs/en/reference/runtime-helpers/node.js#arc.events', + '/guides/cors': '/docs/en/reference/runtime-helpers/node.js#responses', + '/guides/logging': '/docs/en/guides/developer-experience/logging-and-monitoring', + '/guides/custom-dns': '/docs/en/guides/domains/overview', + '/guides/ws': '/docs/en/reference/runtime-helpers/node.js#arc.ws', + '/guides/documentdb': currentRoot, + '/guides/multiple-aws-accounts': '/docs/en/get-started/detailed-aws-setup', + '/guides/iam': '/docs/en/get-started/detailed-aws-setup', + '/guides/yaml-and-json': '/docs/en/get-started/project-manifest', + '/guides/deps': '/docs/en/guides/developer-experience/dependency-management', + '/reference/arc-audit': currentRoot, + '/reference/arc-config': currentRoot, + '/reference/arc-create': '/docs/en/reference/cli/init', + '/reference/arc-deploy': '/docs/en/reference/cli/deploy', + '/reference/arc-dns': currentRoot, + '/reference/arc-env': '/docs/en/reference/cli/env', + '/reference/arc-hydrate': '/docs/en/reference/cli/hydrate', + '/reference/arc-inventory': currentRoot, + '/reference/arc-logs': '/docs/en/reference/cli/logs', + '/reference/arc-repl': currentRoot, + '/reference/arc-sandbox': '/docs/en/reference/cli/sandbox', + '/reference/app': '/docs/en/reference/project-manifest/app', + '/reference/aws': '/docs/en/reference/project-manifest/aws', + '/reference/domain': currentRoot, + '/reference/events': '/docs/en/reference/project-manifest/events', + '/reference/http': '/docs/en/reference/project-manifest/http', + '/reference/indexes': '/docs/en/reference/project-manifest/tables-indexes', + '/reference/queues': '/docs/en/reference/project-manifest/queues', + '/reference/scheduled': '/docs/en/reference/project-manifest/scheduled', + '/reference/static': '/docs/en/reference/project-manifest/static', + '/reference/tables': '/docs/en/reference/project-manifest/tables', + '/reference/ws': '/docs/en/reference/project-manifest/ws', + '/reference/events-publish': '/docs/en/reference/runtime-helpers/node.js#arc.events', + '/reference/events-subscribe': '/docs/en/reference/runtime-helpers/node.js#arc.events', + '/reference/http-functions': '/docs/en/reference/runtime-helpers/node.js#arc.http', + '/reference/http-helpers': '/docs/en/reference/runtime-helpers/node.js#arc.http', + '/reference/http-session': '/docs/en/reference/runtime-helpers/node.js#arc.http.session', + '/reference/middleware': '/docs/en/reference/runtime-helpers/node.js#arc.http', + '/reference/proxy-public': '/docs/en/reference/runtime-helpers/node.js#@architect/Fasap', + '/reference/proxy-read': '/docs/en/reference/runtime-helpers/node.js#@architect/Fasap', + '/reference/queues-publish': '/docs/en/reference/runtime-helpers/node.js#arc.queues', + '/reference/queues-subscribe': '/docs/en/reference/runtime-helpers/node.js#arc.queues', + '/reference/tables-destroy': '/docs/en/reference/runtime-helpers/node.js#arc.tables', + '/reference/tables-insert': '/docs/en/reference/runtime-helpers/node.js#arc.tables', + '/reference/tables-update': '/docs/en/reference/runtime-helpers/node.js#arc.tables', + '/reference/ws-functions': '/docs/en/reference/runtime-helpers/node.js#arc.ws', + '/reference/data': '/docs/en/reference/runtime-helpers/node.js#arc.tables', + '/reference/data-name': '/docs/en/reference/runtime-helpers/node.js#arc.tables', + '/reference/data-db': '/docs/en/reference/runtime-helpers/node.js#arc.tables', + '/reference/data-doc': '/docs/en/reference/runtime-helpers/node.js#arc.tables', + '/reference/data-get': '/docs/en/reference/runtime-helpers/node.js#arc.tables', + '/reference/data-query': '/docs/en/reference/runtime-helpers/node.js#arc.tables', + '/reference/data-scan': '/docs/en/reference/runtime-helpers/node.js#arc.tables', + '/reference/data-put': '/docs/en/reference/runtime-helpers/node.js#arc.tables', + '/reference/data-update': '/docs/en/reference/runtime-helpers/node.js#arc.tables', + '/reference/data-delete': '/docs/en/reference/runtime-helpers/node.js#arc.tables', +} + +export async function redirect (req) { + const reqPath = req.requestContext.http.path + const isGet = req.requestContext.http.method.toLowerCase() === 'get' + + if (isGet && (tempRedirects[reqPath] || permanentRedirects[reqPath])) { + const env = process.env.ARC_ENV + const url = (stage, path) => `https://${stage}arc.codes${path}` + + let location = tempRedirects[reqPath] || permanentRedirects[reqPath] + if (env === 'staging') location = url('staging.', location) + if (env === 'production') location = url('', location) + + return { + statusCode: tempRedirects[reqPath] ? 302 : 301, + headers: { + location, + }, + } + } + return +} diff --git a/src/views/docs/en/:tutorials/beyond-hello-world.md b/src/views/docs/en/:tutorials/beyond-hello-world.md new file mode 100644 index 00000000..674455eb --- /dev/null +++ b/src/views/docs/en/:tutorials/beyond-hello-world.md @@ -0,0 +1,14 @@ +--- +title: 'Going Beyond "Hello World"' +category: Tutorials +description: Next steps in developing an application with Architect +sections: + - "Static assets + CDNs" + - "Database tables" + - "Environment variables" + - "CI/CD" + - "Event functions" + - "Scheduled Functions" + - "Queue functions" + - "Macros" +--- diff --git a/src/views/docs/en/:tutorials/configuring-aws.md b/src/views/docs/en/:tutorials/configuring-aws.md new file mode 100644 index 00000000..f98fff9d --- /dev/null +++ b/src/views/docs/en/:tutorials/configuring-aws.md @@ -0,0 +1,130 @@ +--- +title: Configuring AWS +category: Tutorials +description: Set up your Architect project for AWS +sections: + - "Get AWS IAM credentials" + - "Minimum viable permissions" + - "Configure AWS CLI" + - "Working with multiple profiles" + - "Credentials file vs. environment variables" + - "Deploy buckets" + - "Runtime Environments" +--- + +## Get AWS IAM credentials + +You'll need an Amazon Web Services account and credentials set up on your development machine and / or CI systems. If you yet set it up, here's a useful guide for [Configuring the AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html). + +In the context of a deployment tool, Architect requires account credentials with IAM `AdministratorAccess` privileges. In turn, Architect will create and attach least-privilege IAM roles to runtime resources within your application, ensuring strict security boundaries by default. + +> ℹ️ While it is possible to limit Architect's deployment credentials to specific IAM and CloudFormation privileges, such an exercise would only be performative. Credentials capable of creating IAM roles can grant and attach new roles with `AdministratorAccess`. + +On \*nix systems AWS Credentials are listed in: + +```bash +~/.aws/credentials +``` + +Or on Windows systems: + +```bash +C:\Users\USER_NAME\.aws\credentials +``` + +If that file doesn't exist, create it, and add the following: + +```bash +[default] +aws_access_key_id=xxx +aws_secret_access_key=xxx +``` + +> If you prefer, you can also use: *Control Panel » System » Advanced System Settings » Environment Variables*. + +## Minimum viable permissions +... + +## Configure AWS CLI + +The [AWS Command Line Interface](https://docs.aws.amazon.com/cli/) is the main interface for interacting with all parts of AWS using your computer's terminal. Architect uses the AWS CLI to package and deploy your app via CloudFormation. Follow this guide to [installing the AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) for your preferred environment. + +## Working with multiple profiles + +It is possible to configure more than one AWS profile. This example credentials file lists 3 profiles: + +```bash +[default] +aws_access_key_id=xxx +aws_secret_access_key=xxx + +[work] +aws_access_key_id=xxx +aws_secret_access_key=xxx + +[personal] +aws_access_key_id=xxx +aws_secret_access_key=xxx +``` + +## Credentials file vs. environment variables + +While it is recommended to explicitly declare your application's AWS profile and region, you may also want to use a default profile and region on your machine with the following environment variables: + +- `AWS_PROFILE` +- `AWS_REGION` + +To set these variables on Linux, macOS, or UNIX, use `export` in your shell's configuration (e.g. `~/.zshrc` or `~/.bashrc`): + +```bash +export AWS_PROFILE=work +export AWS_REGION=us-west-1 +``` + +Or for Windows, add this to your PowerShell `$profile`: + +```powershell +$env:AWS_PROFILE='work' +$env:AWS_REGION='us-west-1' +``` + +## Deploy buckets +... + +## Runtime environments + +Architect supports the following runtime versions: + +- **Node.js**: `>= 18.x` using `npm` + - Unless otherwise specified in your project manifest, Node.js is the default runtime for new functions +- **Python**: `3.13`, `3.12`, `3.11`, `3.10`, or `3.9` using `pip3` +- **Ruby**: `3.3`, or `3.2` using `bundle` +- **Deno**: `1.6.x` ([under development](../reference/runtime-helpers/deno)) + +> ⚠️ Working locally with the Sandbox requires target runtimes to be available in your `$PATH`. + +Additionally, all other standard AWS-managed runtimes are supported in Architect applications (but may not be supported in [Sandbox](../reference/cli/sandbox)), including: + +- **Go**: `1.x` +- **.NET**: `8`, and `9` +- **Java**: `21`, `17`, `11`, and `8` + +Architect also supports _any custom runtime_ in using either [Lambda Layers](https://docs.aws.amazon.com/lambda/latest/dg/configuration-layers.html) or [Lambda container images](https://docs.aws.amazon.com/lambda/latest/dg/images-create.html). + +Change a project's default runtime by specifying [an explicit environment](https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html) or an alias in `app.arc` with [the `@aws` pragma](../reference/project-manifest/aws). + +### Examples + +```arc +# version pins the default runtime to Python 3.13 +@aws +runtime python3.13 +``` + +```arc +# always run the latest supported version of Python +@aws +runtime python +``` + +> ℹ️ This setting can be overridden on a per-function basis with [`config.arc`](../reference/configuration/function-config). diff --git a/src/views/docs/en/:tutorials/set-up-a-domain/overview.md b/src/views/docs/en/:tutorials/set-up-a-domain/overview.md new file mode 100644 index 00000000..6435545f --- /dev/null +++ b/src/views/docs/en/:tutorials/set-up-a-domain/overview.md @@ -0,0 +1,52 @@ +--- +title: Setting up a domain +category: Tutorials +description: Setting up a domain for an Architect application +sections: + - "Overview" + - "DNS guides" + - "Setting up your project" +--- + +## Give your Architect application a proper domain name + +DNS is how you assign a domain name to a deployed app. This guide lists ways to set up custom DNS with several popular DNS providers and we are always happy to accept contributions for steps to use additional providers. + +You may use a free registrar-based DNS to host your domain such as GoDaddy, Namecheap, One, etc., but Route53 is the preferred registrar and DNS management system for Architect users. This is because: + +- It's integrated with Amazon's other cloud services. +- Your DNS will resolve from 15+ locations worldwide, making your website faster for your end-users. +- Route53 is a DNS management system (Smart DNS) compared to all the others, which are merely domain registrars with a limited feature set for manipulating DNS. + +## Setting up your project + +To prepare your arc app for setting up a custom domain, you first have to deploy your app to `staging` and `production`. + +Deploy to a `staging` stack: + +```bash +arc deploy +``` +> Protip: create additional `staging` stacks with `--name` + +Ship a `production` stack: + +```bash +arc deploy production +``` + +All done! + +> Remember to save the two generated `URLs` because we will need to input these into the AWS console in the next steps. + +## DNS guides + +Setting up a custom domain for each registrar will be a bit different. To manually configure DNS, you can follow these guides below: + + +- [Route53](/docs/en/guides/domains/registrars/route53) +- [Route53 & CloudFront](/docs/en/guides/domains/registrars/route53-and-cloudfront) +- [Dreamhost](/docs/en/guides/domains/registrars/dreamhost) +- [GoDaddy](/docs/en/guides/domains/registrars/godaddy) +- [Namecheap](/docs/en/guides/domains/registrars/namecheap) +- [One](/docs/en/guides/domains/registrars/one) diff --git a/src/views/docs/en/:tutorials/set-up-a-domain/registrars/dreamhost.md b/src/views/docs/en/:tutorials/set-up-a-domain/registrars/dreamhost.md new file mode 100644 index 00000000..46dc140a --- /dev/null +++ b/src/views/docs/en/:tutorials/set-up-a-domain/registrars/dreamhost.md @@ -0,0 +1,62 @@ +--- +title: Dreamhost +category: Domain Registrars +description: Setting up a domain name with Dreamhost +--- + +## Prerequisites + +- Sign up for a domain on [Dreamhost](https://www.dreamhost.com/domains/) +- Make sure your domain is set to `DNS Only` in the Dreamhost console. +- Deploy an app with Architect and make note of the `staging` and `production` URLs +- Make sure your app is deployed to `us-east-1` +- Ensure the `@app` name is uniquely named after the domain. + +## Step 1: setup SSL certificates with AWS Certificate Manager + +In this step we will request a certificate from Amazon for our domain. + +- Open up AWS Certificate Manager in the AWS Console in `us-east-1` (region is required!) +- Click `Request a certificate` and then `Request a public certificate` +- Ensure `example.com` and `*.example.com` for sub domains to work +- Choose `DNS validation` and click `Next` +- Add any tags and confirm the request +- Open up Dreamhost backend and click `Manage Domains` +- Click the `DNS` link under your domain to add custom DNS records +- Create CNAME records of both issued certificates +- Wait until they change from `pending` to `success` + +## Step 2: setup CloudFront + +Generate a CloudFront distribution with the certificate from step 1. + +- Sign into AWS CloudFront in the AWS Console +- Click `Create Distribution` and then click `Get Started` +- Open API Gateway and make note of the Invoke URL. +- Enter the URL from API Gateway in `Origin Domain Name` +- Set `Origin Protocol Policy` to `Match Viewer` +- Add the `Alternate Domain Names (CNAMEs)` that you will be using. ex. `example.com`. +- Set `Viewer Protocol Policy` to `Redirect HTTP to HTTPS` +- Set `Allowed HTTP Methods` to `GET, HEAD, OPTIONS, PUT, POST, PATCH, DELETE` +- Set `Compress Objects Automatically` to `Yes` +- Set `SSL Certificate` to `Custom SSL Certificate` and select the cert from step 1 +- Click `Create Distribution` +- Repeat for `staging` domain. + +## Step 3: configure the domain Alias in Dreamhost + +Add `Alias` and `CNAME` records to DNS. + +- Sign into Dreamhost +- Navigate to the domain by clicking `Manage Domains` in the sidebar. +- Click `DNS` under the domain +- Click `Add Record` +- Use record type `Alias` for the root domain. + - Leave `Host` input blank and add the CloudFront domain that was created in step 2 to the `Points to` input. +- Use record type `CNAME` for the `staging` domain. + - Add the word `staging` to the `Host` input and add the CloudFront domain that was created in step 2 to the `Points to` input. +- Click `Add records` + +## Conclusion + +Now we're done! You can check to see if your domains are online with the DNS checker tool provided by Dreamhost. You can also use this [DNS Checker tool](https://dnschecker.org/). diff --git a/src/views/docs/en/:tutorials/set-up-a-domain/registrars/godaddy.md b/src/views/docs/en/:tutorials/set-up-a-domain/registrars/godaddy.md new file mode 100644 index 00000000..40e8a635 --- /dev/null +++ b/src/views/docs/en/:tutorials/set-up-a-domain/registrars/godaddy.md @@ -0,0 +1,58 @@ +--- +title: GoDaddy +category: Domain Registrars +description: Setting up a domain name with GoDaddy +--- + +## Prerequisites + +- Sign up for a domain on [GoDaddy](https://www.godaddy.com/) +- Deploy an app with Architect and make note of the `staging` and `production` URLs +- Ensure the app is deployed to `us-east-1` +- Ensure the `@app` name is uniquely named after the domain. + +## Step 1: setup SSL certificates with AWS Certificate Manager + +In this step we will request a certificate from Amazon for our domain. + +- Open up AWS Certificate Manager in the AWS Console in `us-east-1` (region is required!) +- Click `Request a certificate` and then `Request a public certificate` +- Ensure `example.com` and `*.example.com` for sub domains to work +- Choose `DNS validation` and click `Next` +- Add any tags and confirm the request +- Open up GoDaddy account dashboard and find the `DNS` settings for the particular domain you want to use. +- Click `ADD` and select `CNAME` +- Create CNAME records of both issued certificates +- Wait until they change from `pending` to `success` + +## Step 2: setup CloudFront + +Generate a CloudFront distribution with the certificate from step 1. + +- Sign into AWS CloudFront in the AWS Console +- Click `Create Distribution` and then click `Get Started` +- Open API Gateway and make note of the `Invoke URL`. +- Enter the URL from API Gateway in `Origin Domain Name` +- Set `Origin Protocol Policy` to `Match Viewer` +- Add the `Alternate Domain Names (CNAMEs)` that you will be using. ex. `example.com`. +- Set `Viewer Protocol Policy` to `Redirect HTTP to HTTPS` +- Set `Allowed HTTP Methods` to `GET, HEAD, OPTIONS, PUT, POST, PATCH, DELETE` +- Set `Compress Objects Automatically` to `Yes` +- Set `SSL Certificate` to `Custom SSL Certificate` and select the cert from step 1 +- Click `Create Distribution` +- Repeat for `staging` domain. + +## Step 3: configure the domain Alias in GoDaddy + +Add `A` and `CNAME` records to DNS. + +- Open up GoDaddy account dashboard and find the `DNS` settings for the particular domain you want to use. +- Click `ADD`. +- Use record type `A` for the root domain. + - Leave `Host` input empty and add the IP address of the CloudFront domain that was created in step 2 to the `Points to` input. +- Use record type `CNAME` for the `staging` domain. + - Add the word `staging` to the `Host` input and add the CloudFront domain that was created in step 2 to the `Points to` input. + +## Conclusion + +Now we're done! You can check to see if your domains are online with the DNS checker tool [DNS Checker tool](https://dnschecker.org/). diff --git a/src/views/docs/en/:tutorials/set-up-a-domain/registrars/namecheap.md b/src/views/docs/en/:tutorials/set-up-a-domain/registrars/namecheap.md new file mode 100644 index 00000000..0b8caae8 --- /dev/null +++ b/src/views/docs/en/:tutorials/set-up-a-domain/registrars/namecheap.md @@ -0,0 +1,60 @@ +--- +title: Namecheap +category: Domain Registrars +description: Setting up a domain name with Namecheap +--- + +## Prerequisites + +- Sign up for a domain on [Namecheap](https://www.Namecheap.com/domains/) +- Deploy an app with Architect and make note of the `staging` and `production` URLs +- Make sure your app is deployed to `us-east-1` +- Ensure the `@app` name is uniquely named after the domain. + +## Step 1: setup SSL certificates with AWS Certificate Manager + +In this step we will request a certificate from Amazon for our domain. + +- Open up AWS Certificate Manager in the AWS Console in `us-east-1` (region is required!) +- Click `Request a certificate` and then `Request a public certificate` +- Ensure `example.com` and `*.example.com` for sub domains to work +- Choose `DNS validation` and click `Next` +- Add any tags and confirm the request +- Open up Namecheap account dashboard and click `Manage` for the particular domain you want to use. +- Open the `Advanced DNS` tab. +- Click `ADD A NEW RECORD` +- Create CNAME records of both issued certificates +- Wait until they change from `pending` to `success` + +## Step 2: setup CloudFront + +Generate a CloudFront distribution with the certificate from step 1. + +- Sign into AWS CloudFront in the AWS Console +- Click `Create Distribution` and then click `Get Started` +- Open API Gateway and make note of the `Invoke URL`. +- Enter the URL from API Gateway in `Origin Domain Name` +- Set `Origin Protocol Policy` to `Match Viewer` +- Add the `Alternate Domain Names (CNAMEs)` that you will be using. ex. `example.com`. +- Set `Viewer Protocol Policy` to `Redirect HTTP to HTTPS` +- Set `Allowed HTTP Methods` to `GET, HEAD, OPTIONS, PUT, POST, PATCH, DELETE` +- Set `Compress Objects Automatically` to `Yes` +- Set `SSL Certificate` to `Custom SSL Certificate` and select the cert from step 1 +- Click `Create Distribution` +- Repeat for `staging` domain. + +## Step 3: configure the domain Alias in Namecheap + +Add `Alias` and `CNAME` records to DNS. + +- Open up Namecheap account dashboard and click `Manage` for the particular domain you want to use. +- Open the `Advanced DNS` tab. +- Click `ADD A NEW RECORD` +- Use record type `Alias` for the root domain. + - Add `@` in the `Host` input and add the CloudFront domain that was created in step 2 to the `Value` input. +- Use record type `CNAME` for the `staging` domain. + - Add the word `staging` to the `Host` input and add the CloudFront domain that was created in step 2 to the `Value` input. + +## Conclusion + +Now we're done! You can check to see if your domains are online with the DNS checker tool [DNS Checker tool](https://dnschecker.org/). diff --git a/src/views/docs/en/:tutorials/set-up-a-domain/registrars/one.md b/src/views/docs/en/:tutorials/set-up-a-domain/registrars/one.md new file mode 100644 index 00000000..f17bb460 --- /dev/null +++ b/src/views/docs/en/:tutorials/set-up-a-domain/registrars/one.md @@ -0,0 +1,60 @@ +--- +title: One +category: Domain Registrars +description: Setting up a domain name with One +--- + +## Prerequisites + +- Sign up for a domain on [One](https://www.one.com/en/domain) +- Deploy an app with Architect and make note of the `staging` and `production` URLs +- Ensure the app is deployed to `us-east-1` +- Ensure the `@app` name is uniquely named after the domain. + +## Step 1: setup SSL certificates with AWS Certificate Manager + +In this step we will request a certificate from Amazon for our domain. + +- Open up AWS Certificate Manager in the AWS Console in `us-east-1` (region is required!) +- Click `Request a certificate` and then `Request a public certificate` +- Ensure `example.com` and `*.example.com` for sub domains to work +- Choose `DNS validation` and click `Next` +- Add any tags and confirm the request +- Open up One account dashboard and click `DNS settings` for the particular domain you want to use. +- Open the `DNS records` tab. +- Click the `CNAME` tab in the `Create new record` box +- Create CNAME records of both issued certificates +- Wait until they change from `pending` to `success` + +## Step 2: setup CloudFront + +Generate a CloudFront distribution with the certificate from step 1. + +- Sign into AWS CloudFront in the AWS Console +- Click `Create Distribution` and then click `Get Started` +- Open API Gateway and make note of the `Invoke URL`. +- Enter the URL from API Gateway in `Origin Domain Name` +- Set `Origin Protocol Policy` to `Match Viewer` +- Add the `Alternate Domain Names (CNAMEs)` that you will be using. ex. `example.com`. +- Set `Viewer Protocol Policy` to `Redirect HTTP to HTTPS` +- Set `Allowed HTTP Methods` to `GET, HEAD, OPTIONS, PUT, POST, PATCH, DELETE` +- Set `Compress Objects Automatically` to `Yes` +- Set `SSL Certificate` to `Custom SSL Certificate` and select the cert from step 1 +- Click `Create Distribution` +- Repeat for `staging` domain. + +## Step 3: configure the domain Alias in One + +Add `A` and `CNAME` records to DNS. + +- Open up One account dashboard and click `DNS settings` for the particular domain you want to use. +- Open the `DNS records` tab. +- Click the `A` tab in the `Create new record` box +- Use record type `A` for the root domain. + - Leave `Hostname` input empty and add the IP address of the CloudFront domain that was created in step 2 to the `Will point to` input. +- Use record type `CNAME` for the `staging` domain. + - Add the word `staging` to the `Hostname` input and add the CloudFront domain that was created in step 2 to the `Is an alias of` input. + +## Conclusion + +Now we're done! You can check to see if your domains are online with the DNS checker tool [DNS Checker tool](https://dnschecker.org/). diff --git a/src/views/docs/en/:tutorials/set-up-a-domain/registrars/route53-and-cloudfront.md b/src/views/docs/en/:tutorials/set-up-a-domain/registrars/route53-and-cloudfront.md new file mode 100644 index 00000000..5e00cab5 --- /dev/null +++ b/src/views/docs/en/:tutorials/set-up-a-domain/registrars/route53-and-cloudfront.md @@ -0,0 +1,53 @@ +--- +title: Route53 & CloudFront +category: Domain Registrars +description: Setting up a domain name with Route53 and CloudFront +--- + +## Prerequisites + +- [Register](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/domain-register.html) or [transfer](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/domain-transfer-to-route-53.html) a domain with Route53 +- Deploy an app with Architect and make note of the `staging` and `production` URLs + +## Step 1: setup SSL certificates with AWS Certificate Manager + +In this step we will request a certificate from Amazon for our domain. + +- Open up AWS Certificate Manager in the AWS Console in `us-east-1` (region is required!) +- Click `Request a certificate` and then `Request a public certificate` +- Ensure `example.com` and `*.example.com` for sub domains to work +- Choose `DNS validation` and click `Next` +- Add any tags and confirm the request +- Expand the domain and click `Create record in Route53` button +- Verify CNAME record created in Route53 console Hosted zone + +## Step 2: setup CloudFront + +Generate a CloudFront distribution with the certificate from step 1. + +- Sign into AWS CloudFront in the AWS Console +- Click `Create Distribution` and then click `Get Started` +- Enter the URL from API Gateway in `Origin Domain Name` +- Set `Origin Protocol Policy` to `Match Viewer` +- Set `Viewer Protocol Policy` to `Redirect HTTP to HTTPS` +- Set `Allowed HTTP Methods` to `GET, HEAD, OPTIONS, PUT, POST, PATCH, DELETE` +- Set `Compress Objects Automatically` to `Yes` +- Enter the domain alias in `Alternate Domain Names` (which you will configure in step 3) +- Set `SSL Certificate` to `Custom SSL Certificate` and select the cert from step 1 +- Click `Create Distribution` + +## Step 3: configure the domain Alias in AWS Route53 + +- Sign into AWS Route53 in the AWS Console +- Navigate to the Hosted zone for the domain +- Click `Create record` +- Enter the `Record name` +- Record type is `A` and toggle `Alias` checkbox on +- Select `Alias to CloudFront` +- Select the region +- Select the CloudFront distribution domain (should be the same value as the domain generated in Step 2) +- Click `Create records` + +## Conclusion + +Now we're done! You can check to see if your domains are online with this [DNS Checker tool](https://dnschecker.org/). diff --git a/src/views/docs/en/:tutorials/set-up-a-domain/registrars/route53.md b/src/views/docs/en/:tutorials/set-up-a-domain/registrars/route53.md new file mode 100644 index 00000000..58491512 --- /dev/null +++ b/src/views/docs/en/:tutorials/set-up-a-domain/registrars/route53.md @@ -0,0 +1,51 @@ +--- +title: Route53 +category: Domain Registrars +description: Setting up a domain name with Route53 +--- + +## Prerequisites + +- [Register](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/domain-register.html) or [transfer](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/domain-transfer-to-route-53.html) a domain with Route53 +- Deploy an app with Architect and make note of the `staging` and `production` URLs + +## Step 1: setup SSL certificates with AWS Certificate Manager + +In this step we will request a certificate from Amazon for our domain. + +- Open up AWS Certificate Manager in the AWS Console in `us-east-1` (region is required!) +- Click `Request a certificate` and then `Request a public certificate` +- Ensure `example.com` and `*.example.com` for sub domains to work +- Choose `DNS validation` and click `Next` +- Add any tags and confirm the request +- Expand the domain and click `Create record in Route53` button +- Verify CNAME record created in Route53 console Hosted zone + +## Step 2: setup custom domain with AWS API Gateway + +Generate a domain with the certificate from Step 1. + +- Sign into AWS API Gateway in the AWS Console +- Navigate to `Custom domain names` and click `Create` +- Enter the domain name (e.g. `staging.example.com` for the `staging` app or `example.com` for the `production` app) +- Select the certificate created in Step 1 +- Click `Create domain name` +- Make note of the generated `API Gateway domain name` in `Endpoint configuration` +- Click on the tab `API mappings` and `Configure API mappings` +- For `API` select the API and for `Stage` select `$default` and click `Save` + +## Step 3: configure the domain Alias in AWS Route53 + +- Sign into AWS Route53 in the AWS Console +- Navigate to the Hosted zone for the domain +- Click `Create record` +- Enter the `Record name` +- Record type is `A` and toggle `Alias` checkbox on +- Select `Alias to API Gateway` +- Select the region +- Select the API (should be the same value as the domain generated in Step 2) +- Click `Create records` + +## Conclusion + +Now we're done! You can check to see if your domains are online with this [DNS Checker tool](https://dnschecker.org/). diff --git a/src/views/docs/en/:tutorials/using-plugins.md b/src/views/docs/en/:tutorials/using-plugins.md new file mode 100644 index 00000000..a7c3efc0 --- /dev/null +++ b/src/views/docs/en/:tutorials/using-plugins.md @@ -0,0 +1,14 @@ +--- +title: Using Architect plugins +category: Tutorials +description: Install and use Arc plugins +sections: + - "Overview" + - "Installation" + - "Plugin API" + - "Helper methods" + - "Examples" + # - "Modeling & persisting data" + # - "Building single page apps (SPAs); include aliasing" + # - "Implementing CORS" +--- diff --git a/src/views/docs/en/about/community.md b/src/views/docs/en/about/community.md index 5d8f54e0..71fad68e 100644 --- a/src/views/docs/en/about/community.md +++ b/src/views/docs/en/about/community.md @@ -6,7 +6,7 @@ description: Architect is an open source project and we want YOUR help! Architect is an open source project hosted by the [OpenJS Foundation](https://openjsf.org) and it is totally ok to ask for help! Use the issue tracker to file bugs, request features, or even just to ask a question. -- [Come chat with us in Slack](https://join.slack.com/t/architecture-as-text/shared_invite/MjE2MzU4Nzg0NTY1LTE1MDA2NzgyMzYtODE2NzRkOGRmYw) +- [Come chat with us in Discord](https://discord.gg/y5A2eTsCRX) - [🌟 `@architect/architect` on GitHub](https://github.com/architect/architect) - [Learn how to contribute](/docs/en/about/contribute) @@ -26,3 +26,6 @@ The `app.arc` manifest takes inspiration from UNIX 'run command' files (like `.v The code was developed building [Begin](https://begin.com) and granted to [JS Foundation](https://js.foundation/) in July of 2017 under the `Apache-2.0` license. +## Community Call + +Join us for our monthly community calls on [Discord](https://discord.gg/y5A2eTsCRX) held on the fourth Wednesday of each month at 10:15 am Pacific Time / 1:15 pm Eastern Time / 18:15 UTC. Meeting agendas will be posted in the announcements channel of the Architect Discord. We will use this time to demo upcoming new features and triage [outstanding issues](https://github.com/architect/architect/issues). Please join us and have your voice heard in the project's direction. diff --git a/src/views/docs/en/about/contribute.md b/src/views/docs/en/about/contribute.md index ece8e4dd..498b372e 100644 --- a/src/views/docs/en/about/contribute.md +++ b/src/views/docs/en/about/contribute.md @@ -1,48 +1,113 @@ --- title: Contributor guide -category: about +category: About description: How to contribute to Architect. --- -Architect is an open source project and you can totally help out! Contributing doesn't just mean landing code. It can be reporting bugs, helping us triage bugs, suggesting new features, writing docs, sharing examples and plain kicking it in our community chat. These are all helpful contributions! +Architect is an open source project and you can totally help out! Contributing doesn't just mean landing code. It can be reporting bugs, helping us triage bugs, suggesting new features, writing docs, sharing examples, and plain kicking it in our community chat. These are all helpful contributions! + + +## Helping out + +We are always happy to review and potentially accept pull request to Architect repositories. In general, we suggest opening an issue and starting a discussion in the [Discord](https://discord.gg/y5A2eTsCRX) before you embark on any large project, as there may be requirements or considerations that you may not be aware of in advance. + +If you have a larger problem to solve or idea for a new feature, please file an issue for community discussion. If you have a time-sensitive problem and need to talk things through, you can almost always find someone online in our [Discord](https://discord.gg/y5A2eTsCRX). (It's also a great place to socialize new ideas and to solicit help on how to model specific [Functional Web App (FWA)](https://fwa.dev) patterns.) + +Please help us by making it easy for us to help you! If you are experiencing a bug, please have a reduced test case and steps to reproduce prepared. In our process, the first step to resolution is to create a failing test case so we can ensure there are no future regressions. By having a reduced case (even just an example project) and steps to reproduce, you will dramatically reduce the time to getting your bug fixed. + ## Agreement to the Architect Code of Conduct -By participating in and contributing to the Architect community — including, but not limited to its open source projects, any related online venues such as GitHub, Slack, and in-person events, etc. — you agree to the Architect Code of Conduct. +By participating in and contributing to the Architect community — including, but not limited to its open source projects, any related online venues such as GitHub, Discord, and in-person events, etc. — you agree to the Architect Code of Conduct. Lack of familiarity with this Code of Conduct is not an excuse for not adhering to it. -## Project Code Structure -The Architect project distribution code is bundled in [`architect/architect`](https://github.com/architect/architect) which also serves as the [primary project issue tracker](https://github.com/architect/architect/issues). +## Project structure -The Architect project is composed of multiple core code repositories on GitHub: +The Architect project distribution code is bundled in [`@architect/architect`](https://github.com/architect/architect) which also serves as the [primary project issue tracker](https://github.com/architect/architect/issues). + +The Architect project is comprised of multiple core repositories: + +- [`@architect/create`](https://github.com/architect/create) - scaffold and generate project code +- [`@architect/deploy`](https://github.com/architect/deploy) - deploy an Architect project +- [`@architect/destroy`](https://github.com/architect/destroy) - destroy an Architect app and its related resources +- [`@architect/env`](https://github.com/architect/env) - read / write project environment variables +- [`@architect/hydrate`](https://github.com/architect/hydrate) - ensures Lambda dependencies are installed and ready for use in an AWS environment +- [`@architect/inventory`](https://github.com/architect/inventory) - enumerate an Architect project (including plugin-generated resources) into a common internal intermediary format +- [`@architect/logs`](https://github.com/architect/logs) - read/write Lambda logs +- [`@architect/package`](https://github.com/architect/package) - consumes an Inventory object, and outputs a CloudFormation document for deployment +- [`@architect/parser`](https://github.com/architect/parser) - parser/lexer for Architect project manifest formats (`app.arc`, `.arc`, `arc.json`, `arc.yaml`, and `arc.yml`) +- [`@architect/sandbox`](https://github.com/architect/sandbox) - local development environment; mocks API Gateway, SNS, SQS, DynamoDB, Lambda, etc. +- [`@architect/utils`](https://github.com/architect/utils) - various shared internal helpers and utilities -- [`architect/parser`](https://github.com/architect/parser) - parser/lexer for arcfile formats (`.arc`, `app.arc`, `arc.json`, `arc.yaml`, `arc.yml` and `arc.toml`) -- [`architect/package`](https://github.com/architect/package) - a pure function that consumes `architect/parser` output and returns a CloudFormation document -- [`architect/deploy`](https://github.com/architect/deploy) - a wrapper for the AWS CLI `package` and `deploy` commands -- [`architect/sandbox`](https://github.com/architect/sandbox) - the local sandbox (mocks API Gateway, SNS, SQS, DynamoDB, and Lambda) -- [`architect/env`](https://github.com/architect/env) - read/write arcfile project environment variables with SSM -- [`architect/hydrate`](https://github.com/architect/hydrate) - ensures function deps are synced (including src/shared and src/views) -- [`architect/logs`](https://github.com/architect/logs) - read/write function CloudWatch logs -- [`architect/create`](https://github.com/architect/create) - code generation Projects built with Architect are encouraged to use the following runtime helper libraries: -- [`architect/functions`](https://github.com/architect/functions) - runtime helpers for NodeJS -- [`architect/functions-python`](https://github.com/architect/functions-python) - runtime helpers for Python -- [`architect/functions-ruby`](https://github.com/architect/functions-ruby) - runtime helpers for Ruby +- [`@architect/functions`](https://github.com/architect/functions) - Lambda runtime helpers for Node.js +- [`architect/functions-python`](https://github.com/architect/functions-python) - Lambda runtime helpers for Python +- [`architect/functions-ruby`](https://github.com/architect/functions-ruby) - Lambda runtime helpers for Ruby > Note: runtime helpers are not required to use Architect; they do make dealing with AWS nicer however -Architect project documentation is in the following repos: -- architect/arc.codes.next - new docs site in progress (version 6+) -- architect/arc.codes - the primary documentation website (version 6) -- architect/v5.arc.codes - version 5 docs site +It is also worthwhile to take a look at Architect's various supported plugins and example repos: -## Helping out +- [`architect/plugins`](https://github.com/architect/plugins) - officially supported plugins for Architect 10+ +- [`architect-examples`](https://github.com/architect-examples) - example Architect apps and projects + + +## Architect releases + +- Architect and its constituent libraries follow [SemVer](https://SemVer.org/), taking into consideration [author-time, deploy-time, and runtime lifecycle stages](https://github.com/architect/architect/issues/938) +- Architect (`@architect/architect`) releases are as deterministic as the `package.json` format allows; this is enforced by the following build dependency requirements: + - All first-order `@architect/architect` dependencies are version-pinned + - All second order `@architect/*` dependencies must use SemVer `~` + - The above versioning (pinning + `~`) do not apply to `devDependencies` + + +### Creating an Architect release + +Architect releases are published via CI / CD, with a degree of manual input. The process for creating a new Architect release looks like this: + +1. In the project to be changed, open a PR + - Ensure test coverage is maintained or added, and that tests pass (of course) + - Ideally: verify your changes locally before asking others to review your work. Sometimes the fastest path to doing this is simply to monkey-patch your local Architect installation +2. If the PR is in good shape and approved by a maintainer, merge the PR to `main` +3. Make sure the `changelog.md` file is updated with the correct version, date, and description of changes +4. Use **`npm version`** to publish a new version (from `main`) + - Tip: make sure you push the git tag created by `npm version` (e.g. `git push && git push --tags`), or the release will not publish to npm +5. Once the build is complete and the new release of your module is live, if necessary, update any other Architect modules that consume this change + - Example: if you publish a new minor or major release of `@architect/package`, you will then have to update `@architect/deploy` to use that new dependency, and publish a new version of that package as well +6. Once all the impacted modules are published, prepare a release of `@architect/architect` + - Bump the changed versions in `@architect/architect` + - Make sure the main Architect `changelog.md` incorporates all the various changelog changes + - Ship the new version of Architect (see: step 4) + + +### Architect module release order + +Due to internal module dependencies, Architect has the following module release order: + +0. `parser` +1. `asap` + `utils` +2. `inventory` +3. `create` + `package` + `hydrate` + `destroy` + `env` + `logs` + `functions` +4. `sandbox` + `deploy` +5. `@architect/architect` + +Per this module release, should you make a SemVer major or minor change in any of the packages earlier the list, many (or possibly all) packages below it will need to be updated and re-published. + +Examples: + +- If you make a SemVer major or minor change to `hydrate`, you will have to consume that change in `sandbox` and `deploy` before publishing `@architect/architect` +- If you make a SemVer major or minor change to `inventory`, you will have to consume that change in all packages in order stages 3, then 4, before publishing `@architect/architect` + + +### Non-reliance on automation for some release processes + +While some projects opt for highly automated module publishing and changelog / release notes, Architect has thus far been successful by utilizing a tight and reliable, albeit more manual, deployment process. -We are always happy to accept a pull request to any of the repositories above. If you have a larger problem to solve or idea for a new feature please file an issue for community discussion. If you're having a time sensitive problem and need to talk things through you can almost always find someone in our community chat. It's also a great place to socialize new ideas and to solicit help on how to model specific serverless patterns. +For example, our release notes are written in plain, highly readable and explanatory language (as opposed to in commit message format). -Please help us by making it easy for us to help you! If you are experiencing a bug, please, have a reduced test case and steps to reproduce prepared. In our process, the first step to resolution is to create a failing test case so we can be sure there are no future regressions. By having a reduced case, even just an example project, and steps to reproduce you will save us all time getting to the fix! +Of course, we are always open to streamlining our processes, so please feel free to suggest improvements. diff --git a/src/views/docs/en/about/ejecting-from-architect.md b/src/views/docs/en/about/ejecting-from-architect.md new file mode 100644 index 00000000..cac60c7d --- /dev/null +++ b/src/views/docs/en/about/ejecting-from-architect.md @@ -0,0 +1,33 @@ +--- +title: Ejecting from Architect +category: Extend +description: Ejecting from Architect +--- + +Architect makes it very easy to cease using it in favor of any another tool or framework, such as AWS SAM. + + +## Ejecting to CloudFormation + +In your project directory, run: + +```bash +npx arc deploy --eject +``` + +This will generate your application's `sam.json` + `sam.yaml` documents, as well as print out the necessary AWS CLI instructions to deploy your application without Architect. + + +## Considerations + +It is worth noting that by ejecting from Architect to "raw" AWS or another framework, you may lose access some or all of the following: + +- Expansive, fast, and customizable local development environment +- Baked-in security + performance defaults +- Automated dependency management +- Workflows for managing environment variables, logs, etc. +- Single-command deployments +- Built in testing, staging, production environments +- Human-centered service discovery for machine-generated AWS resources + +Either way, we at Architect hope we'll see you again in the future! diff --git a/src/views/docs/en/about/mission.md b/src/views/docs/en/about/mission.md index 53919406..6bf06200 100644 --- a/src/views/docs/en/about/mission.md +++ b/src/views/docs/en/about/mission.md @@ -1,6 +1,6 @@ --- title: Mission -category: about +category: About description: Architect's Mission --- diff --git a/src/views/docs/en/about/playground.md b/src/views/docs/en/about/playground.md index 3b31c143..cf42c0a3 100644 --- a/src/views/docs/en/about/playground.md +++ b/src/views/docs/en/about/playground.md @@ -1,6 +1,6 @@ --- title: Playground -category: about +category: About description: Architect's infrastructure as code playground. --- diff --git a/src/views/docs/en/about/upgrade.md b/src/views/docs/en/about/upgrade-guide.md similarity index 65% rename from src/views/docs/en/about/upgrade.md rename to src/views/docs/en/about/upgrade-guide.md index 9f4e3847..c8279332 100644 --- a/src/views/docs/en/about/upgrade.md +++ b/src/views/docs/en/about/upgrade-guide.md @@ -1,9 +1,12 @@ --- -title: Changelog -category: Get started +title: Upgrade guide +category: About description: This document covers upgrading from previous versions of Architect sections: - Overview of Architect versions + - Architect 10 → 11 + - Architect 9 → 10 + - Architect 8 → 9 - Architect 7 → 8 - Architect 6 → 7 - Architect 5 → 6 @@ -17,9 +20,30 @@ This document covers upgrading from previous versions of Architect. As a general philosophy, Architect's core maintainers endeavor to minimize the frequency and impact of breaking changes wherever possible; in many cases, major releases may have no impact on existing applications. +## Releases + +### Architect 11 (Cadborosaurus) + +Architect 11 (Cadborosaurus) is a speed, stability, and security release, marking the first version utilizing [`aws-lite`](https://aws-lite.org) instead of the AWS SDK. Architect 11 now installs significantly faster, with a size on disk of roughly 49 MB, down from 191 MB, a 74% reduction. Arc can now also deploy in seconds with *fast mode*. + +Because Architect no longer includes the AWS SDK, any projects that use it to make calls to AWS services must install the AWS SDK as a dependency. [See more below](#architect-10-→-11). + + +### Architect 10 (Taniwha) + +Architect 10 (Taniwha) is a major feature release, introducing the Architect plugins API, and cleaning up internal legacy code, module APIs, and other bits from earlier on in Architect's history. While few of these changes should impact existing projects, users should still [see below for potential impacts to upgrading](#architect-9-→-10). + + +### Architect 9 (La Chupacabra) + +Architect 9 (La Chupacabra) is primarily a maintenance release, dropping support for Node.js 10.x (now end-of-life) and Node.js 12.x, and removing support for Architect 5 (and lower). + +[See below for potential impacts to upgrading](#architect-8-→-9). + + ### Architect 8 (El Chupacabra) -Architect 8 (El Chupacabra) improves API Gateway `HTTP` APIs by adding [`@proxy`](/docs/en/reference/arc-pragmas/@proxy) support for migrating old APIs, and `any` HTTP method support and `*` catchall syntax, while also improving the default greedy catchall behavior of `get /` to be literal to what's found in the Architect manifest. +Architect 8 (El Chupacabra) improves API Gateway `HTTP` APIs by adding [`@proxy`](/docs/en/reference/project-manifest/proxy) support for migrating old APIs, and `any` HTTP method support and `*` catchall syntax, while also improving the default greedy catchall behavior of `get /` to be literal to what's found in the Architect manifest. Although uncommon, certain Architect applications that use `get /` beyond handling `get` requests to `/` may be impacted by this change. [See more below](#architect-7-→-8). @@ -53,18 +77,189 @@ Architect 4 (Yeti) introduced generic, dependency-free HTTP functions, enhanced --- -### Topics +## Upgrade guides +- [Architect 9 → 10](#architect-10-→-11) +- [Architect 9 → 10](#architect-9-→-10) +- [Architect 8 → 9](#architect-8-→-9) - [Architect 7 → 8](#architect-7-→-8) - [Architect 6 → 7](#architect-6-→-7) - [Architect 5 → 6](#architect-5-→-6) -- [Architect 5 LTS (maintenance schedule)](#architect-5-lts-maintenance-schedule) - [Architect 4 → 5](#architect-4-→-5) - [Architect Functions](#architect-functions) - [Architect Data](#architect-data) --- +## Architect 10 → 11 + +Architect 11 (Cadborosaurus) is a speed, stability, and security release, marking the first version utilizing [`aws-lite`](https://aws-lite.org) instead of the AWS SDK. + +Architect 11 now installs significantly faster, with a size on disk of roughly 49 MB, down from 191 MB (a 74% reduction!). Arc also no longer makes use of the AWS CLI, and can now also deploy in seconds with *fast mode*. + + +### Breaking changes + +- Architect no longer includes any versions of AWS SDK as dependencies. Any projects that use AWS SDK v2 or v3 to make calls to AWS services must install it as a dependency. + - Remedy: run the following command in your project, depending on the AWS SDK version(s) you need: + - AWS SDK v2 - `npm i -D aws-sdk` + - AWS SDK v3 - `npm i -D @aws-sdk/client-apigatewaymanagementapi @aws-sdk/client-dynamodb @aws-sdk/client-s3 @aws-sdk/client-sns @aws-sdk/client-sqs @aws-sdk/client-ssm @aws-sdk/lib-dynamodb` + - Alternative remedy: begin transitioning to [`aws-lite`](https://aws-lite.org), which is 2-5x faster, has nice docs, excellent errors, support for types, and is fully open to community contribution +- Due to the upcoming deprecation of `nodejs16.x` and AWS SDK v2 in Lambda, Architect now defaults to `nodejs20.x` + - Remedy: if you still use SDK v2 in your Lambdas by default, add `@aws runtime nodejs16.x` to your [project manifest](/docs/en/reference/project-manifest/aws#runtime) or any relevant [config.arc files](/docs/en/reference/configuration/function-config) + - However, it must be noted that Lambda is retiring `nodejs16.x` with AWS SDK v2 later this year; as above, we are now encouraging folks to transition to `aws-lite`, where possible +- `arm64` is now the default Lambda architecture + - This change only impacts projects that utilize native modules or Lambda layers with binaries; projects that make use of regular Node.js packages will not be impacted by this change + - Remedy: if your native modules / layers aren't yet available for `arm64` Linux, or you just aren't certain about the state of your dependency tree, add `runtime x86_64` to the `@aws` pragma in your project manifest +- Removed support for Node.js 14.x (now EOL, and no longer available to created in AWS Lambda) +- Resolved mismatch between `RouteSelectionExpression` in deployed Architect apps vs. locally in Sandbox + - The `RouteSelectionExpression` is now `$request.body.action`, meaning WebSocket code running locally can now be the same as in production + - Remedy: if you use custom `@ws` handlers and invoke them in Sandbox, you can remove conditional logic renaming the `message` property to `action`. Everything should now use `action`, like so: `ws.send(JSON.stringify({ action: 'custom-endpoint', ... }))` + + +### Notable changes + +- Added experimental `--fast` flag, which ships project to AWS without waiting around to determine if the deployment completed successfully. Use with care! +- Architect no longer requires the AWS CLI, nor Python. So if you'd like to remove either or both, feel free! +- Deploy no longer writes `sam.json` + `sam.yaml` files upon each deploy + - However, if you do want to see the `sam.json` being deployed, use the `--dry-run` or `--debug|-d` CLI flags + + +### Compatibility with `@architect/functions` + +Arc 11 also ships with Architect Functions 8, which also makes use of `aws-lite`. This is an important upgrade, as version 8 no longer suffers from 500-1000ms cold starts due to instantiating the AWS SDK. Version 8 is now between 2-5x faster, and uses 2-4x less memory. + +Version 8 also introduces two important breaking changes noted below; while we do not recommend using use version 7 due to the deprecation of AWS SDK v2 and ongoing performance issues with AWS SDK v3, you may continue to do so as long as the AWS SDK is installed in your project's dependencies. + +Additionally, you can use Architect Functions 8 in Arc 10 projects. + + +#### DynamoDB client instantiation + +Because the AWS SDK can no longer be assumed to be installed in Architect projects, `@architect/functions` offers a new `aws-lite`-based DynamoDB client (`_client`), and provides an opt-in affordance for using AWS SDK-based DynamoDB clients: +- If you only rely on the DocumentClient (`data._doc`), you may want to just try using the new [`@aws-lite/dynamodb`](https://aws-lite.org/services/dynamodb)-based `_client`, which is functionally the same, but significantly faster +- Code depending on `data._db` or `data._doc` must now instantiate with the `awsSdkClient` boolean option, like so: `await arc.tables({ awsSdkClient: true })` + - Using the `awsSdkClient` option necessitates having AWS SDK v2 or v3 installed, according to your Lambda's Node.js version (v2 for 16.x, v3 for 20.x+) + + +#### Error semantics + +Similar to how AWS SDK v3 introduced breaking changes to error semantics from AWS SDK v2, `aws-lite` errors may also be different. We've taken efforts to ensure the maximum degree of compatibility with both AWS SDK v2 and v3 errors, but they may still vary slightly. +- This only really applies if your error handling relies on specific error properties or values +- If you just `console.log()` your errors, you will be totally fine, and the quality of the errors you get via `aws-lite` will most likely improve with this change +- Note: if you're an AWS SDK v2 user considering migrating to v3, error incompatibility will apply even more so; v3 errors are incompatible with v2, whereas `aws-lite` errors attempt to be compatible with both SDK v2 + v3 where possible + + +#### Backward compatibility + +Again, it should be noted that `@architect/functions` 8 is not a required upgrade; you may continue using `@architect/functions` 7 so long as the AWS SDK is installed in your project's dependencies. + +--- + +## Architect 9 → 10 + +Architect 10 (Taniwha) is a major feature release, introducing the Architect plugins API, and cleaning up internal legacy code, module APIs, and other bits from earlier on in Architect's history. + +Most of Architect 10's breaking changes were internal; most users should not encounter breaking changes when upgrading Architect to v10, and Functions + ASAP to v5. + + +### Removed + +- Removed the `package` command, which was no longer able to represent the final state of Architect projects + - Remedy: its (improved) replacement is now: `deploy --eject` +- Removed support for legacy `.arc-env` env files (initially deprecated in late 2020) + - Remedy: if you are still using a `.arc-env` file, please move your [local env vars to `prefs.arc`](/docs/en/reference/configuration/local-preferences#%40env) or [`.env`](/docs/en/reference/configuration/local-preferences#.env-file-support) +- [Removed `toml` support](https://github.com/architect/architect/discussions/1294) (e.g. `arc.toml`) +- Removed built-in support for the `REST` API Gateway. Support is moved to an external plugin, [`plugin-rest-api`](https://github.com/architect/plugin-rest-api). + + +### Breaking changes + +- The beta plugins API has been largely refactored; wherever possible, hooks from the beta API have been ported to the final shipping plugin API. However, many things changed, so if you authored plugins against the beta API, please refer to the [new plugin documentation](/docs/en/reference/plugins/overview) to ensure compatibility +- Due to ongoing issues with unpredictable behavior with certain external libraries, Architect no longer makes use of the `NODE_ENV` environment variable, nor is it automatically added to deployed apps. + - Remedy: if your code relies on Architect automatically populating `NODE_ENV`, you should add it to your userland environment variables, like so: `npx arc env --add --env testing NODE_ENV testing` (and again for `staging` + `production`) +- All support for bare CLI flags has been removed from Architect commands + - All functionality has been retained, but now proper flags must be used + - Example: `npx arc deploy production` should now be `npx arc deploy --production` +- Sandbox's new automatic port selection should frequently actually improve and un-break common uses of Sandbox in testing. However, if your tests or tools rely on Sandbox's default ports (e.g. `3334` for `@events`), you will need to make some minor changes: + - Remedy: Lambda invocations in Sandbox are now passed an environment variable called `ARC_SANDBOX`, which contains a JSON serialized `ports` property; use this property to obtain the currently configured port for a given service + - Should you need to have consistent, non-automatically-selecting ports for Sandbox's internal services, use Sandbox's standard means for explicitly defining port configuration; **do not merely rely on Sandbox's default ports**, as they should be expected to change at any time +- Breaking change: legacy `@tables` / `@tables-streams` folders (`src/tables/...` and `src/streams/...`) are now deprecated in favor of `src/tables-streams/...` + - Remedy: existing stream functions can simply have their folders renamed to `src/tables-streams/{name}` (or use a custom `src` property on them if you'd prefer to keep your existing folder structure) +- Breaking change: `@indexes` is now fully deprecated + - Remedy: simply change the `@indexes` pragma name to `@tables-indexes`; no other changes are required +- Breaking change: moved legacy API Gateway REST API provisioning to `@architect/plugin-rest-api` plugin; to continue deploying REST APIs with Architect: + - Remedy: install `@architect/plugin-rest-api` to your project's dependencies, add `@plugins architect/plugin-rest-api` and `@aws apigateway rest` to your project manifest + + +### Internal breaking changes + +The following internal changes should not have any impact on Architect users should Architect v10 be paired with Functions v5, but just in case anyone used these somewhat more obscure internal features, environment variables, etc., we'll enumerate the changes here: + +- All Architect modules' CLI APIs have been revamped, and now accept an object containing Inventory +- `@architect/env`'s module API has been significantly revamped; if you use `env` as a module, please refer to its documentation +- The following internal Architect environment variables are no longer used: `ARC_CLOUDFORMATION`, `ARC_HTTP`, `ARC_SANDBOX_PATH_TO_STATIC` +- Inventory structure changes: + - `inv._project.src` now represents the default source tree folder (e.g. `$cwd/src`), while `_project.cwd` (new) refers to the current working directory of the project + - `inv._project.env` is now an object populated by three properties: `local`, `plugins`, and `aws`, each reflecting the env vars found for each context's three environments (`testing`, `staging`, `production`) + - `lambda.handlerFunction` has been renamed to `lambda.handlerMethod` +- Architect now prioritizes the AWS region passed via Inventory params over the `AWS_REGION` env var; in practice this should have no practical effect for most users +- Per Deno's guidelines, Architect now prioritizes `mod.ts|js` handlers in Deno Lambdas over the other supported files; in practice this should have no practical effect unless your handler has both an `index.ts|js` file and a `mod.ts|js` file in its root +- Deprecated `ARC_SANDBOX_ENABLE_CORS` env var option from Sandbox + - Remedy: this option predates Architect's support of `options`, which landed in version 8; handling options requests with an `options` (or `any`) handler is the preferred approach to handling CORS +- Internal change: stopped optimistically populating default `arc-sessions` + `data` tables in Sandbox + - This was a very obscure and quirky holdover behavior from early Architect that differed Sandbox from live AWS behavior + - Remedy: realistically no one ever actually used this feature in production, because to do so would have necessitated defining an `arc-sessions` or `data` table in your project manifest; that said, if you experimented with these default DynamoDB tables and want to use them in production, simply add them to your `@tables` pragma + + +### Important non-breaking change + +In a future major release, Architect will deprecate all non-namespaced environment variables. For now, Architect prefers the namespaced versions of the same env var, but will support both; some examples: + +- `ARC_HTTP_PORT` is preferred to `PORT` in Sandbox +- `ARC_SESSION_TABLE_NAME` is preferred to `SESSION_TABLE_NAME` in Sandbox + +If you key on or use non-namespaced Architect env vars, we suggest changing them over to their namespaced equivalents as soon as is convenient. + + +### Compatibility with `@architect/functions` + +Due to internal breaking changes, `@architect/functions` v4 and below is incompatible with Architect 10. +- Remedy: upgrade `@architect/functions` to v5 or above; all existing functionality has been retained, [see more here](#architect-functions) + +--- + +## Architect 8 → 9 + +Architect 9 (La Chupacabra) is a maintenance release, primarily aimed at removing support for Node.js 10.x (now end-of-life) and Node.js 12.x. With this release, after two years since the release of Architect 6, Architect 5 is no longer supported. + +Additionally, Architect's default runtime is now `nodejs14.x` – if your existing functions do not specify a runtime, they will be automatically and seamlessly upgraded from `nodejs10.x` or `nodejs12.x` to `nodejs14.x`. + + +### Breaking changes + +- Removed support for Node.js 10.x (now EOL, and no longer available to created in AWS Lambda) and Node.js 12.x +- `arc destroy` + - `--app` must now be used to destroy apps, while `--name` may only be used to destroy stacks + - Removed support for deprecated `--nuke` flag +- `arc hydrate` + - Usage remains the same, but its module API has removed support for `hydrate()` in favor of explicit `hydrate.install|update|shared()` methods + + +### Compatibility with `@architect/functions` + +Architect 9 is fully compatible with `@architect/functions`. However, `@architect/functions` 4.0, released alongside Architect 9, has some breaking changes: + +- Removed support for Node.js 10.x (now EOL, and no longer available to created in AWS Lambda); Node.js 12.x will continue to be supported until it is EOL in AWS Lambda +- `arc.http.proxy` is now `@architect/asap`, and has been removed from `@architect/functions` 4.0 +- `arc.http.proxy` calls can now be sent as-is to ASAP + - For more details, please see the [@architect/functions changelog](https://github.com/architect/functions/blob/master/_changelog.md#200-2021-07-25) and [@architect/asap changelog](https://github.com/architect/asap/blob/master/_changelog.md#400-2021-07-25) +- Removed support for handling requests from Architect 5 (and lower) APIs + - Responding to requests has not changed, however! + - Old response semantics from Architect 5 (and lower) will continue to be supported, so you'll always have a clear, clean upgrade path from older Architect projects to newer APIs + +--- + ## Architect 7 → 8 ### Overview @@ -339,7 +534,7 @@ Architect 6 is a milestone release that solves some of the most crucial feedback - This includes Ruby and Python support for the `sandbox` local dev server - It also includes ports of the Architect Functions module for [Ruby](https://github.com/architect/arc-functions-ruby) and [Python](https://github.com/architect/arc-functions-python) - Note: Functions for Ruby and Python is not yet at feature parity with the Node.js version, largely dictated by the availability and consistency of certain lower-level APIs in those runtimes. - - As such, broadly speaking, if using Architect Functions in your [HTTP functions](/en/reference/functions/http-functions) we recommend the Node.js version, which includes the most extensive support for front-end facing use cases. + - As such, broadly speaking, if using Architect Functions in your [HTTP functions](/docs/en/reference/project-manifest/http) we recommend the Node.js version, which includes the most extensive support for front-end facing use cases. - **Architect 6 now provisions CDNs with the `@cdn` pragma** - Finally, provision fast, fully-featured, global CDNs to live in front of your web app - This enables your web application to take advantage of crucial features like edge caching and global points of presence @@ -405,7 +600,7 @@ The following Architect 5 `request` parameters changed in Architect 6: - Still one of `GET`, `POST`, `PATCH`, `PUT`, or `DELETE` - `body` is no longer a pre-parsed object - `body` is now either `null` or a base64-encoded buffer - - `body` must first be decoded, then parsed, to make use of it; Architect provides a handy helper to take care of this for you, see: [parsing HTTP request bodies](/en/reference/functions/http-functions#requests) + - `body` must first be decoded, then parsed, to make use of it; Architect provides a handy helper to take care of this for you, see: [parsing HTTP request bodies](/docs/en/reference/runtime-helpers/node.js#arc.http) - `params` is now `pathParameters` - Still an object containing any URL params, if defined in your HTTP function's path (e.g. `foo` in `GET /:foo/bar`) - `query` is now `queryStringParameters` @@ -501,8 +696,8 @@ Architect version 5 is here! Things we added: -- [WebSocket support](/en/guides/tutorials/adding-websockets-to-your-app) -- [New middleware](/en/reference/runtime-helper-reference/arc-http) - added later in arc 4, and out of the box in Arc 5 +- [WebSocket support](/docs/en/reference/project-manifest/ws) +- [New middleware](/docs/en/reference/runtime-helpers/node.js#arc.http) - added later in arc 4, and out of the box in Arc 5 ### 4.x @@ -607,7 +802,7 @@ exports.handler = arc.http(log, route) 1. Open up IAM in the AWS Console 2. Select **Roles** → **arc-role** 3. Click **Attach Policies** -4. Select **AWSLambdaSQSQueueExecutionRole** +4. Select **`AWSLambdaSQSQueueExecutionRole`** 5. Click **Attach Policy** Now existing functions can publish to SQS queues. @@ -620,9 +815,9 @@ If the command line is more your style you can upgrade with the following: aws iam attach-role-policy --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaSQSQueueExecutionRole --role-name arc-role ``` -**With NodeJS** +**With Node.js** -If you prefer to script this upgrade you can use the NodeJS `aws-sdk`: +If you prefer to script this upgrade you can use the Node.js `aws-sdk`: ```javascript let aws = require('aws-sdk') @@ -641,36 +836,36 @@ iam.attachRolePolicy({ ### Overview -`@architect/functions` runtime helper library will continue to be actively maintained, and is forwards compatible with Architect 6 → 7 upgrades, and most Architect 5 → 6 upgrades. +`@architect/functions` runtime helper library is actively maintained. Version 5 is required for Architect 10+. Users on Architect 6 - 9 should continue to use Functions v4 (which will work in perpetuity, but not be separately maintained). The Architect Functions module is now also available as a dependency for [Ruby](https://github.com/architect/arc-functions-ruby) and [Python](https://github.com/architect/arc-functions-python) functions. ### Changes +- `arc.http.express` was removed in February 2022, and we recommend using `@vendia/serverless-express` or `serverless-http` as replacement modules +- `arc.http.proxy` (previously `arc.proxy`) was deprecated in July 2021, and is now the standalone `@architect/asap` module + - Its methods are functionally the same - `arc.http.middleware` was deprecated in August 2019, and is now `arc.http.async` - These methods are functionally the same - `arc.http.helpers.static` was deprecated in June 2019, and is now `arc.static` - These methods are functionally the same - Due to some under-the-hood changes, if you use `arc.http.helpers.static` or `arc.static`, you will need to upgrade to `@architect/functions` version `^3.3.4` (or greater) in Architect 6 -- `arc.proxy` was deprecated in May 2020, and is now `arc.http.proxy` - - These methods are functionally the same -In all three cases, these are functionally the same. The old aliases will remain for a while to come, but we suggest moving any deprecated calls over to their new equivalents by mid-2020. +### Does `@architect/functions` work in Architect 10+? -### Does `@architect/functions` work in Architect 7? +Yes! Version 5 is required to work correctly in Architect 10+. -Yes! It is supported by and forwards compatible in Architect 7, including use with HTTP API Lambda v2.0 payloads. +### Does `@architect/functions` work in Architect 6 - 9? -### Does `@architect/functions` work in Architect 6? +Absolutely, version 4.x is supported by Arc 6 - 9, including use with HTTP API Lambda v2.0 payloads (introduced in Arc 7). However, version 5+ is incompatible with Arc 6 - 9. -Yes! It is supported by and forwards compatible in Architect 6. Additionally, it has been expanded to include [`@tables` support for working with data](/en/reference/databases/tables). ### Will `@architect/functions` continue working in Architect 5? -Yes! `@architect/functions` is fully backward compatible with Architect 5. You can safely update this dependency, and expect related bugs to be patched. **However, it is worth noting that the new `tables()` method is an Architect 6-only feature.** +Version 4.x remains largely compatible with Architect 5; version 5+ is no longer backward compatible with Architect 5 (support for which officially ended in July of 2021). --- @@ -681,7 +876,7 @@ As of the release of Architect 6, the **Architect Data module (`@architect/data` ### Overview -`@architect/data` will no longer be maintained, and upgrading to Architect 6 will likely be a breaking change. +`@architect/data` will no longer be maintained, and upgrading to Architect 6 will likely be a breaking change for `@architect/data` usage. ### Does `@architect/data` work in Architect 6? @@ -699,7 +894,7 @@ If you built your `@architect/data` calls with `async/await`, `@architect/functi ### Example of `@architect/data` → `@architect/functions` `tables()` #### `@architect/data` before: -```js +```javascript // src/http/get-index/index.js let data = require('@architect/data') @@ -712,7 +907,7 @@ exports.handler = async () => { #### `@architect/functions` `tables()` after: -```js +```javascript // src/http/get-index/index.js let {tables} = require('@architect/functions') @@ -723,4 +918,3 @@ exports.handler = async () => { return {body} } ``` - diff --git a/src/views/docs/en/app-limits.md b/src/views/docs/en/app-limits.md new file mode 100644 index 00000000..1afafe4c --- /dev/null +++ b/src/views/docs/en/app-limits.md @@ -0,0 +1,4 @@ +--- +title: App limits +category: Architect +--- diff --git a/src/views/docs/en/faq.md b/src/views/docs/en/faq.md new file mode 100644 index 00000000..5943529b --- /dev/null +++ b/src/views/docs/en/faq.md @@ -0,0 +1,4 @@ +--- +title: FAQ +category: Architect +--- diff --git a/src/views/docs/en/get-started/detailed-aws-setup.md b/src/views/docs/en/get-started/detailed-aws-setup.md new file mode 100644 index 00000000..2a565979 --- /dev/null +++ b/src/views/docs/en/get-started/detailed-aws-setup.md @@ -0,0 +1,200 @@ +--- +title: Detailed AWS setup +category: Get started +description: Setting up, installing, and working with Architect and AWS. +--- + +> To work locally all you need is [Node](https://nodejs.org), any additional [supported runtimes](#runtime-environments) you plan to use, and the [Architect CLI](#install-architect). + +## AWS deployment requirements + +1. [Node.js](https://nodejs.org) for Architect +2. Any additional [supported runtimes](#runtime-environments) you plan to use in your application +3. [AWS credentials](#credentials) +4. [Architect CLI](#install-architect) + +--- + +### Runtime environments + +Architect supports the following runtimes for composing your application's business logic: + +- **Node.js**: >= 18.x using `npm` + - Unless otherwise specified in your project manifest, Node.js 22.x is the default runtime for new functions +- **Python**: >= 3.9 using `pip3` + - Unless otherwise specified in your project manifest, Python 3.13 is the default Python runtime +- **Ruby**: >= 3.2 using `bundle` +- **Deno**: `1.6.x` ([under development](../reference/runtime-helpers/deno)) + +> ⚠️ Working locally with the Sandbox requires target runtimes to be available in your `$PATH`. + +Additionally, other standard AWS-managed runtimes are supported in Architect applications (but may not be supported in [Sandbox](../reference/cli/sandbox)). Learn more about [Architect's runtime support](/docs/en/get-started/runtime-support). + +Architect also supports _any custom runtime_ in using either [Lambda Layers](https://docs.aws.amazon.com/lambda/latest/dg/configuration-layers.html) or [Lambda container images](https://docs.aws.amazon.com/lambda/latest/dg/images-create.html). + +Change a project's default runtime by specifying [an explicit environment](https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html) or an alias in `app.arc` with [the `@aws` pragma](../reference/project-manifest/aws). + +#### Examples + +```arc +# version pins the default runtime to Python 3.13 +@aws +runtime python3.13 +``` + +```arc +# always run the latest supported version of Python +@aws +runtime python +``` + +> ℹ️ This setting can be overridden on a per-function basis with [`config.arc`](../reference/configuration/function-config). + +--- + +### Credentials + +You'll need an Amazon Web Services account and credentials set up on your development machine and / or CI systems. If you don't yet have credentials on your development machine (like from [configuring the AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-welcome.html)), here's a guide for [gathering credentials from the AWS Console](../guides/developer-experience/create-aws-credentials). + +In the context of a deployment tool, Architect requires account credentials with IAM `AdministratorAccess` privileges. In turn, Architect will create and attach least-privilege IAM roles to runtime resources within your application, ensuring strict security boundaries by default. + +> ℹ️ While it is possible to limit Architect's deployment credentials to specific IAM and CloudFormation privileges, such an exercise would only be performative. Credentials capable of creating IAM roles can grant and attach new roles with `AdministratorAccess`. + +On \*nix systems AWS Credentials are listed in: + +```bash +~/.aws/credentials +``` + +Or on Windows systems: + +```bash +C:\Users\USER_NAME\.aws\credentials +``` + +If that file doesn't exist, create it, and add something like the following (assuming you have multiple AWS accounts): + +```bash +[default] +aws_access_key_id=xxx +aws_secret_access_key=xxx + +[work] +aws_access_key_id=xxx +aws_secret_access_key=xxx + +[personal] +aws_access_key_id=xxx +aws_secret_access_key=xxx +``` + +While it is recommended to explicitly declare your application's AWS profile and region in the `@aws` pragma of your project's manifest, you may also set a (default) profile and region with the `AWS_PROFILE` + `AWS_REGION` environment variables. + +--- + +### Install Architect + +The following command uses `npm`, the package manager for Node.js. + +To create an entirely new Architect project: + + +
+ +
Bash/cmd.exe
+
+ +```bash +npm init @architect ./testapp +``` +
+
+ + +
PowerShell
+
+ +```powershell +npm init "@architect" ./testapp +``` +
+
+
+
+ +To install Architect locally into an existing project: + + +
+ +
Bash/cmd.exe
+
+ +```bash +npm i -D @architect/architect +``` +
+
+ + +
PowerShell
+
+ +```powershell +npm i -D "@architect/architect" +``` +
+
+
+
+ +Or you can install Architect globally, enabling you to use Architect from any directory on your computer. + +```bash +npm i -g @architect/architect +``` + +--- + +## Interacting with AWS services + +Each Lambda runtime version includes its own built-in version of the AWS SDK. However, due to ongoing performance and developer ergonomics issues with the AWS SDK, we recommend utilizing the community-driven [`aws-lite`](https://aws-lite.org) SDK. + + +### Using `aws-lite` + +Since the AWS SDK is an extremely large library, we strongly recommend you do not ship your own version as a dependency, either in full or as a bundle. Doing so may have some of the following unintended side effects: + +- Slower Lambda coldstart and / or invocation +- Reduced available code payload size +- Possibly increased difficulty debugging (in the case of bundles) + +Instead, we recommend interacting with AWS services via [`aws-lite`](https://aws-lite.org) where possible, as it is 2-5x faster, and hundreds of megabytes smaller than the AWS SDK. [Learn more here](https://aws-lite.org/performance). + +Where that is not possible, we recommend using AWS Lambda's provided, *non-bundled* version of the AWS SDK. See [below for details](#node.js-aws-sdk-versions). + + +### Architect's AWS SDK strategy + +A core goal of Architect is to make building [Functional Web Apps](https://fwa.dev) simpler, and an important aspect of that objective is to help (automatically) manage the many dependencies in use across your Lambdas, whether your project has one or one hundred of them. + +However, in the of AWS SDK, AWS manages that dependency within the Lambda environment. For the aforementioned reasons, Architect does not attempt to automatically manage or include any version of AWS SDK in the resources it manages on your behalf. + +Practically speaking, that means if you rely on Architect's Lambda treeshaking feature – which scans your Lambda code and automatically installs `require`d or `import`ed dependencies at deploy-time – versions of the AWS SDK will not be automatically added to your Lambda dependencies by Architect (as it is already expected to be present in the Lambda environment). + +Architect will, however, attempt to provide helpful warnings where possible. For example: if your `nodejs18.x` Lambda `import`s `aws-sdk`, the now-deprecated v2 that is not built into the Lambda container, Architect will warn you of this during deployment. + + +### Node.js AWS SDK versions + +AWS has opted Node.js developers into a migration from AWS SDK v2 ([`aws-sdk`](https://www.npmjs.com/package/aws-sdk), now deprecated)) to v3 ([`@aws-sdk/*`](https://github.com/aws/aws-sdk-js-v3)): + +- Lambda `nodejs18.x` and greater use AWS SDK v3 (`@aws-sdk/*`) +- Lambda `nodejs16.x` and prior use AWS SDK v2 (`aws-sdk`) + - The last Lambda runtime to use SDK v2, `nodejs16.x`, is deprecated mid-2024 + +Migrating from AWS SDK v2 to v3 represents a meaningful change, and should be investigated thoroughly and with care. Key interfaces have been retired (such as `.promise()`), and some core SDK methods have changed significantly. (Example: [`S3.GetObject` no longer returns a Buffer](https://github.com/aws/aws-sdk-js-v3/issues/1877).) + +Instead of adopting the forced breaking changes of a migration to AWS SDK v3, we instead recommend migrating (and contributing, where possible) to the [`aws-lite`](https://aws-lite.org) SDK. + +> Architect maintains an ongoing commitment to backward compatibility wherever possible; as such, [`@architect/functions`](/docs/en/reference/runtime-helpers/node.js#%40architect%2Ffunctions) users can likely safely and reliably upgrade handlers in most cases. For additional details, please refer to the [Architect upgrade guide](/docs/en/about/upgrade-guide). diff --git a/src/views/docs/en/guides/get-started/project-layout.md b/src/views/docs/en/get-started/project-manifest.md similarity index 60% rename from src/views/docs/en/guides/get-started/project-layout.md rename to src/views/docs/en/get-started/project-manifest.md index 10a278a3..eb9561d1 100644 --- a/src/views/docs/en/guides/get-started/project-layout.md +++ b/src/views/docs/en/get-started/project-manifest.md @@ -1,5 +1,5 @@ --- -title: Project layout +title: Project manifest format category: Get started description: The layout and default structure of an Architect project --- @@ -12,7 +12,7 @@ Architect projects have the following significant folder structure by default: ├── src │ ├── shared ...... # Code shared by all Lambda functions │ ├── views ....... # Code shared by HTTP GET Lambda functions -│ ├── macros ...... # Modify the generated CloudFormation +│ ├── plugins ..... # Modify anything Architect does; including generated CloudFormation │ ├── http ........ # @http Lambda functions │ ├── events ...... # @event Lambda functions │ ├── queues ...... # @queue Lambda functions @@ -22,7 +22,7 @@ Architect projects have the following significant folder structure by default: └── app.arc ``` -> All folders are **OPTIONAL**. Architect ignores any additional folders. All source paths can be reconfigured to suit unique project needs. +> All folders are **optional**. Architect ignores any additional folders, and all source paths can be reconfigured to suit unique project needs. --- @@ -38,15 +38,13 @@ Architect supports the following manifest files: - `arc.yml` - `package.json` - `arc.json` -- `arc.toml` -> The maintainers are considering `arc.xml`; if this is something you want let us know! ## More on `app.arc` The `app.arc` format follows a few simple rules: -- whitespace is significant +- whitespace is significant and must be spaces - comments start with `#` - a pragma starts with `@` - pragmas can be ordered arbitrarily @@ -61,34 +59,35 @@ The `app.arc` format follows a few simple rules: These sections are for global system level env configuration. The most important being the `@app` namespace which is used to prefix all generated resources. -- [`@app`](/docs/en/reference/app.arc/app) **[Required]** application namespace -- [`@aws`](/docs/en/reference/app.arc/aws) AWS specific configuration -- [`@views`](/docs/en/reference/app.arc/views) configure path to view source code -- [`@shared`](/docs/en/reference/app.arc/shared) configure path to shared source code -- [`@macros`](/docs/en/reference/app.arc/macros) modify generated CloudFormation +- [`@app`](../reference/project-manifest/app) **[Required]** application namespace +- [`@aws`](../reference/project-manifest/aws) AWS specific configuration +- [`@views`](../reference/project-manifest/views) configure path to view source code +- [`@shared`](../reference/project-manifest/shared) configure path to shared source code +- [`@macros`](../reference/project-manifest/macros) modify generated CloudFormation ### Lambda resource definition These sections deal with Lambda functions and their event sources. Architect conventionally promotes one event source per function. Single responsibility functions are faster to deploy, easier to debug and secure to least privilege. -- [`@http`](/docs/en/reference/app.arc/http) HTTP routes (API Gateway) -- [`@events`](/docs/en/reference/app.arc/events) Event pub/sub (SNS) -- [`@queues`](/docs/en/reference/app.arc/queues) queues and handlers for them (SQS) -- [`@scheduled`](/docs/en/reference/app.arc/scheduled) Invoke functions specified times (CloudWatch Events) -- [`@ws`](/docs/en/reference/app.arc/ws) Web Socket functions (API Gateway) +- [`@http`](../reference/project-manifest/http) HTTP routes (API Gateway) +- [`@events`](../reference/project-manifest/events) Event pub/sub (SNS) +- [`@queues`](../reference/project-manifest/queues) queues and handlers for them (SQS) +- [`@scheduled`](../reference/project-manifest/scheduled) Invoke functions specified times (CloudWatch Events) +- [`@ws`](../reference/project-manifest/ws) Web Socket functions (API Gateway) ### Persistence resource definition These pragmas represent persistence resources. -- [`@static`](/docs/en/reference/app.arc/static) Bucket for hosting static assets (S3) -- [`@tables`](/docs/en/reference/app.arc/tables) Database tables and trigger functions (DynamoDB) -- [`@indexes`](/docs/en/reference/app.arc/indexes) Table global secondary indexes (DynamoDB) +- [`@static`](../reference/project-manifest/static) Bucket for hosting static assets (S3) +- [`@tables`](../reference/project-manifest/tables) Database tables and trigger functions (DynamoDB) +- [`@tables-indexes`](../reference/project-manifest/tables-indexes) Table global secondary indexes (DynamoDB) +- [`@tables-streams`](../reference/project-manifest/tables-streams) Table stream handler functions (DynamoDB + Lambda) ## Example -
+
arc
@@ -122,12 +121,15 @@ daily-affirmation rate(1 day) @tables likes likeID *String - stream true -@indexes +@tables-streams +likes + +@tables-indexes likes date *String ``` +
@@ -141,45 +143,41 @@ likes "static": { "fingerprint": true }, - "ws": [ - "action", - "connect", - "default", - "disconnect" - ], + "ws": ["action", "connect", "default", "disconnect"], "http": [ ["get", "/"], ["get", "/likes"], ["post", "/likes"] ], - "events": [ - "hit-counter" - ], + "events": ["hit-counter"], "scheduled": { - "daily-affirmation": "rate(1 day)" + "daily-affirmation": { + "rate": [1, "day"] + } }, "tables": { "likes": { - "likeID": "*String", - "stream": true + "likeID": "*String" } }, - "indexes": { + "tables-streams": ["likes"], + "tables-indexes": { "likes": { "date": "*String" } } } ``` +
-
yaml
```yaml +--- app: "hello" static: fingerprint: true @@ -197,51 +195,13 @@ events: scheduled: - daily-affirmation: "rate(1 day)" tables: - - likes: {likeID: "*String", stream: true} -indexes: - - likes: {date: "*String"} + - likes: { likeID: "*String" } +tables-streams: + - likes +tables-indexes: + - likes: { date: "*String" } ``` -
-
- - -
toml
-
- -```toml -app="hello" -[static] -fingerprint=true - -ws=[ - "action", - "connect", - "default", - "disconnect" -] - -http=[ - ["get", "/"], - ["get", "/likes"], - ["post", "/likes"], -] - -events=["hit-counter"] - -[scheduled] -daily-affirmation=["rate(1 day)"] - -[[tables]] - -[tables.likes] -likeiD="*String" -stream=true - -[[indexes]] -[indexes.likes] -date="*String" -```
@@ -250,28 +210,28 @@ date="*String" Running `arc init` in the same directory as the file above generates the following Lambda function code: -``` +```bash . ├── src +│ ├── events +│ │ └── hit-counter/index.js +│ │ │ ├── http │ │ ├── get-index/index.js │ │ ├── get-likes/index.js │ │ └── post-likes/index.js │ │ -│ ├── events -│ │ └── hit-counter/ -│ │ │ ├── scheduled -│ │ └── daily-affirmation/ +│ │ └── daily-affirmation/index.js │ │ -│ ├── tables -│ │ └── likes/ +│ ├── tables-streams +│ │ └── likes/index.js │ │ │ └── ws -│ ├── action/ -│ ├── connect/ -│ ├── default/ -│ └── disconnect/ +│ ├── action/index.js +│ ├── connect/index.js +│ ├── default/index.js +│ └── disconnect/index.js │ └── app.arc ``` diff --git a/src/views/docs/en/get-started/quickstart.md b/src/views/docs/en/get-started/quickstart.md new file mode 100644 index 00000000..77cd2d8d --- /dev/null +++ b/src/views/docs/en/get-started/quickstart.md @@ -0,0 +1,60 @@ +--- +title: Quickstart +category: Get started +description: Get started quickly with Architect +--- + +> Architect is a simple framework for building and delivering powerful [Functional Web Apps (FWAs)](https://fwa.dev) on AWS + +## Create a new project + +Assuming Node.js 16+ is installed, open your terminal and create a new Architect project: + + +
+ +
Bash/cmd.exe
+
+ +```bash +npm init @architect your-app +``` +
+
+ + +
PowerShell
+
+ +```powershell +npm init "@architect" your-app +``` +
+
+
+
+ +Start the local dev server: + +```bash +cd your-app +npx arc sandbox +``` +> `Cmd / Ctrl + c` exits the sandbox + +## Deploy to AWS + +Deploy to your built-in `staging` environment: + +```bash +npx arc deploy +``` +> Protip: create additional named development environments with the `--name` flag + +Ship to your built-in `production` environment: + +```bash +npx arc deploy --production +``` + +> Be safe! Set the `ARC_APP_SECRET` environment variable in production to secure your HTTP sessions; more information in the [`env` CLI reference](../reference/cli/env) diff --git a/src/views/docs/en/get-started/runtime-support.md b/src/views/docs/en/get-started/runtime-support.md new file mode 100644 index 00000000..c2b94942 --- /dev/null +++ b/src/views/docs/en/get-started/runtime-support.md @@ -0,0 +1,71 @@ +--- +title: Architect runtime support +category: Getting started +description: Architect runtime support documentation +--- + +## Overview + +This document outlines the current runtime support commitments Architect makes, both for official AWS Lambda runtimes (such as Node.js), and Architect-specific runtimes (such as TypeScript and Deno). + + +## Runtime support definitions + +### Deployment + +Architect will deploy your function to AWS. Caveats: +- If you are using a compiled language, such as .NET, you may be responsible for compiling your own handlers via custom scripts or [Architect plugins](/docs/en/guides/plugins/overview); however, once compiled, [Deploy](/docs/en/reference/cli/deploy) will ship your code. +- Some runtimes may be supported by way of an Architect runtime plugin. For example, Architect supports Rust via its [official Rust plugin](https://github.com/architect/plugin-rust). + + +### Sandbox + +Architect's local development environment, [Sandbox](/docs/en/reference/cli/sandbox), will execute your code locally in a fully-emulated Lambda. This may also include support for automatically compiling / transpiling handlers via plugin. + + +### Runtime utils + +Architect authors and maintains a runtime utility library, such as [`@architect/functions`](/docs/en/reference/runtime-helpers/node.js) (Node.js) or [`architect-functions`](/docs/en/reference/runtime-helpers/python) (Python), with helpers for various Architect primitives (`@http` events, etc.), service discovery support, and more. + + +### Automated dependency management + +Architect [Deploy](/docs/en/reference/cli/deploy) (via [Hydrate](/docs/en/reference/cli/hydrate)) can be relied upon to automatically install each handler's unique dependency tree without any manual management of per-handler dependency manifests (e.g. `src/http/get-index/package.json|requirements.txt`, etc.). + + +## Runtime support matrix + +Runtime | [Deployment][1] | [Sandbox][2] | [Runtime utils][3] | [Automated dependency management][4] | +------------|-----------------|---------------|---------------------|---------------------------------------| +Node.js | **✓** | **✓** | [**✓**][5] | **✓** +TypeScript¹ | **✓** | **✓** | [**✓**][5] | **✓** +Python | **✓** | **✓** | [**✓**][6] | **✓** +Deno² | **✓** | **✓** | [~²][7] | +Ruby | **✓** | **✓** | | +Rust³ | **✓** | **✓** | | +Go⁴ | **✓** | **✓** | | +Java | **✓** | & | | +.NET | **✓** | & | | +Custom | & | & | | + +Legend: +- `✓` full support +- `~` partial support +- `&` planned support + + +¹ TypeScript supported via [official plugin](https://github.com/architect/plugin-typescript) + +² Deno support may not be current or stable due to ongoing issues related to [Deno compiling to AWS Linux](https://github.com/denoland/deno/issues/17925); an [in-development Deno utility library can be found here](https://github.com/architect/functions-deno) + +³ Rust supported via [official plugin](https://github.com/architect/plugin-rust) + +⁴ Go supported via [official plugin](https://github.com/architect/plugin-go) + +[1]: #deployment +[2]: #sandbox +[3]: #runtime-utils +[4]: #automated-dependency-management +[5]: /docs/en/reference/runtime-helpers/node.js +[6]: /docs/en/reference/runtime-helpers/python +[7]: /docs/en/reference/runtime-helpers/deno diff --git a/src/views/docs/en/guides/get-started/why-architect.md b/src/views/docs/en/get-started/why-architect.md similarity index 73% rename from src/views/docs/en/guides/get-started/why-architect.md rename to src/views/docs/en/get-started/why-architect.md index 428699c9..36107e16 100644 --- a/src/views/docs/en/guides/get-started/why-architect.md +++ b/src/views/docs/en/get-started/why-architect.md @@ -4,11 +4,13 @@ category: Get started description: Why is Architect important to you? --- -> Architect provides everything you need to build massively scalable serverless apps with low code, clear and terse config, and zero ceremony +> Architect provides everything you need to build massively scalable [Functional Web Apps (FWAs)](https://fwa.dev) with low code, clear and terse config, and zero ceremony + ## The best developer experience -Going serverless is fraught with complex vendor arcana and market noise. This is where Architect comes in. Architect is an opinionated developer experience (DX) for building database backed web apps with AWS. We remove all the noise and friction to building serverlessly. We prioritize speed with fast local dev, smart configurable defaults and flexible Infrastructure as Code. And then we get out of your way so you can focus on business logic instead of glue code and only pay for services while in use, on-demand, and otherwise _scale to zero_. +Building a functional web app can be fraught with complex vendor arcana and market noise. This is where Architect comes in. Architect is an opinionated developer experience (DX) for building database backed web apps with AWS. We remove all the noise and friction to building FWAs. We prioritize speed with fast local dev, smart configurable defaults and flexible Infrastructure as Code. And then we get out of your way so you can focus on business logic instead of glue code and only pay for services while in use, on-demand, and otherwise _scale to zero_. + ### Work locally @@ -16,7 +18,7 @@ Developers need to work locally, to debug, test, and preview code before deployi ### Infrastructure as Code -At its heart Architect is an [Infrastructure as Code](https://en.wikipedia.org/wiki/Infrastructure_as_code) (IaC) framework. Architect defines a high level manifest file, in multiple open text formats, and turns formerly complex cloud infrastructure provisioning into a build artifact. You define the cloud infrastructure your application code requires and check that manifest into version control so infra and code are always aligned and deterministic. Architect compiles manifest code into AWS CloudFormation and deploys it. Architect supports a native text file format `app.arc` in addition to popular formats: `package.json`, `arc.json`, `arc.yaml` and `arc.toml`. Teams can choose the dialect that works best for them. +At its heart Architect is an [Infrastructure as Code](https://en.wikipedia.org/wiki/Infrastructure_as_code) (IaC) framework. Architect defines a high level manifest file, in multiple open text formats, and turns formerly complex cloud infrastructure provisioning into a build artifact. You define the cloud infrastructure your application code requires and check that manifest into version control so infra and code are always aligned and deterministic. Architect compiles manifest code into AWS CloudFormation and deploys it. Architect supports a native text file format `app.arc` in addition to popular formats: `package.json`, `arc.json`, and `arc.yaml`. Teams can choose the dialect that works best for them. ### Secured to least privilege by default diff --git a/src/views/docs/en/guides/backend/begin-data-backup.md b/src/views/docs/en/guides/backend/begin-data-backup.md new file mode 100644 index 00000000..c618b386 --- /dev/null +++ b/src/views/docs/en/guides/backend/begin-data-backup.md @@ -0,0 +1,142 @@ +--- +title: Download a Begin environment's data +description: +--- + +Begin was a common means of deploying Architect apps, and a long-time sponsor of the Architect project. Prior to the shutdown of Begin, customers can download their environment data for use in Architect apps like so: + + +## Usage + +Node.js 20.x+ is required. + +Use this script to download a Begin app environment's data. Either a single table (recommended) or all tables. + +First, create a script file: + +```bash +touch ./begin-download.mjs +``` + +Then paste in the [script contents below](#download-script). + +This program will use your stored Begin credentials, and ask for an `appID`, `envID`, and the name of your table. +A `.json` file will be saved to disk alongside the `begin-download.mjs` file: + +```javascript +// data-appID-envID-tables.json +[ + { + name: String, + info: DynamoDB.TableDescription, + items: [DynamoDB.Item], + complete: Boolean, // true, if the entire table was downloaded + } +] +``` + +When ready, run it like so: + +```bash +node begin-download.mjs +``` + + +## 100MB Limit + +If the requested table (or all tables when no table name is specified) exceeds 100MB, partial data will be returned. +If you encounter this, you can request a custom export via [Discord](https://discord.com/invite/y5A2eTsCRX) or support@begin.com. + +## Notes + +Retrieve app and env IDs with the Begin CLI: + +```bash +begin list +# or, if the CLI isn't installed: +npx @begin/deploy@latest list +``` + +Refer to your application's `*.arc` file for a list of table names. + +--- + +## Download script + +```javascript +// begin-download.mjs +import fs from 'node:fs' +import path from 'node:path' +import process from 'node:process' +import readline from 'node:readline' +import url from 'node:url' + +const rl = readline.createInterface({ input: process.stdin, output: process.stdout }) +const currentDir = path.dirname(url.fileURLToPath(import.meta.url)) +const homeDir = process.env.HOME || process.env.USERPROFILE || '~' +const configPath = path.join(homeDir, '.begin', 'config.json') + +function question (query) { + return new Promise((resolve) => rl.question(query, resolve)) +} + +async function main() { + let TOKEN + try { + const config = JSON.parse(fs.readFileSync(configPath, 'utf8')) + TOKEN = config.access_token + if (!TOKEN) throw 'access_token not found in Begin config' + } catch (error) { + TOKEN = question('Enter Begin API access token: ') + } + if (!TOKEN) throw 'Begin API token required!' + + const APP_ID = await question('Enter appID: ') + if (APP_ID.length < 8) throw 'appID required' + const ENV_ID = await question('Enter envID: ') + if (ENV_ID.length < 8) throw 'envID required' + const TABLE = await question('Enter table name (optional, press Enter to skip): ') + + let URL = 'https://api.begin.com/v1' + URL += `/apps/${APP_ID}/${ENV_ID}/data` + if (TABLE) URL += `?table=${TABLE}` + + const response = await fetch( + URL, + { + headers: { + authorization: `bearer ${TOKEN}`, + 'content-type': 'application/json', + }, + } + ) + + if (!response.ok) throw `ERROR: ${await response.text()}` + + const tables = await response.json() + + if (!tables || tables.length === 0) throw 'No data returned' + + let filename = `data-${APP_ID}-${ENV_ID}-${TABLE ? `${TABLE}` : 'tables'}.json` + fs.writeFileSync( + path.join(currentDir, filename), + JSON.stringify(tables, null, 2), + ) + + for (const { info, name, items, complete } of tables) { + console.log(`\nTable "${name}"`) + if (!complete) console.log('Incomplete scan! Table too large.') + console.log(` <${info.TableId}>`) + console.log(` (${items.length} items)`) + } + + console.log(`\nSaved ./${filename}`) + + rl.close() +} + +main().catch((e) => { + console.error(e) + process.exit(1) +}) +``` diff --git a/src/views/docs/en/guides/backend/database-functions.md b/src/views/docs/en/guides/backend/database-functions.md index 07922839..bfd4b80a 100644 --- a/src/views/docs/en/guides/backend/database-functions.md +++ b/src/views/docs/en/guides/backend/database-functions.md @@ -11,7 +11,7 @@ sections: Architect database functions are a log of information about changes to items in a DynamoDB table. When you enable a stream on a table under the `@tables` pragma in your `app.arc` file, DynamoDB captures information about every modification to data items in the table. -This functionality is commonly used to stream data to other sources and provide data-based event triggers. (eg. when I add new user row to the accounts table …send a welcome email) +This functionality is commonly used to stream data to other sources and provide data-based event triggers. (e.g. when I add new user row to the accounts table …send a welcome email) Database functions capture a time-ordered sequence of item-level modifications in any DynamoDB table and stores this information in a log for up to 24 hours. Applications can access this log and view the data items as they appeared before and after they were modified, in near-real-time. diff --git a/src/views/docs/en/guides/backend/event-functions.md b/src/views/docs/en/guides/backend/event-functions.md index 0826a339..4e309c5a 100644 --- a/src/views/docs/en/guides/backend/event-functions.md +++ b/src/views/docs/en/guides/backend/event-functions.md @@ -36,13 +36,14 @@ account-signup account-check-email ``` -### `@events` Syntax +### `@events` syntax -- Lowercase alphanumeric string -- Maximum of 50 characters -- Dashes are allowed; underscores are not allowed +- Lower + upper case alphanumeric string +- Maximum of 240 characters +- Dashes, periods, and underscores are allowed - Must begin with a letter + ### Provisioning new event functions To provision a new event function, in the root of your project, open your app's Architect project manifest file (usually `app.arc`): @@ -76,7 +77,7 @@ Additionally `AWS::SSM::Parameter` resources are created for every SNS Topic whi - **`/[StackName]/events/[EventName]`** with a value of the generated SNS Topic ARN -> All runtime functions have the environment variable `AWS_CLOUDFORMATION` which is the currently deployed CloudFormation stack name; this combined w the runtime `aws-sdk` or `@architect/functions` can be used to lookup these values in SSM +> All runtime functions have the environment variables `ARC_APP_NAME` and `ARC_STACK_NAME` which is the currently deployed CloudFormation stack name; this can be used by [`aws-lite`](https://aws-lite.org), `aws-sdk`, and `@architect/functions` to look up these values in SSM --- @@ -172,4 +173,3 @@ function count(payload, callback) { exports.handler = arc.events.subscribe(count) ``` - diff --git a/src/views/docs/en/guides/backend/events-and-queues.md b/src/views/docs/en/guides/backend/events-and-queues.md index 11544016..e3dc2aca 100644 --- a/src/views/docs/en/guides/backend/events-and-queues.md +++ b/src/views/docs/en/guides/backend/events-and-queues.md @@ -11,7 +11,7 @@ sections: ## Overview -Background tasks are a common workload and perfect for serverless environments. They reinforce event-driven architecture and allow you to perform asynchronous work across a distributed system. These functions are well suited for processes that don't require an immediate response, or are too resource intensive for a single function. +Background tasks are a common workload and perfect for [Functional Web Apps](https://fwa.dev). They reinforce event-driven architecture and allow you to perform asynchronous work across a distributed system. These functions are well suited for processes that don't require an immediate response, or are too resource intensive for a single function. Architect has three main primitives of background functions: - [@events](/docs/en/reference/arc-pragmas/@events) - A pub/sub service that uses SNS. @@ -34,10 +34,32 @@ In this tutorial, we will create an event topic, POST JSON data to invoke a subs 1. We will start with a fresh project and install dependencies. -``` bash -npm init @architect ./arc-event-app + +
+ +
Bash/cmd.exe
+
+ +```bash +npm init @architect arc-event-app cd arc-event-app ``` +
+
+ + +
PowerShell
+
+ +```powershell +npm init "@architect" arc-event-app +cd arc-event-app +``` +
+
+
+
+ 2. Open up your `app.arc` file and add the `@event` pragma along with a POST route ```arc @@ -121,7 +143,7 @@ async function yolo() { return { location: '/' } } -exports.handler = arc.http.async(yolo) +exports.handler = arc.http(yolo) ``` Test it locally by running `npm start` in your terminal from the project root. Architect's Sandbox environment will emulate the same behavior once your project is deployed. Within the event function, you can split logic among code from `src/shared`. @@ -137,10 +159,31 @@ Another common background task is `@scheduled` functions. These functions are in The first thing we will need is a fresh Architect project. We can create one directly from the terminal. + +
+ +
Bash/cmd.exe
+
+ ```bash npm init @architect ./arc-scheduled-app cd arc-scheduled-app ``` +
+
+ + +
PowerShell
+
+ +```powershell +npm init "@architect" ./arc-scheduled-app +cd arc-scheduled-app +``` +
+
+
+
Now we can open up the `app.arc` file and add a scheduled function to the manifest. @@ -183,7 +226,7 @@ From the terminal, run `arc deploy --dry-run` and take a look at `sam.yaml` in t "Properties": { "Handler": "index.handler", "CodeUri": "./src/scheduled/daily", - "Runtime": "nodejs12.x", + "Runtime": "nodejs14.x", "MemorySize": 1152, "Timeout": 5, "Environment": { @@ -196,7 +239,7 @@ From the terminal, run `arc deploy --dry-run` and take a look at `sam.yaml` in t }, "ARC_APP_NAME": "arc-scheduled-app", "ARC_HTTP": "aws_proxy", - "NODE_ENV": "staging", + "ARC_ENV": "staging", "SESSION_TABLE_NAME": "jwe" } }, @@ -231,10 +274,31 @@ The `Events` property on the `Daily` function shows that this is a scheduled eve Let's make an example message queue by starting with a fresh Architect project. + +
+ +
Bash/cmd.exe
+
+ ```bash npm init @architect ./arc-queues-app cd arc-queues-app ``` +
+
+ + +
PowerShell
+
+ +```powershell +npm init "@architect" ./arc-queues-app +cd arc-queues-app +``` +
+
+
+
Open up the `app.arc` file and modify the manifest to include our `@queues` function as follows: diff --git a/src/views/docs/en/guides/backend/persist-data.md b/src/views/docs/en/guides/backend/persist-data.md index c73a0a92..8071d051 100644 --- a/src/views/docs/en/guides/backend/persist-data.md +++ b/src/views/docs/en/guides/backend/persist-data.md @@ -24,6 +24,7 @@ In this tutorial you will build a simple note taking application, with multiple **Sections** +- [Overview](#overview) - [Generating the Data Layer](#generating-the-data-layer) - [Implementing an Admin Interface](#implementing-an-admin-interface) - [Implementing Signup](#implementing-signup) @@ -99,13 +100,13 @@ touch src/shared/layout.js // src/shared/layout.js let arc = require('@architect/functions'), - url = arc.http.helpers.url, - static = arc.http.helpers.static + url = arc.http.helpers.url, + static = arc.http.helpers.static module.exports = function layout(contents, showNav = true, isLoggedIn = true) { - var nav = '' + let nav = '' - var navLinks = ` + let navLinks = ` Log in Sign up ` @@ -132,9 +133,7 @@ module.exports = function layout(contents, showNav = true, isLoggedIn = true) { Architect demo app - - - + ${ nav } @@ -154,8 +153,8 @@ Next, we require the layout module in the root of our application. This will sho // src/http/get-index/index.js let arc = require('@architect/functions'), - layout = require('@architect/shared/layout'), - url = arc.http.helpers.url + layout = require('@architect/shared/layout'), + url = arc.http.helpers.url require('@architect/shared/globals') @@ -165,7 +164,7 @@ exports.handler = async function http(request) { let isLoggedIn = !!state.person - var loggedInPage = ` + let loggedInPage = `

Welcome back ${ email }!

You've logged in. That's so cool.

@@ -173,7 +172,7 @@ exports.handler = async function http(request) {

` - var notLoggedInPage = ` + let notLoggedInPage = `

Welcome to the Architect demo app!

It looks like it's your first time here. You should sign up now!

@@ -199,9 +198,9 @@ Our signup page is a simple form: // src/http/get-signup/index.js let arc = require('@architect/functions'), - layout = require('@architect/shared/layout'), - url = arc.http.helpers.url, - static = arc.http.helpers.static + layout = require('@architect/shared/layout'), + url = arc.http.helpers.url, + static = arc.http.helpers.static require('@architect/shared/globals') @@ -216,7 +215,7 @@ exports.handler = async function http(req) { } } - var signupPage = ` + let signupPage = `

Welcome to the Notes page ${ state.person.email }!

${ greeting }

@@ -690,12 +688,12 @@ module.exports = async function getNotes(email) { log(`Searching for notes for "${ email }". Found ${ result.Count } results`) - var notes = result.Items + let notes = result.Items return notes } ``` -Now that we've got the form, let's implement the POST handler. We'll use the [hashids](https://hashids.org/) library to help create keys for our notes. +Now that we've got the form, let's implement the POST handler. We'll use the [`hashids`](https://hashids.org/) library to help create keys for our notes. ```bash cd src/http/post-notes @@ -897,7 +895,7 @@ npm i @architect/data Now running `npx repl` opens a REPL into your Dynamo schema running locally and in-memory. If you are running the app with `npx sandbox` in another tab, it connects to that database. -Try starting the REPL and running: `data.notes.scan({}, console.log)` to see all the current notes. The REPL can attach itself to the `staging` and `production` databases also by setting the appropriate `NODE_ENV` environment variable flag. +Try starting the REPL and running: `data.notes.scan({}, console.log)` to see all the current notes. The REPL can attach itself to the `staging` and `production` databases also by setting the appropriate `ARC_ENV` environment variable flag. --- @@ -940,5 +938,4 @@ let deleteNote = async function route(request) { exports.handler = arc.middleware(requireLogin, deleteNote) ``` -> 🎩 Tip: `data._db` and `data._doc` return instances of `DynamoDB` and `DynamoDB.DocumentClient` for directly accessing your data; use `data._name` to resolve the table names with the app name and environment prefix. - +> Use `data._name` to resolve table names with the app name and environment prefix diff --git a/src/views/docs/en/guides/backend/queue-functions.md b/src/views/docs/en/guides/backend/queue-functions.md index f0f0fd8b..e86a73c6 100644 --- a/src/views/docs/en/guides/backend/queue-functions.md +++ b/src/views/docs/en/guides/backend/queue-functions.md @@ -48,9 +48,9 @@ Which generates the corresponding code: ### Syntax -- Lowercase alphanumeric string -- Maximum of 50 characters -- Dashes are allowed; underscores are not allowed +- Lower + upper case alphanumeric string +- Maximum of 240 characters +- Dashes, periods, and underscores are allowed - Must begin with a letter --- @@ -80,7 +80,7 @@ Additionally `AWS::SSM::Parameter` resources are created for every SQS Queue whi - **`/[StackName]/events/[QueueName]`** with a value of the generated SQS Queue URL -> All runtime functions have the environment variable `AWS_CLOUDFORMATION` which is the currently deployed CloudFormation stack name; this combined w the runtime `aws-sdk` or `@architect/functions` can be used to lookup these values in SSM +> All runtime functions have the environment variables `ARC_APP_NAME` and `ARC_STACK_NAME` which is the currently deployed CloudFormation stack name; this can be used by [`aws-lite`](https://aws-lite.org), `aws-sdk`, and `@architect/functions` to look up these values in SSM ### Deploy @@ -132,5 +132,3 @@ def handler(request, context): arc.queues.publish(name='account-signup', payload={'ok':True}) return {'statusCode': 201} ``` - - diff --git a/src/views/docs/en/guides/backend/scheduled-functions.md b/src/views/docs/en/guides/backend/scheduled-functions.md index 4a8f8773..028cb42c 100644 --- a/src/views/docs/en/guides/backend/scheduled-functions.md +++ b/src/views/docs/en/guides/backend/scheduled-functions.md @@ -23,9 +23,10 @@ Scheduled functions are functions that are invoked at specified times and can be To get started with scheduled functions we must first add the `@scheduled` pragma to our `app.arc` manifest file. There is a specific syntax for setting the frequency for triggering our scheduled functions. ### Syntax -- Lowercase alphanumeric string -- Maximum of 20 characters -- Dashes are allowed; underscores are not allowed + +- Lower + upper case alphanumeric string +- Maximum of 240 characters +- Dashes, periods, and underscores are allowed - Must begin with a letter - Followed by a valid `rate` or `cron` expression ([more info here](https://docs.aws.amazon.com/lambda/latest/dg/tutorial-scheduled-events-schedule-expressions.html)) @@ -59,7 +60,7 @@ Which generates the following code: Cron expressions allow us to give our functions granular settings. `crons` have six required fields, which are separated by white space. | Field | Values | Wildcards | -|--------------|-----------------|---------------| +| ------------ | --------------- | ------------- | | Minutes | 0-59 | , - * / | | Hours | 0-23 | , - * / | | Day-of-month | 1-31 | , - * ? / L W | @@ -81,7 +82,7 @@ Cron expressions allow us to give our functions granular settings. `crons` have - The L wildcard in the Day-of-month or Day-of-week fields specifies the last day of the month or week. -- The W wildcard in the Day-of-month field specifies a weekday. In the Day-of-month field, 3W specifies the weekday closest to the third day of the month. +- The W wildcard in the Day-of-month field specifies a weekday. In the Day-of-month field, `3W` specifies the weekday closest to the third day of the month. - The # wildcard in the Day-of-week field specifies a particular instance of the specified day of the week within a month. For example, 3#2 would be the second Tuesday of the month: the 3 refers to Tuesday because it is the third day of each week, and the 2 refers to the second day of that type within the month. diff --git a/src/views/docs/en/guides/backend/tables.md b/src/views/docs/en/guides/backend/tables.md index 42c2b58c..92a6d699 100644 --- a/src/views/docs/en/guides/backend/tables.md +++ b/src/views/docs/en/guides/backend/tables.md @@ -9,7 +9,7 @@ sections: ## Overview -How we store information is one of the pillars of app development. Durable persistence of structured data is the foundation for all powerful web apps. Data needs to be instantaneous, consistent, secure, and able to scale easily to meet demand. `@tables` defines DynamoDB database tables and trigger functions for them to read and write data with single digit millisecond latency. +How we store information is one of the pillars of app development. Durable persistence of structured data is the foundation for all powerful web apps. Data needs to be instantaneous, consistent, secure, and able to scale easily to meet demand. `@tables` defines DynamoDB database tables and trigger functions for them to read and write data with single digit millisecond latency. Architect `@tables` defines DynamoDB tables and `@indexes` define global secondary indexes to facilitate more advanced access patterns. @@ -61,14 +61,15 @@ accounts `@tables` defines DynamoDB database tables and trigger functions for them. + #### Table name syntax -- Lowercase alphanumeric string +- Lower + upper case alphanumeric string - Between 3 and 255 characters -- Dashes are allowed -- Underscores are not allowed +- Dashes, periods, and underscores are allowed - Must begin with a letter + #### Table structure syntax - Keys and Lambdas are defined by indenting two spaces @@ -79,6 +80,7 @@ accounts > **Protip:** table names can be anything but choose a consistent naming scheme within your app namespace; one useful scheme is plural nouns like: `accounts` or `email-invites` + ### Indexes Indexes give you access to alternate query patterns, and can speed up queries. Architect provides fast access to items in a table by specifying primary key values. Indexing comes into the picture if you want to fetch the data of attributes other than the primary key. diff --git a/src/views/docs/en/guides/developer-experience/continuous-integration/aws-ec2.md b/src/views/docs/en/guides/developer-experience/continuous-integration/aws-ec2.md new file mode 100644 index 00000000..115a4ed7 --- /dev/null +++ b/src/views/docs/en/guides/developer-experience/continuous-integration/aws-ec2.md @@ -0,0 +1,50 @@ +--- +title: Deploying from an EC2 instance +category: Continuous integration +description: Setting up Architect project deployment from EC2 with proper IAM roles. +--- + +If you're deploying from a CI/CD pipeline that runs on AWS EC2 instances and uses [IAM roles for EC2](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html) to define the pipeline's permissions, you'll need to generate temporary credentials via the AWS SDK and use them to either define the AWS env vars (`AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, and `AWS_SESSION_TOKEN`). Once the credentials are in a form that Architect can read, you can call the deploy command as normal. + +Below is a basic example using the AWS JavaScript SDK that deploys a stack to the staging environment: + +```javascript +const { exec } = require('child_process') +const AWS = require('aws-sdk') + +// Assumes that you have a 'build' script defined in package.json for building +// frontend assets. +const DEPLOY_COMMAND = 'npm run build && npx arc deploy' + +const deploy = () => { + // JS-specific: you need to execute the deployment command or script in a subprocess + // in order to have access to the updated env vars. + return exec(DEPLOY_COMMAND, (error, stdout, stderr) => { + console.log(stdout) + + if (error) { + console.error(stderr) + console.error(error) + process.exit(error.code) + } + }) +} + +const deployWithAwsCredentials = (error, credentials) => { + if (error) { + console.error(error) + return process.exit(1) + } + + process.env.AWS_ACCESS_KEY_ID = + process.env.AWS_ACCESS_KEY_ID || credentials.accessKeyId + process.env.AWS_SECRET_ACCESS_KEY = + process.env.AWS_SECRET_ACCESS_KEY || credentials.secretAccessKey + process.env.AWS_SESSION_TOKEN = + process.env.AWS_SESSION_TOKEN || credentials.sessionToken + + return deploy() +} + +AWS.config.getCredentials(deployWithAwsCredentials) +``` diff --git a/src/views/docs/en/guides/developer-experience/continuous-integration/github-actions.md b/src/views/docs/en/guides/developer-experience/continuous-integration/github-actions.md new file mode 100644 index 00000000..c95038f7 --- /dev/null +++ b/src/views/docs/en/guides/developer-experience/continuous-integration/github-actions.md @@ -0,0 +1,130 @@ +--- +title: Deploying from GitHub Actions +category: Continuous integration +description: Deploy an Architect project from GitHub Actions. +--- + +Architect projects can be tested and deployed from GitHub Actions. + +## Architect-provided actions + +Architect has created [`architect/action-build`](https://github.com/architect/action-build) and [`architect/action-deploy`](https://github.com/architect/action-deploy) for GitHub Actions. These can be included as a part of your project's workflows. + +> 🔑 Required: `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` must be set in [your GitHub repository or organization secrets](https://docs.github.com/en/actions/security-guides/encrypted-secrets). + +The deploy action follows a standard pattern where commits to the `main` branch are deployed to a staging environment and git tags that begin with `v` are deployed to production. + +This enables a workflow where a pull request can be merged into `main` and automatically promoted to staging. When a git tag is created (like with `npm version patch|minor|major`) the project is deployed to production. +It is helpful to ["follow tags" when git pushing](https://git-scm.com/docs/git-push#Documentation/git-push.txt---follow-tags). + +### Usage example + +```yaml +# .github/workflows/build-deploy.yml +name: Build and deploy + +on: [ push, pull_request ] + +jobs: + # Build and test + build: + runs-on: ubuntu-latest + steps: + - name: Build App + uses: architect/action-build@v3 + + # Deploy main branch to staging and git tags to production + deploy: + needs: build + if: github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v') + runs-on: ubuntu-latest + + steps: + - name: Deploy app + uses: architect/action-deploy@v1 + with: + aws_access_key_id: ${{secrets.AWS_ACCESS_KEY_ID}} + aws_secret_access_key: ${{secrets.AWS_SECRET_ACCESS_KEY}} +``` + +## Custom action sample + +The following example is similar to the Architect actions but will deploy commits to the `dev` branch to staging and commits to `main` to production. Extract or add steps as needed for your pipeline. + +### Action YAML template + +```yaml +# ./.github/workflows/test-deploy.yml +name: Test and deploy + +on: + push: + branches: + - main + - dev + pull_request: {} + +jobs: + test: + name: Test + runs-on: ubuntu-latest + steps: + - name: Cancel previous runs + uses: styfle/cancel-workflow-action@0.11.0 + - name: Checkout repository + uses: actions/checkout@v3 + - name: Setup Node.js + uses: actions/setup-node@v3 + with: + node-version: 14 + - name: Install dependencies + uses: bahmutov/npm-install@v1 + - name: Arc hydrate + run: arc hydrate + - name: Run tests + run: npm test + + deploy: + name: Deploy + needs: test + runs-on: ubuntu-latest + steps: + - name: Cancel previous runs + uses: styfle/cancel-workflow-action@0.11.0 + - name: Checkout repository + uses: actions/checkout@v3 + - name: Setup Node.js + uses: actions/setup-node@v3 + with: + node-version: 14 + - name: Env report + run: | + echo "Event name: ${{ github.event_name }}" + echo "Git ref: ${{ github.ref }}" + echo "GH actor: ${{ github.actor }}" + echo "SHA: ${{ github.sha }}" + VER=`node --version`; echo "Node ver: $VER" + VER=`npm --version`; echo "npm ver: $VER" + - name: Install dependencies + uses: bahmutov/npm-install@v1 + - name: Arc hydrate + run: arc hydrate + - name: Staging deploy + if: github.ref == 'refs/heads/dev' + run: arc deploy --staging -v --prune + env: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + - name: Production deploy + if: github.ref == 'refs/heads/main' + run: arc deploy --production -v --prune + env: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} +``` + + diff --git a/src/views/docs/en/guides/developer-experience/continuous-integration/gitlab-pipelines.md b/src/views/docs/en/guides/developer-experience/continuous-integration/gitlab-pipelines.md new file mode 100644 index 00000000..c423e8be --- /dev/null +++ b/src/views/docs/en/guides/developer-experience/continuous-integration/gitlab-pipelines.md @@ -0,0 +1,71 @@ +--- +title: Deploying from GitLab Pipelines +category: Continuous integration +description: Deploy an Architect project from GitLab Pipelines. +--- + +Architect projects can be tested and deployed from GitLab Pipelines. + +## Custom pipeline sample + +The following example uses a standard pattern where commits to the `main` branch are deployed to a staging environment and git tags that begin with `v` are deployed to production. + +This enables a workflow where a pull request can be merged into `main` and automatically promoted to staging. When a git tag is created (like with `npm version patch|minor|major`) the project is deployed to production. +It is helpful to ["follow tags" when git pushing](https://git-scm.com/docs/git-push#Documentation/git-push.txt---follow-tags). + +> 🔑 Required: `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` must be set in [your GitLab repository or group variables](https://docs.gitlab.com/ee/ci/variables/#add-a-cicd-variable-to-a-project). + +### Pipeline YAML template + +Extract or add steps as needed for your pipeline. + +```yaml +# .gitlab-ci.yml +image: node:14-alpine + +stages: + - test + - deploy + +# cache npm across stages +cache: + key: $CI_COMMIT_REF_SLUG + paths: + - .npm/ + +# run npm install with known cache before each stage +before_script: + - npm ci --cache .npm --prefer-offline + +# build and test +test: + stage: test + script: + - npm run build --if-present + - arc hydrate + - npm test + +# deploy stages install aws-cli and deploy +.deploy: + script: + - apk add --no-cache aws-cli + - arc deploy --${env} -v --prune + +deploy-to-staging: + stage: deploy + extends: .deploy + variables: + env: staging + rules: + # if we're on main branch, deploy to staging + - if: $CI_COMMIT_BRANCH == 'main' + +deploy-to-production: + stage: deploy + extends: .deploy + variables: + env: production + rules: + # if git tag resembles a npm version + - if: $CI_COMMIT_TAG =~ /^v(?:\d+.){2}(?:\d+)$/ +``` diff --git a/src/views/docs/en/guides/developer-experience/create-aws-credentials.md b/src/views/docs/en/guides/developer-experience/create-aws-credentials.md new file mode 100644 index 00000000..6214824e --- /dev/null +++ b/src/views/docs/en/guides/developer-experience/create-aws-credentials.md @@ -0,0 +1,92 @@ +--- +title: Get your AWS credentials +category: Get started +description: Gather required credentials from the AWS Console +--- + +Outlined below are the steps to create an IAM user and get the access keys required to deploy to AWS with Architect. +An AWS account with access to create IAM users is required to complete these steps. + +## Locate or create a config file + +> This file is `~/.aws/credentials` on Linux and macOS systems, and `%USERPROFILE%\.aws\credentials` on Windows. + +Open or create the `credentials` file (no file extension) in your favorite editor. + + + +[Reference](https://docs.aws.amazon.com/sdkref/latest/guide/file-location.html) + + + +## Create your IAM user + +Sign in to the console and navigate to the "IAM" service. + +![IAM service on AWS Console](/images/aws-credentials/1.png) + +Go to the "Users" section (from the left side nav) and _click "Create user"_. + +Choose a user name for an admin on your machine that runs Architect. +_Click "Next"_ + +![Create new user form](/images/aws-credentials/2.png) + +From the "Set permissions" section choose "Attach policies directly" +Search for and select "AdministratorAccess". +_Click "Next"_ + +!["AdministratorAccess" is checked in Set permissions](/images/aws-credentials/3.png) + +Proceed to "Review and create" and _click "Create user"_ to finish creating the user. + +![Review the new user entry](/images/aws-credentials/4.png) + + + +[Reference](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_users_create.html#id_users_create_console) + + + +## Get your access keys + +Having created a new user with the correct access, create an access key to use with Architect. + +Navigate to the new user. _Click "View user"_ on the success notice. + +![Success banner with View user action](/images/aws-credentials/5.png) + +Visit the "Security credentials" tab below "Summary". +_Click "Create access key"_. + +![Security credentials tab for the new user](/images/aws-credentials/6.png) + +Select "Other" for "Use Case". +_Click "Next"_ + +![Use case is set to Other](/images/aws-credentials/7.png) + +Optionally, add a "tag" or leave it blank. +_Click "Create access key"_ + +![A form for the optional tag](/images/aws-credentials/8.png) + +Copy the "Access key" and "Secret access key" values or download as .csv. +_You won't have access to the secret again._ +**Do not commit these values to source control.** + +Save the keys to [your config file](#locate-or-create-a-config-file): + +``` +[default] +aws_access_key_id = AKIAIOSFODNN7EXAMPLE +aws_secret_access_key = wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY +``` + + + +[Reference](https://docs.aws.amazon.com/cli/latest/userguide/cli-authentication-user.html) + + + +**Architect can now use these credentials to [deploy to AWS](../../guides/developer-experience/deployment).** diff --git a/src/views/docs/en/guides/developer-experience/custom-cloudformation.md b/src/views/docs/en/guides/developer-experience/custom-cloudformation.md new file mode 100644 index 00000000..766a480c --- /dev/null +++ b/src/views/docs/en/guides/developer-experience/custom-cloudformation.md @@ -0,0 +1,35 @@ +--- +title: Custom CloudFormation +category: Developer experience +description: How to use Architect Plugins to define or modify AWS resources with CloudFormation +--- + +One of Architect's chief responsibilities is to generate a standard CloudFormation document for deployment to AWS. This infrastructure-as-code document is available to Architect plugins to mutate however you see fit. + + +## `deploy.start` plugins + +[`deploy.start` plugins](/docs/en/guides/plugins/deploy#deploy.start) allow you to process and mutate Architect-generated CloudFormation prior to deployment. This enables deep customization of any Architect default behavior, as well as allowing apps to extend into the entire AWS ecosystem of services. + +Architect [`@plugins`](/docs/en/guides/plugins/overview) are implemented as a Node.js CJS module with the following function signature: + +```javascript +// Do something only for staging deployments +module.exports = { deploy: { + start: async ({ arc, cloudformation, dryRun, inventory, stackName, stage }) => { + if (stage !== 'staging') return + + let config = await getSomeConfig() + cloudformation.Resources.whatever = config + // The returned mutated CloudFormation document will be passed to any other `deploy.start` plugins in sequence + return cloudformation + } +} } +``` + +## Additional resources + +- [Learn more about authoring `deploy.start` plugins](/docs/en/guides/plugins/deploy#deploy.start) +- [Port existing `@macros` extensions to `deploy.start` plugins](/docs/en/guides/plugins/porting-macros-to-plugins) + +> Tip: preview generated CloudFormation without deploying by running `arc deploy --dry-run` and viewing `sam.json` diff --git a/src/views/docs/en/guides/developer-experience/custom-source-paths.md b/src/views/docs/en/guides/developer-experience/custom-source-paths.md index 99ae1e81..ebe0f84b 100644 --- a/src/views/docs/en/guides/developer-experience/custom-source-paths.md +++ b/src/views/docs/en/guides/developer-experience/custom-source-paths.md @@ -5,40 +5,131 @@ category: Developer experience Define resources in a more verbose format to configure custom Lambda source directories. Custom source paths are completely opt-in on a Lambda by Lambda basis; this is trading off default conventions for flexibility at the price of slightly more `app.arc` verbosity. -### Use cases +## Use cases -- Migrate existing repos to serverless tech +- Migrate existing repos to the [Functional Web App pattern](https://fwa.dev) - Use frontend frameworks that have their own folder requirements - Better enable local code transpilation by pointing to generated `./dist` directories + ## Example + +
+ + +
arc
+
+ ```arc +@app +my-arc-app + @http # simple get /foo - # verbose /bar method get - src whatever/dir/you/want + src whatever/http/dir/you/want @events # simple an-event - # verbose another-event - src foo + src whatever/events/dir/you/want @scheduled # simple a-schedule rate(1 day) - # verbose another-schedule rate 1 day - src something + src whatever/scheduled/dir/you/want + +``` +
+
+ + +
json
+
+ +```json +{ + "app": "my-arc-app", + "http": [ + ["get", "/foo"], + { + "/bar": { + "method": "get", + "src": "whatever/http/dir/you/want" + } + } + ], + "events": [ + "an-event", + { + "another-event": { + "src": "whatever/events/dir/you/want" + } + } + ], + "scheduled": { + "a-schedule": { + "rate": [1, "day"] + }, + "another-schedule": { + "rate": [1, "day"], + "src": "whatever/schedueld/dir/you/want" + } + } +} + ``` +
+
+ + +
yaml
+
+```yaml +--- +app: my-arc-app + +http: +# simple +- get: "/foo" +# verbose +- "/bar": + method: "get" + src: "whatever/http/dir/you/want" + +events: +# simple +- "an-event" +# verbose +- "another-event": + src: "whatever/events/dir/you/want" + +scheduled: +# simple +- "a-schedule": + rate: + - 1 + - day +# verbose +- "another-schedule": + rate: + - 1 + - day + src: "whatever/scheduled/dir/you/want" + +``` +
+
+
+
diff --git a/src/views/docs/en/guides/developer-experience/dependency-management.md b/src/views/docs/en/guides/developer-experience/dependency-management.md index 00c13b7d..5e0c11a3 100644 --- a/src/views/docs/en/guides/developer-experience/dependency-management.md +++ b/src/views/docs/en/guides/developer-experience/dependency-management.md @@ -4,67 +4,234 @@ category: Developer experience description: Architect dependency structure and hydration --- -All serverless applications have project level dependencies and function level dependencies. Project level dependencies are defined at the root of the project. Lambda functions within a project are deployed individually and subsequently need to package their own dependencies. Different deployment frameworks handle this in different ways depending on the runtime; Architect projects encourage single responsibility functions with the minimum number of dependencies. This intentional isolation leads to easier debugging, faster coldstart and least privilege security. +## Overview -## Node +[Functional Web Apps](https://fwa.dev) have both project-level dependencies and individual function-level dependencies. -If there is a `package.json` in the Lambda function folder it will be used. If there is no `package.json` in the function folder Architect will statically analyze the code and transparently tree shake an optimal `node_modules` folder for that specific Lambda function. Imported code must be in a `package.json` file or relative to the root of the function and or the module will not load once it has been deployed! +Project-level dependencies are defined at the root of the project, and may include things like developer dependencies. -For example, assuming the current directory is `src/http/get-index`: +Function-level dependencies are isolated to each deployed cloud function. + +To optimize for startup, invocation, and deployment performance, and to minimize bug surface area, Architect projects encourage single responsibility functions with the minimum number of dependencies. This intentional isolation leads to easier debugging, faster responses, and least privilege security. + + +## Node.js + +### Automated dependency treeshaking + +Most Architect developers using Node.js (and [TypeScript](/docs/en/guides/developer-experience/using-typescript)) use Architect's built-in dependency treeshaking. Here's (roughly) it works: + +- Install your dependencies in your root `package.json` (e.g. `npm i @architect/functions`) +- `import` or `require` your dependencies as normal +- When working locally, [Sandbox](/docs/en/reference/cli/sandbox)'s Node.js process will resolve your dependencies from your project's root `node_modules/` dir +- During deployment, Architect statically analyzes your code and resolves your (non-dynamic) `import` and `require` statements +- Dependencies are installed to and deployed with your Lambda +- For debugging purposes, the automatically generated `package.json` can be found in your Lambda's `node_modules/` dir (until the next deployment or Sandbox run) + - Example path: `src/http/get-index/node_modules/_arc-autoinstall/package.json` + +Using Architect's Lambda treeshaking, generally most developers do not need to spend any time managing individual dependencies across their project's many functions. + +> Tip: Lambda treeshaking also supports dependencies found in [`shared` and `views`](/docs/en/guides/developer-experience/sharing-code) + +Automatically install [`@architect/functions`](/docs/en/reference/runtime-helpers/node.js): ```javascript -// this is ok if it exists in package.json -let arc = require('@architect/functions') +// src/http/get-index/index.js +import arc from '@architect/functions' -// this will fail -let foo = require('../foo') +export const handler = arc.http(async req => { + return { ok: true } +}) +``` -// this will work -let foo = require('./foo') +Automatically install an optional dependency to ensure it's available to the Lambda: -// this is also ok (if foo exists in @shared) -let foo = require('@architect/shared/foo') +```javascript +// src/http/get-index/index.js +// Note: you don't have to actually use the dep, merely importing or requiring is sufficient +require('an-optional-dependency') + +export const handler = async req => { + return { ok: true } +} ``` -Recommended additional reading for working with the Node runtime: -- Use `@shared` and `@views` to [share code](/docs/en/guides/developer-experience/sharing-code) between functions -- Use [`@architect/functions`](/docs/en/reference/runtime/node) to make working with Node a bit nicer +### Manual dependency management -## Ruby +Sometimes you may need fine-grained control over an individual function's dependencies. In these cases, Architect enables per-Lambda dependency management by way of a `package.json` file in your Lambda's folder. -Architect uses `Gemfile` and `Gemfile.lock` with `bundle` to ensure Lambda function dependencies are deterministic. Code must be relative to the root of Lambda function directory. +If a `package.json` file with a `dependencies` property is found in your Lambda, Architect's treeshaking functionality will be automatically disabled for that specific Lambda, and your `package.json` file will be in complete control of that Lambda's dependencies. -```ruby -require 'architect/functions' # is ok if it is vendored in the Lambda folder -require '../foo' # this will fail -require './foo' # this will work -require 'architect/shared/foo' # this is also ok (if foo exists in @shared) +Assuming the following `src/http/get-index/package.json`: + +```json +{ + "dependencies": { + "glob": "latest" + } +} ``` -Install runtime helpers for Ruby: +This Lambda would work locally in Sandbox (due to Node's module resolution algorithm on your local machine), but would fail once deployed: -```bash -cd path/to/lambda -bundle init -bundle install --path vendor/bundle -bundle add architect-functions +```javascript +// src/http/get-index/index.js +import arc from '@architect/functions' +import glob from 'glob' + +export const handler = arc.http(async req => { + return glob('**/**') +}) +``` + + +### Relative modules & code sharing + +Individual Lambdas can `import` / `require` code from within their own path, but it is important to understand they cannot make use of local files that descend into other Lambdas. Attempting to do so will not work once deployed. + +To share code across multiple Lambdas, please make use of `@shared` and `@views`, and refer to our [guide on code sharing](/docs/en/guides/developer-experience/sharing-code). + +For example, assume the following `src/http/get-index/index.js` handler: + +```javascript +// This is ok if it exists in the root package.json +import arc from '@architect/functions' + +// This will fail +import foo from '../foo.js' + +// This will work (if present, of course) +import foo from './foo.js' + +// This is also ok (if foo exists in @shared) +import foo from '@architect/shared/foo.js' ``` +--- + ## Python -Architect uses `requirements.txt` with `pip3` to ensure Lambda function dependencies are deterministic. Code must be relative to the root of Lambda function directory. +### Automated dependency treeshaking + +As with Node.js, we suggest Architect developers using Python utilize Architect's built-in dependency treeshaking. Here's (roughly) it works in Python: + +- In addition to Architect, you will need to have `pipdeptree` installed to your system (`pip3 install pipdeptree`) +- Install your dependencies in your root `requirements.txt` (e.g. `pip3 install architect-functions -r requirements.txt`) +- `import` your dependencies as normal +- When working locally, [Sandbox](/docs/en/reference/cli/sandbox) will find your dependencies from your system $PATH +- During deployment, Architect statically analyzes your code and resolves your `import` statements into `pypi` packages + - Supported options in your root `requirements.txt` file are respected, such as `--extra-index-url https://test.pypi.org/simple/` +- Dependencies are installed to and deployed with your Lambda +- For debugging purposes, the automatically generated `requirements.txt` can be found in your Lambda's `vendor/` dir (until the next deployment or Sandbox run) + - Example path: `src/http/get-index/vendor/_arc_autoinstall/requirements.txt` + +Using Architect's Lambda treeshaking, generally most developers do not need to spend any time managing individual dependencies across their project's many functions. + + + +Automatically install [`architect-functions`](/docs/en/reference/runtime-helpers/python): ```python -import arc # is ok if it is vendored in the Lambda folder -import ..foo # this will fail -import .foo # this will work -from arc.shared import foo # this is also ok (if foo exists in @shared) +# src/http/get-index/lambda.py +import arc + +def handler(req): + return {"ok": True} ``` -Install runtime helpers for Python: + +#### Native packages + +By default, `pip` attempts to [install packages as wheels](https://packaging.python.org/en/latest/tutorials/installing-packages/#source-distributions-vs-wheels) that match the platform it's running on. For example: if you're running a Mac, `pip` will attempt to download the Mac wheel of the package you're installing. This is great for working locally in Sandbox. + +During deployment to AWS, Architect's dependency hydration makes a best-effort attempt to guide `pip` to download wheels compatible with AWS Linux 2 (AL2), the underlying OS of Lambda (and many other AWS compute services). However, not all packages publish AL2 compatible wheels. Packages that do not publish source distributions, or that incorrectly tag `glibc` versions in their wheel distributions, for example, may have issues running in AWS. + +Due to this potential variability in dependency compatibility, we advise thoroughly testing your Python deployments in staging before promoting to production. + + +### Manual dependency management + +Sometimes you may need fine-grained control over an individual function's dependencies. In these cases, Architect enables per-Lambda dependency management by way of a `requirements.txt` file in your Lambda's folder. + +If a `requirements.txt` file is found in your Lambda, Architect's treeshaking functionality will be automatically disabled for that specific Lambda, and your `requirements.txt` file will be in complete control of that Lambda's dependencies. + + +### Relative modules & code sharing + +Individual Lambdas can `import` code from within their own path, but it is important to understand they cannot make use of local files that descend into other Lambdas. Attempting to do so will not work once deployed. + +To share code across multiple Lambdas, please make use of `@shared` and `@views`, and refer to our [guide on code sharing](/docs/en/guides/developer-experience/sharing-code). + +For example, assume the following `src/http/get-index/lambda.py` handler: + +```python +# This is ok if it exists in the `requirements.txt` file +import arc # → architect-functions + +# This will fail +from ..foo import bar + +# This will work (assuming `foo.py` exists in the same directory, of course) +from foo import bar + +# This is also ok (if `foo.py` exists in @shared) +from vendor.shared.foo import bar +``` + +--- + +## Ruby + +### Automated dependency treeshaking + +Per the [runtime support matrix](/docs/en/get-started/runtime-support), Architect does not support automated dependency management for Ruby Lambdas at this time. + + +### Manual dependency management + +Architect installs per-Lambda dependencies with `bundle` according to each `Gemfile` (and `Gemfile.lock`) found in each function directory. + + +### Relative modules & code sharing + +Individual Lambdas can `require` code from within their own path, but it is important to understand they cannot make use of local files that descend into other Lambdas. Attempting to do so will not work once deployed. + +To share code across multiple Lambdas, please make use of `@shared` and `@views`, and refer to our [guide on code sharing](/docs/en/guides/developer-experience/sharing-code). + +For example, assume the following `src/http/get-index/lambda.rb` handler: + +```ruby +# Initialize Bundler +require 'bundler/setup' + +# This is ok if it exists in the Lambda's Gemfile +require 'architect/functions' + +# This will fail +require '../foo' + +# This will work (if present, of course) +require './foo' + +# This is also ok (if foo exists in @shared) +require './vendor/shared/foo' +``` + + +### Deployment configuration + +Prior to deploying, it is recommended to configure Bundler to work in a Lambda environment. + +You'll need to let Bundler know about Lambda's platform architecture by adding an entry to the `Gemfile.lock` (see below). + +Additionally, Bundler often tries to write to the filesystem at runtime. Freeze the bundle by setting the `BUNDLE_FROZEN` environment variable to `1`. ```bash -cd path/to/lambda -pip install --target ./vendor architect-functions +# Declare the Lambda platform; assumes you have not configured your Lambda to use ARM +bundle lock --add-platform x86_64-linux + +# Use Architect to set a Bundler-specific env var for staging & production +npx arc env -a -e staging BUNDLE_FROZEN 1 +npx arc env -a -e production BUNDLE_FROZEN 1 ``` diff --git a/src/views/docs/en/guides/developer-experience/deployment.md b/src/views/docs/en/guides/developer-experience/deployment.md index 971187af..deaedad1 100644 --- a/src/views/docs/en/guides/developer-experience/deployment.md +++ b/src/views/docs/en/guides/developer-experience/deployment.md @@ -4,9 +4,15 @@ category: Developer experience description: How to deploy your Architect app --- -Architect makes deploying AWS CloudFormation completely painless. +Architect makes deploying AWS CloudFormation "stacks" painless. +AWS credentials are required to deploy. +Learn about [AWS setup](../../get-started/detailed-aws-setup) and [gathering credentials from the AWS Console](./create-aws-credentials) -## Deploy to staging +> 🧑‍🏫 CloudFormation stack identifiers are created from the name specified in the `@app` pragma and are unique to an AWS region. Changing the project name or region will create a new CF stack. + +## Command examples + +### Deploy to staging Deploy a CloudFormation template to a `staging` environment. @@ -16,26 +22,36 @@ arc deploy > Protip: create arbitrary named staging environments with `arc deploy --name [your name]` -## Deploy to production +### Deploy to production Deploy a CloudFormation template to an identical `production` environment. ```bash -arc deploy production +arc deploy --production +``` + +### Deploy a function directly + +Rather than deploying the entire stack with CloudFormation you can quickly deploy code for one function by supplying a path. + +```bash +arc deploy --direct path/to/code ``` -## Deploy a function directly to staging +Combine with the `--production` flag to update one function to the production stack. + +### Deploy static assets to S3 -Rather than deploying the entire stack with CloudFormation you can quickly deploy code for one function to `staging` by supplying a path. +Sometimes you need to just update the frontend. ```bash -arc deploy path/to/code +arc deploy --static ``` -## Deploy static assets to S3 +## Deployment credentials -Sometimes you need to update the frontend! +If your machine has an AWS credentials file (`~/.aws/credentials`) but you would like to override those credentials using environment variables, you must also set `ARC_AWS_CREDS=env`. For example: ```bash -arc deploy static +ARC_AWS_CREDS=env AWS_ACCESS_KEY_ID=... AWS_SECRET_ACCESS_KEY=... arc deploy --production ``` diff --git a/src/views/docs/en/guides/developer-experience/local-development.md b/src/views/docs/en/guides/developer-experience/local-development.md index c758cb44..428233d6 100644 --- a/src/views/docs/en/guides/developer-experience/local-development.md +++ b/src/views/docs/en/guides/developer-experience/local-development.md @@ -6,15 +6,24 @@ description: How to develop locally with Architect sandbox Fast local development creates a tighter feedback loop maximizing developer velocity. +## Requirements + +- **Platforms:** Linux, macOS, Windows +- **Architect runtime:** Node.js 14+ +- **Function runtimes & package managers:** + - Node.js (optional, [supported versions](https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html)) with [`npm`](https://www.npmjs.com/) 6+ or [`yarn`](https://yarnpkg.com/) 1+ + - Python (optional, [supported versions](https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html)) and optionally [`pip3`](https://pip.pypa.io/en/stable/) + - Ruby (optional, [supported versions](https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html)) and optionally [`bundle`](https://bundler.io/) + ## Preview -Follow the [quickstart](/docs/en/guides/get-started/quickstart) to get everything wired up. To preview a project running locally in a web browser: +Follow the [quickstart](/docs/en/get-started/quickstart) to get everything wired up. To preview a project running locally in a web browser: ```bash cd myproject -arc sandbox +npx arc sandbox ``` -> `arc sandbox` kicks up a local web server with all the resources defined in your `app.arc` file +> `npx arc sandbox` kicks up a local web server with all the resources defined in your `app.arc` file ## Testing Checkout [a complete example project for working locally](https://github.com/architect-examples/arc-example-working-locally). diff --git a/src/views/docs/en/guides/developer-experience/logging-and-monitoring.md b/src/views/docs/en/guides/developer-experience/logging-and-monitoring.md index 9cf26942..4b284981 100644 --- a/src/views/docs/en/guides/developer-experience/logging-and-monitoring.md +++ b/src/views/docs/en/guides/developer-experience/logging-and-monitoring.md @@ -6,6 +6,7 @@ description: How to use log output in your Architect project Architect logs to [AWS CloudWatch](https://aws.amazon.com/cloudwatch/). + ## Supported runtimes - **Node** `console.log` or any logging library that writes to stdout or stderr @@ -13,12 +14,14 @@ Architect logs to [AWS CloudWatch](https://aws.amazon.com/cloudwatch/). - **Ruby** `puts` or any logging library that writes to stdout or stderr - **Python** `print` or any logging library that writes to stdout or stderr + ### See also -- CloudWatch captures many metrics from serverless primitives especially [API Gateway](https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-metrics-and-dimensions.html) +- CloudWatch captures many metrics from [Functional Web App](https://fwa.dev) primitives, especially [API Gateway](https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-metrics-and-dimensions.html) - [X-Ray](https://aws.amazon.com/xray/) offers deeper service call introspection capabilities - There are many third party tools to further extend your app with structured logs + ## Example Given the following `app.arc` file: diff --git a/src/views/docs/en/guides/developer-experience/sharing-code.md b/src/views/docs/en/guides/developer-experience/sharing-code.md index fc68e5fa..18026dcc 100644 --- a/src/views/docs/en/guides/developer-experience/sharing-code.md +++ b/src/views/docs/en/guides/developer-experience/sharing-code.md @@ -9,43 +9,217 @@ sections: - src/views --- -Architect makes it easy to share code across many Lambda functions. Apps most commonly need to share business logic and view templates so Architect provides `@shared` and `@views` capability. Architect copies the contents of `src/shared` into all Lambdas and `src/views` into Lambda functions wired to respond to `@http` GET requests. +Architect makes it easy to share code across Lambda functions. Out of the box, with zero configuration, code is shared across Lambdas as follows: -Example `app.arc` +- `src/shared/` - files in this dir are shared across all Lambdas +- `src/views/` - files in this dir are shared to all `@http` `get` Lambdas (if any) + +Additional configuration (such as folder configuration) can be managed with the [`@shared`](../../reference/project-manifest/shared) and [`@views`](../../reference/project-manifest/views) pragmas. + + +## Example usage + +Given a simple `app.arc`: + +```arc +@app +myapp + +@http +get / +post /like +``` + +Where utility code lives in `./src/shared/` and common view code in `./src/views/`: + +```bash +. +├── src +│ ├── http +│ │ ├── get-index +│ │ │ └── index(.js|.py|.rb) +│ │ └── post-like +│ │ └── index(.js|.py|.rb) +│ ├── shared +│ │ └── authenticate(.js|.py|.rb) +│ └── views +│ └── document(.js|.py|.rb) +└── app.arc +``` + +`get-index` can use shared code by requiring it from `@architect/shared/` and views code from `@architect/views/`: + + +
+ + +
js
+
+ +```javascript +// get-index/index.js +const auth = require('@architect/shared/authenticate') +const document = require('@architect/views/document') +``` + +
+
+ + +
rb
+
+ +```ruby +# get-index/lambda.rb +require_relative './vendor/shared/authenticate' +require_relative './vendor/views/document' +``` + +
+
+ + +
py
+
+ +```python +# get-index/lambda.py +from vendor.shared.authenticate import foo +from vendor.views.document import bar +``` + +
+
+ +
+
+ +The `post-like` route has access to shared code as well, but not views because it is a `post` request handler, not a `get` handler. + + +
+ + +
js
+
+ +```javascript +// post-like/index.js +const auth = require('@architect/shared/authenticate') +``` + +
+
+ + +
rb
+
+ +```ruby +# post-like/lambda.rb +require_relative './vendor/shared/authenticate' +``` + +
+
+ + +
py
+
+ +```python +# post-like/lambda.py +from vendor.shared.authenticate import foo +``` + +
+
+ +
+
+ +## Custom shared paths + +The default shared and views directories can be overridden: ```arc @app myapp @shared -src src/shared # this is the default +src path/to/shared @views -src src/views # this is the default +src path/to/views +``` -@http -get / -post /like +They are still required in the same way: + + +
+ + +
js
+
+ +```javascript +// get-index/index.js +const auth = require('@architect/shared/authenticate') +const document = require('@architect/views/document') +``` + +
+
+ + +
rb
+
+ +```ruby +# get-index/lambda.rb +require_relative './vendor/shared/authenticate' +require_relative './vendor/views/document' +``` + +
+
+ + +
py
+
+ +```python +# get-index/lambda.py +from vendor.shared.authenticate import foo +from vendor.views.document import bar ``` +
+
+ +
+
+ +## Runtime details + No matter where `@shared` source is configured it gets copied to every Lambda. The destination is slightly different depending on runtime: | Runtime | `@shared` destination | -| :------ | :-------------------- | -| Node | `src/http/get-index/node_modules/@architect/shared` | -| Ruby | `src/http/get-index/vendor/shared` | -| Python | `src/http/get-index/vendor/shared` | +| --- | --- | +| Node | `src/http/get-index/node_modules/@architect/shared` | +| Ruby | `src/http/get-index/vendor/shared` | +| Python | `src/http/get-index/vendor/shared` | Likewise, `@views` runtime destinations: | Runtime | `@views` destination | -| :------ | :-------------------- | -| Node | `src/http/get-index/node_modules/@architect/views` | -| Ruby | `src/http/get-index/vendor/views` | -| Python | `src/http/get-index/vendor/views` | +| --- | --- | +| Node | `src/http/get-index/node_modules/@architect/views` | +| Ruby | `src/http/get-index/vendor/views` | +| Python | `src/http/get-index/vendor/views` | > Tip: the entire contents of `src/shared` are copied so we strongly suggest keeping the directory structure as flat as possible, and the payloads as small as possible to ensure the best performance. -## Dependencies +## Shared code dependencies -`@shared` and `@views` support having their own dependencies defined by `package.json`, `requirements.txt` or `Gemfile`. +`@shared` and `@views` resources can have their own dependencies defined by `package.json`, `requirements.txt` or `Gemfile`. These dependencies will also be copied to corresponding Lambdas. diff --git a/src/views/docs/en/guides/developer-experience/using-deno.md b/src/views/docs/en/guides/developer-experience/using-deno.md new file mode 100644 index 00000000..60da8f4e --- /dev/null +++ b/src/views/docs/en/guides/developer-experience/using-deno.md @@ -0,0 +1,58 @@ +--- +title: Using Deno +category: Developer Experience +description: Deno runtime support +--- + +The Architect team has a strong interest in [Deno](https://deno.land/) as a runtime. Architect provides experimental support for Deno via a [Lambda _layer_](https://docs.aws.amazon.com/lambda/latest/dg/gettingstarted-concepts.html#gettingstarted-concepts-layer). + +## Current support + +Deno is not currently an officially supported AWS Lambda runtime, but Architect adds a Lambda _layer_ to provide the Deno executable on AWS infrastructure. +Additionally, [Sandbox](../../reference/cli/sandbox) will use `deno` to invoke functions while developing locally. + +> ⚠️ Lambda layers can increase response times when the function is cold. This approach to enabling Deno is not ideal for user interaction. + +### Project configuration + +Set the Deno runtime in your [project (`app.arc`)](../../reference/project-manifest/aws) or [function (`config.arc`)](../../reference/configuration/function-config) configuration: + +```arc +@aws +runtime deno +``` + +This setting will enable Deno in your project (or a single function), allowing handlers to be written in TypeScript or JavaScript and run with Deno: + +```typescript +// src/http/get-index/index.ts +export async function handler() { + const myUUID = crypto.randomUUID(); + const resp = await fetch('https://api.github.com/users/architect'); + const arcGitHub = await resp.json(); + + return { + statusCode: 200, + headers: { 'content-type': 'text/html; charset=utf8' }, + body: ` +

🎲 ${myUUID}

+

🦖 ${Deno.version.deno}

+

${JSON.stringify(arcGitHub, null, 2)}

+ `, + }; +} +``` + +> 🦕 To enable Sandbox support for Deno, make sure you [install Deno locally](https://deno.land/#installation). + +Architect and Sandbox will still run with Node.js, but will orchestrate `deno` processes when running your functions. + + + +### Deno runtime version + +The current provided layer is Deno v1.19.1. + +> 🧑‍🔬 The Arc team is actively working on providing up-to-date and configurable Deno layers. diff --git a/src/views/docs/en/guides/developer-experience/using-esm.md b/src/views/docs/en/guides/developer-experience/using-esm.md new file mode 100644 index 00000000..ecdd3c0f --- /dev/null +++ b/src/views/docs/en/guides/developer-experience/using-esm.md @@ -0,0 +1,76 @@ +--- +title: Using ESM (ECMAScript Modules) +category: Developer experience +description: How to use ECMAScript Modules in functions +--- + +[AWS Lambdas support ES modules and top-level await](https://aws.amazon.com/blogs/compute/using-node-js-es-modules-and-top-level-await-in-aws-lambda/). Architect adheres to Node.js conventions and the AWS implementation. + +## Example project + +A working Architect project with each method for using ESM and CJS can be found on GitHub: [`architect-examples/arc-example-esm-cjs`](https://github.com/architect-examples/arc-example-esm-cjs). + +## ESM by default + +Architect projects default to ESM. Architect will always support CJS so long as Node.js does. + +## ESM with `.mjs` + +The simplest way to start using ESM is to create JavaScript files with a `.mjs` extension. For example, no configuration is needed for the following shared helper and HTTP GET function to work as ES modules: + +```javascript +// ./src/shared/helper.mjs +export default function () { + return "Don't panic." +} +``` + +The shared ESM file can be imported in any function. Remember, ESM `import` statements require the file extension. + +```javascript +// ./src/http/get-index/index.mjs +import myHelper from '@architect/shared/helper.mjs' + +export async function handler (event) { + return { body: myHelper() } +} +``` + +## ESM with `package.json` + +Alternatively, if you are using [manual per-function dependency management](dependency-management#manual-dependency-management), the `"type"` property of a function's `package.json` file can be set to `"module"` to declare the function is ESM. That function's handler file can then use a `.js` file extension: + +```json +// ./src/http/get-index/package.json +{ + "description": "other attributes are allowed", + "type": "module" +} +``` + +``` +. +├── src +│ └── http +│ └── get-index +│ ├── index.js +│ └── package.json +└── app.arc +``` + +Declaring dependencies in a function's `package.json` will disable [automated dependency treeshaking](dependency-management#automated-dependency-treeshaking) for that function. This is true no matter the module type. + +> ℹ️ Setting `"type": "module"` in the project's root `package.json` will not affect function module types. + +## Explicitly using CommonJS + +Users can explicitly use a `.cjs` file extension to declare a JS file is a CommonJS module. + +``` +. +├── src +│ └── http +│ └── get-index +│ └── index.cjs +└── app.arc +``` diff --git a/src/views/docs/en/guides/developer-experience/using-typescript.md b/src/views/docs/en/guides/developer-experience/using-typescript.md index 67a18436..74cc797f 100644 --- a/src/views/docs/en/guides/developer-experience/using-typescript.md +++ b/src/views/docs/en/guides/developer-experience/using-typescript.md @@ -1,17 +1,80 @@ --- title: Using TypeScript category: Developer experience -description: How to use Architect with TypeScript +description: How to use TypeScript with Architect --- -Architect and TypeScript work great together. Types are available in the [@types/architect\_\_functions](https://www.npmjs.com/package/@types/architect__functions) package. Write functions in TypeScript and just be sure to build your functions with TypeScript (`tsc`) before running or deploying. For example, your `package.json` might have the following scripts to build the TypeScript code before deploying and running in the [sandbox for local development](/docs/en/guides/developer-experience/local-development): +Architect and TypeScript work great together. Architect maintains a first-party TypeScript integration via [`@architect/plugin-typescript`](https://github.com/architect/plugin-typescript). + +Architect's TypeScript integration takes care of transpiling, source maps, source vs. build paths, and integration with Sandbox. All you have to do is author your functions (and, optionally, run `tsc` within your tests). + + +## Getting started + +In your Architect project run: + +```bash +npm i @architect/plugin-typescript --save-dev +``` + +Add the following to your Architect project manifest (usually `app.arc`): + +```arc +@aws +runtime typescript # sets TS as the the default runtime for your entire project + +@plugins +architect/plugin-typescript +``` + +## Usage + +Author (or port) Lambdas in the `src` tree with `index.ts` handlers. For example: + +```javascript +// src/http/get-index/index.ts +export async function handler (request: any, context: any): Promise { + return request +} +``` + +The above function will be automatically transpiled by Architect to `./.build/http/get-index.js`. The destination build directory is configurable, as is `tsconfig.json`, and esbuild plugins; [see the plugin documentation for more options](https://github.com/architect/plugin-typescript).) + +When working locally, Sandbox automatically detects changes to your TypeScript handlers and re-transpiles them (and adds environment-specific sourcemaps) for you. + +You can use TypeScript in as many or few Lambdas as you like, relying on project or Lambda-level [`runtime` function config](/docs/en/reference/configuration/function-config). + + +## `@architect/functions` + +We recommend using the [`@architect/functions`](/docs/en/reference/runtime-helpers/node.js) runtime helper to smooth over some rough edges in working with various aspects of AWS (as well as to add built-in session support, and other niceties). + +If you do, TypeScript types are available in the [@types/architect__functions](https://www.npmjs.com/package/@types/architect__functions) package. + + +## Shared code + +It is possible to use Architect's built-in shared code folders (`src/shared` + `src/views`) with TypeScript handlers while still maintaining functionality across vanilla JS handlers. + +A sample `tsconfig.json` for shared code paths with a custom `shared-ts` folder: ```json -"scripts": { - "start": "arc sandbox", - "prestart": "npm run build", - "deploy": "arc deploy", - "predeploy": "npm run build", - "build": "tsc" +{ + "compilerOptions": { + "baseUrl": ".", + "paths": { + "@architect/views": [ "src/views" ], + "shared": [ "src/shared-ts" ] + } + } } ``` + +In the above example, TypeScript handlers can use a shared backend code folder (`src/shared-ts`), while still making use of shared code in `src/views` (by way of tsconfig.json paths setting). See the [example below](#example-project) for a functioning project with this set up. + + +## Example project + +View the [example TypeScript project on GitHub](https://github.com/architect-examples/typescript-example). + +This example project demonstrates how to use both TS and JS side-by-side in a project, automatic sourcemaps, and shared code. diff --git a/src/views/docs/en/guides/domains/custom-domain.md b/src/views/docs/en/guides/domains/custom-domain.md new file mode 100644 index 00000000..f28fe1dc --- /dev/null +++ b/src/views/docs/en/guides/domains/custom-domain.md @@ -0,0 +1,160 @@ +--- +title: Custom domain +category: Domains +description: How to use a custom domain with Architect +--- + +## Goals + +Deploy an Architect app to an owned AWS account and access that application at a custom domain name. + +This assumes you have Architect working locally and [connected to your AWS account](/docs/en/guides/developer-experience/create-aws-credentials). + +For this guide, we will walk through setting up an existing Architect (+ [Enhance](https://enhance.dev/)) app with the domain: `hnr.app`. + +Additionally, we will skip a "staging" deploy and domain, but that is highly recommended and follows the same steps. This guide will add SSL certs for wildcard subdomains, allowing you to use `staging.example.com`. + + +### Resources created + +When the process is complete, these resources will be created in AWS: + +- CloudFormation: one Stack (via `arc deploy`, you may already have one!) +- API Gateway: one Custom Domain + mapping +- ACM: two certificates +- CloudFront: one Cache policy, Origin request policy, and Distribution + +Additionally, two DNS records on the domain will be created / changed. + +You likely will not need to modify these in the future. Depending on the needs of your application, you may want to alter CloudFront policies, but we recommending starting with these and optimizing later. + + +## Deploy with Architect + +The application must exist in our AWS account to start the domain process. +If you already have a deployed app, proceed to the next section. + +1. Run the application locally with `arc sandbox` +2. Check `@aws` config in your Arc app manifest (typically `app.arc`) + - Verify your runtime, etc., and make note of the region you have selected +3. Run a production deploy: `arc deploy --production` +4. Confirm the CloudFormation stack was created in the expected region + +![CloudFormation stack](/images/custom-domain/1.png) + +5. Set env variables + - `npx arc env -a -e production ARC_APP_SECRET keyboardcat` (where `keyboardcat` is a secret of your choosing) +6. Deploy again so that your application picks up the env variables: `arc deploy --production` +7. `arc` will provide a `amazonaws.com` API Gateway URL; confirm your app is reachable + +Depending on the functionality of your application, you should be able to see its primary functionality working in the browser. + + +## SSL Certificates + +We will create two public certificates in Amazon Certificate Manager (ACM), one in the region you selected, and the other in `us-east-1`. (If you're using `us-east-1`, you only need to create a single certificate in that region.) + +1. AWS Cert Manager > request + +![ACM request](/images/custom-domain/2.png) + +2. The new cert should be in the same region as your application: + - Choose public certificate + - Enter your domain like `example.com` **AND** `\*.example.com` + - Select DNS validation + - RSA 2048 for encryption +3. After the cert is created, grab the DNS validation CNAME and Value + - If your domain uses Route53, you may also click the `Create records in Route 53` button to skip step 4. + +![ACM validation](/images/custom-domain/3.png) + +4. Add the validation record to your DNS provider (if you're using AWS, this is a Route 53 HostedZone) + +![ACM validation](/images/custom-domain/4.png) + +5. Wait for the cert validation to show "Issued" and "Success" + +![ACM issued](/images/custom-domain/5.png) + +6. Repeat in `us-east-1`! + - CloudFront must have certificates in `us-east-1`; we'll use it later when we set up the CDN + - The CNAME record will likely be the same and the cert should be quick to validate + + +## API Gateway Custom Domain + +Navigate to the API Gateway console for the same region where your app resides: + +1. Find the "Custom domains" section and choose "Create" + - Enter your domain + - Select "Regional" for type (Arc apps are not typically of the `REST` API Gateway type) + - The ACM cert menu should have an option for the new cert you just created + +![API Gateway custom domain](/images/custom-domain/6.png) + +2. After creation, configure "API mappings" + +![API Gateway mappings](/images/custom-domain/7.png) + +3. Select the API Gateway for your app (this was created earlier with `arc deploy`), use the `$default` stage, and leave path blank + +![API Gateway mappings](/images/custom-domain/8.png) + +4. Copy the custom domain's ``API Gateway domain name`, which we'll need for the next step. + + +## CloudFront + +### Policies + +We'll create a couple CloudFront behavior policies to use with the distribution we'll create momentarily. + +These are the recommended policies for getting started as they're fairly permissive, do not strip much from the original request, and cache more broadly than other AWS preset policies. + +Policies can be changed later as your application requirements change or become clearer. + + +#### Cache policy + +The cache policy determines which request attributes will create a cache key (essentially a unique object in CloudFront's cache). + +We always cache on query strings (e.g. search params), as that often represents a unique query to the server (unless your users are logged in). Additionally, compression is supported for both Gzip and Brotli. + +Further, it is a safe bet to include `Authorization` headers in the cache key, even if your application is not yet using that header. Again, this can be altered for your app's purposes. + +![Cache policy](/images/custom-domain/9.png) + +![Cache policy](/images/custom-domain/10.png) + + +#### Origin request policy + +The origin request policy determines what parts of the request will make it to our application. In this case, we want as much data as possible, including headers, query strings, and cookies. + +![Origin request policy](/images/custom-domain/11.png) + + +### Distribution + +In the final step, we'll create our CloudFront distribution and get ready to ship that into your domain's DNS settings: + +1. CloudFront > Create distribution + +Update these settings as needed for your application: + +![Distribution](/images/custom-domain/12.png) + +![Distribution](/images/custom-domain/13.png) + +![Distribution](/images/custom-domain/14.png) + +![Distribution](/images/custom-domain/15.png) + +2. Copy the distribution domain name + +![Distribution](/images/custom-domain/16.png) + + +## Update your DNS apex record + +You may now use the CloudFront distribution domain name (e.g. `$GUID.cloudfront.net`) as the `A` / `Apex` / `ALIAS` for the root of your domain's DNS records. diff --git a/src/views/docs/en/guides/domains/overview.md b/src/views/docs/en/guides/domains/overview.md new file mode 100644 index 00000000..729d80c6 --- /dev/null +++ b/src/views/docs/en/guides/domains/overview.md @@ -0,0 +1,44 @@ +--- +title: Overview +category: Domains +description: Setting up a domain for an Architect application +sections: + - Overview + - DNS guides + - Setting up your project +--- + +## Give your Architect application a proper domain name + +DNS is how you assign a domain name to a deployed app. This guide lists ways to set up custom DNS with several popular DNS providers and we are always happy to accept contributions for steps to use additional providers. + +You may use a free registrar-based DNS to host your domain such as GoDaddy, Namecheap, One, etc., but Route53 is the preferred registrar and DNS management system for Architect users. This is because: + +- It's integrated with Amazon's other cloud services. +- Your DNS will resolve from 15+ locations worldwide, making your website faster for your end-users. +- Route53 is a DNS management system (Smart DNS) compared to all the others, which are merely domain registrars with a limited feature set for manipulating DNS. + +## Setting up your project + +To prepare your arc app for setting up a custom domain, you first have to deploy your app to `staging` and `production`. + +Deploy to a `staging` stack: + +```bash +arc deploy +``` +> Protip: create additional `staging` stacks with `--name` + +Ship a `production` stack: + +```bash +arc deploy --production +``` + +All done! + +> Remember to save the two generated `URLs` because we will need to input these into the AWS console in the next steps. + +## DNS guides + +Setting up a custom domain for each registrar will be a bit different. To manually configure AWS and DNS, follow [the custom domain guide](/docs/en/guides/domains/custom-domain). diff --git a/src/views/docs/en/guides/domains/registrars/dreamhost.md b/src/views/docs/en/guides/domains/registrars/dreamhost.md new file mode 100644 index 00000000..46dc140a --- /dev/null +++ b/src/views/docs/en/guides/domains/registrars/dreamhost.md @@ -0,0 +1,62 @@ +--- +title: Dreamhost +category: Domain Registrars +description: Setting up a domain name with Dreamhost +--- + +## Prerequisites + +- Sign up for a domain on [Dreamhost](https://www.dreamhost.com/domains/) +- Make sure your domain is set to `DNS Only` in the Dreamhost console. +- Deploy an app with Architect and make note of the `staging` and `production` URLs +- Make sure your app is deployed to `us-east-1` +- Ensure the `@app` name is uniquely named after the domain. + +## Step 1: setup SSL certificates with AWS Certificate Manager + +In this step we will request a certificate from Amazon for our domain. + +- Open up AWS Certificate Manager in the AWS Console in `us-east-1` (region is required!) +- Click `Request a certificate` and then `Request a public certificate` +- Ensure `example.com` and `*.example.com` for sub domains to work +- Choose `DNS validation` and click `Next` +- Add any tags and confirm the request +- Open up Dreamhost backend and click `Manage Domains` +- Click the `DNS` link under your domain to add custom DNS records +- Create CNAME records of both issued certificates +- Wait until they change from `pending` to `success` + +## Step 2: setup CloudFront + +Generate a CloudFront distribution with the certificate from step 1. + +- Sign into AWS CloudFront in the AWS Console +- Click `Create Distribution` and then click `Get Started` +- Open API Gateway and make note of the Invoke URL. +- Enter the URL from API Gateway in `Origin Domain Name` +- Set `Origin Protocol Policy` to `Match Viewer` +- Add the `Alternate Domain Names (CNAMEs)` that you will be using. ex. `example.com`. +- Set `Viewer Protocol Policy` to `Redirect HTTP to HTTPS` +- Set `Allowed HTTP Methods` to `GET, HEAD, OPTIONS, PUT, POST, PATCH, DELETE` +- Set `Compress Objects Automatically` to `Yes` +- Set `SSL Certificate` to `Custom SSL Certificate` and select the cert from step 1 +- Click `Create Distribution` +- Repeat for `staging` domain. + +## Step 3: configure the domain Alias in Dreamhost + +Add `Alias` and `CNAME` records to DNS. + +- Sign into Dreamhost +- Navigate to the domain by clicking `Manage Domains` in the sidebar. +- Click `DNS` under the domain +- Click `Add Record` +- Use record type `Alias` for the root domain. + - Leave `Host` input blank and add the CloudFront domain that was created in step 2 to the `Points to` input. +- Use record type `CNAME` for the `staging` domain. + - Add the word `staging` to the `Host` input and add the CloudFront domain that was created in step 2 to the `Points to` input. +- Click `Add records` + +## Conclusion + +Now we're done! You can check to see if your domains are online with the DNS checker tool provided by Dreamhost. You can also use this [DNS Checker tool](https://dnschecker.org/). diff --git a/src/views/docs/en/guides/domains/registrars/godaddy.md b/src/views/docs/en/guides/domains/registrars/godaddy.md new file mode 100644 index 00000000..40e8a635 --- /dev/null +++ b/src/views/docs/en/guides/domains/registrars/godaddy.md @@ -0,0 +1,58 @@ +--- +title: GoDaddy +category: Domain Registrars +description: Setting up a domain name with GoDaddy +--- + +## Prerequisites + +- Sign up for a domain on [GoDaddy](https://www.godaddy.com/) +- Deploy an app with Architect and make note of the `staging` and `production` URLs +- Ensure the app is deployed to `us-east-1` +- Ensure the `@app` name is uniquely named after the domain. + +## Step 1: setup SSL certificates with AWS Certificate Manager + +In this step we will request a certificate from Amazon for our domain. + +- Open up AWS Certificate Manager in the AWS Console in `us-east-1` (region is required!) +- Click `Request a certificate` and then `Request a public certificate` +- Ensure `example.com` and `*.example.com` for sub domains to work +- Choose `DNS validation` and click `Next` +- Add any tags and confirm the request +- Open up GoDaddy account dashboard and find the `DNS` settings for the particular domain you want to use. +- Click `ADD` and select `CNAME` +- Create CNAME records of both issued certificates +- Wait until they change from `pending` to `success` + +## Step 2: setup CloudFront + +Generate a CloudFront distribution with the certificate from step 1. + +- Sign into AWS CloudFront in the AWS Console +- Click `Create Distribution` and then click `Get Started` +- Open API Gateway and make note of the `Invoke URL`. +- Enter the URL from API Gateway in `Origin Domain Name` +- Set `Origin Protocol Policy` to `Match Viewer` +- Add the `Alternate Domain Names (CNAMEs)` that you will be using. ex. `example.com`. +- Set `Viewer Protocol Policy` to `Redirect HTTP to HTTPS` +- Set `Allowed HTTP Methods` to `GET, HEAD, OPTIONS, PUT, POST, PATCH, DELETE` +- Set `Compress Objects Automatically` to `Yes` +- Set `SSL Certificate` to `Custom SSL Certificate` and select the cert from step 1 +- Click `Create Distribution` +- Repeat for `staging` domain. + +## Step 3: configure the domain Alias in GoDaddy + +Add `A` and `CNAME` records to DNS. + +- Open up GoDaddy account dashboard and find the `DNS` settings for the particular domain you want to use. +- Click `ADD`. +- Use record type `A` for the root domain. + - Leave `Host` input empty and add the IP address of the CloudFront domain that was created in step 2 to the `Points to` input. +- Use record type `CNAME` for the `staging` domain. + - Add the word `staging` to the `Host` input and add the CloudFront domain that was created in step 2 to the `Points to` input. + +## Conclusion + +Now we're done! You can check to see if your domains are online with the DNS checker tool [DNS Checker tool](https://dnschecker.org/). diff --git a/src/views/docs/en/guides/domains/registrars/namecheap.md b/src/views/docs/en/guides/domains/registrars/namecheap.md new file mode 100644 index 00000000..0b8caae8 --- /dev/null +++ b/src/views/docs/en/guides/domains/registrars/namecheap.md @@ -0,0 +1,60 @@ +--- +title: Namecheap +category: Domain Registrars +description: Setting up a domain name with Namecheap +--- + +## Prerequisites + +- Sign up for a domain on [Namecheap](https://www.Namecheap.com/domains/) +- Deploy an app with Architect and make note of the `staging` and `production` URLs +- Make sure your app is deployed to `us-east-1` +- Ensure the `@app` name is uniquely named after the domain. + +## Step 1: setup SSL certificates with AWS Certificate Manager + +In this step we will request a certificate from Amazon for our domain. + +- Open up AWS Certificate Manager in the AWS Console in `us-east-1` (region is required!) +- Click `Request a certificate` and then `Request a public certificate` +- Ensure `example.com` and `*.example.com` for sub domains to work +- Choose `DNS validation` and click `Next` +- Add any tags and confirm the request +- Open up Namecheap account dashboard and click `Manage` for the particular domain you want to use. +- Open the `Advanced DNS` tab. +- Click `ADD A NEW RECORD` +- Create CNAME records of both issued certificates +- Wait until they change from `pending` to `success` + +## Step 2: setup CloudFront + +Generate a CloudFront distribution with the certificate from step 1. + +- Sign into AWS CloudFront in the AWS Console +- Click `Create Distribution` and then click `Get Started` +- Open API Gateway and make note of the `Invoke URL`. +- Enter the URL from API Gateway in `Origin Domain Name` +- Set `Origin Protocol Policy` to `Match Viewer` +- Add the `Alternate Domain Names (CNAMEs)` that you will be using. ex. `example.com`. +- Set `Viewer Protocol Policy` to `Redirect HTTP to HTTPS` +- Set `Allowed HTTP Methods` to `GET, HEAD, OPTIONS, PUT, POST, PATCH, DELETE` +- Set `Compress Objects Automatically` to `Yes` +- Set `SSL Certificate` to `Custom SSL Certificate` and select the cert from step 1 +- Click `Create Distribution` +- Repeat for `staging` domain. + +## Step 3: configure the domain Alias in Namecheap + +Add `Alias` and `CNAME` records to DNS. + +- Open up Namecheap account dashboard and click `Manage` for the particular domain you want to use. +- Open the `Advanced DNS` tab. +- Click `ADD A NEW RECORD` +- Use record type `Alias` for the root domain. + - Add `@` in the `Host` input and add the CloudFront domain that was created in step 2 to the `Value` input. +- Use record type `CNAME` for the `staging` domain. + - Add the word `staging` to the `Host` input and add the CloudFront domain that was created in step 2 to the `Value` input. + +## Conclusion + +Now we're done! You can check to see if your domains are online with the DNS checker tool [DNS Checker tool](https://dnschecker.org/). diff --git a/src/views/docs/en/guides/domains/registrars/one.md b/src/views/docs/en/guides/domains/registrars/one.md new file mode 100644 index 00000000..f17bb460 --- /dev/null +++ b/src/views/docs/en/guides/domains/registrars/one.md @@ -0,0 +1,60 @@ +--- +title: One +category: Domain Registrars +description: Setting up a domain name with One +--- + +## Prerequisites + +- Sign up for a domain on [One](https://www.one.com/en/domain) +- Deploy an app with Architect and make note of the `staging` and `production` URLs +- Ensure the app is deployed to `us-east-1` +- Ensure the `@app` name is uniquely named after the domain. + +## Step 1: setup SSL certificates with AWS Certificate Manager + +In this step we will request a certificate from Amazon for our domain. + +- Open up AWS Certificate Manager in the AWS Console in `us-east-1` (region is required!) +- Click `Request a certificate` and then `Request a public certificate` +- Ensure `example.com` and `*.example.com` for sub domains to work +- Choose `DNS validation` and click `Next` +- Add any tags and confirm the request +- Open up One account dashboard and click `DNS settings` for the particular domain you want to use. +- Open the `DNS records` tab. +- Click the `CNAME` tab in the `Create new record` box +- Create CNAME records of both issued certificates +- Wait until they change from `pending` to `success` + +## Step 2: setup CloudFront + +Generate a CloudFront distribution with the certificate from step 1. + +- Sign into AWS CloudFront in the AWS Console +- Click `Create Distribution` and then click `Get Started` +- Open API Gateway and make note of the `Invoke URL`. +- Enter the URL from API Gateway in `Origin Domain Name` +- Set `Origin Protocol Policy` to `Match Viewer` +- Add the `Alternate Domain Names (CNAMEs)` that you will be using. ex. `example.com`. +- Set `Viewer Protocol Policy` to `Redirect HTTP to HTTPS` +- Set `Allowed HTTP Methods` to `GET, HEAD, OPTIONS, PUT, POST, PATCH, DELETE` +- Set `Compress Objects Automatically` to `Yes` +- Set `SSL Certificate` to `Custom SSL Certificate` and select the cert from step 1 +- Click `Create Distribution` +- Repeat for `staging` domain. + +## Step 3: configure the domain Alias in One + +Add `A` and `CNAME` records to DNS. + +- Open up One account dashboard and click `DNS settings` for the particular domain you want to use. +- Open the `DNS records` tab. +- Click the `A` tab in the `Create new record` box +- Use record type `A` for the root domain. + - Leave `Hostname` input empty and add the IP address of the CloudFront domain that was created in step 2 to the `Will point to` input. +- Use record type `CNAME` for the `staging` domain. + - Add the word `staging` to the `Hostname` input and add the CloudFront domain that was created in step 2 to the `Is an alias of` input. + +## Conclusion + +Now we're done! You can check to see if your domains are online with the DNS checker tool [DNS Checker tool](https://dnschecker.org/). diff --git a/src/views/docs/en/guides/domains/registrars/route53-and-cloudfront.md b/src/views/docs/en/guides/domains/registrars/route53-and-cloudfront.md new file mode 100644 index 00000000..5e00cab5 --- /dev/null +++ b/src/views/docs/en/guides/domains/registrars/route53-and-cloudfront.md @@ -0,0 +1,53 @@ +--- +title: Route53 & CloudFront +category: Domain Registrars +description: Setting up a domain name with Route53 and CloudFront +--- + +## Prerequisites + +- [Register](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/domain-register.html) or [transfer](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/domain-transfer-to-route-53.html) a domain with Route53 +- Deploy an app with Architect and make note of the `staging` and `production` URLs + +## Step 1: setup SSL certificates with AWS Certificate Manager + +In this step we will request a certificate from Amazon for our domain. + +- Open up AWS Certificate Manager in the AWS Console in `us-east-1` (region is required!) +- Click `Request a certificate` and then `Request a public certificate` +- Ensure `example.com` and `*.example.com` for sub domains to work +- Choose `DNS validation` and click `Next` +- Add any tags and confirm the request +- Expand the domain and click `Create record in Route53` button +- Verify CNAME record created in Route53 console Hosted zone + +## Step 2: setup CloudFront + +Generate a CloudFront distribution with the certificate from step 1. + +- Sign into AWS CloudFront in the AWS Console +- Click `Create Distribution` and then click `Get Started` +- Enter the URL from API Gateway in `Origin Domain Name` +- Set `Origin Protocol Policy` to `Match Viewer` +- Set `Viewer Protocol Policy` to `Redirect HTTP to HTTPS` +- Set `Allowed HTTP Methods` to `GET, HEAD, OPTIONS, PUT, POST, PATCH, DELETE` +- Set `Compress Objects Automatically` to `Yes` +- Enter the domain alias in `Alternate Domain Names` (which you will configure in step 3) +- Set `SSL Certificate` to `Custom SSL Certificate` and select the cert from step 1 +- Click `Create Distribution` + +## Step 3: configure the domain Alias in AWS Route53 + +- Sign into AWS Route53 in the AWS Console +- Navigate to the Hosted zone for the domain +- Click `Create record` +- Enter the `Record name` +- Record type is `A` and toggle `Alias` checkbox on +- Select `Alias to CloudFront` +- Select the region +- Select the CloudFront distribution domain (should be the same value as the domain generated in Step 2) +- Click `Create records` + +## Conclusion + +Now we're done! You can check to see if your domains are online with this [DNS Checker tool](https://dnschecker.org/). diff --git a/src/views/docs/en/guides/domains/registrars/route53.md b/src/views/docs/en/guides/domains/registrars/route53.md new file mode 100644 index 00000000..58491512 --- /dev/null +++ b/src/views/docs/en/guides/domains/registrars/route53.md @@ -0,0 +1,51 @@ +--- +title: Route53 +category: Domain Registrars +description: Setting up a domain name with Route53 +--- + +## Prerequisites + +- [Register](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/domain-register.html) or [transfer](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/domain-transfer-to-route-53.html) a domain with Route53 +- Deploy an app with Architect and make note of the `staging` and `production` URLs + +## Step 1: setup SSL certificates with AWS Certificate Manager + +In this step we will request a certificate from Amazon for our domain. + +- Open up AWS Certificate Manager in the AWS Console in `us-east-1` (region is required!) +- Click `Request a certificate` and then `Request a public certificate` +- Ensure `example.com` and `*.example.com` for sub domains to work +- Choose `DNS validation` and click `Next` +- Add any tags and confirm the request +- Expand the domain and click `Create record in Route53` button +- Verify CNAME record created in Route53 console Hosted zone + +## Step 2: setup custom domain with AWS API Gateway + +Generate a domain with the certificate from Step 1. + +- Sign into AWS API Gateway in the AWS Console +- Navigate to `Custom domain names` and click `Create` +- Enter the domain name (e.g. `staging.example.com` for the `staging` app or `example.com` for the `production` app) +- Select the certificate created in Step 1 +- Click `Create domain name` +- Make note of the generated `API Gateway domain name` in `Endpoint configuration` +- Click on the tab `API mappings` and `Configure API mappings` +- For `API` select the API and for `Stage` select `$default` and click `Save` + +## Step 3: configure the domain Alias in AWS Route53 + +- Sign into AWS Route53 in the AWS Console +- Navigate to the Hosted zone for the domain +- Click `Create record` +- Enter the `Record name` +- Record type is `A` and toggle `Alias` checkbox on +- Select `Alias to API Gateway` +- Select the region +- Select the API (should be the same value as the domain generated in Step 2) +- Click `Create records` + +## Conclusion + +Now we're done! You can check to see if your domains are online with this [DNS Checker tool](https://dnschecker.org/). diff --git a/src/views/docs/en/guides/examples.md b/src/views/docs/en/guides/examples.md new file mode 100644 index 00000000..a60adda7 --- /dev/null +++ b/src/views/docs/en/guides/examples.md @@ -0,0 +1,43 @@ +--- +title: Examples +category: Guides +description: Examples of Architect projects from the organization and the community +--- + +Architect is a flexible framework for dynamic applications. The following examples represent some of ways to use it. Some of these examples are part of the project and others are contributed by community members. + +## Working with functions + +- [Example of using WebSockets in Architect 5.0](https://github.com/architect-examples/arc-example-ws) + + +## Working with data + +- [Best Practices for Modeling Relational Data in DynamoDB](https://github.com/konsumer/arc-example) + - Created by community member [konsumer](https://github.com/konsumer) + - This is a complete example app, with a focus on [the original AWS article](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/bp-relational-modeling.html) + + +## Example apps + +- [Frontend patterns: Using `@views`](https://github.com/architect-examples/arc-examples-views) + +- [Frontend patterns: Serving ES Modules](https://github.com/architect-examples/arc-example-es-modules) + +- [An example docs site (that also happens to be our real docs site)](https://github.com/architect/arc.codes) + +- [Example login / logout flow](https://github.com/architect-examples/arc-example-login-flow) + +- [Example note taking app with support for multiple users](https://github.com/architect-examples/arc-example-notes) + +- [Example repo for working offline and locally](https://github.com/architect-examples/arc-example-working-locally) + +- [Random quote generator](https://github.com/architect-examples/arc-example-random-mitch-hedberg-quote) + + +## Community AWS projects + +- [Omniwallet - Multi-currency web wallet](https://github.com/OmniLayer/omniwallet) + - A hybrid web wallet for the Omni Protocol, supporting Bitcoin, OMNI, and all Omni Protocol tokens + - Designed to run on AWS infrastructure with a multi-tier architecture + - Funds assets managed through PNC Bank diff --git a/src/views/docs/en/guides/extend/add-a-custom-domain.md b/src/views/docs/en/guides/extend/add-a-custom-domain.md index d6ce0afe..f921243c 100644 --- a/src/views/docs/en/guides/extend/add-a-custom-domain.md +++ b/src/views/docs/en/guides/extend/add-a-custom-domain.md @@ -1,5 +1,6 @@ --- title: Assigning a domain name to your app +category: Extend description: 160 (or fewer) character description of this document! sections: - Overview @@ -24,7 +25,7 @@ DNS is how you assign a domain name to a deployed app. This guide lists ways to - [DNS with Route53](#dns-with-route53) - [Starting over](#starting-over) - [The manual way](the-manual-way) - + ## Setup @@ -62,7 +63,7 @@ Run `npx dns` and follow the instructions. The process is: The certificate, CloudFront distributions and DNS records in general can take time to propagate. Be Zen! Running and re-running `npx dns` is safe. -## DNS with Route53 +## DNS with Route53 **Opt-in, but recommended!** @@ -83,7 +84,7 @@ If something goes wrong you can destroy the generated resources and re-create. - `npx dns nuke` destroys the certificate and CloudFront domain distributions - `ARC_NUKE=route53 npx dns nuke` destroys the certificate, CloudFront domain distributions, the Hosted Zone, certificate validation CNAME and Alias records -> 🤷🏽‍♀️ DNS propagation can take time: have patience! +> 🤷🏽‍♀️ DNS propagation can take time: have patience! ## The manual way @@ -97,7 +98,7 @@ If you _really_ want to manually configure DNS you can follow these guides below Follow these instructions to manually configure Route 53 to serve your application from your domain. As a friendly reminder: the `arc` happy path for using Route 53 remains the [`@domain`](/en/reference/arc-pragmas/@domain) section (per the instructions above). -> ⛳️ Tip: These instructions will serve your app's production environment; if you'd also like a friendly URL for your staging environment (i.e. `staging.foo.com`), follow steps 10-15 below a second time, swapping `production` values for `staging` values. +> ⛳️ Tip: These instructions will serve your app's production environment; if you'd also like a friendly URL for your staging environment (i.e. `staging.foo.com`), follow steps 10-15 below a second time, swapping `production` values for `staging` values. 1. Sign into the AWS Console, head to the Route 53 service, and click on **Hosted Zones** 2. Create a **Hosted Zone** diff --git a/src/views/docs/en/guides/extend/custom-cloudformation.md b/src/views/docs/en/guides/extend/custom-cloudformation.md deleted file mode 100644 index 79f444ef..00000000 --- a/src/views/docs/en/guides/extend/custom-cloudformation.md +++ /dev/null @@ -1,38 +0,0 @@ ---- -title: Custom CloudFormation -description: How to use Architect Macros to define or modify resources with CloudFormation ---- - -Architect generates a standard CloudFormation document for deployment with the AWS CLI. `@macros` allow you to process the Architect generated CloudFormation before deployment. This enables customization of any Architect default behavior as well as allowing apps extend into the entire AWS ecosystem of services. - -Architect `@macros` are implemented as a standard Node module with the following function signature: - -```javascript - /** - * Starter macro template - * - * @param {object} arc - Parsed `app.arc` value - * @param {object} sam - Generated CloudFormation template - * @param {string} stage - Deployment target runtime environment 'staging' or 'production' - * @returns {object} Modified CloudFormation template - */ -module.exports = async function mymacro (arc, sam, stage='staging') { - // modify sam cloudformation here - console.log({ arc, sam, stage }) - return sam -} -``` - -> Tip: preview generated CloudFormation without deploying by running `arc deploy --dry-run` and viewing `sam.json` - -Architect finds `@macros` in `src/macros` or project root `node_modules`. An app opts into using `@macros` by adding them to `app.arc`: - -```arc -@app -myapp - -@macros -mymacro -``` - -> In the example above running `arc deploy` will look for `src/macros/mymacro` and then `./node_modules/mymacro` diff --git a/src/views/docs/en/guides/extend/custom-iam-roles.md b/src/views/docs/en/guides/extend/custom-iam-roles.md index 3a79a5bf..eb880cd0 100644 --- a/src/views/docs/en/guides/extend/custom-iam-roles.md +++ b/src/views/docs/en/guides/extend/custom-iam-roles.md @@ -1,5 +1,6 @@ --- title: Custom IAM roles +category: Extend description: Defining custom execution roles for individual Lambda functions sections: - Overview @@ -7,13 +8,13 @@ sections: ## Overview -[IAM, Identity and Access Management,](https://docs.aws.amazon.com/IAM/latest/UserGuide/introduction.html) is a feature of AWS which controls how AWS resources can be accessed by users. IAM roles use policies to define the permissions for a given resource. For example, a Lambda function will assume an IAM role during execution that tells AWS what other resources that Lambda has access to and what operations it is allowed to perform. +[IAM, Identity and Access Management,](https://docs.aws.amazon.com/IAM/latest/UserGuide/introduction.html) is a feature of AWS which controls how AWS resources can be accessed by users. IAM roles use policies to define the permissions for a given resource. For example, a Lambda function will assume an IAM role during execution that tells AWS what other resources that Lambda has access to and what operations it is allowed to perform. -Imagine that IAM roles represent different hats that a user puts on in order to perform an operation. You would put on your "Read-Only hat" to limit the scope of a Lambda execution to just reading a table and deny write access. +Imagine that IAM roles represent different hats that a user puts on in order to perform an operation. You would put on your "Read-Only hat" to limit the scope of a Lambda execution to just reading a table and deny write access. You can define a custom IAM role for your Lambda Function with an `.arc-config` file in the Lambda function code folder. -By default, your functions are executed with a least privileged role, which means that it only has access to the services it needs and nothing else. +By default, your functions are executed with a least privileged role, which means that it only has access to the services it needs and nothing else. If we take a look at the IAM policy for a Lambda function that is created by Architect, we can see that it can be executed with only the ability to create logs in CloudWatch. @@ -38,15 +39,15 @@ Using a custom IAM role allows you to limit interactions even further, like rest Here's how you would add a custom role to your Lambda with a `.arc-config` file. -```md +```arc // src/http/get-orders/.arc-config @aws -runtime nodejs12.x +runtime nodejs14.x memory 512mb concurrency 1 policies arn:aws:iam::aws:policy/AmazonDynamoDBReadOnlyAccess ``` -ARNs, Amazon Resource Names, are globally unique identifiers to specify a resource. You can create new policies in the AWS console or use Managed Policies that are made available by AWS. +ARNs, Amazon Resource Names, are globally unique identifiers to specify a resource. You can create new policies in the AWS console or use Managed Policies that are made available by AWS. [Check out the AWS documentation for more information on Managed Policies](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_managed-vs-inline.html#aws-managed-policies) diff --git a/src/views/docs/en/guides/extend/eject-to-cloudformation.md b/src/views/docs/en/guides/extend/eject-to-cloudformation.md deleted file mode 100644 index c138766d..00000000 --- a/src/views/docs/en/guides/extend/eject-to-cloudformation.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -title: Ejecting to CloudFormation -category: Tutorials -description: Ejecting to CloudFormation -sections: - - Overview - - Tutorial example ---- - -## Overview - -Need to install @architect/architect - -And then in the project directory run arc package - -That will generate sam.json (which is CloudFormation) and print out AWS CLI instructions for deploying \ No newline at end of file diff --git a/src/views/docs/en/guides/extend/plugins.md b/src/views/docs/en/guides/extend/plugins.md new file mode 100644 index 00000000..41ca14b5 --- /dev/null +++ b/src/views/docs/en/guides/extend/plugins.md @@ -0,0 +1,452 @@ +--- +title: Plugins (beta) +category: Extend +description: How to extend Architect using lifecycle hooks +--- + +> ⚠️ NOTE: Plugin support was added in version 8.6.0, is currently in beta, the interface is subject to change and only supports Node.js + +Using [`@macros`][macros] allows you to augment the Architect-generated CloudFormation before deployment. However, augmenting CloudFormation may not be sufficient for certain extensions. For example, if you want to extend Architect with: + +- Your own custom Lambda integrations (e.g. Kinesis Stream or Lex conversation bot) and want Architect to [`create`][create], [`hydrate`][hydrate] and be able to retrieve the [`logs`][logs] for these functions +- A better local development experience for your extension by hooking into the Architect [`sandbox`][sandbox] + +Architect `@plugins` solves these use cases by providing a variety of [interfaces](#interface) that plugin authors may implement to hook into various Architect capabilities. + +> Architect Plugins are backwards-compatible with [`@macros`][macros] + +## Table of Contents + +1. [Installation](#installation) +2. [Interface](#interface) + 1. [`package`](#package) + 2. [`functions`](#functions) + 3. [`variables`](#variables) + 4. [`sandbox.start`](#sandbox.start) + 5. [`sandbox.end`](#sandbox.end) +3. [Helper Methods for Plugin Authors](#helper-methods-for-plugin-authors) + 1. [`createFunction`](#createfunction) + 2. [`invokeFunction`](#invokefunction) +4. [Example Plugins](#example-plugins) + +## Installation + +Similar to [`@macros`][macros], Architect will look for any `@plugins` in `src/plugins` or the project root `node_modules`. An app opts into using `@plugins` by adding them to `app.arc`: + +```arc +@app +myapp + +@plugins +myplugin +``` + +In the example above running any `arc` commands will look for `./src/plugins/myplugin`, `./src/plugins/myplugin.js`, `./node_modules/myplugin` and finally `./node_modules/@myplugin`. The `myplugin` entry in this example is assumed to be the _plugin name_. + +## Interface + +Plugin authors should create a module that exports an object with properties of the object being different functions that hook into core Architect capabilities. + +```javascript + /** + * Starter plugin template + */ + +module.exports = { + package: function ({ arc, cloudformation, stage='staging', inventory, createFunction }) {}, + functions: function ({ arc, inventory }) {}, // also aliased to `pluginFunctions` + variables: function ({ arc, stage, inventory }) {}, + sandbox: { + start: function ({ arc, inventory, invokeFunction, services }) {}, + end: function ({ arc, inventory, services }) {} + } +} +``` + +A deep dive into the [`package`](#package), [`functions`](#functions), [`variables`](#variables), [`sandbox.start`](#sandbox.start) and [`sandbox.end`](#sandbox.end) methods follows. + +### `package` + +> `package({ arc, cloudformation, stage, inventory, createFunction })` + +This method encapsulates [Architect's existing @macro functionality][macros]: extending Architect's generated CloudFormation `sam.json` with your own custom extensions. The additional capability provided by `@plugins` over `@macros` is that `@plugins` provide a convenient way for your extension to define its own ephemeral cloud functions (AWS Lambdas). Plugin authors wanting to manage cloud functions in their plugins would: + +1. Leverage the convenience method [`createFunction`](#createfunction), which is injected as a parameter into `package`, to create CloudFormation JSON defining the AWS Lambda resources you want to manage within your plugin, and +2. Implement the [`functions`](#functions) plugin interface method to inform Architect of new Lambdas you are creating. + +This method can be implemented as an `async` function or not. + +#### Arguments + +All arguments arrive as a bag of options with the following properties: + +| Argument | Description | +| --- | --- | +| `arc` | Object representing the [parsed Architect project manifest](https://github.com/architect/parser) file for the current project | +| `cloudformation` | The [CloudFormation JSON template][cfn-ref] making up the Architect project | +| `stage` | The name of the environment; usually one of `staging` or `production` | +| `inventory` | An [Architect inventory object][inv] representing the current Architect project | +| `createFunction` | A helper method for creating CloudFormation Resource JSON defining any cloud functions (AWS Lambdas) your plugin manages. Please see the [`createFunction`](#createfunction) section for details on this method. | + +#### Returns + +You _must_ return the `cloudformation` argument after modifying it with your own extensions. + +### `functions` + +> `functions({ arc, inventory })` +> `pluginFunctions({ arc, inventory })` + +The plugin author must implement this method if the plugin defines new Lambda functions. This method is used by Architect to allow your custom plugin Lambdas to hook into Architect's capabilities and lifecycle in a variety of ways: + +- instructing [`arc create`][create] to create new files and directories in the project for your custom plugin Lambdas +- instructing [`arc hydrate`][hydrate] to hydrate dependencies of your custom plugin Lambdas +- instructing [`arc logs`][logs] as to where CloudWatch execution logs for your custom plugin Lambdas are located + +#### Arguments + +All arguments arrive as a bag of options with the following properties: + +| Argument | Description | +| --- | --- | +| `arc` | Object representing the [parsed Architect project manifest](https://github.com/architect/parser) file for the current project | +| `inventory` | An [Architect inventory object][inv] representing the current Architect project | + +#### Returns + +`functions` should return an array of objects, each object representing a new Lambda being defined by the plugin. Each object should have the following format: + +```javascript +{ + src: '/Users/filmaj/src/my-arc-project/src/rules/rule-one', + body: 'exports.handler = async function (ruleEvent) { console.log(ruleEvent) }' +} +``` + +- `src`: a string containing the fully qualified absolute path to the source code location for the Lambda function. This path _must_ point to a location under the project's `src/` directory. See the example section below on how to assemble such a path using the base project source directory available via the `inventory` parameter. +- `body`: a string containing template code for the Lambda function handler + +#### Example `functions` implementation + +The following example implementation is for a plugin that allows consumers to define `@rules` Lambdas in their `app.arc` manifest: + +```javascript +let path = require('path') + +module.exports = { + functions: function ({ arc, inventory }) { + if (!arc.rules) return [] // if plugin consumer didnt define any @rules, return empty array signifying no new lambdas to add + const cwd = inventory.inv._project.src // base project source directory + return arc.rules.map((rule) => { // for each @rules + let src = path.join(cwd, 'src', 'rules', rule[0]) // each @rules Lambda will exist under src/rules/ + return { + src, + body: `exports.handler = async function (event) { + console.log(event); +};` + } + }) + } +} +``` + +The above instructs Architect's various capabilities to interact with cloud functions under the `src/rules/` directory inside the project hierarchy. With the above `functions` method and given `app.arc` contents like so: + +```arc +@rules +rule-one +rule-two +``` + +... running: + +- [`arc create`][create] would create the folders `src/rules/rule-one` and `src/rules/rule-two`, with `index.js` files in each containing the contents of the `body` returned by `functions` +- [`arc hydrate`][hydrate] would hydrate the above two folders +- [`arc logs src/rules/rule-one`][logs] would pull in any deployed-to-staging execution logs for the `rule-one` function + +### `variables` + +> `variables({ arc, cloudformation, stage, inventory })` + +The plugin author should implement this method if the plugin would like to provide any manner of data to Lambda functions at runtime. For example, perhaps you would like to expose the physical ID of some AWS resource (i.e. ARN) to your runtime code so that you can interact with it using the AWS SDK. + +Architect provides a suite of runtime helpers via the [`@architect/functions`][functions] library. This library leverages functionality provided by [`deploy`][deploy] and [`sandbox`][sandbox] to expose runtime variables enabling [service discovery][discovery] - the automatic configuration, search and discovery of infrastructure and services making up your application. The `variables` plugin method enables plugin authors to hook into the Architect service discovery mechanism. + +The `variables` plugin method is only necessary to implement if you would like your plugin to provide runtime data within Lambdas via the [`@architect/functions`][functions] library. The exported variables would be available via the [`services` function][services] provided by [`@architect/functions`][functions] (namespaced under the plugin name). For more information on how to query the service discovery mechanism using [`@architect/functions`][functions] at runtime, check out the [`@architect/functions` `services` documentation][services]. + +#### Arguments + +All arguments arrive as a bag of options with the following properties: + +| Argument | Description | +| --- | --- | +| `arc` | Object representing the [parsed Architect project manifest](https://github.com/architect/parser) file for the current project | +| `cloudformation` | The [CloudFormation JSON template][cfn-ref] making up the Architect project | +| `stage` |The name of the environment; usually one of `testing`, `staging` or `production`; `testing` is provided when running in a [`sandbox`][sandbox] context whereas `staging` and `production` are provided at Architect CLI runtime when either `arc deploy staging` or `arc deploy production` are invoked, respectively | +| `inventory` | An [Architect inventory object][inv] representing the current Architect project | + +#### Returns + +This method should always return an object. Each property on the object represents a variable name, and the value for each property contains the variable value. + +> 🏌️‍♀️ Protip: When this method is invoked in a pre-deploy context, acceptable values for the variables include CloudFormation JSON. This is essential to expose CloudFormation-managed infrastructure; see the example below. + +#### Example `variables` implementation + +The following example `variables` implementation demonstrates a plugin that creates a new S3 Bucket. It may be desirable to provide variables related to the location of and credentials for the bucket: + +```javascript +module.exports = { + variables: function ({ arc, cloudformation, stage, inventory }) { + if (!arc['myS3Bucket']) return {} // if the user isn't using this plugin, return an empty object signifying no variables need exporting + const isLocal = stage === 'testing' // stage will equal 'testing' when running in sandbox, otherwise will be one of 'staging' or 'production' when running in a `deploy` context + const bucketName = `${arc.app}-newS3Bucket` + return { + bucketName, + accessKey: isLocal ? 'S3RVER' : { Ref: 'MyS3BucketCreds' }, + secretKey: isLocal ? 'S3RVER' : { 'Fn::GetAtt': [ 'MyS3BucketCreds', 'SecretAccessKey' ] } + } + } +} +``` + +The above example returns three variables that would be provided at runtime: `bucketName`, `accessKey` and `secretKey`. Depending on whether the plugin executes in a local development environment context via [`sandbox`][sandbox] or in a pre-deploy context via [`deploy`][deploy], the contents of these credentials would differ: + +- The `secretKey` and `accessKey` variables would contain hard-coded values when running locally in [`sandbox`][sandbox] (both would have a value of `S3RVER`). These hard-coded values could be used by the plugin author when implementing the [`sandbox.start`](#sandbox.start) method to provide a seamless local development experience. +- The `secretKey` and `accessKey` variables are CloudFormation JSON referencing a set of credentials called `MyS3BucketCreds` when running in a pre-deploy context. These dynamic values reference pre-existing CloudFormation Resources which would be implemented by the author in the plugin's [`package`](#package) method. + +The variables are namespaced on the [`@architect/functions` `services()`][services] object under a property matching the plugin name; check out the [`services`][services] documentation for more details. + + +#### Example service discovery usage with `@architect/functions` + +How would a plugin consumer use these variables at runtime in their own application? Let's take a look at the below example, which builds upon the S3 Bucket example from the previous section. It demonstrates one possible [`@http`][http] GET route implementation rendering a form allowing a user to upload to the plugin-generated S3 Bucket: + +```javascript +let arc = require('@architect/functions') +let form = require('./form') // helper that creates a form element we can render for users to upload their assets to our S3 bucket +let awsLite = require('@aws-lite/client') + +exports.handler = arc.http(async function getIndex (req) { + const services = await arc.services() + const { bucketName, accessKey, secretKey } = services.imagebucket // plugin variables are namespaced under the plugin name; here we assume the plugin name is called 'imagebucket' and is present in the app's app.arc file as 'imagebucket' under the @plugins section + const region = process.env.AWS_REGION + const upload = form({ bucketName, accessKey, secretKey, region }) + const aws = await awsLite() + const images = await aws.s3.ListObjects({ Bucket: bucketName, Prefix: 'thumb/' }) + const imgTags = images.Contents.map(i => i.Key).map(i => ``).join('\n') + return { + headers: { + 'cache-control': 'no-cache, no-store, must-revalidate, max-age=0, s-maxage=0', + 'content-type': 'text/html; charset=utf8' + }, + body: ` + + +

Hi! Upload something directly from the browser to the S3 bucket:

+ ${upload} +

And here are all the previously uploaded images:

+ ${imgTags} + +` + } +}) +``` + +### `sandbox.start` + +> `start({ arc, inventory, invokeFunction, services }, callback)` + +The plugin author must implement this method if the plugin wants to hook into the startup routine for [`sandbox`][sandbox]. This would allow plugin authors to emulate the cloud services their plugin provides in order to provide a local development experience for consumers of their plugin. It also allows modifying the behavior of [`sandbox`][sandbox]'s built-in local development services for [`@http`][http], [`@events`][events], [`@queues`][queues] and [`@tables`][tables] via the `services` argument. Finally, a helper method [`invokeFunction` (described in more detail below)](#invokefunction) is provided as an argument in order to allow plugin authors to invoke specific Lambdas from their plugin sandbox service code. + +This method can either be `async` or not; if the plugin author implements it as `async`, then the final `callback` argument may be ignored. Otherwise, the `callback` argument should be invoked once the plugin's sandbox service is ready. + +#### Arguments + +All arguments arrive as a bag of options with the following properties: + +| Argument | Description | +| --- | --- | +| `arc` | Object representing the [parsed Architect project manifest](https://github.com/architect/parser) file for the current project | +| `inventory` | An [Architect inventory object][inv] representing the current Architect project | +| `invokeFunction` | A helper method that can be used for invoking any cloud functions (AWS Lambdas) your plugin manages during runtime in a local development context inside [`sandbox`][sandbox]. Please see the [`invokeFunction`](#invokefunction) section for details on this method. | +| `services` | An object containing `http`, `events` and `tables` properties that represent local servers that [`sandbox`][sandbox] manages to provide a local development experience. A plugin author may want to modify the behavior of these pre-existing services in order for their plugin to provide a better local development experience. `http` is an instance of the npm package [`router`][router] and mocks API Gateway and Lambda. `events` is a Node.js HTTP server that mocks SNS and SQS by listening for JSON payloads and marshaling them to the relevant Lambda functions (see its [listener module](https://github.com/architect/sandbox/blob/master/src/events/_listener.js) for more details). `tables` is an instance of the npm package [`dynalite`][dynalite] and mocks DynamoDB. | +| `callback` | Can be ignored if the method implementation is an `async function`; otherwise, `callback` must be invoked once the plugin's local development `sandbox` service is ready | + +#### Example `start` implementation + +An example is [provided below that leverages the `invokeFunction` helper method](#invokefunction). + +### `sandbox.end` + +> `end({ arc, inventory, services }, callback)` + +If the plugin author implements the [`sandbox.start`](#sandbox.start) method, then they must also implement the `sandbox.end` method. This method gives the plugin the opportunity to gracefully shut down any services powering local development support of the plugin. + +This method can either be `async` or not; if the plugin author implements it as `async`, then the final `callback` argument may be ignored. Otherwise, the `callback` argument should be invoked once the plugin's sandbox service is ready. + +#### Arguments + +All arguments arrive as a bag of options with the following properties: + +| Argument | Description | +| --- | --- | +| `arc` | Object representing the [parsed Architect project manifest](https://github.com/architect/parser) file for the current project | +| `inventory` | An [Architect inventory object][inv] representing the current Architect project | +| `services` | [`sandbox`][sandbox] runs [local in-memory servers to mock out HTTP, events, queues and database functionality](https://github.com/architect/sandbox/blob/master/src/sandbox/index.js#L19-L24); if you need to modify these services, use this argument | +| `callback` | Can be ignored if the method implementation is an `async function`; otherwise, `callback` must be invoked once the plugin's local development `sandbox` service has been shut down | + + +## Helper methods for plugin authors + +For common Architect Plugin use cases, Architect provides a few helper functions available as parameters injected as arguments into plugin methods to make life easier for plugin authors. + +### `createFunction` + +> `createFunction({ inventory, src })` + +This method should be leveraged inside a plugin's [`package`](#package) method in order to more easily define CloudFormation JSON representing Lambdas created by the plugin. Use of this method for defining Lambdas is an Architect best practice as certain specific conventions that Architect relies on can be maintained. + +While the AWS Lambda logical ID is generally not a concern for developers using Architect, Architect relies on a logical ID naming convention to e.g. retrieve execution logs of a deployed Lambda via [`arc logs`][logs]. This helper method helps enforce such conventions. Leveraging this method also gives the plugin-generated Lambdas transparent support for [Architect's per-function runtime configuration via the `config.arc` file][config-arc]. + +#### Arguments + +All arguments arrive as a bag of options with the following properties: + +| Argument | Description | +| --- | --- | +| `inventory` | An [Architect inventory object][inv] representing the current Architect project | +| `src` | A string representing the fully qualified absolute path to where code for the Lambda exists locally | + +#### Returns + +A tuple (array of two objects) containing: + +1. A string representing an AWS-friendly Lambda resource name (which is based on the path to the function code), and +2. A JSON object that can be assigned to a CloudFormation `sam.json`'s `Resources` section. This would define a Lambda that Architect would create during a [`deploy`][deploy] + +#### Example usage of `createFunction` + +```javascript +let path = require('path') + +module.exports = { + package: async function IoTRulesLambdas ({ arc, cloudformation, createFunction, stage = 'staging', inventory }) { + if (arc.rules) { + const cwd = inventory.inv._project.src + arc.rules.forEach(rule => { + let src = path.join(cwd, 'src', 'rules', rule[0]) + let [functionName, functionDefn] = createFunction({ inventory, src }) + cloudformation.Resources[functionName] = functionDefn + }) + } + return cloudformation + } +} +``` + +### `invokeFunction` + +> `invokeFunction({ src, payload }, callback)` + +This method should be leveraged inside a plugin's [`sandbox.start`](#sandbox.start) method in order to easily invoke project Lambdas locally within an [`npx arc sandbox`][sandbox] local development runtime context. For example, if your plugin manages Lambdas related to some AWS service, it may be nice to provide a local development experience for consumers of your plugin. To provide a great local experience, consumers of your plugin will want to exercise your plugin-generated Lambdas when running locally. Using the combination of the [`sandbox.start`](#sandbox.start) and `invokeFunction` methods, plugin authors can implement a local development experience for plugin consumers. + +#### Arguments + +All arguments arrive as a bag of options with the following properties: + +| Argument | Description | +| --- | --- | +| `src` | A string representing the fully qualified absolute path to where code for the Lambda exists locally | +| `payload` | JSON payload to deliver to the function | +| `callback` | Function with signature `function(error, result)` that is invoked with either the error or the result from the local function invocation | + +#### Example usage of `invokeLambda` + +The below plugin's `sandbox.start` method listens for the "I" keyboard keypress, prompts the user which of the plugin's Lambdas the user wants to invoke and what payload to deliver to the user, before using `invokeFunction` to invoke the Lambda code with the specified payload. + +```javascript +let path = require('path') +let prompt = require('prompt') + +module.exports = { + functions: async function ({ arc, inventory }) { + if (!arc.rules) return [] + const cwd = inventory.inv._project.src + return arc.rules.map((rule) => { + let src = path.join(cwd, 'src', 'rules', rule[0]) + return { + src, + body: `exports.handler = async function (event) { + console.log(event) +}` + } + }) + }, + sandbox: { + start: function IoTRulesServiceStart ({ arc, inventory, invokeFunction, services }, callback) { + let rules = module.exports.functions({ arc, inventory }).map(rule => rule.src) + process.stdin.on('keypress', async function IoTRulesKeyListener (input, key) { + if (input === 'I') { + const response = await prompt([ { + type: 'select', + name: 'rule', + message: 'Which IoT Rule do you want to trigger an event for?', + choices: rules + }, { + type: 'input', + name: 'payload', + message: 'Type out the JSON payload you want to deliver to the rule (must be valid JSON!):', + initial: '{}', + validate: function (i) { + try { + JSON.parse(i) + } + catch (e) { + return e.message + } + return true + }, + result: function (i) { + return JSON.parse(i) + } + } ]) + invokeFunction({ src: response.rule, payload: response.payload }, function (err, result) { + if (err) console.error(`Error invoking lambda ${response.rule}!`, err) + else console.log(`${response.rule} invocation result:`, result) + }) + } + }) + console.log('IoT Rules Sandbox Service Started; press "I" (capital letter) to trigger a rule.') + callback() + } + } +} +``` + +## Example plugins + +- [plugin-iot-rules](https://www.npmjs.com/package/@copper/plugin-iot-rules): adds AWS IoT Topic event Lambdas +- [plugin-parcel](https://www.npmjs.com/package/@copper/plugin-parcel): compiles project Lambda code with the Parcel bundler both during local development via [`sandbox`][sandbox] and before [`deploy`s][deploy] +- [arc-plugin-esbuild](https://www.npmjs.com/package/arc-plugin-esbuild): compiles project Lambda code with the esbuild bundler watching during local development via [`sandbox`][sandbox] and before [`deploy`s][deploy] +- [arc-plugin-s3-image-bucket](https://www.npmjs.com/package/arc-plugin-s3-image-bucket): manages an S3 bucket purpose-built for allowing direct-from-user image uploads, includes support for customizable Lambda triggers based on bucket events + +[inv]: https://github.com/architect/inventory +[macros]: custom-cloudformation +[config-arc]: ../../reference/configuration/function-config +[http]: ../../reference/project-manifest/http +[events]: ../../reference/project-manifest/events +[queues]: ../../reference/project-manifest/queues +[tables]: ../../reference/project-manifest/tables +[sandbox]: ../../reference/cli/sandbox +[create]: ../../reference/cli/init +[hydrate]: ../../reference/cli/hydrate +[logs]: ../../reference/cli/logs +[deploy]: ../../reference/cli/deploy +[functions]: ../../reference/runtime-helpers/node.js +[services]: ../../reference/runtime-helpers/node.js#arc.services +[cfn-ref]: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/template-reference.html +[discovery]: https://en.wikipedia.org/wiki/Service_discovery +[ssm]: https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-parameter-store.html +[router]: https://www.npmjs.com/package/router +[dynalite]: https://www.npmjs.com/package/dynalite diff --git a/src/views/docs/en/guides/frontend/cdn.md b/src/views/docs/en/guides/frontend/cdn.md index 762408a5..10e0f060 100644 --- a/src/views/docs/en/guides/frontend/cdn.md +++ b/src/views/docs/en/guides/frontend/cdn.md @@ -1,5 +1,6 @@ --- title: CDN +category: Frontend description: Content delivery network (CDN) with AWS CloudFront sections: - Overview diff --git a/src/views/docs/en/guides/frontend/cors.md b/src/views/docs/en/guides/frontend/cors.md index 0dc52d7d..17022e6b 100644 --- a/src/views/docs/en/guides/frontend/cors.md +++ b/src/views/docs/en/guides/frontend/cors.md @@ -1,5 +1,6 @@ --- title: Implementing CORS +category: Frontend --- Cross-origin resource sharing (CORS) is a mechanism that that uses additional HTTP headers to tell browsers to give a web application running at one origin access resources from another domain outside the domain from which the first resource was served. A web page may freely embed cross-origin images, stylesheets, scripts, iframes, and videos. @@ -28,7 +29,7 @@ The API route `src/http/get-api` is CORS enabled with one flag: // src/http/get-api/index.js let arc = require('@architect/functions') -exports.handler = arc.http.async(handler) +exports.handler = arc.http(handler) async function handler (req) { return { @@ -40,7 +41,7 @@ async function handler (req) { ## Restricting Domains -You can restrict domains within your Lambda function code. +You can restrict domains within your Lambda function code. Continuing from the `/api` endpoint, your API might operate differently based on the request's domain of origin: @@ -63,4 +64,3 @@ exports.handler = async function http(req) { } } ``` - diff --git a/src/views/docs/en/guides/frontend/http-functions.md b/src/views/docs/en/guides/frontend/http-functions.md index b77ea8dd..3dcec46c 100644 --- a/src/views/docs/en/guides/frontend/http-functions.md +++ b/src/views/docs/en/guides/frontend/http-functions.md @@ -1,5 +1,6 @@ --- title: HTTP functions +category: Frontend description: HTTP Functions are the building blocks of the modern web app sections: - Overview @@ -26,7 +27,7 @@ Within your project, each HTTP Function can contain and utilize an arbitrary qua The HTTP handler API follows a simple [request](#requests) / [response](#responses) pattern. Let's look at an example of a basic HTTP Function: -```js +```javascript // src/http/get-index/index.js let body = ` @@ -94,11 +95,11 @@ Each HTTP Function maps to a logical HTTP route. For example: - `get /about` is serviced by `src/http/get-about` - `post /form` is serviced by `src/http/post-form` -All HTTP Functions begin with `/`, and can include letters, numbers, and slashes, underscores, dashes, and periods, up to 35 characters. +All HTTP Functions begin with `/`, and can include letters, numbers, and slashes, underscores, dashes, and periods, with an advised (but not enforced) maximum of 100 characters. Importantly and uniquely, you can also use URL parameters to build dynamic paths – more on that below. -> ✨ Tip: It's possible to have multiple HTTP methods respond from the same path. For example: `get /contact-us` and `post /contact-us` is totally valid, as you'd expect. +> ✨ Tip: It's possible to have multiple HTTP methods respond from the same path. For example: `get /contact-us` and `post /contact-us` is totally valid, as you'd expect. ### Greedy root @@ -145,7 +146,7 @@ The `handler` function invoked by a client request receives a `request` object c Here's an example of an incoming `request` object, being handled by the HTTP Function `GET /salutations/:greeting`: -```js +```javascript // Client requested https://begin.com/hello-world?testing=123 { httpMethod: 'GET', @@ -171,7 +172,7 @@ Here's an example of an incoming `request` object, being handled by the HTTP Fun To use request.body you'll need to parse it first. Architect Functions provides a simple body parser helper; this helper takes a request object, and returns a parsed body object. All bodies are unparsed, base64-encoded strings. Parse it with `arc.http.helpers.bodyParser()`. Here's an example: -```js +```javascript // Request is form URL-encoded string: 'greeting=howdy' let arc = require('@architect/functions') @@ -207,7 +208,7 @@ Responses are returned by your `handler` function in an object, and use the foll Here's a simple example response for an API endpoint: -```js +```javascript // Responding to a successful POST return { @@ -223,7 +224,7 @@ Many remote networks rely on overly aggressive reverse-proxy caches to conserve Because of the highly adverse effects network-level caching can on your application, we strongly suggest that most HTTP Function responses include anti-caching headers – especially when returning `HTML` and `JSON` responses. For example: -```js +```javascript return { headers: { 'content-type': 'text/html; charset=utf8', @@ -237,9 +238,8 @@ return { By default, all HTTP functions (as well as all other functions) generated with Architect have one generated [IAM role](https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html#grant-least-privilege) with the least privilege possible to operate. This means HTTP functions can only access other resources defined in the same Architect project. -Wider account access can be explicitly granted with custom resource policies, [defined in a .arc-config file](/en/reference/architect-manifest-and-config/function-config-file) placed in the HTTP function directory. +Wider account access can be explicitly granted with custom resource policies, [defined in a `config.arc` file](/en/reference/architect-manifest-and-config/function-config-file) placed in the HTTP function directory. ## Examples ADD ME! - diff --git a/src/views/docs/en/guides/frontend/middleware.md b/src/views/docs/en/guides/frontend/middleware.md index c23cf561..63243411 100644 --- a/src/views/docs/en/guides/frontend/middleware.md +++ b/src/views/docs/en/guides/frontend/middleware.md @@ -1,12 +1,11 @@ --- title: Cloud function middleware -category: Tutorials +category: Frontend description: Short Tutorial showing both async/await and callback-style runtime helpers to modify request objects. sections: - Overview - arc.http.async - arc.http - - arc.http.express - Example --- @@ -26,7 +25,6 @@ Both middleware helpers conveniently attach user sessions to the incoming `reque - [Overview](#overview) - [arc.http.async](#arc.http.async) - [arc.http](#arc.http) - - [arc.http.express](#arc.http.express) - [Example](#example) ## `arc.http.async` @@ -142,7 +140,7 @@ Navigate to `http://localhost:3333/dashboard?user=nic_cage` and you should see t - Tracking user interactions (kick off a `@event` to save something to the database without blocking the request) - Adding additional information to requests ------------- +--- ## `arc.http` @@ -215,7 +213,7 @@ exports.handler = arc.http(handler) 6. We can also replace the contents of `/src/http/post-count/index.js` with the following: -``` javascript +```javascript var arc = require('@architect/functions') function handler(req, res) { @@ -251,43 +249,3 @@ arc sandbox - optionally the `Error` instance property of `code`, `status` or `statusCode` can be one of `403`, `404` or `500` to change the HTTP status code - `next` (optional) is a function to continue middleware execution ---- - -## arc.http.express - -Architect also has a middleware function to wrap Express.js logic, this is good for migrating paths from existing Express applications into a serverless environment. It should be noted that bundling an entire web server in a Lambda function will result in poor performance if the entire function payload with dependencies exceeds 5MB. But, if you are already comfortable with understanding Express routing for backend APIs, then this helper can get your app up and running. - -### Example - -1. Let's make a new Architect project directly from the command line! - -This command will create a new directory, install a local version of Architect, and generate a folder structure. - -```bash -npm init @architect ./myexpress -``` - -2. Take a look inside and you will see one HTTP function, `get-index`. This will be a single Lambda that will be our entire Express app behind an API Gateway endpoint. - -3. Let's add our dependencies to `get-index` so we can require `@architect/functions`. - -```bash -cd myexpress/src/http/get-index -npm init -y -npm i express @architect/functions -``` - -4. Replace the contents of `src/http/get-index/index.js` with the following: - -```javascript -let arc = require('@architect/functions') -let express = require('express') -let app = express() -app.get('/', (req, res) => res.send('Hello World!')) -app.get('/cool', (req, res)=> res.send('very cool')) -exports.handler = arc.http.express(app) -``` - -5. Start Sandbox, the local dev server with `npm start` and check out the results by navigating to `http://localhost:3333` - -Each time a visitor reaches the root of your app, it will take the request and it behave the same way as a full express server. diff --git a/src/views/docs/en/guides/frontend/sessions.md b/src/views/docs/en/guides/frontend/sessions.md index 64e13197..0bc15391 100644 --- a/src/views/docs/en/guides/frontend/sessions.md +++ b/src/views/docs/en/guides/frontend/sessions.md @@ -1,270 +1,247 @@ --- -title: HTTP & WebSocket sessions -description: 160 (or fewer) character description of this document! +title: Architect sessions +category: Frontend +description: Use HTTP sessions in an Architect project sections: - Overview - HTTP sessions - - WebSocket sessions + - Choosing a session store + - Session configuration + - Strong session secret + - Examples --- ## Overview -Developing database backed stateful web applications used to require a web server, a database server, a whole supporting cast of software and frameworks and all the near-constant maintenance those things required. Now anyone with a text editor can handle POST requests directly with a Lambda function and API Gateway. +Session state is the first primitive to understand for building stateful interactions on the web. HTTP is a stateless protocol, which is a fancy way of saying every HTTP request is effectively a clean slate. If you want to remember things between HTTP requests you need a session. -The first primitive to understand for building stateful interactions on the web is session state. HTTP is a stateless protocol which is a fancy way of saying every HTTP request is like a completely clean slate. If we want to remember things between HTTP requests you need a session. +This guide will go over several ways to store session state within your Architect app. There is an example app at the end that demonstrates how sessions work. -In this tutorial, we will go over several ways to store session state within your app. There is an example app at the end that we will build to display how sessions work within Architect. - -**Sections** -- [HTTP sessions](#http-sessions) -- [Database Sessions](#database-sessions) -- [WebSocket sessions](#websocket-sessions) -- [Strong Key](#strong-key) -- [Common Session Use Cases](#common-session-use-cases) -- [Example](#example) - ---- ## HTTP sessions -All `@http` defined routes are session capable via `@architect/functions`. - -- Requests are tagged to a session via a stateless, signed, encrypted, httpOnly cookie `_idx` -- Session data expires after a week of inactivity +Architect provides built-in session capabilities for `@http` defined routes via [`@architect/functions`](/docs/en/reference/runtime-helpers/node.js) (Node.js) and [`architect-functions`](/docs/en/reference/runtime-helpers/python) (Python). -This allows you to write fully stateful applications despite Lambda functions being completely stateless. +- Requests are tagged to a session via a stateless, signed, `httpOnly` cookie: `_idx` +- By default session data is stored in JWE, but we suggest making use of Architect's built-in database-backed session storage (more on that below) +- JWE session data is long-lived in the client, while database-backed session data expires after a week of inactivity + - Session expiration for both JWE and database-backed sessions is configurable -Read the session: +This allows you to write fully stateful applications despite Lambda functions being completely stateless. Here's a minimal example of how to read and write a session using Architect's runtime helpers: ```javascript +// a simple request counter let arc = require('@architect/functions') +// Session is appended to the request by `@architect/functions`; we'll increment it each time exports.handler = async function http(req) { - // reads the session from DynamoDB - let session = await arc.http.session.read(req) + let { session } = req // Session will be {} if unknown or invalid + session.count = (session.count || 0) + 1 return { - type: 'text/html; charset=utf8', - body: `
${JSON.stringify(session, null, 2)}
` + session, + html: `
${JSON.stringify(session, null, 2)}
`, } } ``` -Write the session: +> See the [Node.js sessions](../../reference/runtime-helpers/node.js#arc.http.session) and [Python sessions](../../reference/runtime-helpers/python#arc.http.session) references for more details -```javascript -let arc = require('@architect/functions') -exports.handler = async function http(req) { - // reads the session from DynamoDB - let session = await arc.http.session.read(req) +## Choosing a session store - // modify the state - session.count = (session.count || 0) + 1 - // save the session state to DynamoDB - let cookie = await arc.http.session.write(session) - let status = 302 - let location = '/' - // respond (and update the session cookie) - return {cookie, status, location} -} -``` +Architect provides two means of session storage: [JWE (cookie-based)](#jwe-backed-sessions) and [database-backed](#database-backed-sessions). Below we'll explore the advantages and disadvantages to each, and how to [configure them](#session-configuration). ---- +Architect's methods are the same no matter which session storage method you use, so business logic won't be a factor in your decision. That said, **if you choose to change session storage method from one to the other, existing sessions will be invalidated during the change**. -## Database Sessions -If you have stricter security requirements and do not want to expose any session state to clients you can opt into sessions backed by DynamoDB tables. +### JWE-backed sessions -You'll need to define a session table in your `app.arc` file with `_idx` partition key and `_ttl` attribute for token expiry: +By default, Architect sessions in new projects default to [JWE (JSON web encryption)](https://datatracker.ietf.org/doc/html/rfc7516) based storage. While JWE-backed sessions are fast and reliable, we recommend [database-backed sessions](#database-backed-sessions) for mission-critical applications. -```arc -@app -testapp +Pros +- Slightly easier to set up +- Minimal latency associated with reading / writing session data -@http -get / +Cons +- Significantly less session data can be stored (~4KB) +- Session data is stored in the client, meaning developers cannot as easily mutate, fix, or analyze user session data -@tables -session - _idx *String - _ttl TTL -``` -Run `npx create` to generate the session database tables. Next opt your Lambda functions into using that table by overriding `SESSION_TABLE_NAME`: +### Database-backed sessions -```bash -npx env staging SESSION_TABLE_NAME jwe -npx env production SESSION_TABLE_NAME testapp-production-session -``` +Architect recommends database-backed sessions, which are fast, robust, easy to use, and can be mutated or analyzed out of band when necessary. -This will sync all production lambdas to use the DynamoDB table while testing and staging environments will continue to use the stateless cookie. If you add new routes you will need to remember to sync by running `npx env verify`. +Pros +- Up to ~400KB session storage data (10 times greater than JWE) +- Ability to manually mutate, fix, or analyze session data out of band ---- +Cons +- If your app scales to many millions or billions of requests per month, you may incur small charges -## WebSocket sessions -ADD ME! +## Session configuration + +Outside of the [session storage medium](#storage-medium) configuration, almost all session configuration is done via environment variables. ---- -## Strong Key +### Storage medium -Ensure your app has a strong secret key: +By default, Architect sessions are JWE-backed, and no further configuration is necessary to begin using them. To configure database-backed sessions: + +1. Add a sessions table to your project manifest + +```arc +@tables +sessions # Any name is fine, we suggest 'sessions'; do not use 'jwe' + _idx * # [Required] + ttl ttl # [Required] +``` + +2. Add the corresponding `ARC_SESSION_TABLE_NAME` environment variable to your environments (in this example, for the table named `sessions`): ```bash -npx env production ARC_APP_SECRET something-much-better-than-this +npx arc env -e testing -a ARC_SESSION_TABLE_NAME sessions +npx arc env -e staging -a ARC_SESSION_TABLE_NAME sessions +npx arc env -e production -a ARC_SESSION_TABLE_NAME sessions ``` -Environment variables are automatically synced with all your lambda functions. When you add new functions you will need to sync their env variables by running `npx env verify`. +3. If your app is already deployed, you will then need to [redeploy](/docs/en/reference/cli/deploy) it for the changes to take effect. ---- +To reconfigure JWE-backed sessions, simply run the same commands above, setting the `ARC_SESSION_TABLE_NAME` env var to `jwe` (instead of your sessions table name). -## Common Session Use Cases -- Authentication -- Error messages -- Shopping carts +### General settings -> See [the sessions reference](/docs/en/reference/macros/runtime-helper-reference/arc-http-session) for more details. +The following configuration environment variables are available to all Architect sessions: ---- +- `ARC_APP_SECRET` - **Strongly recommended!** Please see the section below for [setting a strong session secret](#strong-session-secret) +- `ARC_SESSION_DOMAIN` - Not set by default; define the cookie's [`Domain` attribute](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#define_where_cookies_are_sent) +- `ARC_SESSION_SAME_SITE` - Set to `lax` by default; define the cookie's [`SameSite` attribute](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#samesite_attribute) +- `ARC_SESSION_TTL` - Default is very long-lived; define the cookie's `expires` attribute; expiry in seconds (e.g. configure `86400` to set the expiry for 1 day of inactivity) + - Note: despite whatever is set in the cookie's `expires` attribute, database-backed sessions are always automatically expunged in the database after one week of inactivity -## Example -1. Create a fresh Architect project +### JWE encryption configuration -```bash -mkdir -p ./mysesh -cd mysesh -``` +- `ARC_APP_SECRET_ALGO` - Set to `A256GCM` by default; configure the `AES/GCM` encryption key bit-length, must be one of `A256GCM`, `A192GCM`, `A128GCM` + - Architect's default key **should not be assumed to be safe or secure**; for many basic purposes that may be ok, although we strongly recommend [setting a strong session secret](#strong-session-secret) + - Please note the minimum algorithm key sizes listed here. Longer keys will be truncated to the appropriate key size, while shorter keys will result in a validation error: + - `A256GCM` 256 bit (32 octet) + - `A192GCM` 192 bit (24 octet) + - `A128GCM` 128 bit (16 octet) +- `ARC_FORCE_LEGACY_JWE_SECRET` - Forces compatibility with JWE session secrets from older versions of `@architect/functions`; not suggested unless you cannot upgrade to v7 or greater. -2. Create a `app.arc` file -```arc -@app -bigsesh +## Strong session secret -@http -get / -post /count -post /reset -``` +Architect's default session secret key **should not be assumed to be safe or secure**. As such, we strongly recommend setting a strong session secret. This secret is used for encryption in JWE sessions, and securely signing database-backed sessions. -And generate the boilerplate code by running: +Database-backed sessions should use a very high entropy secret (we suggest at least 32 characters / 256 bit), but any value will be used. JWE-backed sessions must have a 256 bit or higher secret by default; any characters beyond that length will be truncated. ```bash -arc init +npx arc --env production --add ARC_APP_SECRET something-significantly-better-than-this ``` -3. Add the `@architect/functions` runtime helper library to your functions. This gives the request object a method to read and write sessions. +Quickly generate a secret with `openssl`: ```bash -cd src/http/get-index -npm init -f -npm i @architect/functions - -cd ../post-count -npm init -f -npm i @architect/functions - -cd ../post-reset -npm init -f -npm i @architect/functions +npx arc env -e staging -a ARC_APP_SECRET $(openssl rand -base64 32) +npx arc env -e production -a ARC_APP_SECRET $(openssl rand -base64 32) ``` -4. Add `src/http/get-index/render.js` with plain vanilla HTML forms for adding and resetting the session -```javascript -// this is perfectly acceptable and FAST server side rendering - -module.exports = function render({ count }) { - return ` - - - - - - -
- -
- - - ` -} -``` +## Examples -5. Modify `src/http/get-index/index.js` to read the session if it exists and render the forms with the session state +### Example repo -```javascript -let arc = require('@architect/functions') -let render = require('./render') +Please do check out the [`architect-examples/sessions`](https://github.com/architect-examples/sessions) for a complete example project for working with Architect JWE and database-backed sessions in either JS or Python. -async function home(req) { - let count = req.session.count || 0 - return { - html: render({ count }) - } -} -exports.handler = arc.http.async(home) -``` +### Return an incremented session -6. Modify `src/http/post-count/index.js` to mutate the session and redirect home +Assumes you have [`@architect/functions`](https://www.npmjs.com/package/@architect/functions) or [`architect-functions`](https://pypi.org/project/architect-functions/) installed. + +
+ +
Node.js
+
```javascript -let arc = require('@architect/functions') +import arc from '@architect/functions' -async function counter(req) { +export const handler = arc.http(async req => { let count = (req.session.count || 0) + 1 + let session = { count } return { - session: { count }, - location: '/' + session, + json: session } -} +}) +``` + +
+
-exports.handler = arc.http.async(counter) + +
Python
+
+ +```python +import arc + +def handler(req, context): + sesh = arc.http.session_read(req) + count = sesh.get("count", 0) + 1 + session = {"count": count} + return arc.http.res(req, {"session": session, "json": session}) ``` -> FYI: Per recommended security practice Architect applications use `httpOnly` cookies for storing session state; anyone can implement their own mechanism using Set-Cookie headers directly +
+
+
+
+ + +### Destroy a session's contents -7. Modify `src/http/post-reset/index.js` to clear the session state +Assumes you have [`@architect/functions`](https://www.npmjs.com/package/@architect/functions) or [`architect-functions`](https://pypi.org/project/architect-functions/) installed. + + +
+ +
Node.js
+
```javascript -let arc = require('@architect/functions') +import arc from '@architect/functions' -async function reset(req) { +export const handler = arc.http(async req => { return { session: {}, - location: '/' + json: { ok: true } } -} - -exports.handler = arc.http.async(reset) +}) ``` -> For more information about `arc.http.async` helper, [check out the documentation](/docs/en/reference/macros/runtime-helper-reference/arc-http-async) +
+
-8. Initialize a `package.json` in the root of your project, and install `@architect/sandbox` for a local development server + +
Python
+
-```bash -npm init -f -npm install @architect/sandbox -``` +```python +import arc -9. Add a start command to the scripts section in `package.json` found at the root of your project - -```json -... -"scripts": { - "start": "sandbox" -} -... +def handler(req, context): + return arc.http.res( + req, + {"session": {}, "json": {"ok": True}} + ) ``` -10. Preview by starting the dev server - -```bash -npm start -``` +
+
+
+
diff --git a/src/views/docs/en/guides/frontend/single-page-apps.md b/src/views/docs/en/guides/frontend/single-page-apps.md index 7b30857e..b398feb8 100644 --- a/src/views/docs/en/guides/frontend/single-page-apps.md +++ b/src/views/docs/en/guides/frontend/single-page-apps.md @@ -1,5 +1,6 @@ --- title: Single page apps +category: Frontend description: Static and dynamic API endpoints coexisting at the same origin sections: - Overview @@ -42,7 +43,7 @@ staging spa-stage-bukkit production spa-prod-bukkit ``` -> 👉🏽 Note: S3 buckets are global to all of AWS so you may need to try a few different names! +> 👉🏽 Note: S3 buckets are global to all of AWS so you may need to try a few different names! ## Proxy Public @@ -130,7 +131,7 @@ Architect supports the following transform plugins: - `@architect/proxy-plugin-css-urls` adds `/staging` or `/production` to CSS `@imports` statements - `@architect/proxy-plugin-mjs-urls` adds `/staging` or `/production` to JS module `import` statements -- **esmodules** +- **ES modules** - `@architect/proxy-plugin-bare-imports` map bare imports to fully qualified URLs - **syntax transpilers** @@ -195,18 +196,41 @@ exports.handler = arc.proxy.public({ There are many ways to build a single-page application. Larger applications can benefit from using a frontend library and bundler. Various libraries help us organize code, and the bundler helps us package it for optimal production delivery. -In this guide, we'll be using the frontend library
React with the Parcel bundler. React is probably the most popular framework and works with many bundlers, but we like Parcel because of its speed and simplicity. +In this guide, we'll be using the frontend library [React](https://reactjs.org/) with the [Parcel bundler](https://parceljs.org). React is probably the most popular framework and works with many bundlers, but we like Parcel because of its speed and simplicity. 1. Create a fresh Architect project Initialize an Architect project, change directories into the project folder, create a `package.json` file, and install NPM packages: + +
+ +
Bash/cmd.exe
+
+ ```bash npm init @architect --static ./my-spa cd my-spa npm init -f npm install react react-dom parcel-bundler @architect/sandbox ``` +
+
+ + +
PowerShell
+
+ +```powershell +npm init "@architect" --static ./my-spa +cd my-spa +npm init -f +npm install react react-dom parcel-bundler @architect/sandbox +``` +
+
+
+
2. Update the build folder configuration in `app.arc` diff --git a/src/views/docs/en/guides/frontend/static-assets.md b/src/views/docs/en/guides/frontend/static-assets.md index 97cc2cec..9fe6d5b3 100644 --- a/src/views/docs/en/guides/frontend/static-assets.md +++ b/src/views/docs/en/guides/frontend/static-assets.md @@ -1,36 +1,58 @@ --- title: Static assets +category: Frontend description: Architect projects support text and binary static assets such as images, styles, and scripts. --- -Architect projects support text and binary static assets such as images, styles, and scripts. By default `@static` assets live in the `/public` folder locally and an S3 bucket once deployed. +Architect projects support text and binary static assets such as images, styles, and scripts. By default `@static` assets live locally in the `/public` folder at the root of your project and in an S3 bucket once deployed. ## `@static` configuration -- `folder public` configures the deployment folder (default is `public`) -- `fingerprint true` enables asset fingerprinting (default is `false`) -- `ignore` asset to ignore from deployment to S3 +- `fingerprint` - **boolean** or **string** (defaults to false) + - Enable long-lived caching headers by static asset file fingerprinting +- `folder` - **string** (defaults to `./public`) + - Designate the local folder to upload static assets from. +- `ignore` - **list** + - Define which assets in the static `folder` should be ignored during upload +- `prefix` - **string** + - Set a top-level directory in the S3 bucket where files will be deployed +- `prune` - **boolean** (defaults to false) + - Automatically remove assets from S3 bucket not found in the static `folder` +- `spa` - **boolean** (defaults to false) + - Enable "Single Page App" delivery: all page requests route to the root. + +> Tip: `@static` assets are available at `/_static` which makes them **same-origin** ✨ -> Tip: `@static` assets are available at `/_static` which makes them **same-origin** +### `fingerprint` -### `folder` +When set to `true`, fingerprinting adds a unique SHA to a file name based on the file content before uploading to S3. The file can then be cached effectively forever. Whenever the contents of the file changes so does the SHA invalidating the cache. ```arc @static -folder dist +fingerprint true ``` -### `fingerprint` +The Node.js runtime helper, [`@architect/functions`, provides a `static`](../../reference/runtime-helpers/node.js#arc.static) method to help create a path for a given fingerprinted asset. See [below for an example](#fingerprinted-file-paths). -Fingerprinting adds a unique SHA to a file name based on the file content before uploading to S3. The file can then be cached effectively forever. Whenever the contents of the file changes so does the SHA invalidating the cache. -Enable fingerprinting: +By setting fingerprinting to `external`, the file name is not changed before uploading to S3. You should only do this if you can ensure that the file name changes when you change the file content. This setting is incompatible with the Node.js runtime helper mentioned above. ```arc @static -fingerprint true +fingerprint external +``` + +### `folder` + +Use a custom folder name or path for static assets. + +```arc +@static +folder dist ``` +Architect will expect assets in the folder `dist` at the root of your project. + ### `ignore` Ignore zip and tar files in the `@static` folder: @@ -46,9 +68,52 @@ Ignore is greedy. For example if you ignore "foo", all filenames containing "foo > By default, Architect ignores `.DS_Store`, `node_modules`, and `readme.md` files -### Referencing fingerprinted file paths at runtime +### `prefix` + +Advanced option to specify a top-level directory inside the S3 bucket to add static files to when deploying. In this example files from `./public` will be uploaded to `/downloads/`: + +```arc +@static +prefix downloads +``` + +> Note: this setting is not accounted for in [`@architect/functions`'s `static` method](../../reference/runtime-helpers/node.js#arc.static). + +### `prune` + +Tell Architect to automatically delete files from the S3 bucket that do not exist in the next deployment. Effectively the same as running `arc deploy` with the `--prune` flag. Useful for removing old fingerprinted assets and keeping your bucket tidy. + +```arc +@static +prune true +``` + +### `spa` + +Enable "Single Page Applications" by transforming and redirecting requests to root path. This will allow `/index.html` to handle any requests that do not match a declared [`@http`](../../reference/project-manifest/http) function. + +In the following example, pointing a browser to `/`, `/people`, `/any/path`, etc. will render index.html. Getting `/api` will not. + +```arc +@http +get /api + +@static +spa true +``` + +## Deployment + +`arc deploy --static` deploys static assets to `staging` from `public/` or configured folder. +`arc deploy --static --production` deploys static assets to `production` from `public/` or configured folder. + +Static assets will also be uploaded during an `arc deploy` along with your function code. + +`arc deploy --static --prune` deletes static assets from the S3 bucket that are not present in the configured static asset folder. + +## Fingerprinted file paths -To get the path for generated files at runtime use `arc.static`. +To get the path for generated files at runtime use `arc.static` from `@architect/functions`, the [Architect Node.js runtime helper](../../reference/runtime-helpers/node.js#arc.static). ```javascript // src/http/get-index/index.js @@ -67,7 +132,10 @@ exports.handler = async function http () { ` return { - type: 'text/html', + statusCode: 200, + headers: { + 'Content-Type': 'text/html', + }, body: html.trim() } } diff --git a/src/views/docs/en/guides/frontend/web-sockets.md b/src/views/docs/en/guides/frontend/web-sockets.md index d4864f8f..1bb58270 100644 --- a/src/views/docs/en/guides/frontend/web-sockets.md +++ b/src/views/docs/en/guides/frontend/web-sockets.md @@ -1,7 +1,7 @@ --- title: Adding WebSockets to your app -category: Tutorials -description: Add real-time connections between clients with serverless functions. +category: Frontend +description: Add real-time connections between clients with cloud functions. sections: - Overview - Connecting to your WebSocket @@ -10,10 +10,10 @@ sections: ## Overview -The [`@ws`](/docs/en/reference/arc-pragmas/@ws) primitive creates a WebSocket endpoint and stateless handler functions: +The [`@ws`](/docs/en/reference/arc-pragmas/@ws) primitive creates a WebSocket endpoint and stateless handler functions: - **`connect`**: This handler function is used when a client first connects to your WebSocket API. -- **`disconnect`**: This handler function is used when a client disconnects from your API. +- **`disconnect`**: This handler function is used when a client disconnects from your API. - **`default`**: Used when the route selection expression produces a value that does not match any of the other route keys in your API routes. This can be used, for example, to implement a generic error handling mechanism. These handler functions allow you to hold a long lived state connection between two different endpoints. @@ -105,7 +105,7 @@ The URL lookup code could use environment variables if hardcoding seems rash. // src/http/get-index/get-web-socket-url.js module.exports = function getWS() { - let env = process.env.NODE_ENV + let env = process.env.ARC_ENV let testing = 'ws://localhost:3333' let staging = 'wss:// fixme: these urls are printed after create' let production = 'wss:// fixme: these urls are printed after create' @@ -179,4 +179,4 @@ The `ws-default` Lambda will be the main event bus for web socket events. The `e The `ws-disconnect` Lambda is used to cleanup any records of `event.requestContext.connectionId`. -> 🔭 Find [the example repo on GitHub](https://github.com/architect/arc-example-ws). +> 🔭 Find [the example repo on GitHub](https://github.com/architect/arc-example-ws). diff --git a/src/views/docs/en/guides/frontend/websocket-functions.md b/src/views/docs/en/guides/frontend/websocket-functions.md index 4edefd2a..a6b618f8 100644 --- a/src/views/docs/en/guides/frontend/websocket-functions.md +++ b/src/views/docs/en/guides/frontend/websocket-functions.md @@ -1,5 +1,6 @@ --- title: WebSocket functions +category: Frontend description: WebSockets provide a persistent connection between a client and a server. sections: - Overview @@ -13,11 +14,12 @@ sections: WebSockets provide a persistent connection between a client and a server. When we need persistent real-time data, we create a web socket server and then a connecting client for the two endpoints to exchange messages back-and-forth. -We accomplish this by using [API Gateway](https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-websocket-api-overview.html), which assembles client connections for HTTP functions. WebSockets can now be an event source for Lambda. In other words, you can now add WebSockets to your application without running, maintaining, and operating servers/containers/VMs. +We accomplish this by using [API Gateway](https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-websocket-api-overview.html), which assembles client connections for HTTP functions. WebSockets can now be an event source for Lambda. In other words, you can now add WebSockets to your application without running, maintaining, and operating servers / containers / VMs. Architect provides endpoints pre-configured with Lambda handler functions deployed and ready to iterate, complete with local development and isolated staging and production environments. -### When and why would someone want to use a serverless WebSocket? + +### When and why would someone want to use a cloud function WebSocket? - Your app needs real time push of data; this includes (but is not limited to) web browsers. (Many things speak the `wss` protocol!) - You desire a stateless runtime execution model for your app layer; your app receives WebSocket events, processes them, possibly communicates back to socket clients by posting through an HTTP API, and then terminates execution @@ -31,6 +33,7 @@ Architect provides endpoints pre-configured with Lambda handler functions deploy [Event Payload](#event-payload) [Examples](#examples) + ## Getting started ### Provision @@ -121,6 +124,7 @@ WebSocket functions are always invoked with an `event` payload that contains use - `event.requestContext.apiId` the currently executing WebSocket `apiId` - `event.body` the message payload + ### Publish event payload (JSON) to a WebSocket client Node @@ -130,7 +134,7 @@ let arc = require('@achitect/functions') await arc.ws.send({ id: event.context.connectionId - payload: {action: 'ping'}, + payload: { action: 'ping' }, }) ``` @@ -139,7 +143,7 @@ Ruby ```ruby require 'architect/functions' -Arc::WS.send id: event.context.connectionId, payload: {action: 'ping'} +Arc::WS.send id: event.context.connectionId, payload: { action: 'ping' } ``` Python @@ -147,7 +151,7 @@ Python ```python import arc.ws -arc.ws.send(id=event.context.connectionId, payload={'action': 'ping'}) +arc.ws.send(id=event.context.connectionId, payload={ 'action': 'ping' }) ``` --- @@ -224,11 +228,12 @@ msg.addEventListener('keyup', function(e) { if (e.key == 'Enter') { let text = e.target.value // get the text e.target.value = '' // clear the text - ws.send(JSON.stringify({text})) + ws.send(JSON.stringify({ text })) } }) ``` + ### Send Events from Lambda Send a JSON payload to any `connectionId` from runtime function code. @@ -240,12 +245,8 @@ let arc = require('@architect/functions') exports.handler = async function connected(event) { let id = event.requestContext.connectionId - let payload = {ok: true, ts: Date.now()} - await arc.ws.send({id, payload}) - return {statusCode: 200} + let payload = { ok: true, ts: Date.now() } + await arc.ws.send({ id, payload }) + return { statusCode: 200 } } ``` - - - - diff --git a/src/views/docs/en/guides/get-started/detailed-aws-setup.md b/src/views/docs/en/guides/get-started/detailed-aws-setup.md deleted file mode 100644 index 56bbf8e8..00000000 --- a/src/views/docs/en/guides/get-started/detailed-aws-setup.md +++ /dev/null @@ -1,128 +0,0 @@ ---- -title: Detailed setup -category: Get started -description: Setting up and installing Architect. ---- - -> To work locally all you need is [Node](https://nodejs.org), any additional [supported runtimes](#supported-runtimes) you plan to use, and the [Architect CLI](#install-architect). - -## AWS deployment requirements - -1. [Node](https://nodejs.org) for Architect -2. [Python](https://www.python.org) for the AWS CLI -3. Any additional [supported runtimes](#supported-runtimes) you plan to use -4. [AWS CLI](#aws-cli) -5. [AWS credentials](#credentials) -6. [Architect CLI](#install-architect) - ---- - -### Supported runtimes - -Architect supports the following runtime versions: - -- Node `12.x` using `npm` -- Deno `1.6.x` -- Ruby `2.5` using `bundle` -- Python `3.8` using `pip3` - -> Working locally with the Architect `sandbox` requires target runtimes to be available in your `$PATH`. - -To change the default runtime add it to the `app.arc` under the `@aws` pragma: - -```arc -# Valid runtimes: -# - nodejs12.x (default) -# - deno -# - python3.8 -# - ruby2.5 - -@aws -runtime python3.8 -``` - -> This setting can be overridden on a function-by-function basis with [`config.arc`](/docs/en/reference/config.arc/@aws). - -Architect supports the following standard AWS managed runtimes in live infra, but not while working locally (at present): - -- Go: `1.x`, -- .NET: `2.1` -- Java: `8` - -Architect can support _any custom runtime_ in live infra using either [Lambda Layers](https://docs.aws.amazon.com/lambda/latest/dg/configuration-layers.html) or [Lambda container images](https://docs.aws.amazon.com/lambda/latest/dg/images-create.html). - ---- - -### AWS CLI - -The [AWS Command Line Interface](https://docs.aws.amazon.com/cli/) is the main interface for interacting with all parts of AWS using your computer's terminal. Architect uses the AWS CLI to package and deploy CloudFormation. Follow this guide to [installing the AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) for your preferred environment. - ---- - -### Credentials - -You'll need an Amazon Web Services account and credentials set up on your development machine. If you haven't done it before, here's a useful guide for [Configuring the AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html). - -On \*nix systems AWS Credentials are listed in: - -```bash -~/.aws/credentials -``` - -Or on Windows systems: - -```bash -C:\Users\USER_NAME\.aws\credentials -``` - -If that file doesn't exist, create it, and add something like the following (assuming you have multiple AWS accounts): - -```bash -[default] -aws_access_key_id=xxx -aws_secret_access_key=xxx - -[work] -aws_access_key_id=xxx -aws_secret_access_key=xxx - -[personal] -aws_access_key_id=xxx -aws_secret_access_key=xxx -``` - -You will also need to set a default profile and region with the environment variables - -- `AWS_PROFILE` -- `AWS_REGION` - -To set these variables on Linux, macOS, or UNIX, use export in your `~/.bashrc` (or equivalent shell configuration): - -```bash -export AWS_PROFILE=work -export AWS_REGION=us-west-1 -``` - -Or for Windows, add this to your PowerShell `$profile`: - -```powershell -$env:AWS_PROFILE='work' -$env:AWS_REGION='us-west-1' -``` - -> If you prefer, you can also use: *Control Panel » System » Advanced System Settings » Environment Variables*. - - -### Install Architect - -The following command uses `npm`, the package manager for Node, to install Architect globally. This will allow you to use Architect in any directory on your computer. - -```bash -npm i -g @architect/architect -``` - -Or, if you prefer, you can install Architect locally into a project: - -```bash -npm init @architect ./testapp -``` diff --git a/src/views/docs/en/guides/get-started/quickstart.md b/src/views/docs/en/guides/get-started/quickstart.md deleted file mode 100644 index d362aeed..00000000 --- a/src/views/docs/en/guides/get-started/quickstart.md +++ /dev/null @@ -1,61 +0,0 @@ ---- -title: Quickstart -category: Get started -description: Get started quickly with Architect ---- - -> Architect is the quickest way to build serverless web apps on AWS - -Open your terminal to install `arc`: - -```bash -npm i -g @architect/architect -``` - -Check the version: - -```bash -arc version -``` - -> Protip: run `arc` with no arguments to get help - -## Work locally - -Create a new app: - -```bash -mkdir testapp -cd testapp -arc init -``` - -Kick up the local dev server: - -```bash -arc sandbox -``` -> `Cmd / Ctrl + c` exits the sandbox - -## Deploy to AWS - -Deploy to a `staging` stack: - -```bash -arc deploy -``` -> Protip: create additional `staging` stacks with `--name` - -Ship a `production` stack: - -```bash -arc deploy production -``` - -Or eject to CloudFormation and deploy with the AWS SAM CLI: - -``` -arc deploy --dry-run -sam package --template-file sam.json --output-template-file out.yaml --s3-bucket mybukkit -sam deploy --template-file out.yaml --stack-name MyStack --s3-bucket mybukkit --capabilities CAPABILITY_IAM -``` diff --git a/src/views/docs/en/guides/plugins/create.md b/src/views/docs/en/guides/plugins/create.md new file mode 100644 index 00000000..fe445aeb --- /dev/null +++ b/src/views/docs/en/guides/plugins/create.md @@ -0,0 +1,82 @@ +--- +title: 'create plugins' +category: Plugins +description: 'create lifecycle hook plugins' +--- + +`create` lifecycle hook plugins extend Architect's ability to generate new Lambda handlers as you expand the capabilities of your application. + +`create` plugins execute when users run [`arc create` (aka `arc init`)](/docs/en/reference/cli/init), and in related contexts. + + +## `create.register` + +`create.register` is a string or array of built-in runtime names (e.g. `nodejs18.x`) or aliases (`node`). + +> Note: if your plugin creates a custom runtime with both `set.runtimes` and `create.handlers`, it is unnecessary to use `create.register`. This is because `set.runtimes` informs Architect that the plugin can be expected to deal with the runtime(s) it populates; thus, `create.register` is only necessary if you wish to have a plugin do handler creation for built-in runtimes, or require very deep customization. + + +## `create.handlers` + +`create.handlers` plugins are either async or synchronous functions, and receive a single argument, which is an object containing the following properties: + +| Property | Type | Description | +|-------------|---------|---------------------------------------------------| +| `arc` | object | Raw Architect project object | +| `inventory` | object | [Inventory](./inventory) object | +| `lambda` | object | Properties f the specific Lambda being created | + +`create.handlers` can execute arbitrary commands, write files to disk, and generally take care of whatever is required in preparation for the new Lambda handler. + +Alternately, you can return a single object or an array of objects with the following properties, which will be conveniently written into your handler folder for you: + +| Property | Type | Description | +|-------------|---------|-------------------------------------------| +| `filename` | string | Handler-relative file path to be created | +| `body` | string | Contents of the file to be written | + +> Note: if you do not return one or more files for the Create API to write to your handler directory, make sure you write your files to the `lambda.src` directory. + + +## Examples + +```javascript +// Write two custom files for Node.js Lambda handlers +module.exports = { + create: { + register: 'node', + handler: async ({ arc, inventory, lambda }) => { + return [ + { + filename: 'index.mjs', + body: 'export let handler = async req => req' + }, + { + filename: 'config.arc', + body: '@aws\n' + 'memory 3072' + } + ] + } + } +} +``` + +```javascript +// Run external commands that generate handlers; notes: +// - `create.register` is not necessary because `set.runtimes` is used +// - This example is contrived for brevity; actual `cargo-lambda` behavior differs +let { execSync } = require('child_process') +module.exports = { + set: { + runtimes: () => { + return { name: 'rust', type: 'transpiled' } + } + }, + create: { + handler: async ({ arc, inventory, lambda }) => { + let cmd = `cargo lambda new --http bootstrap` + execSync(cmd, { cwd: lambda.src }) + } + } +} +``` diff --git a/src/views/docs/en/guides/plugins/deploy.md b/src/views/docs/en/guides/plugins/deploy.md new file mode 100644 index 00000000..1dc8f7e8 --- /dev/null +++ b/src/views/docs/en/guides/plugins/deploy.md @@ -0,0 +1,118 @@ +--- +title: 'deploy plugins' +category: Plugins +description: 'deploy lifecycle hook plugins' +--- + +`deploy` lifecycle hook plugins expose functionality to extend the capabilities of [Architect deployments](../../../cli/reference/deploy). + + +## Plugin parameters + +All `deploy` methods accept async or synchronous functions, and receive a single argument, which is an object containing the following properties: + +| Property | Type | Description | +|-------------------|---------|---------------------------------------------------| +| `arc` | object | Raw Architect project object | +| `cloudformation` | object | [CloudFormation template][cfn-json] in JSON | +| `dryRun` | boolean | `true` if `--dry-run` or `--eject` flags are used | +| `inventory` | object | [Inventory](./inventory) object | +| `stackName` | string | [CloudFormation stack][cfn-stack] name | +| `stage` | string | `testing`, `staging` or `production` | + + +## `deploy.start` + +Run arbitrary pre-deploy operations + customize CloudFormation. (This API is a descendant of `@macros` extensions, with added functionality, and within the broader plugins context.) + +Example: + +```javascript +// Do something only for staging deployments +module.exports = { deploy: { + start: async ({ arc, cloudformation, dryRun, inventory, stackName, stage }) => { + if (stage !== 'staging') return + + let config = await getSomeConfig() + cloudformation.Resources.whatever = config + // The returned mutated CloudFormation document will be passed to any other `deploy.start` plugins in sequence + return cloudformation + } +} } +``` + + +## `deploy.services` + +Hook into Architect's [service discovery](/docs/en/reference/runtime-helpers/node.js#arc.services) to create references to custom resources, or populate config data. Note: each returned service object can only store up to 4KB of data (as a string). + +Examples: + +```javascript +// Create some identifiers associated with custom S3 bucket credentials +module.exports = { deploy: { + services: async ({ arc, cloudformation, dryRun, inventory, stackName, stage }) => { + // If the user isn't using this plugin, you can just return + if (!arc['myS3Bucket']) return + + const { inv } = inventory + // Stage will equal 'testing' when run by Sandbox, otherwise will be `staging` or `production` in a `deploy` context + const isLocal = stage === 'testing' + const bucketName = `${inv.app}-newS3Bucket` + // Here we'll return both a string literal (`bucketName`) and resource identifiers to be populated by CloudFormation + return { + bucketName, + accessKey: isLocal ? 'local' : { Ref: 'MyS3BucketCreds' }, + secretKey: isLocal ? 'local' : { 'Fn::GetAtt': [ 'MyS3BucketCreds', 'SecretKey' ] } + } + } +} } +``` + +```javascript +// Make up fo 4KB of configuration data available for your Lambdas to fetch via arc.services() +module.exports = { deploy: { + services: async ({ arc, cloudformation, dryRun, inventory, stage }) => { + let config = await getSomeConfig() + return { config: JSON.stringify(config) } + } +} } +``` + + +## `deploy.target` (in beta) + +Bypass CloudFormation deployment to AWS, and ship the project to an AWS intermediary or provider of your choosing. This endpoint enables Architect to be used to develop and deliver applications without relying solely on AWS CloudFormation. + +> Note: this interface should be considered in beta and subject to change; specifically in relation to how Architect otherwise enacts post-deployment operations (such as static asset publishing). + +Example: + +```javascript +module.exports = { deploy: { + target: async ({ arc, cloudformation, dryRun, inventory, stackName, stage }) => { + if (dryRun) return + + deployToAnotherService({ inventory, stage }) + } +} } +``` + + +## `deploy.end` + +Run arbitrary post-deploy operations. + +Example: + +```javascript +const { rm } = require('fs/promises') +module.exports = { deploy: { + end: async ({ arc, cloudformation, dryRun, inventory, stackName, stage }) => { + await rm(someBuildArtifact) + } +} } +``` + +[cfn-json]: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cloudformation-overview.html#cfn-concepts-templates +[cfn-stack]: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cloudformation-overview.html#cfn-concepts-stacks diff --git a/src/views/docs/en/guides/plugins/hydrate.md b/src/views/docs/en/guides/plugins/hydrate.md new file mode 100644 index 00000000..0e21926d --- /dev/null +++ b/src/views/docs/en/guides/plugins/hydrate.md @@ -0,0 +1,95 @@ +--- +title: 'hydrate plugins' +category: Plugins +description: 'hydrate lifecycle hook plugins' +--- + +`hydrate` lifecycle hook plugins expose functionality to extend the capabilities of Hydrate, Architect's tool for managing dependencies and shared code. + + +## `hydrate.copy` (experimental) + +> Note: this API is currently experimental and [collecting feedback here](https://github.com/architect/architect/issues/1369), please let us know what you think! + +Copy arbitrary files or folders into your project's Lambdas' dependency directories (e.g. `./src/$pragma/$name/node_modules/` if Node.js, or `./src/$pragma/$name/vendor` for Python, Ruby, etc.), and run arbitrary operations. + +`hydrate.copy` runs after all dependency installation and shared file operations are complete, ensuring that any filesystem mutations will be present in final deployment artifacts. + +`hydrate.copy` plugins are either async or synchronous functions, and receive a single argument, which is an object containing the following properties: + +| Property | Type | Description | +|-------------|-----------------|------------------------------------ | +| `arc` | object | Raw Architect project object | +| `inventory` | object | [Inventory](./inventory) object | +| `copy` | async function | [Copy file(s)](#copy-function) | + + +### Copy function + +`hydrate.copy` plugins are provided an async `copy` function capable of copying one or more files or folders into all Lambdas in the project (including Lambdas created by plugins with [`set` methods](./set)). Invocations accept the following properties: + +| Property | Type | Description | +|-----------|-------------------|---------------------------------------------------------------| +| `source` | string (required) | Relative or absolute path of the source file or folder | +| `target` | string | Relative path of the target file or folder within each Lambda | + + +### Source paths + +The `source` path (required) is a relative or absolute file path of the source file or folder to be copied into your Lambdas. + +> Note: as of right now, `source` must be found somewhere within the project directory. This may change in the future based on feedback. + + +### Target paths + +The `target` path (optional) is a relative path of the being copied into your Lambdas. If not provided, `hydrate.copy` will add the file or folder to the root of your dependencies directory. + +For example, if you specify a `target` of `hi/there.json`, the following file will be written: `./src/$pragma/$name/$node_modules_or_vendor/hi/there.json` + +If you specify a `source` of `howdy.json` and **do not** specify a `target`, the following file will be written: `./src/$pragma/$name/$node_modules_or_vendor/howdy.json` + + +## Examples + +```javascript +// Copy a single file or folder to the same name +module.exports = { hydrate: { + copy: async ({ arc, inventory, copy }) => { + await copy({ + source: 'project/relative/path/file.txt', + target: 'foo/file.txt', // Copied to `./src/$pragma/$name/$node_modules_or_vendor/foo/file.txt` + }) + } +} } +``` + +```javascript +// Copy multiple files or folders +module.exports = { hydrate: { + copy: async ({ arc, inventory, copy }) => { + await copy([ + { + source: 'project/relative/path/file.txt', + target: 'file.txt', // Copied to `./src/$pragma/$name/$node_modules_or_vendor/foo/file.txt` + }, + { + source: 'project/relative/path/subfolder', + target: 'some-subfolder', // Copied to `./src/$pragma/$name/$node_modules_or_vendor/some-subfolder` + }, + ]) + } +} } +``` + +```javascript +// Assume the target is the root dependency dir +module.exports = { hydrate: { + copy: async ({ arc, inventory, copy }) => { + await copy({ + source: 'project/relative/path/file.txt', + // Copied to `./src/$pragma/$name/$node_modules_or_vendor/file.txt` + }) + } +} } +``` diff --git a/src/views/docs/en/guides/plugins/inventory.md b/src/views/docs/en/guides/plugins/inventory.md new file mode 100644 index 00000000..d002bf65 --- /dev/null +++ b/src/views/docs/en/guides/plugins/inventory.md @@ -0,0 +1,131 @@ +--- +title: Architect Inventory +category: Plugins +description: Information about the Architect Inventory format +--- + +All Architect plugins receive an `inventory` object that enumerates all the resources in a project. So what is the Architect Inventory format, anyway? + + +## How Architect Inventory works + +Every time Architect initiates a command, your project manifest is parsed and interpreted into the internal Architect Inventory format that serves as the single source of truth for your project's state. Whenever your project resources or configuration changes in any way, it is reflected in the Inventory. + +For example, let's say your project manifest looks like this: + +```arc +@http +get /foo +``` + +Inventory responsible for enumerating the HTTP pragma's `get /foo` route, and its current configuration (e.g. Node.js version, memory, timeout, etc.) based on Architect's defaults, any project-level defaults you may have set, and any per-Lambda configuration via `config.arc`. + +Inventory is also responsible for lower-level project configuration. For example: the above project does not define a handler at the root of your API; Inventory automatically creates a configuration to ensure something exists to handle requests to your root (so your users don't get a `5xx` error when visiting the root of your site). + +In the same vein, Inventory would also set up default project options for static assets in anticipation of their use by your HTTP route(s). + + +## The Inventory format + +The Inventory format represents every possible setting, option, and configuration in Architect. Since it isn't practical to document all that here. We'll cover the basics to help you navigate it. + +The basics: + +- Plugins are passed both `arc` (the raw manifest in the parsed Architect format) and `inventory` (the Architect Inventory format, probably what you'll most often be using) + - The `arc` format is still helpful to have for inspecting custom pragmas. For example, if you wanted your users to configure your plugin with a custom `@my-plugin-config` pragma, that would only appear in `arc`, and not in `inventory` +- The `arc` + `inventory` objects passed to plugins are frozen copies; only Architect is responsible for changing or updating current `inventory` +- `inventory` contains two objects: + - [`inv`](#inv): the current project Inventory + - [`get`](#get): a convenient set of resource getter methods +- With limited exceptions, `inventory` values of `null` represent the lack of a userland configuration; anything non-`null` almost always represents something that was user-configured (or inferred by configuration) + + +## `inv` + +The `inv` object contains a property for each Architect [pragma](/docs/en/get-started/project-manifest#more-on-app.arc), and a handful of meta properties and resource collections. Here is a basic example `inv` object: + +```javascript +inventory.inv = { + _arc: { /* Architect metadata: current version, pragmas, stage, etc. */ }, + _project: { /* Project metadata: cwd, preferences, env vars, etc. */ }, + ...pragmas, /* Properties for each pragma: aws, http, tables, etc. */, + lambdaSrcDirs: [ /* Array of the source directory of each project Lambda */ ], + lambdasBySrcDir: { /* Object containing each Lambda, named by its source directory */ }, +} +``` + +Each pragma's Lambdas may contain different properties and configurations; for example, `@scheduled` Lambdas have a `rate` or `cron` property, while `@http` Lambdas have a `method` and `path` property. + +Here's an example of what the above `get /foo` Lambda object might look like: + +```javascript +inventory.inv.http = [ + { + name: 'get /foo', + method: 'get', + path: '/foo', + // Lambda configuration + config: { + timeout: 10, + memory: 3008, + runtime: 'nodejs20.x', + architecture: 'arm64', + handler: 'index.handler', + state: 'n/a', + concurrency: 'unthrottled', + layers: [], + policies: [], + shared: true, + env: true, + }, + // Other Architect config + paths and files + src: '/your/project/path/src', + handlerFile: '/your/project/path/src/http/get-foo/index.js', + handlerMethod: 'handler', + handlerModuleSystem: 'esm', + configFile: '/your/project/path/src/http/get-foo/config.arc', + pragma: 'http' + } +] +``` + +We encourage you to explore the Inventory format on your own; a quick way to inspect your project's Inventory would be to just log it out from a plugin like so: + +```javascript +module.exports = { sandbox: { start: function ({ inventory }) { + console.dir(inventory.inv, { depth: null }) +} } } +``` + +> Note: remember, [`set` plugins](./set) run before the rest of the Inventory, and thus [receive incomplete Inventory objects](./set#caveats). + + +## `get` + +The `get` object contains a variety of helpful methods for querying the inventory. For example, say a project had the following manifest: + +```arc +@http +get /foo +post /bar +get /fiz +put /buz + +@static +fingerprint true +``` + +Now say you wanted to get the Inventory Lambda object of `@http get /foo`. You could use `find()` on the `http` array to search for the `name` property of `get /foo` (e.g. `inventory.inv.http.find(({ name }) => name === 'get /foo')`). Or you can use `inventory.get`: + +```javascript +console.log(get.http('get /foo')) // Your `get /foo` Lambda +console.log(get.http('get /bar')) // null +``` + +`get` also works on settings pragmas, like `@static` or `@aws`: + +```javascript +console.log(get.static('fingerprint')) // true +console.log(get.static('prune')) // null +console.log(get.aws('region')) // 'us-west-2' +``` diff --git a/src/views/docs/en/guides/plugins/overview.md b/src/views/docs/en/guides/plugins/overview.md new file mode 100644 index 00000000..6f273aa9 --- /dev/null +++ b/src/views/docs/en/guides/plugins/overview.md @@ -0,0 +1,117 @@ +--- +title: Plugins overview +category: Plugins +description: Overview of Architect's plugin API +--- + +Architect's plugin API exposes [workflow lifecycle hooks][hooks] (such filesystem events in the [Sandbox][sandbox]) and interfaces for [generating cloud resources][setters] (such as custom Lambdas, or environment variables). + +This document assumes some existing knowledge of how Architect works, including the [project manifest][manifest], [deterministic deployment][deployment] to AWS via CloudFormation, etc. + + +## Finding & installing plugins + +[Architect maintains a list of officially supported and community developed plugins](https://github.com/architect/plugins); npm is also a great place to [find plugins](https://www.npmjs.com/search?q=arc-plugin-) (and [legacy macros][macros]). + +To install a plugin, add a [`@plugins` pragma][plugins] to your [project manifest][manifest]. For example, if you wanted to `npm install` and use both `@architect/plugin-typescript` and `arc-macro-cors`, this is what you'd add to your manifest: + +```arc +@plugins +architect/plugin-typescript # note: leading @ must be removed from namespaced packages +arc-macro-cors +``` + +You can also use unpublished local plugins like so: + +```arc +@plugins +my-private-plugin # loads from `src/plugins/my-private-plugin[.[m]js|/index.[m]js]` +another-private-plugin # loads from `foo/index.js` + src foo +``` + + +## Authoring plugins + +Architect provides a suite of workflows, conventions, and optimized defaults for building excellent [Functional Web Apps][fwa] with AWS. Architect plugins enable developers to extend (or override) this functionality in a variety of ways with interfaces into both workflows and resource creation. + +To create a fresh plugin, you can run `npx arc create --plugin my-plugin-name` + +Learn more about [hooks for workflow lifecycles][hooks] and [cloud resource generation][setters] below. + + +### Workflow hooks + +Workflow hooks enable developers to extend Architect workflows and hook into the various Architect CLI commands. + +- [`deploy`](./deploy) - hooks for [the `deploy` command][deploy] + - [`start`](./deploy#deploy.start) - run arbitrary pre-deploy operations like adding custom resources or modifying first-class Architect-created resources by customizing CloudFormation (formerly `@macros`) + - [`services`](./deploy#deploy.services) - hook into Architect's service discovery to create runtime references to custom resources, or populate config data + - [`target`](./deploy#deploy.target) - bypass CloudFormation deployment to AWS, and ship the project to an AWS intermediary + - [`end`](./deploy#deploy.end) - run arbitrary post-deploy operations +- [`create`](./create) - hooks for [the `init` command][init] + - [`register`](./create#create.register) - register runtimes to create handlers for + - [`handlers`](./create#create.handlers) - dynamically generate new runtime handlers as you expand your project +- [`hydrate`](./hydrate) - hooks for [the `hydrate` command][hydrate] + - [`copy`](./hydrate#hydrate.copy) - copy files and folders during [dependency hydration][hydrate] +- [`sandbox`](./sandbox) - hooks for [the `sandbox` command][sandbox] + - [`start`](./sandbox#sandbox.start) - run arbitrary operations during [Sandbox][sandbox] startup + - [`watcher`](./sandbox#sandbox.watcher) - act on project filesystem events (e.g. `src/http/get-foo/auth.js` → `updated`) + - [`end`](./sandbox#sandbox.end) - run arbitrary operations during [Sandbox][sandbox] shutdown + + +### Resource setters + +Resource setters are small, synchronous methods that enable the rapid programmatic creation of various Architect resources, ranging from HTTP routes, to environment variables, to custom runtimes. + +Any Lambdas or resources defined by setters is treated by Architect as a first-class primitive, as though it was built into Architect itself. + +- [Pragmas](/docs/en/guides/plugins/set#pragmas) + - [`events`](/docs/en/guides/plugins/set#set.events) - register async events + - [`http`](/docs/en/guides/plugins/set#set.http) - register HTTP routes + - [`queues`](/docs/en/guides/plugins/set#set.queues) - register async event queues + - [`scheduled`](/docs/en/guides/plugins/set#set.scheduled) - register scheduled events + - [`tables-streams`](/docs/en/guides/plugins/set#set%5B'tables-streams'%5D) - register DynamoDB event streams + - [`ws`](/docs/en/guides/plugins/set#set.ws) - register WebSocket routes +- [Resources](/docs/en/guides/plugins/set#resources) + - [`env`](/docs/en/guides/plugins/set#set.env) - register environment variables + - [`customLambdas`](/docs/en/guides/plugins/set#set.customLambdas) - register bare Lambdas without a pre-associated event source + - [`runtimes`](/docs/en/guides/plugins/set#set.runtimes) - register custom runtimes + + +## Publishing plugins + +Should you opt to publish your plugin for others to use – and we hope you will! – they should be published to `npm` with the following naming convention: `arc-plugin-{your unique plugin name}`. + +Official plugins by Architect maintainers are published under the `@architect` org as `@architect/plugin-{plugin name}`. + + +## Macros + +Long-time Architect users may be familiar with macros (`@macros` – extensions that enable customization of CloudFormation at deploy time). Macros are forward-compatible with plugins, but are no longer the preferred way of customizing CloudFormation. + +**Existing macros will continue to work indefinitely; you do not need to update them to the new plugins API.** However, the macros API is no longer receiving updates, whereas the [`deploy.start` plugin API](./deploy) has new features that you may find helpful. + +[Learn more about porting existing macros to plugins](./porting-macros-to-plugins). + +If you have existing macros, they can live side by side in their respective pragma (so long as plugins and macros do not have any conflicting names): + +```arc +@plugins +my-private-plugin + +@macros +my-private-macro +``` + +[hooks]: #workflow-hooks +[sandbox]: ../../reference/cli/sandbox +[plugins]: ../../reference/project-manifest/plugins +[setters]: #resource-setters +[manifest]: ../../get-started/project-manifest +[deployment]: ../developer-experience/deployment +[macros]: #macros +[fwa]: https://fwa.dev +[hydrate]: ../../reference/cli/hydrate +[deploy]: ../../reference/cli/deploy +[init]: ../../reference/cli/init diff --git a/src/views/docs/en/guides/plugins/porting-macros-to-plugins.md b/src/views/docs/en/guides/plugins/porting-macros-to-plugins.md new file mode 100644 index 00000000..0a71f827 --- /dev/null +++ b/src/views/docs/en/guides/plugins/porting-macros-to-plugins.md @@ -0,0 +1,78 @@ +--- +title: 'Porting @macros to @plugins' +category: Plugins +description: 'How-to port existing @macros to @plugins' +--- + +With the introduction of Architect 10, [`@macros`](/docs/en/reference/project-manifest/macros) were superseded by [`@plugins`](/docs/en/reference/project-manifest/plugins). It is important to note that [`@macros` will not cease to work, and will not be deprecated](/docs/en/guides/plugins/deploy#deploy.start). You do not need to port any macros you may have written for Architect. + +However, there are a number of reasons that you may want to port your existing macro to a plugin: + +- The `@macros` interface is also no longer actively maintained, whereas `@plugins` have numerous improvements and will continue to receive future upgrades +- The `@plugins` API surface is significantly more expansive and can do significantly more than `@macros` +- `@macros` and `@plugins` share the same logical namespace, so you may need to ensure there is not a name conflict + + +## How-to port your macro + +### Before + +In this example, we'll assume your project uses a private macro located at `src/macros/extend-arc/index.js`, and a project manifest that looks like so: + +```arc +@app +my-app + +@macros +extend-arc +``` + +The `@macros` API calls for a single function (async or sync) to be exported, and accepts the following arguments: + +```javascript +// src/macros/extend-arc/index.js +module.exports = async function macro (arc, cloudformation, stage) { + // modify cloudformation.Resources here + return cloudformation +} +``` + + +### After + +The [`deploy.start` plugin API](/docs/en/guides/plugins/deploy#deploy.start), which is the direct successor to the `@macros` interface, is pretty close. To port the above macro, follow these steps: + +1. Move your macro to `src/plugins/extend-arc/index.js` +2. Update your project manifest from `@macros` to `@plugins`: + +```arc +@app +my-app + +@plugins +extend-arc +``` + +3. Update your function to the new semantics: +```javascript +// src/plugins/extend-arc/index.js +module.exports = { + deploy: { + start: async function ({ arc, cloudformation, dryRun, inventory, stage }) { + // modify cloudformation.Resources here + return cloudformation + } + } +} +``` + +As you may notice, once inside the function, mutations to the `cloudformation` document are passed back in the very same way. (Similarly, returning early does not mutate `cloudformation`.) + +The most important change to note is that your macro, which accepted positional arguments (that you may or may not have named as above) must now be exported as `deploy.start`, and has accepts a single object with named properties. + +Otherwise, they are functionally identical. That's it! + + +### Help + +If you have any questions or issues with your port to plugins, please don't hesitate to [chat with us in Discord](https://discord.gg/y5A2eTsCRX), we'd love to hear from you. diff --git a/src/views/docs/en/guides/plugins/sandbox.md b/src/views/docs/en/guides/plugins/sandbox.md new file mode 100644 index 00000000..900892f7 --- /dev/null +++ b/src/views/docs/en/guides/plugins/sandbox.md @@ -0,0 +1,105 @@ +--- +title: 'sandbox plugins' +category: Plugins +description: 'sandbox lifecycle hook plugins' +--- + +`sandbox` lifecycle hook plugins expose functionality to extend the capabilities of Sandbox, Architect's local development environment. + + +## `sandbox.start` + +Run arbitrary operations during Sandbox startup. At the time of `sandbox.start` execution, all core Sandbox services are guaranteed to be running. + +> This method is similar to [`@sandbox-start` scripts](/docs/en/reference/configuration/local-preferences#%40sandbox-start), but authored in JS, and controlled entirely via plugin. If in doubt, we suggest using `sandbox.start` over [`@sandbox-start` scripts. + +`sandbox.start` plugins are either async or synchronous functions, and receive a single argument, which is an object containing the following properties: + +| Property | Type | Description | +|-------------|-----------------|------------------------------------ | +| `arc` | object | Raw Architect project object | +| `inventory` | object | [Inventory](./inventory) object | +| `invoke` | async function | [Invoke a Lambda](#invoke-function) | + +Example: + +```javascript +let { readFile } = require('fs/promises') +// Invoke a scheduled event whenever its business logic changes +module.exports = { sandbox: { + start: async ({ arc, inventory, invoke }) => { + await loadDatabaseMock() + await seedLocalDatabase() + console.log('Seeded database!') + } +} } +``` + + +## `sandbox.watcher` + +Run arbitrary operations on filesystem events within your project. By default, the watcher ignores changes to `vendor`, `node_modules`, `.git`, and other such files. + +`sandbox.watcher` plugins are either async or synchronous functions, and receive a single argument, which is an object containing the following properties: + +| Property | Type | Description | +|-------------|-----------------|-----------------------------------------------------| +| `filename` | string | Absolute path of the project file that was changed | +| `event` | string | One of `add`, `update`, or `remove` | +| `inventory` | object | [Inventory](./inventory) object | +| `invoke` | async function | [Invoke a Lambda](#invoke-function) | + + +## `sandbox.end` + +Run arbitrary operations on Sandbox shutdown. `sandbox.end` plugins are executed just prior to all core Sandbox services being shut down. + +`sandbox.end` plugins are either async or synchronous functions, and receive a single argument, which is an object containing the following properties: + +| Property | Type | Description | +|-------------|-----------------|-------------------------------------| +| `arc` | object | Raw Architect project object | +| `inventory` | object | [Inventory](./inventory) object | +| `invoke` | async function | [Invoke a Lambda](#invoke-function) | + + +## Invoke function + +Sandbox plugins are provided an async `invoke` function capable of arbitrarily executing any Lambda in the project (including Lambdas created by plugins with [`set` methods](./set)). Invocations must include the following properties: + +| Property | Type | Description | +|-----------|---------|-------------------------------------------------------------| +| `pragma` | string | The pragma of the Lambda, without `@` (e.g. `http`) | +| `name` | string | Inventory name of the Lambda being invoked (e.g. `get /foo` | +| `payload` | object | Payload to invoke with; an empty object (`{}`) is allowed | + +Examples: + +Assuming an `app.arc` file with the following: + +```arc +@app +my-app + +@scheduled +my-scheduled-event rate(1 day) +``` + +Your `my-scheduled-event` Lambda could be invoked locally on demand like so: + +```javascript +let { readFile } = require('fs/promises') +// Invoke a scheduled event whenever its business logic changes +module.exports = { sandbox: { + watcher: async ({ filename, event, inventory, invoke }) => { + if (filename.includes('src/scheduled/my-scheduled-event')) { + let rawPayload = await readFile('test/fixtures/event-payload.json') + invoke({ + pragma: 'scheduled', + name: 'my-scheduled-event', + payload: JSON.parse(rawPayload), + }) + } + } +} } +``` diff --git a/src/views/docs/en/guides/plugins/set.md b/src/views/docs/en/guides/plugins/set.md new file mode 100644 index 00000000..f8397d3d --- /dev/null +++ b/src/views/docs/en/guides/plugins/set.md @@ -0,0 +1,707 @@ +--- +title: 'set plugins' +category: Plugins +description: 'set cloud resource generator plugins' +--- + +`set` plugins generate various kinds of resources for Architect projects. Resource setters are small, synchronous methods that can create many common kinds of resources, from HTTP routes, to environment variables, to custom runtimes. + +Any Lambdas or resources defined by a `set` plugin is treated by Architect as a first-class primitive. For example: if you build a plugin that creates a route with `set.http`, Architect would treat that route as though the user had actually added it to their [project manifest](/docs/en/get-started/project-manifest)'s [`@http` pragma](/docs/en/reference/project-manifest/http). + + +## Caveats + +Unlike [workflow lifecycle plugins](./overview#workflow-hooks), which execute only when needed, all `set` plugins must execute every time Architect (or any of its modules) run. Thus, they are expected to be synchronous and fast. + +Because of this, we advise against such things as filesystem reads and writes from within `set` plugins. Definitely do not start services, or run large, CPU intensive operations from within `set` plugins. + +Additionally, it is worth noting that `set` plugins are among the very first things to run in any Architect execution. Because they are run before the rest of the project been enumerated, they are not passed a complete [Inventory](./inventory) object. If your plugin requires knowledge of your project, please access the `arc` property. + + +## Plugin parameters + +All `set` methods are synchronous functions, and receive a single argument, which is an object containing the following properties: + +| Property | Type | Description | +|-------------|---------|---------------------------------------------------| +| `arc` | object | Raw Architect project object | +| `inventory` | object | Partial [Inventory](./inventory) object | + + +## Valid returns + +All `set` methods can return a single resource object, and those that create resources (like `set.http`) can return an array of resource objects. + +There is no limit to the number of resources a `set` plugin can return, however AWS does have limits on the number of resources in a CloudFormation deployment (and a hard cap on the size of a given CloudFormation document). + +Generated resources that require a `src` property accept an absolute or relative file path. Additionally, file paths will be automatically platform normalized (so you do not have to use `path.join()` for other platforms if you're publicly publishing your plugin). + +By default, Lambdas created by the `set` API are assumed to run the latest version of Node.js ([unless configured otherwise](#lambda.config)) + +--- + +# Pragmas + +## `set.events` + +Register async events (as in the [`@events`][events] pragma). Return a single object or an array of objects with the following properties: + +| Property | Type | Description | +|-----------|---------|---------------------------------------------------------------------| +| `name` | string | Event name (follows [`@events` syntax][events]) | +| `src` | string | Absolute or relative file path to the handler | +| `required`| boolean | Fail if plugin conflicts with project manifest (defaults to `false`)| + +Example: + +```javascript +// Return a single async event +module.exports = { + set: { + events ({ arc, inventory }) { + return { + name: 'an-async-event', + src: __dirname + '/handler' // Points to a handler dir inside the plugin + } + } + } +} +``` + + +## `set.http` + +Register HTTP routes (as in the [`@http`][htp] pragma). Return a single object or an array of objects with the following properties: + +| Property | Type | Description | +|-----------|---------|---------------------------------------------------------------------| +| `method` | string | HTTP method (follows [`@http` syntax][http]) | +| `path` | string | HTTP path (follows [`@http` syntax][http]) | +| `src` | string | Absolute or relative file path to the handler | +| `required`| boolean | Fail if plugin conflicts with project manifest (defaults to `false`)| + +Example: + +```javascript +// Return multiple HTTP routes +module.exports = { + set: { + http ({ arc, inventory }) { + let src = __dirname + '/handler' + // Multiple Lambdas can use the same handler + return [ + { method: 'get', path: '/foo', src }, + { method: 'put', name: '/bar', src } + ] + } + } +} +``` + + +## `set.proxy` + +Set URLs for API Gateway to forward all requests by default; individual routes can be overridden by `@http` / `set.http`. Return a single object with the following properties: + +| Property | Type | Description | +|-------------|---------|-------------------------------------------------------------| +| `testing` | string | URL to forward requests to from the testing environment | +| `staging` | string | ... the same, but for the staging environment | +| `production`| string | ... the same, but for the production environment | + +Example: + +```javascript +module.exports = { + set: { + proxy ({ arc, inventory }) { + return { + testing: 'https://testing-url.com', + staging: 'https://staging-url.com', + production: 'https://production-url.com', + } + } + } +} +``` + + +## `set.queues` + +Register async event queues (as in the [`@queues`][queues] pragma). Return a single object or an array of objects with the following properties: + +| Property | Type | Description | +|-----------|---------|---------------------------------------------------------------------| +| `name` | string | Event name (follows [`@queues` syntax][queues]) | +| `src` | string | Absolute or relative file path to the handler | +| `required`| boolean | Fail if plugin conflicts with project manifest (defaults to `false`)| + +Example: + +```javascript +// Return a single async event queue +module.exports = { + set: { + queues ({ arc, inventory }) { + return { + name: 'a-queue', + src: __dirname + '/handler' // Points to a handler dir inside the plugin + } + } + } +} +``` + + +## `set.scheduled` + +Register scheduled event (as in the [`@scheduled`][scheduled] pragma). Return a single object or an array of objects with the following properties: + +| Property | Type | Description | +|-----------|---------|---------------------------------------------------------------------| +| `name` | string | Event name (follows [`@scheduled` syntax][scheduled]) | +| `rate` | string | [Rate expression][sched-expr], cannot be used with `cron` property | +| `cron` | string | [Cron expression][sched-expr], cannot be used with `rate` property | +| `src` | string | Absolute or relative file path to the handler | +| `required`| boolean | Fail if plugin conflicts with project manifest (defaults to `false`)| + +> Note: unlike in `@scheduled` pragma use, `rate` + `cron` properties should not be returned in parenthesis. + +Example: + +```javascript +// Return two scheduled events: one using rate syntax, and one using cron syntax +module.exports = { + set: { + scheduled ({ arc, inventory }) { + let src = __dirname + '/handler' + return [ + { + name: 'scheduled-using-rate', + rate: '1 day', + src, + }, + { + name: 'scheduled-using-cron', + cron: '15 10 * * ? *', + src, + } + ] + } + } +} +``` + +## `set.shared` + +Set a custom source path for Architect's code sharing system ([`@shared`][shared]). Return a single object with the following property: + +| Property | Type | Description | +|-----------|---------|-----------------------------------------------------------| +| `src` | string | Absolute or relative file path to the shared code path | +| `required`| boolean | Enforce the existence of `src` folder (defaults to `false`) | + +Example: + +```javascript +module.exports = { + set: { + shared ({ arc, inventory }) { + return { + src: __dirname + '/shared-libs' + } + } + } +} +``` + + +## `set.static` + +Modify settings for static asset handling. Return a single object with the following properties: + +| Property | Type | Description | +|---------------|---------|-----------------------------------------------------------------| +| `compression` | string | Enable static asset compression; `true` or `br` compresses with brotli, or `gzip` compresses with gzip | +| `fingerprint` | boolean | Enable Architect's static asset fingerprinting; also accepts `external` (string) to assume assets have filenames fingerprinted by another tool or system | +| `folder` | string | Relative file path of static asset dir (defaults to `public`) | +| `ignore` | array | File names or paths within `folder` to ignore during deployment | +| `prefix` | string | Path prefix for publishing assets to S3 | +| `prune` | boolean | Enable Architect to prune S3 files not found within `folder` | +| `spa` | boolean | Enable single page app (SPA) mode (defaults to `false`) | + +Example: + +```javascript +module.exports = { + set: { + static ({ arc, inventory }) { + return { + fingerprint: true, + folder: 'static-assets', + } + } + } +} +``` + + +## `set.tables` + +Register DynamoDB tables (as in the [`@tables`][tables]) pragma). Return a single object or an array of objects with the following properties: + +| Property | Type | Required | Description | +|---------------------|---------|-----------|-----------------------------------------------------| +| `name` | string | Yes | Table name (follows [`@tables` syntax][tables]) | +| `partitionKey` | string | Yes | Partition key name | +| `partitionKeyType` | string | Yes | Partition key type (`string` or `number`) | +| `sortKey` | string | No | Sort key name | +| `sortKeyType` | string | No | Sort key type (`string` or `number`) | +| `stream` | boolean | No | Enable DynamoDB stream (see [`@tables-streams`][tables-streams]) | +| `ttl` | string | No | Time-to-live timestamp column to expire records +| `encrypt` | boolean | No | Enable server-side encryption with AWS KMS | +| `pitr` | boolean | No | Enable point-in-time recovery | + +Example: + +```javascript +// Return a single table +module.exports = { + set: { + tables ({ arc, inventory }) { + return { + name: 'a-table', + partitionKey: 'id', + partitionKeyType: 'string', + // These are all optional + sortKey: 'ts', + sortKeyType: 'number', + pitr: true, + } + } + } +} +``` + + +## `set['tables-indexes']` + +Register DynamoDB table indexes (as in the [`@tables-indexes`][tables-indexes]) pragma). Return a single object or an array of objects with the following properties: + +| Property | Type | Required | Description | +|---------------------|---------|-----------|-----------------------------------------------------| +| `name` | string | Yes | Table name (follows [`@tables` syntax][tables]) | +| `partitionKey` | string | Yes | Partition key name | +| `partitionKeyType` | string | Yes | Partition key type (`string` or `number`) | +| `sortKey` | string | No | Sort key name | +| `sortKeyType` | string | No | Sort key type (`string` or `number`) | +| `indexName` | boolean | No | Custom index name | + +Example: + +```javascript +// Return a single table index +module.exports = { + set: { + 'tables-indexes': ({ arc, inventory }) => { + return { + name: 'a-table', + partitionKey: 'secondary-index', + partitionKeyType: 'string', + indexName: 'my-custom-index-name', // Optional! + } + } + } +} +``` + + +## `set['tables-streams']` + +Register DynamoDB event streams (as in the [`@tables-streams`][tables-streams]) pragma). Return a single object or an array of objects with the following properties: + +| Property | Type | Description | +|-----------|---------|---------------------------------------------------------------------| +| `name` | string | Event name (follows [`@tables-streams` syntax][tables-streams]) | +| `table` | string | Logical DynamoDB table name (as in your project manifest) | +| `src` | string | Absolute or relative file path to the handler | +| `required`| boolean | Fail if plugin conflicts with project manifest (defaults to `false`)| + +Example: + +```javascript +// Return a single table stream +module.exports = { + set: { + 'tables-streams': ({ arc, inventory }) => { + return { + name: 'a-table-stream-event', + table: 'my-logical-table-name', + src: __dirname + '/handler' // Points to a handler dir inside the plugin + } + } + } +} +``` + + +## `set.views` + +Set a custom source path for Architect's frontend views code sharing system ([`@views`][views]). Return a single object with the following property: + +| Property | Type | Description | +|-----------|---------|-----------------------------------------------------------| +| `src` | string | Absolute or relative file path to the views code path | +| `required`| boolean | Enforce the existence of `src` folder (defaults to `false`) | + +Example: + +```javascript +module.exports = { + set: { + views ({ arc, inventory }) { + return { + src: 'app/views' + } + } + } +} +``` + + +## `set.ws` + +Register WebSocket routes (as in the [`@ws`][ws] pragma). Return a single object or an array of objects with the following properties: + +| Property | Type | Description | +|-----------|---------|---------------------------------------------------------------------| +| `name` | string | Route name (follows [`@ws` syntax][ws]) | +| `src` | string | Absolute or relative file path to the handler | +| `required`| boolean | Fail if plugin conflicts with project manifest (defaults to `false`)| + +> Note: WebSockets is required to have three default routes (`$connect`, `$disconnect`, `$default`), which Architect populates with the addition of the `@ws` pragma. If the consumer of your plugin does not specify `@ws` in their manifest, using `set.ws` will infer it for them; you should not attempt to return any of the default routes in your `set.ws` plugin. + +Example: + +```javascript +// Return a single WebSocket route +module.exports = { + set: { + ws ({ arc, inventory }) { + return { + name: 'refresh', + src: __dirname + '/handler' // Points to a handler dir inside the plugin + } + } + } +} +``` + + +--- + +# Resources + + +## `set.env` + +Register environment variables for all Lambdas. To create an environment variable for all Lambdas, return an object with names and values. To create an environment variable specific to Architect's built in `testing`, `staging`, and `production` environments, return an object containing one or more of those properties, each containing an object with names and value. + +> Note: if an object or array is passed as a value, the `set.env` API will automatically JSON-serialize it into your environment variable. + +Examples: + +```javascript +// Return an environment variable for all Lambdas +module.exports = { + set: { + env ({ arc, inventory }) { + return { + API_SECRET: process.env.API_SECRET // Handy for exporting secrets in CI/CD + } + } + } +} +``` + +```javascript +// Return a different environment variables for different stages +module.exports = { + set: { + env ({ arc, inventory }) { + return { + testing: { + API_SECRET: 'sample-key' + }, + staging: { + API_SECRET: process.env.API_SECRET + }, + production: { + API_SECRET: process.env.API_SECRET + }, + } + } + } +} +``` + + +## `set.customLambdas` + +Register bare Lambdas without a pre-associated event source. `set.customLambdas` pairs nicely with [`deploy.start`](./deploy#deploy-start), where you can customize a custom Lambda's event source in CloudFormation. Return a single object or an array of objects with the following properties: + +| Property | Type | Description | +|-----------|---------|-------------------------------------------------| +| `name` | string | Bare Lambda name | +| `src` | string | Absolute or relative file path to the handler | + +Example: + +```javascript +// Return a single async event +module.exports = { set: { + customLambdas: ({ arc, inventory }) => { + return { + name: 'a-custom-lambda', + src: __dirname + '/handler' // Points to a handler dir inside the plugin + } + } +} } +``` + + +## `set.runtimes` + +Register custom runtimes for Lambdas. Return a single object or an array of objects. Each runtime type has its own set of requirements; `transpiled` and `compiled` are currently supported, [`interpeted` is coming soon](https://github.com/architect/architect/issues/1295). + +No matter which runtime is designated, the source for each Lambda lives within the typical Architect project structure, whether using Architect conventions (e.g. `src/http/$method-$path`) or custom `src` paths. + +> Note: Architect's built in [shared code affordances](/docs/en/guides/developer-experience/sharing-code) are permanently disabled for transpiled and compiled handlers and their build artifacts. + + +### `transpiled` + +Lambdas with a `transpiled` runtime (such as those using TypeScript) undergo a transpilation step that publishes build artifacts to separate dist folder; artifacts in the dist folder are, as one would expect, composed in a corresponding interpreted language (e.g. JavaScript). + +For example, using [Architect TypeScript](https://github.com/architect/plugin-typescript), the `get /foo` handler is authored in `src/http/get-foo/index.ts`, and automatically transpiled to (and run / deployed from) `.build/http/get-foo/index.js`. + +A plugin setting a `transpiled` runtime must return an object (or array of objects) containing the following properties: + +| Property | Type | Required | Description | +|---------------|---------|-----------|-----------------------------------------------| +| `name` | string | Yes | Custom runtime name | +| `type` | string | Yes | Must be `transpiled` | +| `baseRuntime` | string | Yes | Lambda [runtime identifier or alias][runtime] | +| `build` | string | No | Relative build dir path; defaults to `build` | + +> Note: Architect's built in [shared code affordances](/docs/en/guides/developer-experience/sharing-code) are permanently disabled for transpiled output. + +Example: + +```javascript +// Enable a custom build directory with a custom runtime pragma (`@typescript`) +module.exports = { + set: { + runtimes ({ arc, inventory }) { + let { arc } = inventory.inv._project + let build = '.build' + if (arc.typescript) { + arc.typescript.forEach(s => { + if (Array.isArray(s) && s[0] === 'build' && typeof s[1] === 'string') { + build = s[1] + } + }) + } + return { + name: 'typescript', + type: 'transpiled', + baseRuntime: 'nodejs18.x', + build, + } + } + } +} +``` + + +### `compiled` + +Lambdas with a `compiled` runtime (such as those using Rust, Go, Java, etc.) undergo a compilation step that publishes build artifacts to separate dist folder. Per Lambda conventions, this folder is expected to contain a binary artifact called `bootstrap` that will serve as the Lambda handler ([reference](https://docs.aws.amazon.com/lambda/latest/dg/runtimes-custom.html)). + +> Note: When running in Sandbox, your plugin should account for compiling the `bootstrap` for your local system architecture; likewise, during deployment the plugin should ensure the compile target is Amazon Linux 2 (aka `AL2`). A live AWS deployment is indicated by a `inventory.inv._arc.deployStage` value. + +For example, using [Architect Rust](https://github.com/architect/plugin-rust), the `get /foo` handler is authored in `src/http/get-foo/src/main.rs`, and automatically compiled to (and run / deployed from) `.build/http/get-foo/debug/bootstrap`. Remember, during production deploys that dist path may automatically change per your compiler's behavior. + +Because `cargo`'s build tooling automatically compiles the `bootstrap` file to a subfolder called `debug`, this plugin makes use of the `buildSubpath` property. (Without it, Architect would look for (and fail to find) your handler at `.build/http/get-foo/bootstrap`.) + +A plugin setting a `compiled` runtime must return an object (or array of objects) containing the following properties: + +| Property | Type | Required | Description | +|-----------------|---------|-----------|-------------------------------------------------| +| `name` | string | Yes | Custom runtime name | +| `type` | string | Yes | Must be `compiled` | +| `baseRuntime` | string | No | Lambda [runtime identifier or alias][runtime] | +| `build` | string | No | Relative build dir path; defaults to `build` | +| `buildSubpath` | string | No | Optional subpath conforming to compiler output | + + +Example: + +```javascript +// Set `buildSubpath` based on whether the compiler is building locally or for AWS release +let { join } = require('path') +module.exports = { + set: { + runtimes ({ arc, inventory }) { + let { deployStage } = inventory.inv._arc + let buildSubpath = deployStage ? 'release' : 'debug' + return { + name: 'rust', + type: 'compiled', + build, + buildSubpath, + } + } + } +} +``` + +--- + +## Advanced usage + +### `lambda.config` + +Lambdas created by `set` plugins are treated like any other Lambda in the system, which means they are subject to project defaults. This may be convenient: if the project using your Node.js handler created by a `set` plugin is also by default Node.js, you don't have to do anything. + +However, if you are publishing your project publicly, you cannot assume all consumers of your plugin are running the same runtime as you. In this case, and other cases where greater customization and specificity is required, you should include a `config` property in your `set` Lambdas. + +Any of the `set` APIs that create Lambdas (`events`, `http`, `customLambdas`, etc.) accept an optional `config` object with named properties (and values) that are the same as those found in [function config](/docs/en/reference/configuration/function-config). + +These include: `runtime`, `memory`, `timeout`, `concurrency`, `architecture`, and more, and are subject to the same limitations as any other Lambda (e.g. if specifying `layers`, only 5 may be specified, and they must be in the same region as the app is deployed). + +Example: + +```javascript +// Returning this event Lambda assumes user project defaults > Architect defaults +// If the project specifies `@aws runtime python3.9`, and your handler is JS, it will not run +module.exports = { + set: { + events ({ arc, inventory }) { + return { + name: 'an-async-event', + src: __dirname + '/handler' + } + } + } +} +``` + +```javascript +// Returning a `config` property provides control over the configuration of the returned Lambda +module.exports = { + set: { + events ({ arc, inventory }) { + return { + name: 'an-async-event', + src: __dirname + '/handler', + config: { + runtime: 'nodejs14.x', + memory: 3008, // in MB + timeout: 10, // in seconds + } + } + } + } +} +``` + + +### Where `set` Lambdas can live + +Like those created by modifying a project manifest, Lambdas (and their handlers) specified by `set` plugins can live in the user's project. We'll call those [userland Lambdas](#userland-lambdas). + +However, `set` plugins can also point to prepackaged functions and live inside a published plugin that do not allow for customization, because they're intended to do a specific job on behalf of the plugin consumer. We'll call those [pluginland Lambdas](#pluginland-lambdas). + +Let's take a look at some examples of how this might work. + + +#### Userland Lambdas + +Say you're writing a plugin called `local-s3`, which attaches Lambdas to specific S3 events. Your users need to be able to define and maintain the logic executed by S3 event Lambdas provisioned by your plugin. You may ask your user to update their project to define some handlers in a new custom pragma (`@local-s3`): + +```arc +@plugins +local-s3 + +@local-s3 +create +update +delete +``` + +Since these Lambdas live in userland, `set.customLambdas` method might look something like this: + +```javascript +module.exports = { + set: { + customLambdas ({ arc, inventory }) { + let localS3 = arc['local-s3'] + if (!localS3 || !Array.isArray(localS3)) return + + // Create an abritrary number of plugins from the Arc manifest + let lambdas = localS3.map((item) => { + let name = item[0] + return { + name, + src: `src/local-s3/${name}` + } + }) + return lambdas + } + } +} +``` + +This approach puts the Lambdas squarely in the realm of your plugin consumers, and empowers them to make customize the resources you're managing with the plugin. + + +#### Pluginland Lambdas + +Now let's say you're writing a plugin called `autobundle` to accomplish a specific task: automatically bundling JS from your project's [views directory](/docs/en/guides/developer-experience/sharing-code) at `get /_bundle/:entry`. Your users expect to consume this plugin like any other dependency, having it dropped right in and fully maintained by the plugin author. + +Assuming you published your project as `arc-plugin-autobundle`, you might want your `set` plugin to look something like this: + +```javascript +// node_modules/arc-plugin-autobundle/index.js +module.exports = { + set: { + http ({ arc, inventory }) { + return { + method: 'get', + path: 'get /_bundle/:entry', + // Assuming a handler at `node_modules/arc-plugin-autobundle/handler/index.js` + src: __dirname + '/handler' + } + } + } +} +``` + +Returning the Lambda above, Architect will look for your `autobundle` Lambda handler at `node_modules/arc-plugin-autobundle/handler/index.js`. Should your handler have its own dependencies, you must declare them in your plugin's `package.json` file. + + +[events]: /docs/en/reference/project-manifest/events#syntax +[http]: /docs/en/reference/project-manifest/http#syntax +[proxy]: /docs/en/reference/project-manifest/proxy#syntax +[queues]: /docs/en/reference/project-manifest/queues#syntax +[scheduled]: /docs/en/reference/project-manifest/scheduled#syntax +[shared]: /docs/en/reference/project-manifest/shared#syntax +[static]: /docs/en/reference/project-manifest/static#syntax +[tables-indexes]: /docs/en/reference/project-manifest/tables-indexes#syntax +[tables-streams]: /docs/en/reference/project-manifest/tables-streams#syntax +[tables]: /docs/en/reference/project-manifest/tables#syntax +[views]: /docs/en/reference/project-manifest/views#syntax +[ws]: /docs/en/reference/project-manifest/ws#syntax +[sched-expr]: https://docs.aws.amazon.com/lambda/latest/dg/services-cloudwatchevents-expressions.html +[runtime]: /docs/en/reference/configuration/function-config#runtime diff --git a/src/views/docs/en/index.md b/src/views/docs/en/index.md new file mode 100644 index 00000000..30a2f8cf --- /dev/null +++ b/src/views/docs/en/index.md @@ -0,0 +1,8 @@ +--- +title: Home +category: Architect +sections: + - "Welcome" + - "Installation" + - "Get started" +--- diff --git a/src/views/docs/en/reference/app.arc/aws.md b/src/views/docs/en/reference/app.arc/aws.md deleted file mode 100644 index 3a620101..00000000 --- a/src/views/docs/en/reference/app.arc/aws.md +++ /dev/null @@ -1,122 +0,0 @@ ---- -title: '@aws' -description: Define AWS specific configuration. ---- - -Define AWS specific configuration. - -## Syntax -- Accepts values for the following keys: - - **`region`**: [AWS region ID](https://docs.aws.amazon.com/general/latest/gr/rande.html) of the region you'll deploy this project to - - If not specified, defaults to `us-west-2` - - **`profile`**: name of the profile you prefer to use with this project, as defined in your local [AWS profile](/quickstart) - - Can also be specified in `AWS_PROFILE` environment variable - - Required to deploy to AWS - - `runtime`: Lambda runtime, can be one of: - - `nodejs12.x`, `nodejs10.x`, `deno`, `python3.8`, `python3.7`, `python3.6`, `go1.x`, `ruby2.7`, `ruby2.5`, `dotnetcore3.1`, `dotnetcore2.1`, `java11`, `java8` - - **`bucket`**: bucket (in same region) for CloudFormation deployment artifacts - - If not specified, a secure deployment bucket will be auto-created for your app - - `apigateway`: API Gateway API type, can be one of: - - **`http` (default)** - `HTTP` API + Lambda payload format version 2.0 - - `httpv2` – aliased to `http` - - **`httpv1`** - `HTTP` API + Lambda payload format version 1.0 - - **`rest`** - `REST` API + original API Gateway payload format - -Alternatively, if you want a less granular approach, you can declare your preferred region and profile in your `.bashrc` ([more information here](https://docs.aws.amazon.com/cli/latest/userguide/cli-environment.html)). - -If you have AWS exports in your `.bashrc` and `@aws` specified in your `app.arc` project, the `@aws` section will win. - -## Example - -For example, to deploy to the northern California AWS AZ with your AWS `work` profile's credentials, use: - - - -
- - - -
arc
- -
- -```arc -@aws -region us-west-1 -profile work -``` - -
- -
- - - -
json
- -
- -```json -{ - "aws": { - "region": "us-west-1", - "profile": "work" - } -} -``` - -
- -
- - - -
toml
- -
- -```toml -[aws] -region="us-west-1" -profile="work" -``` - -
- -
- - - -
yaml
- -
- -```yaml ---- -aws: - region: us-west-1 - profile: work -``` - -
- -
- -
- -
- - - diff --git a/src/views/docs/en/reference/app.arc/events.md b/src/views/docs/en/reference/app.arc/events.md deleted file mode 100644 index c810a128..00000000 --- a/src/views/docs/en/reference/app.arc/events.md +++ /dev/null @@ -1,93 +0,0 @@ ---- -title: '@events' ---- - - Define SNS topics with Lambda handler functions. - -## Syntax - -- Name - - Lowercase alphanumeric string - - Maximum of 50 characters - - Dashes are allowed; underscores are not allowed - - Must begin with a letter - -## Example - -These configuration examples show how to define events: - - -
- - -
arc
-
- -```arc -@app -myapp - -@events -hit-counter -likes -``` -
-
- - -
json
-
- -```json -{ - "app": "myapp", - "events": [ - "hit-counter", - "likes" - ] -} -``` -
-
- - -
toml
-
- -```toml -app="myapp" -events=[ - "hit-counter", - "likes" -] -``` -
-
- - -
yaml
-
- -```yaml ---- -app: "myapp" -events: -- hit-counter -- likes -``` -
-
- -
-
- -Which generates the following scaffolding: - -```bash -/ -├── events -│ ├── hit-counter/ -│ └── likes/ -├── app.arc -└── package.json -``` diff --git a/src/views/docs/en/reference/app.arc/indexes.md b/src/views/docs/en/reference/app.arc/indexes.md deleted file mode 100644 index 5b603eaa..00000000 --- a/src/views/docs/en/reference/app.arc/indexes.md +++ /dev/null @@ -1,118 +0,0 @@ ---- -title: '@indexes' -description: Define DynamoDB table global secondary indexes. ---- - -Defines [Global Secondary Indexes][gsi] for your project's [DynamoDB][ddb] tables. `@indexes` should only ever be paired with [`@tables`][tables]. - -## Recommended - -[DynamoDB][ddb] is a powerful database, though different from both SQL and NoSQL databases. It is highly recommended to dig into Amazon's resources to familiarize yourself with it: - -- [DynamoDB Core Components (start here!)][core] -- [Global Secondary Indexes in DynamoDB][gsi] -- [Amazon's full DynamoDB documentation][ddb] - -## Syntax - -- `@indexes` is a feature subset of [`@tables`][tables]; as such, the names of your declared indexes must match those of your [`@tables`][tables] -- Otherwise, the basic syntax for defining `@indexes` is the same as [`@tables`][tables]: - - Partition key, defined by a `*`, is required - - Sort key, defined by `**`, is optional - - Currently only `*String`, `**String`, `*Number` and `**Number` are supported - -## Example - -The following `app.arc` file defines a [DynamoDB][ddb] table with two [Global Secondary Indexes][gsi]: - - - -
- -
arc
-
- -```arc -@app -testapp - -@tables -accounts - accountID *String - -@indexes -accounts - email *String - -accounts - created *String -``` -
-
- - -
json
-
- -```json -{ - "app": "testapp", - "tables": [ - { "accounts": { "accountID": "*String" } } - ], - "indexes": [ - { "accounts": { "email": "*String" } }, - { "accounts": { "created": "*String" } } - ] -} -``` -
-
- - -
toml
-
- -```toml -app="testapp" - -[[tables]] -[tables.accounts] -accountID="*String" - -indexes = [ -{ "accounts" = { "email" = "*String" } }, -{ "accounts" = { "created" = "*String" } } -] -``` -
-
- - -
yaml
-
- -```yaml ---- -app: testapp - -tables: -- accounts: - accountID: "*String" - -indexes: -- accounts: - - email: "*String" -- accounts: - - created: "*String" -``` -
-
- -
-
- -[tables]: tables -[core]: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.CoreComponents.html -[ddb]: https://aws.amazon.com/documentation/dynamodb/ -[gsi]: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GSI.html diff --git a/src/views/docs/en/reference/app.arc/scheduled.md b/src/views/docs/en/reference/app.arc/scheduled.md deleted file mode 100644 index 79271586..00000000 --- a/src/views/docs/en/reference/app.arc/scheduled.md +++ /dev/null @@ -1,96 +0,0 @@ ---- -title: '@scheduled' -description: Define EventBridge schedule expressions ---- - -Define EventBridge schedule expressions with Lambda handler functions. - -### Syntax - -- name - - Lowercase alphanumeric string - - Maximum of 20 characters - - Dashes are allowed; underscores are not allowed - - Must begin with a letter - -- Interval - - A valid `rate` or `cron` expression ([more info here](https://docs.aws.amazon.com/lambda/latest/dg/tutorial-scheduled-events-schedule-expressions.html)) - -### Example - -These configuration examples show how to define scheduled functions: - - -
- - -
arc
-
- -```arc -@app -myapp - -@scheduled -daily-update-buddy rate(1 day) -friyay-only cron(0 15 ? * FRI *) -``` -
-
- - -
json
-
- -```json -{ - "app": "myapp", - "scheduled": { - "daily-update-buddy": "rate(1 day)", - "friyay-only": "cron(0 15 ? * FRI *)" } -} -``` -
-
- - -
toml
-
-```toml -app="myapp" - -[scheduled] -daily-update-buddy="rate(1 day)" -friyay-only="cron(0 15 ? * FRI *)" -``` -
-
- - -
yaml
-
- -```yaml -app: myapp -scheduled: - - daily-update-buddy: rate(1 day) - - friyay-only: cron(0 15 ? * FRI *) -``` -
-
- -
-
- - -Which generates the following scaffolding: - -```bash -/ -├── src/ -│ └── scheduled/ -│ ├── daily-update-buddy/ -│ └── friyay-only/ -├── app.arc -└── package.json -``` diff --git a/src/views/docs/en/reference/app.arc/static.md b/src/views/docs/en/reference/app.arc/static.md deleted file mode 100644 index 764a6e68..00000000 --- a/src/views/docs/en/reference/app.arc/static.md +++ /dev/null @@ -1,112 +0,0 @@ ---- -title: '@static' -description: Define S3 bucket ---- - -Configure the static asset S3 bucket. - -## Syntax - -- No parameters are required; `@static` is implied if `@http` is defined -- `folder` defines the folder to upload static assets from. Default is `public` -- `fingerprint` enables static asset file fingerprinting (and long-lived caching headers) -- `ignore` defines which assets to be ignored during upload - -## Example - -This `app.arc` file defines a static bucket: - - - -
- - -
arc
-
- -```arc -@app -testapp - -@static -fingerprint true -ignore - .tar.gz - tmp - user -``` -
-
- - -
json
-
- -```json -{ - "app": "testapp", - "static": { - "fingerprint": true, - "ignore": [ - ".tar.gz", - "tmp", - "user" - ] - } -} -``` -
-
- - -
toml
-
- -```toml -app="testapp" - -[static] -fingerprint=true -ignore=[ - ".tar.gz", - "tmp", - "user" -] - -``` -
-
- - -
yaml
-
- -```yaml ---- -app: testapp - -static: - fingerprint: true - ignore: - - ".tar.gz" - - "tmp" - - "user" -``` -
-
- -
-
- -## Deployment - -`arc deploy --static` deploys static assets to `staging` from `public/` or configured folder. -`arc deploy production --static` deploys static assets to `production` from `public/` or configured folder. - -Static assets will also be uploaded during an `arc deploy` along with your function code. - -`arc deploy static --delete` deletes static assets from the S3 bucket that are not present in the configured static asset folder. - -`arc deploy static --prune` is an alias to delete. - - diff --git a/src/views/docs/en/reference/app.arc/ws.md b/src/views/docs/en/reference/app.arc/ws.md deleted file mode 100644 index 20ed2933..00000000 --- a/src/views/docs/en/reference/app.arc/ws.md +++ /dev/null @@ -1,108 +0,0 @@ ---- -title: '@ws' -description: Defin WebSocket endpoints ---- - -Define WebSocket endpoint and Lambda handler functions. - -### Syntax - -No other config required - -### Example - -This `app.arc` file defines both HTTP and WebSocket endpoints: - - -
- - -
arc
-
- -```arc -@app -myapp - -@http -get / - -@ws -# no other config required - -``` -
-
- - -
json
-
- -```json -{ - "architect": { - "app": "myapp", - "http": [ - [ "get", "/" ] - ], - "ws": {} - }, - "start": "npx sandbox", - "dependencies": { - "@architect/architect": "latest" - } -} -``` -
-
- - -
toml
-
- -```toml -app="testapp" - -http=[ - [ "get", "/" ] -] - -"ws" -# no other config required -``` -
-
- - -
yaml
-
- -```yml ---- -app: testapp - -http: -- get: "/" - -ws: ~ -# no other config required -``` -
-
- -
- - -Running `arc create` generates the following functions: - -```bash -/ -|-src -| |-http -| | '-get-index/ -| '-ws -| |-connect/ -| |-default/ -| '-disconnect/ -'-app.arc -``` diff --git a/src/views/docs/en/reference/cli/deploy.md b/src/views/docs/en/reference/cli/deploy.md index 385509b2..802c9536 100644 --- a/src/views/docs/en/reference/cli/deploy.md +++ b/src/views/docs/en/reference/cli/deploy.md @@ -1,5 +1,6 @@ --- title: arc deploy +category: CLI description: Architect deploy is a module to deploy SAM and CloudFormation templates to an AWS account sections: - Overview @@ -7,58 +8,92 @@ sections: - Flags --- -Deploys code to AWS. +Deploy an Architect project to AWS by creating or updating a [CloudFormation][cfn] Stack with resources declared in [the project manifest][manifest]. + +[CloudFormation][cfn] Stack names are created from the name specified in [the `@app` pragma][app] and are unique to an AWS region. Changing the project name or region will create a new [CloudFormation][cfn] Stack. ## Usage ```bash -arc deploy [production|static|direct] +arc deploy [flags] ``` ## Flags -- `[dirty, --dirty, -d]` Overwrite staging Lambda with local source. A faster way to deploy and test small changes to individual functions without redeploying an entire stack. -- `[--dry-run ]` Creates a CloudFormation template but does not deploy it. A dry-run allows you to check the CloudFormation and SAM output before deploying the actual stack. -- `[production, --production, -p]` Deploys a CloudFormation stack to a production stack. -- `[prune, --prune]` Remove assets not present in the local static folder. -- `[static, --static, -s]` Deploys only the files in the static folder. -- `[verbose, --verbose, -v]` Displays the full deploy status messages. -- `[tags, --tags, -t]` Adds resource tags to the CloudFormation stack. -- `[name, --name, -n]` Deploy a custom named staging stack. +- `--direct path/to/function`: Directly deploy the specified Lambda. A faster way to deploy and test small changes to individual functions without redeploying an entire Stack. +- `--dry-run`: Generate a [CloudFormation][cfn] template but do not deploy it. A dry run allows you to check the [CloudFormation][cfn] and SAM output before deploying the actual stack. +- `--eject`: Generate a [CloudFormation][cfn] template but do not deploy it. Instead, print out the `aws cloudformation` CLI invocation to execute the deployment. +- `--fast`, `-f`: Deploy the stack, but do not block the process until deployment completed. +- `--name`, `-n`: Deploy a custom named staging Stack. E.g. `--name CI` will deploy a `AppnameStagingCI` [CloudFormation][cfn] Stack. +- `--no-hydrate`: Do not automatically [`hydrate`](hydrate) functions prior to deployment. +- `--production`, `-p`: Deploys a [CloudFormation][cfn] Stack to a production Stack. If not specified, will default to deploy to a staging Stack. +- `--prune`: Remove static assets deployed to S3 bucket not present in the local [`@static`][static] folder. +- `--static`, `-s`: Deploys only the files in the [`@static`][static] folder. +- `--tags`, `-t`: Adds resource tags to the CloudFormation stack. The required tag format is `key=value`, e.g. `--tags key1=value1 key2=value2` +- `--verbose`, `-v`: Displays verbose logging. +- `--debug`, `-d`: Displays debug (and verbose) logging. + +## Local preferences: `@create` + +When deploying, Architect can automatically scaffold resources (via [`init`](init)) found in the [application's manifest][manifest] that do not yet exist. Options are set with [`@create` in local preferences](../configuration/local-preferences#%40create). + +- `autocreate` - Set to `true` to enable automatic creation of boilerplate Lambda handlers and static assets if they do not exist. +- `templates` - Specify templates for automatic resource scaffolding. + - ` path/to/template.ext` + - Does not enable `autocreate` + +```arc +@create +autocreate true +templates + http path/to/template/http.js + events path/to/template/events.py +``` ## Examples -### Deploy a staging stack +### Deploy a staging Stack ```bash arc deploy ``` -> Protip: deploy arbitrary named staging stacks with `arc deploy --name my-stack` +### Deploy a production Stack + +```bash +arc deploy --production +``` -### Deploy a production stack +### Deploy a named staging Stack ```bash -arc deploy production +arc deploy --name my-stack ``` +> 💁 Named stacks use `staging` environment variables [set with the `env` command](env). + ### Deploy static assets to S3 ```bash -arc deploy static +arc deploy --static ``` -### Deploy code directly to the staging Lambda +### Deploy the index route directly to staging ```bash -arc deploy dirty src/http/get-index +arc deploy --direct src/http/get-index ``` ### Run deploy without deploying -This is useful for testing `@macros`; it will still generate `sam.json`. +This is useful for testing [`@plugins`][plugins]; it will still generate `sam.json`. ```bash arc deploy --dry-run ``` +[cfn]: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/Welcome.html +[manifest]: ../../get-started/project-manifest +[static]: ../project-manifest/static +[app]: ../project-manifest/app +[plugins]: ../project-manifest/plugins diff --git a/src/views/docs/en/reference/cli/destroy.md b/src/views/docs/en/reference/cli/destroy.md index 37e6c717..67bcf553 100644 --- a/src/views/docs/en/reference/cli/destroy.md +++ b/src/views/docs/en/reference/cli/destroy.md @@ -1,32 +1,64 @@ --- title: arc destroy -description: CLI destory command for removing CloudFormation stack +category: CLI +description: CLI destroy command for removing Architect application +sections: + - Usage + - Flags + - Examples --- -Remove the CloudFormation stack and all related assets for the current project from AWS completely. +Completely removes your Architect application from AWS. This command deletes all resources associated with your application, including the [CloudFormation][cfn] Stack, SSM environment variables, CloudWatch Logs, deployment S3 bucket, and static S3 bucket (if applicable). + +By default, this command destroys your staging environment. Use the `--production` flag to target the production environment instead. For custom environments created with [`deploy --name`][deploy], use the `--name` flag to specify the environment to destroy. + +Large applications may take several minutes to delete and by default this command times out after 150 seconds. Even if this command times out, deletion may still complete successfully as removal completes asynchronously in the background. To have this process block until all application resources are removed, use the `--no-timeout` flag. ## Usage ```bash -arc destroy [production] [--name|--force|-f] +arc destroy [flags] ``` +## Flags + +- `--app`: App name as specified by your application's [`@app` pragma][app] (required for confirmation). +- `--force`, `-f`: Destroy an app that has database [`@tables`][tables] and/or [`@static`][static] assets. +- `--name`: Target a custom named environment created with [`deploy --name`][deploy]. +- `--now`: Skip the 5-second safety delay before destroying resources. +- `--no-timeout`: Wait indefinitely for all application resources to be removed (by default times out after ~150 seconds). +- `--production`, `-p`: Destroy the production environment instead of staging. +- `--verbose`, `-v`: Print more detailed information during the destroy process. +- `--debug`, `-d`: Print even more detailed information for debugging purposes. + ## Examples -### Destroy the staging stack +### Destroy the staging Stack ```bash -arc destroy --name my-app-name +arc destroy --app my-app ``` -### Destroy the production stack +### Destroy the production Stack ```bash -arc destroy production --name my-app-name +arc destroy --app my-app --production ``` -### Destroy stack with S3 bucket and/or Dynamo tables +### Destroy staging Stack with S3 bucket and/or Dynamo tables ```bash -arc destroy --name my-app --force +arc destroy --app my-app --force ``` + +### Destroy custom named Stack + +```bash +arc destroy --app my-app --name Dev +``` + +[deploy]: deploy +[app]: ../project-manifest/app +[tables]: ../project-manifest/tables +[static]: ../project-manifest/static +[cfn]: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/Welcome.html diff --git a/src/views/docs/en/reference/cli/env.md b/src/views/docs/en/reference/cli/env.md index 57d38798..5d5c6374 100644 --- a/src/views/docs/en/reference/cli/env.md +++ b/src/views/docs/en/reference/cli/env.md @@ -1,40 +1,83 @@ --- title: arc env +category: CLI description: Read and write environment variables for Lambda functions. +sections: + - Usage + - Flags + - Security + - Examples + - Reserved names + - Specific function opt-out --- -Read and write environment variables. This allows apps to centrally store sensitive configuration data, such as API keys, outside of the codebase in revision control. +Manage environment variables for your Architect application across different environments. This command allows you to read, add, and remove environment variables that will be available to your Lambda functions at runtime. + +When run without any flags, this command will print out all environment variables and their values, across all environments. + +Environment variables are stored in AWS SSM Parameter Store for staging and production environments. For local development (testing environment), variables are stored in your project's [`preferences.arc` file][prefs] or `.env` file if one exists. + +> `arc env` will **not** upload variables from a project's [local preferences file][env-prefs]; however, it will download variables from AWS and overwrite local preference entries. + +> 💁 `staging` environment variables are also used in named deployments [created with `arc deploy --name `](./deploy). ## Usage ```bash -arc env [testing|staging|production] {VARIABLE_NAME} {VARIABLE_VALUE} +arc env [flags] [VARIABLE_NAME] [VARIABLE_VALUE] ``` +## Flags + +- `-e`, `--env `: Specify environment (`testing`, `staging`, or `production`) +- `-a`, `--add`: Add a new environment variable +- `-r`, `--remove`: Remove an environment variable +- `-v`, `--verbose`: Print more detailed output +- `-d`, `--debug`: Print even more detailed information for debugging + +## Security + +It is imperative that the `ARC_APP_SECRET` environment variable be set to a strong secret - especially in your production environment! **It must have a minimum length of 32 bytes**. This secret is used to encode HTTP sessions if you use the [`@architect/functions` runtime helpers](../runtime-helpers/node.js#arc.http.session). + +## Reserved names + +The following environment variable names are reserved for Architect use and cannot be set in an app: + +- `ARC_ENV` +- `ARC_APP_NAME` +- `ARC_SESSION_TABLE_NAME` + +## Specific function opt-out + +A function can be [configured with a `config.arc`](../configuration/function-config#%40arc) to not load local environment variables. + ## Examples ### Display environment variables for the current `app.arc` -``` +```bash arc env ``` -### Save an environment variable to the staging environment +### Display variables for a specific environment -``` -arc env staging FOO myvalue +```bash +arc env --env staging ``` -> Protip: values that contain special characters like email addresses should be wrapped in double quotes +### Save an environment variable to the staging environment -### Remove an environment variable +Variable values that contain special characters like email addresses should be wrapped in double quotes -``` -arc env remove staging FOO +```bash +arc env --add --env staging SECRET_API_PASSWORD "p@s5w0rd!" ``` -## Reserved names +### Remove an environment variable + +```bash +arc env --remove --env staging SECRET_API_PASSWORD +``` -- `NODE_ENV` -- `ARC_APP_NAME` -- `SESSION_TABLE_NAME` +[prefs]: ../configuration/local-preferences +[env-prefs]: ../configuration/local-preferences#%40env diff --git a/src/views/docs/en/reference/cli/hydrate.md b/src/views/docs/en/reference/cli/hydrate.md index 63d817e7..395dc898 100644 --- a/src/views/docs/en/reference/cli/hydrate.md +++ b/src/views/docs/en/reference/cli/hydrate.md @@ -1,76 +1,43 @@ --- -title: hydrate -description: Quickly install and update dependencies for all functions in the src directory. +title: arc hydrate +category: CLI +description: Install and update dependencies for all functions in a project. --- -## Overview +Ensure that all functions managed by Architect have their dependencies installed. Functions containing all their required dependencies are considered to be "hydrated" - thus the name! -[`@architect/hydrate`](Architect env) ensures that all functions managed by architect have their dependencies installed. Functions containing all its required dependencies are considered to be 'hydrated' - thus the name! +Importantly, `arc hydrate` will also copy [shared code][sharing] from `src/shared` into all functions and `src/views` into [`@http`][http] GET functions. +When [developing locally with Sandbox][local-dev], it is not necessary to manually run `hydrate` since Sandbox handles this automatically. However, it can be helpful to ensure hydration happens prior to a process like `npm test`. ## Usage -> Note: the process running `hydrate` must be the same as the root of the project it's hydrating. So if the project you're trying to `hydrate` is located locally at `/projects/myapp` and you're running `hydrate` from `/scripts/hydrate`, you'll need to ensure you `process.chdir('/projects/myapp')` prior to execution - - -### `hydrate(options)` - -`options` object can include the following properties: - -- `basepath`: What path hydrate should consider as the root for searching for functions to hydrate. Useful if you want to hydrate a subset of functions. Defaults to `src` in the current working directory. -- `install`: If truthy, will invoke [`hydrate.install()`][install]. -- `update`: If truthy, will invoke [`hydrate.update()`][update]. - -By default, invokes [`hydrate.shared()`][shared]. +```bash +arc hydrate [--shared|--update] +``` +## Flags -### `hydrate.install(options, callback)` +- `--shared`, `-s`: Hydrates and copies [shared files][sharing] only +- `--update` `-u`: Updates each function's dependencies to latest versions +- `--verbose`, `-v`: Prints additional output to the console -Installs dependencies for all Functions found in the specified `basepath`. Invokes [`hydrate.shared()`][shared]. +## Notes -Note that for the default value of `basepath='src'`, this means `install` will also hydrate shared folders like `src/shared` and `src/views`. +> ⚠️ This operation can take time to complete depending on how many Lambdas you have and how many modules they require. -To ensure local development behavior is as close to `staging` and `production` as possible, `hydrate.install()` (and other hydrate functions) uses: +Hydrate uses the following commands under the hood, depending on project's or function's runtime: -- **node.js**: `npm ci` if `package-lock.json` is present and `npm i` if not +- **node.js**: `npm ci` - **python**: `pip3 install` - **ruby**: `bundle install` - -### `hydrate.update(options, callback)` - -Updates dependencies in all Functions found in the specified `basepath`. Invokes [`hydrate.shared()`][shared]. Note that this will only functionally differ from [`hydrate.install()`][install] if you use a lock-file like `package-lock.json` or `Gemfile.lock`. - -Note that for the default value of `basepath='src'`, this means `update` will also update dependencies in shared folders like `src/shared` and `src/views`. - -`update` is functionally almost identical to [`install`][install], except it will update dependencies to newer versions _if they exist_. This is done via: +`arc hydrate --update` is almost functionally identical to `arc hydrate`, except it will update dependencies to newer versions _if they exist_. This is done via: - **node.js**: `npm update` - **python**: `pip3 install -U --upgrade-strategy eager` - **ruby**: `bundle update` - -### `hydrate.shared(options, callback)` - -Copies shared code (from `src/shared` and `src/views`) into all Functions. - - -[shared]: #hydratesharedoptions-callback -[install]: #hydrateinstalloptions-callback -[update]: #hydrateupdateoptions-callback -[npm]: https://www.npmjs.com/package/@architect/hydrate - -### `arc hydrate` - -- `arc hydrate` runs `npm i` -- `arc hydrate update` runs `npm update` - -> ⚠️ Note: this operation can take time to complete depending on how many Lambdas you have and how many modules they require. - -## Flags - -`[-s, --shared, shared]` - hydrates and copies shared files only -`[-u, --update, update]` - updates each function's dependencies -`[-v, --verbose, verbose]` - prints additional output to the console - - +[local-dev]: ../../guides/developer-experience/local-development +[sharing]: ../../guides/developer-experience/sharing-code +[http]: ../project-manifest/http diff --git a/src/views/docs/en/reference/cli/init.md b/src/views/docs/en/reference/cli/init.md index 0ffc8247..2adb909e 100644 --- a/src/views/docs/en/reference/cli/init.md +++ b/src/views/docs/en/reference/cli/init.md @@ -1,21 +1,40 @@ --- title: arc init +category: CLI description: Scaffold new resources found in the app.arc file --- -Bootstrap new Architect project code. Running `arc init` in an empty directory creates a default `app.arc` manifest file named after that directory with one default function `src/http/get-index`. Edit `app.arc` adding functions and re-run `arc init` to generate further code. This command is intended to be run and re-run; it will only generate files if they do not already exist. +Bootstrap new Architect project code. Running `arc init` in an empty directory creates a default [`app.arc` manifest file][manifest] named after that directory with one default [`@http` function][http] at `src/http/get-index`. Pass a directory name as a final positional argument to create a project in the specified directory. Edit `app.arc`, expanding the [manifest][manifest], adding functions and re-running `arc init` to generate further code. This command is idempotent: intended to be run and re-run; it will only generate files if they do not already exist. + +If you run this command with the `--plugin` flag, a scaffolded [Architect plugin][plugins] will be created instead. ## Usage ```bash -arc init [-s|--static|static|-r|--runtime|runtime|-v|--verbose|verbose] +arc init [flags] [path/to/project-directory] ``` ## Flags -- `[-s, --static, static]` create a new project with `@static` folder set to `public` -- `[-r, --runtime, runtime ]` create a new project with a specified runtime, options are node, deno, python, or ruby -- `[-v, --verbose, verbose]` even more output +- `-n`, `--name`: Set the [`@app` namespace][app] for the created app +- `--no-install`: Do not automatically install `@architect/architect` as a dependency in the project +- `-p`, `--plugin`: Create a new scaffolded [Architect plugin][plugins] instead of a new Architect project +- `--runtime`, `-r`: Create a new project with the specified runtime. Defaults to `node`. See the [`runtime` configuration documentation][runtimes] for available options. +- `--verbose`, `-v`: Even more output + +## Local preferences: `@create` + +`arc init` can use specified templates when scaffolding new resources. Options are set with [`@create` in local preferences](../configuration/local-preferences#%40create). + +- `templates` - Specify templates for automatic resource scaffolding. + - ` path/to/template.ext` + +```arc +@create +templates + http path/to/template/http.js + events path/to/template/events.py +``` ## Examples @@ -29,6 +48,32 @@ arc init ### Create a Node app with Architect installed locally + +
+ +
Bash/cmd.exe
+
+ ```bash npm init @architect myapp ``` +
+
+ + +
PowerShell
+
+ +```powershell +npm init "@architect" myapp +``` +
+
+
+
+ +[app]: ../project-manifest/app +[http]: ../project-manifest/http +[manifest]: ../../get-started/project-manifest +[plugins]: ../../guides/plugins/overview +[runtimes]: ../project-manifest/aws#runtime diff --git a/src/views/docs/en/reference/cli/logs.md b/src/views/docs/en/reference/cli/logs.md index f9fe1ac1..8f09e7d7 100644 --- a/src/views/docs/en/reference/cli/logs.md +++ b/src/views/docs/en/reference/cli/logs.md @@ -1,26 +1,50 @@ --- title: arc logs -description: Read or clears Lambda function logs. +category: CLI +description: Read or destroy Lambda function logs. +sections: + - Usage + - Flags + - Examples --- -Read or clear Lambda function logs. +Retrieve or delete [CloudWatch][cloudwatch] logs for your Lambda functions. When your functions log to `stdout` or `stderr`, these messages are stored in [AWS CloudWatch][cloudwatch]. This command lets you access these logs for debugging or monitoring. + +You must provide a path to one of your Lambda function directories (e.g., `arc logs src/http/get-index`). By default, this command retrieves logs from your staging environment, unless the `--production` flag is specified. + +If no function path is provided and your project has a root HTTP handler defined, logs for that handler will be retrieved by default. ## Usage ```bash -arc logs [-n|--nuke|nuke] [production] path/to/code +arc logs [flags] ``` +## Flags + +- `--destroy`: Delete logs for the specified function +- `-p`, `--production`: Retrieve logs from the production environment (default is staging) +- `-v`, `--verbose`: Display more detailed information during command execution +- `-d`, `--debug`: Display even more detailed information for debugging purposes + ## Examples -### Read logs +### Read logs from staging ```bash arc logs src/http/get-index ``` -### Nuke logs +### Read logs from production ```bash -arc logs nuke src/http/get-index +arc logs --production src/http/get-index ``` + +### Delete logs + +```bash +arc logs --destroy src/http/get-index +``` + +[cloudwatch]: https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/WhatIsCloudWatch.html diff --git a/src/views/docs/en/reference/cli/package.md b/src/views/docs/en/reference/cli/package.md deleted file mode 100644 index cd746827..00000000 --- a/src/views/docs/en/reference/cli/package.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -title: arc package -description: Generate AWS SAM template based on the current app.arc ---- - -Transform `app.arc` into `sam.json`. [AWS SAM](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/what-is-sam.html) can be deployed with the AWS CLI. - -## Usage - -```bash -arc package -``` diff --git a/src/views/docs/en/reference/cli/sandbox.md b/src/views/docs/en/reference/cli/sandbox.md index c2e2a18b..c6fec1a9 100644 --- a/src/views/docs/en/reference/cli/sandbox.md +++ b/src/views/docs/en/reference/cli/sandbox.md @@ -1,66 +1,230 @@ --- title: arc sandbox +category: CLI description: Local development sandbox. +sections: + - Usage + - Flags + - Environment Variables + - Keyboard Shortcuts + - Local preferences + - Environment variables + - Local database --- -Architect projects work locally and offline. It emulates most app resources defined in `app.arc`: +Start a local development server that emulates AWS infrastructure for your Architect application. Sandbox provides a complete local development environment that includes: -- `@http` -- `@static` -- `@ws` -- `@events` -- `@queues` -- `@tables` and `@indexes` +- HTTP server for your [`@http` routes][http] +- WebSocket server for [`@ws` routes][ws] +- Dynalite in-memory database for [`@tables`][tables] and [`@tables-indexes`][indexes] +- Local event bus for [`@events`][events] and [`@queues`][queues] +- Static asset serving for [`@static`][static] assets +- File watching and live reloading -> At this time `arc sandbox` does not emulate `@scheduled` +> Additionally, `@scheduled` and `@tables-streams` Lambdas can be emulated via the [@architect/plugin-lambda-invoker](https://www.npmjs.com/package/@architect/plugin-lambda-invoker) plugin ## Usage +```bash +arc sandbox [flags] +``` + +## Flags + +- `-p`, `--port`: Port the HTTP server will listen on (default is `3333`) +- `-h`, `--host`: Host the server will bind to (default is `0.0.0.0`) +- `--disable-symlinks`: Do not use symbolic links for [shared code][sharing]; use file copying instead (slower) +- `--disable-delete-vendor`: Do not delete `node_modules` or `vendor` directories upon startup +- `-q`, `--quiet`: Minimize console output during operation +- `-v`, `--verbose`: Print more detailed output during operation +- `-d`, `--debug`: Print even more detailed information for debugging + +## Environment Variables + +The following variables can be set on the command line when running `arc sandbox`: + +- `ARC_HTTP_PORT`, `PORT` - Set the HTTP server port (same as `--port`) +- `ARC_EVENTS_PORT` - Set the events/queues service port (default `4444`) +- `ARC_TABLES_PORT` - Set the DynamoDB emulator port (default `5555`) +- `ARC_HOST` - Set the host the server will bind to +- `ARC_QUIET`, `QUIET` - Minimize console output (same as `--quiet`) +- `ARC_ENV` - `testing|staging|production` + - Defaults to `testing` +- `ARC_LOCAL`- If present and used in conjunction with `ARC_ENV=staging|production`, emulates live `staging` or `production` environment + - Uses your [local preferences `@env`](../configuration/local-preferences#%40env) environment variables for the appropriate stage + - Connects Sandbox to live AWS events and DynamoDB infrastructure + - Requires valid AWS credentials with the same profile name as defined in your [project manifest](../project-manifest/aws#profile) +- `ARC_INTERNAL_PORT`- Manually specify internal Sandbox + AWS services port + - Defaults to `2222` +- `ARC_DB_EXTERNAL` - (Boolean) Use an external DynamoDB tool (such as [AWS NoSQL Workbench](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/workbench.html)) + +### Example + +Run Sandbox in quiet mode on a different port: + +```bash +ARC_QUIET=1 PORT=8888 npx arc sandbox +``` + +## Keyboard Shortcuts + +Sandbox registers keyboard shortcuts to help with local development (note: they are all capital letters!): + +- `S` - Hydrate only `src/shared` +- `V` - Hydrate only `src/views` +- `H` - Hydrate both `src/shared` and `src/views` +- `Ctrl+C` - Gracefully shut down the sandbox + +## Local preferences + +Several Architect [local preferences][prefs] can be leveraged to change how Sandbox works while developing locally. + +### Sandbox preferences + +Check out the [`@sandbox` preferences](../configuration/local-preferences#%40sandbox) reference for more information. + +> ⚠️ These preferences should be used with care as they can allow your local development sandbox to use live deployed infrastructure and data. + +```arc +@sandbox +livereload true +env production +useAws true +no-hydrate true ``` -arc sandbox [--port|--verbose|--disable-symlinks|--no-hydrate] + +### Sandbox startup scripts + +Sandbox can run shell commands on startup by setting [`@sandbox-start`](../configuration/local-preferences#%40sandbox-start) in [local preferences][prefs] like so: + +```arc +@sandbox-start +node scripts/seed_db.js +echo 'hello' +``` + +### `@create` + +Upon starting, Sandbox can automatically scaffold resources (via [`init`](init)) found in the [application's manifest](../../get-started/project-manifest) that do not yet exist. Options are set with [`@create` in local preferences](../configuration/local-preferences#%40create). + +```arc +@create +autocreate true +templates + http path/to/template/http.js + events path/to/template/events.py ``` -### Flags +### `@env` -- `-p`, `--port`, `port` Manually specify HTTP port (default `3333`) -- `-v`, `--verbose`, `verbose` Enable verbose logging -- `--disable-symlinks` Disable symlinking `src/shared` and copy instead -- `--no-hydrate` Disables hydration +Architect Sandbox will load variables for Sandbox's current environment (`testing`, `staging`, or `production`) from a [local preferences file with `@env`](../configuration/local-preferences#%40env). If a project contains a `.env` file, Architect will load those variables _instead_, and only for the `testing` environment. -### Environment variables +Variables from local preference files and `.env` will **not** be merged. Further details, including the variable load-strategy are [outlined below](#environment-variables). -- `NODE_ENV` default `testing` -- `ARC_API_TYPE` - Set the API Gateway API type - - Can be one of `http` (aliased to `httpv2`), `httpv1`, `rest` - - Defaults to `http` -- `ARC_QUIET` - If present, disable (most) logging -- `PORT` - Manually specify HTTP port - - Defaults to `3333` -- `ARC_EVENTS_PORT`- Manually specify event bus port - - Defaults to `3334` -- `ARC_TABLES_PORT`- Manually specify local DynamoDB port - - Defaults to `3335` -- `ARC_LOCAL`- If present and used in conjunction with `NODE_ENV=staging|production`, emulates live `staging` or `production` environment - - Uses your local `.arc-env` file's `@staging` or `@production` environment variables - - Connects Sandbox to live AWS events and DynamoDB infra - - Requires valid AWS credentials with the same profile name as defined in your project manifest +## Environment variables +Sandbox automatically loads environment variables for availability at runtime (`process.env.MY_VAR` in Node.js). Environment variables can be set in a few locations. It's important to understand how each source is prioritized when developing locally. -### Local Database +### Load strategy -Sandbox creates an in-memory instance of [dynalite](https://github.com/mhart/dynalite) with `@tables` and `@indexes` found in the `app.arc` file. When Sandbox is terminated, any data written is cleared from memory. The default endpoint is `http://localhost:5000`. You can set a custom port by using an environment variable, `ARC_TABLES_PORT=5555` +Sandbox will prioritize... -### Connect sandbox to the staging database +1. A project's `.env` file (if it exists), +2. then project-level Architect preferences, +3. and finally global Architect preferences. + +Variables across these sources are **not** merged. + +Using a [local preferences file with `@env`](../configuration/local-preferences#%40env) offers the most flexibility since variables can be specified per environment: `testing`, `staging`, and `production`. + +### Example scenario + +If `.env` is found, Sandbox will only use the variables for the `testing` environment, and not load any variables from any Arc preferences files. Given the following case with 3 environment variable sources: ```bash -NODE_ENV=staging ARC_LOCAL=1 arc sandbox +# ./.env +URL="https://arc.codes" +``` + +```arc +# ./prefs.arc +@env +testing + URL www.architect.fake +``` + +```arc +# ~/.preferences.arc +@env +testing + URL ftp://arc.ftp + ADMIN_PASS zero cool +``` + +The following is true in a Node.js function run with Sandbox: + +```javascript +process.env.URL === 'https://arc.codes' // true +process.env.ADMIN_PASS // undefined ``` -### File watching +## Local database + +Sandbox creates an in-memory instance of [dynalite](https://github.com/architect/dynalite) with [`@tables`][tables] and [`@tables-indexes`][indexes] found in the `app.arc` file. `@tables-streams` is not currently supported by dynalite. + +When Sandbox is terminated, any data written is cleared from memory. + +You can set a custom port by using an environment variable: `ARC_TABLES_PORT=5555`. -The sandbox restarts when the following files or directories are modified: +### Database seed data + +You can automatically seed data to Sandbox upon startup by adding a `sandbox-seed.js` or `sandbox-seed.json` file to the root of your project. (You can also specify a custom path with the `seed-data` preference.) + +Your seed data should be an object whose properties correspond to `@tables` names, and have arrays of rows to seed. For example: + +```arc +@tables +things + id *String + sort **String +``` + +```javascript +// sandbox-seed.js +module.exports = { + things: [ + { + id: 'foo', + sort: 'bar', + arbitrary: 'data', + }, + { + id: 'fiz', + sort: 'buz', + arbitrary: 'info', + } + ] +} +``` + +The above example would add the two rows above to the `things` database each time Sandbox is started. + +> Note: This feature is only enabled if the environment is `testing`, so as to prevent the accidental (over)writing of data to a live database. + +### Live database example + +Connect Sandbox to the DynamoDB staging database on AWS: + +```bash +ARC_ENV=staging ARC_LOCAL=1 npx arc sandbox +``` -- `app.arc` -- `@views` source folder -- `@shared` source folder -- `@static` source folder if fingerprinting is enabled +[http]: ../project-manifest/http +[ws]: ../project-manifest/ws +[tables]: ../project-manifest/tables +[indexes]: ../project-manifest/tables-indexes +[events]: ../project-manifest/events +[queues]: ../project-manifest/queues +[static]: ../project-manifest/static +[sharing]: ../../guides/developer-experience/sharing-code +[prefs]: ../configuration/local-preferences diff --git a/src/views/docs/en/reference/config.arc/aws.md b/src/views/docs/en/reference/config.arc/aws.md deleted file mode 100644 index c665ba60..00000000 --- a/src/views/docs/en/reference/config.arc/aws.md +++ /dev/null @@ -1,49 +0,0 @@ ---- -title: '@aws' -description: Lambda function configuration ---- - -Configure individual Lambda function properties (e.g. `src/http/get-index/config.arc`). - -- `runtime` - Officially supported: one of `nodejs12.x` (default), `deno`, `python3.7`, `python3.6`, or `ruby.5`, etc. - - Also configurable, but not officially supported by Architect: `java8`, `go1.x`, `dotnetcore2.1` -- `memory` - number, between `128`MB and `3008`MB in 64 MB increments - - Memory size also directly correlates with CPU speed; higher memory levels are available in more capable Lambda clusters -- `timeout` - number, in seconds (max `900`) -- `concurrency` - number, `0` to AWS account maximum (if not present, concurrency is unthrottled) -- `layers` - Up to 5 Lambda layer ARNs; **must be in the same region as deployed** -- `policies` - configure [AWS SAM policy templates](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-policy-templates.html) - -> Note: any function configurations made globally in your project manifest will be overridden by individual functions. For example, if your `app.arc` includes `memory 128`, and `src/http/get-index/config.arc` includes `memory 3008`, all functions except `get /` will be configured with 128MB of memory, while `get /` will override that global with 3008MB. - -## Example - -```arc -@aws -runtime python3.7 -memory 256 -timeout 3 -concurrency 1 -layers {ARN} -policies {ARN} -``` - -To use multiple layers or policies: - -```arc -@aws -runtime python3.7 -memory 256 -timeout 3 -concurrency 1 -layers - {ARN1} - {ARN2} - {ARN3} -policies - {ARN4} - {ARN5} - {ARN6} -``` - -Read more about the [Lambda limits](https://docs.aws.amazon.com/lambda/latest/dg/limits.html) and [resource model](https://docs.aws.amazon.com/lambda/latest/dg/resource-model.html). diff --git a/src/views/docs/en/reference/config.arc/concurrency.md b/src/views/docs/en/reference/config.arc/concurrency.md deleted file mode 100644 index 10aa0335..00000000 --- a/src/views/docs/en/reference/config.arc/concurrency.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -title: '@aws concurrency' -description: Lambda function configuration ---- - -Configure Lambda function concurrency. If not present concurrency is unthrottled. - -## Examples - -### Limit execution to one invocation at a time - -```arc -@aws -concurrency 1 -``` - -> Tip: `@events` functions with `concurrency 1` create a queue-like primitive - -### Disable invocation by setting concurrency to zero - -```arc -@aws -concurrency 0 -``` diff --git a/src/views/docs/en/reference/config.arc/layers.md b/src/views/docs/en/reference/config.arc/layers.md deleted file mode 100644 index 8fcab572..00000000 --- a/src/views/docs/en/reference/config.arc/layers.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -title: '@aws layers' -description: Lambda function configuration ---- - -Configure Lambda function `layers` with max 5 Lambda Layer ARNs. - -> Warning: Lambda Layers must be in the same region as they are deployed - -## Examples - -Add one layer: - -```arc -@aws -layers arn:aws:lambda:us-east-1:764866452798:layer:ghostscript:1 -``` - -Or multiple layers: - -```arc -@aws -layers - arn:aws:lambda:us-east-1:764866452798:layer:chrome-aws-lambda:4 - arn:aws:lambda:us-east-1:145266761615:layer:pandoc:1 -``` - -> Tip: find [awesome layers](https://github.com/mthenw/awesome-layers) diff --git a/src/views/docs/en/reference/config.arc/memory.md b/src/views/docs/en/reference/config.arc/memory.md deleted file mode 100644 index be0b3d44..00000000 --- a/src/views/docs/en/reference/config.arc/memory.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -title: '@aws memory' -description: Lambda function configuration ---- - -Configure Lambda function `memory` between `128` MB to `10240` MB, in `1` MB increments. - -## Example - -```arc -@aws -memory 1024 -``` diff --git a/src/views/docs/en/reference/config.arc/policies.md b/src/views/docs/en/reference/config.arc/policies.md deleted file mode 100644 index d7c9595d..00000000 --- a/src/views/docs/en/reference/config.arc/policies.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -title: '@aws policies' -description: Lambda function configuration ---- - -Configure Lambda function `policies` with [AWS SAM policy templates](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-policy-templates.html). - -## Example - -```arc -@aws -policies - S3CrudPolicy -``` diff --git a/src/views/docs/en/reference/config.arc/runtime.md b/src/views/docs/en/reference/config.arc/runtime.md deleted file mode 100644 index 76bdb2ed..00000000 --- a/src/views/docs/en/reference/config.arc/runtime.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -title: '@aws runtime' -description: Lambda function configuration ---- - -Configure Lambda function `runtime`: - -- `nodejs12.x` (default) -- `deno` -- `python3.8` -- `ruby2.5` - -Also configurable but not supported by the sandbox: - -- `java8` -- `go1.x` -- `dotnetcore2.1` - -## Example - -```arc -@aws -runtime deno -``` diff --git a/src/views/docs/en/reference/config.arc/timeout.md b/src/views/docs/en/reference/config.arc/timeout.md deleted file mode 100644 index 7fc38229..00000000 --- a/src/views/docs/en/reference/config.arc/timeout.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -title: '@aws timeout' -description: Lambda function configuration ---- - -Configure Lambda function `timeout` in seconds to a max of `900`. (`15` minutes.) - -## Example - -```arc -@aws -timeout 30 -``` - diff --git a/src/views/docs/en/reference/configuration/function-config.md b/src/views/docs/en/reference/configuration/function-config.md new file mode 100644 index 00000000..29fec210 --- /dev/null +++ b/src/views/docs/en/reference/configuration/function-config.md @@ -0,0 +1,277 @@ +--- +title: Function config +category: Configuration +description: Lambda function configuration +sections: + - 'env' + - 'ignoreDependencies' + - 'shared' + - 'views' + - 'architecture' + - 'concurrency' + - 'fifo' + - 'layers' + - 'memory' + - 'policies' + - 'runtime' + - 'storage' + - 'timeout' +--- + +Individual Lambda function properties can be customized and configured by modifying the `config.arc` file present in each Lambda functions' directory. + +## `@arc` + +Use the `@arc` pragma to disable Architect features for a specific function: + +- [`env`](#env) - boolean, `true` (default) or `false` to disable loading environment variables +- [`ignoreDependencies`](#ignoredependencies) - array, specific dependency names to ignore during Lambda treeshaking +- [`shared`](#shared) - boolean, `true` (default) or `false` to skip hydrating project code from `@shared`. +- [`views`](#views) - boolean, `true` (default) or `false` to skip hydrating project code from `@views`. + +### Example `config.arc` + +```arc +# src/function/dir/config.arc +@arc +env false +ignoreDependencies + some-special-dependency + '@scoped/dependency' +shared false +views false +``` + +### `env` + +Sometimes it's necessary to have an even more isolated, locked down Lambda within an Architect project; in such cases, it can be helpful to set `env` to `false`, which deactivates all project-level environment variables. + +Note: even with `env` set to false, your function still has access to credentials with whatever IAM privileges the Lambda has been granted. To isolate permissions further, please see [`policies`](#policies). + + +### `ignoreDependencies` + +Disable specific dependencies from being installed in Lambdas that rely on [automated dependency treeshaking](/docs/en/guides/developer-experience/dependency-management#automated-dependency-treeshaking). When a dependency has a scope (preceded by '@'), place the name inside quotes. + +> ⚠️ This setting is currently only supported in Node.js Lambdas and does _not_ support ignoring dependencies from the `shared` folder ([#1476](https://github.com/architect/architect/issues/1476)). + +The alternate spelling of `ignoredDependencies` may also be used, although `ignoreDependencies` is preferred. + + +### `shared` + +Disable hydrating [shared code](/docs/en/guides/developer-experience/sharing-code) into a given Lambda. This can be helpful for reducing code payload size when shared code is not being used, or if you are running arbitrary code within a Lambda that may expose internal business logic. + + +### `views` + +Disable hydrating [views code](/docs/en/guides/developer-experience/sharing-code) into a given Lambda. As with [`shared`](#shared), this can be helpful for reducing code payload size for Lambdas in customer hot paths. + + +## `@aws` + +Configure the deployed function with [the `@aws` pragma](../project-manifest/aws) and the following properties: + +- [`architecture`](#architecture) - [AWS Architecture](https://docs.aws.amazon.com/lambda/latest/dg/foundation-arch.html) for the function: `arm64` (default) or `x86_64` +- [`concurrency`](#concurrency) - number, `0` to AWS account maximum (if not present, concurrency is unthrottled) +- [`fifo`](#fifo) - boolean, `true` (default) or `false` to use `standard` SQS type +- [`layers`](#layers) - Up to 5 Lambda layer ARNs; **must be in the same region as deployed** +- [`memory`](#memory) - number, between `128` and `3008` MB in 64 MB increments. +- [`policies`](#policies) - Configure [AWS SAM policy templates](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-policy-templates.html) +- [`provisionedConcurrency`](#provisionedconcurrency) - number, `1` to AWS account maximum (disabled by default) +- [`runtime`](#runtime) - string, Lambda runtime or alias, see [below](#runtime) for details. +- [`storage`](#storage) - number, between `512` (default) and `10240` MB. The function's ephemeral storage (`/tmp` file system). +- [`timeout`](#timeout) - number, in seconds (max `900`) + +> Note: any function configurations made globally in your project manifest will be overridden by individual functions. For example, if your `app.arc` includes `memory 128`, and `src/http/get-index/config.arc` includes `memory 3008`, all functions except `get /` will be configured with 128MB of memory, while `get /` will override that global with 3008MB. + +Read more about the [Lambda limits](https://docs.aws.amazon.com/lambda/latest/dg/limits.html) and [resource model](https://docs.aws.amazon.com/lambda/latest/dg/resource-model.html). + + +### Example `config.arc` + +```arc +# src/function/dir/config.arc +@aws +runtime ruby +memory 256 +timeout 3 +concurrency 1 +layers {ARN} +policies {ARN} +architecture x86_64 +``` + + +### `architecture` + +Configure Lambda function [CPU `architecture`](https://docs.aws.amazon.com/lambda/latest/dg/foundation-arch.html) to be one of `arm64` or `x86_64`. This setting defaults to `arm64` if not specified. + +> Note: locally, Architect Sandbox executes the function's runtime with your machine's native architecture. + +```arc +@aws +architecture x86_64 +``` + + +### `concurrency` + +Configure Lambda function concurrency. If not present concurrency is unthrottled. + +Limit execution to one invocation at a time: + +```arc +@aws +concurrency 1 +``` + +> Tip: `@events` functions with `concurrency 1` create a queue-like primitive + +Disable invocation by setting concurrency to zero: + +```arc +@aws +concurrency 0 +``` + + +### `fifo` + +Configure SQS queue type to `fifo` (`true`, default) or `standard` (`false`). + +```arc +@aws +fifo false +``` + + +### `layers` + +Configure Lambda function `layers` with max 5 Lambda Layer ARNs. + +> Warning: Lambda Layers must be in the same region as they are deployed + +Add one layer: + +```arc +@aws +layers arn:aws:lambda:us-east-1:764866452798:layer:ghostscript:1 +``` + +Or multiple layers: + +```arc +@aws +layers + arn:aws:lambda:us-east-1:764866452798:layer:chrome-aws-lambda:4 + arn:aws:lambda:us-east-1:145266761615:layer:pandoc:1 +``` + +> Tip: find [awesome layers](https://github.com/mthenw/awesome-layers) + + +### `memory` + +Configure Lambda function `memory` between `128` MB to `10240` MB, in `1` MB increments. + +Memory size also directly correlates with CPU speed; higher memory levels are available in more capable Lambda clusters + +```arc +@aws +memory 1024 +``` + + +### `policies` + +Configure custom Lambda function `policies`, enabling granular and specific privileges and access controls. + +The `policies` setting takes one or more IAM policy ARNs or AWS-managed policy names (e.g. `AmazonDynamoDBFullAccess`). + +Configuring one or more policies will completely remove all of Architect's default Lambda privileges. To restore Architect's default privileges, include a policy named `architect-default-policies`. + +> Note: `architect-default-policies` is an internal Architect framework setting based on the least-privilege permissions specific to your project. It is not a managed / public IAM policy, and will not be found in your AWS console. + +Lambda only has a single set of permissions (as defined by the AWS-managed `S3CrudPolicy` policy): + +```arc +@aws +policies + S3CrudPolicy +``` + +Lambda has both an AWS-managed policy (`S3CrudPolicy`) and all default Architect permissions: +```arc +@aws +policies + S3CrudPolicy + architect-default-policies +``` + +Terser single-line version of the above example: +```arc +@aws +policies S3CrudPolicy architect-default-policies +``` + +#### Additional resources + +- [AWS IAM policy ARNs](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_identifiers.html#identifiers-arns) +- [Community-maintained list of AWS-managed policies](https://github.com/z0ph/MAMIP/tree/master/policies) + + +### `provisionedConcurrency` + +Configure a Lambda's provisioned concurrency, which ensures the concurrency specified will always respond without coldstart. By default, Architect never sets provisioned concurrency, as it costs money whether actively in use or not. + +> Note: be warned that this is one of the only features that starts costing money the moment it is enabled. Be especially careful when enabling this feature globally for your project, as it has the potential to run up your costs rather quickly. Please refer to [Lambda's provisioned concurrency pricing guide](https://aws.amazon.com/lambda/pricing/#Provisioned_Concurrency_Pricing). + +Set a provisioned concurrency of 10 warm containers for your Lambda: + +```arc +@aws +provisionedConcurrency 10 +``` + +Pair with `concurrency` to guarantee a Lambda with 10 warm containers that never exceeds 100 concurrent requests: + +```arc +@aws +concurrency 100 +provisionedConcurrency 10 +``` + + +### `runtime` + +Sets the Lambda function runtime to use. A version-less alias always references the latest available version for that runtime. See the [@aws `runtime`](../project-manifest/aws) documentation for full list of supported runtimes in Architect, and official [Lambda documentation](https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html) for further reference. + +```arc +@aws +runtime ruby +``` + + +### `storage` + +Configure Lambda function temporary file system size between `512` MB and `10240` MB, in `1` MB increments. Defaults to `512` MB. + +Ephemeral storage lives at `/tmp` in an AWS Lambda and will not persist between deployments. Amazon describes this disk space as a "scratch resource." Read more in [the AWS announcement post](https://aws.amazon.com/blogs/aws/aws-lambda-now-supports-up-to-10-gb-ephemeral-storage/). + +```arc +@aws +storage 5000 +``` + + +### `timeout` + +Configure Lambda function `timeout` in seconds to a max of `900`. (`15` minutes.) + +The default timeout (if no value supplied) is `5`. (`5` seconds.) + +```arc +@aws +timeout 30 +``` diff --git a/src/views/docs/en/reference/configuration/local-preferences.md b/src/views/docs/en/reference/configuration/local-preferences.md new file mode 100644 index 00000000..79e99482 --- /dev/null +++ b/src/views/docs/en/reference/configuration/local-preferences.md @@ -0,0 +1,162 @@ +--- +title: Local preferences +category: Configuration +description: Sandbox local preferences +sections: + - '@create' + - '@env' + - '@sandbox' + - '@sandbox-start' +--- + +> Architect preferences (`preferences.arc`, or `prefs.arc`) defines settings for local Architect workflows. This file is intended to be added to `.gitignore`. + +- [`@create`](#%40create) - Preferences for resource creation with `arc init` +- [`@env`](#%40env) - Configure environment variables +- [`@sandbox`](#%40sandbox) - Define [Sandbox][sandbox] preferences +- [`@sandbox-start`](#%40sandbox-start) - Hook into [Sandbox][sandbox]'s startup + +## `@create` + +Preferences for resource creation with [`arc init`][init]. + +### `autocreate` + +By adding the `@create` pragma to your preferences file and specifying `autocreate true`, you can enable [`arc sandbox`][sandbox], [`arc deploy`][deploy], and other workflows to automatically run [`arc init`][init] to create boilerplate Lambda handlers and static assets if they do not exist. + +```arc +@create +autocreate true +``` + +### `templates` + +Define custom boilerplate Lambda handlers on a per-pragma basis with `templates`: + +```arc +@create +templates + http path/to/template/http.js + events path/to/template/events.py +``` + +In the above example, new [`@http` functions][http] will use your `path/to/template/http.js` template instead of the Architect default, while creating new [`@events` functions][events] will use the `path/to/template/events.py`. This will work for either `autocreate true` or the [`arc init` command][init]. + +## `@env` + +Configure environment variables for `testing` with Sandbox and deployed `staging` and `production` environments. + +Sync environment variables to your project by using the [`arc env` CLI command][env]. If the preferences file does not exist Architect will generate a `preferences.arc` file. + +> Note: any time you run [`arc env`][env], your unsynced local environment variables will be overwritten. + +### Example + +```arc +@env +testing + A_TESTING_ENV_VAR something-for-testing + ANOTHER_VAR only-for-testing + +staging + A_STAGING_ENV_VAR something-for-staging + +production + A_PRODUCTION_ENV_VAR something-for-production +``` + +### `.env` file support + +Architect [Sandbox][sandbox] supports loading environment variables from a `.env` file. The `.env` will override your `preferences.arc` or `prefs.arc`, and environment variables it defines are only loaded for the `testing` environment. If you require locally configured env vars for `staging` or `production` environments, you must use `pref[erence]s.arc`. + +Note: as a friendly reminder, key / value pairs in `.env` files are separated by the `=` symbol. + +### Example `.env` file + +```bash +A_TESTING_ENV_VAR=something-for-testing +ANOTHER_VAR=only-for-testing +``` + +## `@sandbox` + +Define [Sandbox][sandbox] preferences. If you are not using a `.env` file then any environment variables set using the [`arc env` command][env] will be stored in the preferences file. In this scenario it is best _not_ to revision the preferences file in source control. + +### `livereload` - Boolean + +Enable automatic reload for HTML views when [`@http`][http] Lambda (`get` or `any`), [`@shared`][shared], or [`@views`][views] code changes. Defaults to `false`. `livereload` is helpful when developing view layouts and styling, as open browser sessions will automatically refresh. + +```arc +@sandbox +livereload true +``` + +> ⚠️ `livereload` will execute your [`@http`][http] handler with each change so long as it is a `get` or `any` path. Traditionally, these routes don't create data, but be mindful of how a reload might interact with your app's data layer before enabling. + +### `ports` - List + +Designate the local ports used by [Sandbox][sandbox] services. [Sandbox][sandbox] will scan for and use available ports unless specified. If a specified port is unavailable, [Sandbox][sandbox] will fail to boot. + +```arc +@sandbox +ports + http 4200 + events 4211 + queues 4222 + tables 4255 +``` + +### `env` - String + +Advanced option: override the local environment to use `staging` or `production` [environment variables][env]; if not specified, defaults to [`testing` variables](#%40env). This setting may introduce unexpected side effects, so only use it if you have a specific technical reason. + +```arc +@sandbox +env staging +``` + +### `useAWS` - Boolean + +Advanced option that instruct [Sandbox][sandbox] to use live AWS infrastructure where deployed, specifically: [`@tables`][tables] / [`@tables-indexes`][indexes] (DynamoDB), [`@events`][events] (EventBridge), and [`@queues`][queues] (SQS). Defaults to `false`. Notes: +- To use this feature, your local AWS credentials file must have valid keys to use this infrastructure (or calls to AWS will fail) +- If you do not specify an environment via [the `env` preference](#env---string), `staging` will be set automatically; you can also use `production` + +```arc +@sandbox +useAWS true +``` + +### `no-hydrate` - Boolean + +Disables [hydration][hydrate]. Defaults to `false`. + +```arc +@sandbox +no-hydrate true +``` + +## `@sandbox-start` + +Hook up CLI commands into [Sandbox][sandbox] startup. Helpful for repetitive tasks like seeding a database or starting up additional services for local development. Each command should be a separate unindented line under the `@sandbox-start` pragma. + + +### Example + +```arc +@sandbox-start +node scripts/seed_db.js +echo 'hello' +``` + +[deploy]: ../cli/deploy +[env]: ../cli/env +[hydrate]: ../cli/hydrate +[init]: ../cli/init +[sandbox]: ../cli/sandbox +[events]: ../project-manifest/events +[http]: ../project-manifest/http +[indexes]: ../project-manifest/tables-indexes +[queues]: ../project-manifest/queues +[shared]: ../project-manifest/shared +[tables]: ../project-manifest/tables +[views]: ../project-manifest/views diff --git a/src/views/docs/en/reference/prefs.arc/.env.md b/src/views/docs/en/reference/prefs.arc/.env.md deleted file mode 100644 index a0fb47e0..00000000 --- a/src/views/docs/en/reference/prefs.arc/.env.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -title: '.env' -description: Sandbox environment variables ---- - -Architect sandbox supports loading environment variables from a `.env` file. The `.env` will override your `preferences.arc` or `prefs.arc` and environment variables it defines will be loaded for whichever environment the sandbox is running (`testing`, `staging`, or `production`). - -## Example `.env` file - -```env -A_TESTING_ENV_VAR=something-for-testing -ANOTHER_VAR=only-for-testing -``` diff --git a/src/views/docs/en/reference/prefs.arc/create.md b/src/views/docs/en/reference/prefs.arc/create.md deleted file mode 100644 index 46d5a002..00000000 --- a/src/views/docs/en/reference/prefs.arc/create.md +++ /dev/null @@ -1,30 +0,0 @@ ---- -title: '@create' -description: Sandbox environment variables ---- - -> Architect preferences (`preferences.arc`, or `prefs.arc`) defines settings for local Architect workflows. This file is intended to be added to `.gitignore`. - -Preferences for resource creation with `arc init`. - -## `autocreate` - -By adding the `@create` pragma to your preferences file and specifying `autocreate true`, you can enable `arc sandbox`, `arc deploy`, and other workflows to automatically run `arc init` to create boilerplate Lambda handlers and static assets if they do not exist. - -```arc -@create -autocreate true -``` - -## `templates` - -Define custom boilerplate Lambda handlers on a per-pragma basis with `templates`: - -```arc -@create -templates - http path/to/template/http.js - events path/to/template/events.py -``` - -In the above example, new `@http` functions will use your `path/to/template/http.js` template instead of the Architect default, while creating new `@events` functions will use the `path/to/template/events.py`. This will work for either `autocreate true` or the `arc init` command. diff --git a/src/views/docs/en/reference/prefs.arc/env.md b/src/views/docs/en/reference/prefs.arc/env.md deleted file mode 100644 index accc4f5c..00000000 --- a/src/views/docs/en/reference/prefs.arc/env.md +++ /dev/null @@ -1,27 +0,0 @@ ---- -title: '@env' -description: Sandbox environment variables ---- - -> Architect preferences (`preferences.arc`, or `prefs.arc`) defines settings for local workflows. This file is intended to be added to `.gitignore`. - -Configure environment variables for `testing` with `arc sandbox` and deployed `staging` and `production` environments. - -Sync environment variables to your project by using the [`arc env` CLI command](/reference/cli/env). If the preferences file does not exist Architect will generate `preferences.arc` file. - -> Note: any time you run `arc env`, your unsynced local environment variables will be overwritten. - -## Example - -```arc -@env -testing - A_TESTING_ENV_VAR something-for-testing - ANOTHER_VAR only-for-testing - -staging - A_STAGING_ENV_VAR something-for-staging - -production - A_PRODUCTION_ENV_VAR something-for-production -``` diff --git a/src/views/docs/en/reference/prefs.arc/sandbox-startup.md b/src/views/docs/en/reference/prefs.arc/sandbox-startup.md deleted file mode 100644 index 7fda9a53..00000000 --- a/src/views/docs/en/reference/prefs.arc/sandbox-startup.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -title: '@sandbox-startup' -description: Sandbox scripts ---- - -Hook up CLI commands into [`arc sandbox`](../cli/sandbox) startup. Helpful for repetitive tasks like seeding a database or starting up additional services for local development. Each command should be a separate unindented line under the `@sandbox-startup` pragma. - -### Example - -```arc -@sandbox-startup -node scripts/seed_db.js -echo 'hello' -``` diff --git a/src/views/docs/en/reference/prefs.arc/sandbox.md b/src/views/docs/en/reference/prefs.arc/sandbox.md deleted file mode 100644 index 601b6d25..00000000 --- a/src/views/docs/en/reference/prefs.arc/sandbox.md +++ /dev/null @@ -1,37 +0,0 @@ ---- -title: '@sandbox' -description: Sandbox environment variables ---- - -> Architect preferences (`preferences.arc`, or `prefs.arc`) defines settings for local Architect workflows. - -Define [`arc sandbox`](../cli/sandbox) preferences. If you are not using a [`.env` file](.env) then any environment variables set using the [`arc env` CLI](../cli/env) will be stored in the preferences file. In this scenario it is best _not_ to revision the preferences file in source control. - -### `env` - Boolean - -Advanced option: set the `ARC_ENV` + `NODE_ENV` stage to `staging` or `production` and use the env vars for that stage (see the `@env` pragma above); if not specified, defaults to `testing`. This setting may introduce unexpected side effects, so only use it if you have a specific technical reason. - -```arc -@sandbox -env staging -``` - -### `useAWS` - Boolean - -Advanced option that uses live AWS infrastructure where deployed, specifically: `@tables` / `@indexes` (DynamoDB), `@events` (EventBridge), and `@queues` (SQS). Notes: -- To use this feature, your local AWS credentials file must have valid keys to use this infrastructure (or calls to AWS will fail) -- If you do not specify an environment, `staging` will be set automatically; you can also use `production` - -```arc -@sandbox -useAWS true -``` - -### `no-hydrate` - Boolean - -Disables hydration - -```arc -@sandbox -no-hydrate true -``` \ No newline at end of file diff --git a/src/views/docs/en/reference/app.arc/app.md b/src/views/docs/en/reference/project-manifest/app.md similarity index 54% rename from src/views/docs/en/reference/app.arc/app.md rename to src/views/docs/en/reference/project-manifest/app.md index eeb87738..64aa6bc6 100644 --- a/src/views/docs/en/reference/app.arc/app.md +++ b/src/views/docs/en/reference/project-manifest/app.md @@ -1,23 +1,26 @@ --- -title: '@app' -description: Define the application namespace. +title: '@app' +category: app.arc +description: Define the application namespace --- `@app` declares the application namespace ## Syntax -- Lowercase alphanumeric string -- Maximum of 10 characters -- Dashes allowed; underscores not allowed +- Lower + upper case alphanumeric string +- Maximum of 100 characters +- Dashes and underscores are allowed - Must begin with a letter +> 🪧 Changing the project's app name or [region](./aws#region) between deployments will create a new CloudFormation stack. The app namespace is used to create a stack name, which is unique to each AWS region. + ## Example Create an app with the namespace "foobaz": -
+
arc
@@ -41,16 +44,6 @@ foobaz
- -
toml
-
- -```toml -app="foobaz" -``` -
-
-
yaml
diff --git a/src/views/docs/en/reference/project-manifest/aws.md b/src/views/docs/en/reference/project-manifest/aws.md new file mode 100644 index 00000000..bb70b5c5 --- /dev/null +++ b/src/views/docs/en/reference/project-manifest/aws.md @@ -0,0 +1,172 @@ +--- +title: '@aws' +category: app.arc +description: Define AWS specific configuration. +--- + +Define AWS specific configuration for an entire project or [per function](../configuration/function-config#%40aws). + +## Syntax + +### `region` + +[AWS region ID](https://docs.aws.amazon.com/general/latest/gr/rande.html) where the project will be deployed. +- Defaults to `us-west-2` + + +### `profile` + +Local AWS profile name to use with this project, as defined in your [local AWS configuration](../../get-started/detailed-aws-setup#credentials). +- Can also be specified in `AWS_PROFILE` environment variable +- Required to deploy to AWS + + +### `runtime` + +[Lambda runtime](https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html), as defined by the [`lambda-runtimes`](https://github.com/architect/lambda-runtimes) library. + +> Note: please refer to the [runtime support matrix](/docs/en/get-started/runtime-support) for local [Sandbox](../cli/sandbox) support + +| Runtime | Versions | Example | Alias1 | +|---------|---------------------------------------|---------------|---------------------------| +| Node.js | 22.x (default), 20.x, 18.x | `nodejs20.x` | `node` `nodejs` `node.js` | +| Python | 3.13 (default), 3.12, 3.11, 3.10, 3.9 | `python3.12` | `python` `py` | +| Ruby | 3.3 (default), 3.2 | `ruby3.2` | `ruby` `rb` | +| .NET | 9 (default), 8 | `dotnet8` | `dotnet` `.net` | +| Java | 21 (default), 17, 11, 8.al2 | `java21` | `java` | + +1. Runtime aliases always use Architect's current default runtime version (e.g. `py` is effectively `python3.13`). + + +### `bucket` + +Bucket name (in same region) for CloudFormation deployment artifacts. + +If not specified, a secure deployment bucket will be automatically created. + + +### `policies` + +Configure custom Lambda function `policies`, enabling granular and specific privileges and access controls. + +The `policies` setting takes one or more IAM policy ARNs or AWS-managed policy names (e.g. `AmazonDynamoDBFullAccess`). + +Configuring one or more policies will completely remove all of Architect's default Lambda privileges. To restore Architect's default privileges, include a policy named `architect-default-policies`. + +> Note: `architect-default-policies` is an internal Architect framework setting based on the least-privilege permissions specific to your project. It is not a managed / public IAM policy, and will not be found in your AWS console. + + +### `layers` + +Configure Lambda function `layers` with max 5 Lambda Layer ARNs. Lambda Layers must be in the same region as they are deployed. + + +### `architecture` + +Lambda [CPU Architecture](https://docs.aws.amazon.com/lambda/latest/dg/foundation-arch.html) of your functions. +- `arm64` (default) - 64-bit ARM architecture +- `x86_64` - 64-bit x86 architecture + + +### `storage` + +Lambda ephemeral storage (a "scratch" file system in `/tmp` for each Lambda). A number between `512` (default) - `10240` in MB. + + +### `apigateway` + +API Gateway API type, can be one of: +- `http` (default) - `HTTP` API + Lambda payload format version 2.0 +- `httpv2` – alias of `http` +- `httpv1` - `HTTP` API + Lambda payload format version 1.0 (aka `REST`) +- `rest` - `REST` API + original API Gateway payload format (note: only supported when using the [`plugin-rest-api`](https://github.com/architect/plugin-rest-api) plugin) + +> Note: if configuring `apigateway rest` mode, you must use the `@architect/plugin-rest-api` in order to deploy your `REST` API to AWS + + +## Environment Variables + +Alternatively, if you want a less granular approach, you can declare your preferred region and profile in your shell config like `.bashrc` ([more information here](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html)). + +If you have AWS exports in your shell config and `@aws` specified in your `app.arc` project, the `@aws` section will win. + + +## Examples + +For example, to deploy Ruby to the northern California AWS AZ, with your AWS `work` profile's credentials, and specific policies use: + + +
+ + +
arc
+
+ +```arc +@aws +runtime ruby +region us-west-1 +profile work +storage 5000 # in MB +policies + S3CrudPolicy + architect-default-policies +``` + +
+
+ + +
json
+
+ +```json +{ + "aws": { + "runtime": "ruby", + "region": "us-west-1", + "profile": "work", + "storage": 5000, + "policies": [ + "S3CrudPolicy", + "architect-default-policies" + ] + } +} +``` + +
+
+ + +
yaml
+
+ +```yaml +--- +aws: + runtime: ruby + region: us-west-1 + profile: work + storage: 5000 + architecture: arm64 + policies: + - S3CrudPolicy + - architect-default-policies +``` + +
+
+ +
+
+ +To deploy to Oregon AWS AZ with your AWS `default` profile's credential and a [custom Lambda runtime](https://docs.aws.amazon.com/lambda/latest/dg/runtimes-custom.html) (be sure to set `runtime` to `provided`), use: + +```arc +@aws +region us-west-2 +profile default +runtime provided +layers arn:aws:lambda:us-west-2:800406105498:layer:nsolid-node-10:6 +``` diff --git a/src/views/docs/en/reference/project-manifest/events.md b/src/views/docs/en/reference/project-manifest/events.md new file mode 100644 index 00000000..7802e8a1 --- /dev/null +++ b/src/views/docs/en/reference/project-manifest/events.md @@ -0,0 +1,96 @@ +--- +title: '@events' +category: app.arc +--- + +Define SNS topics with Lambda handler functions. + +## Syntax + +- Lower + upper case alphanumeric string +- Maximum of 240 characters +- Dashes, periods, and underscores are allowed +- Must begin with a letter + +Events can use more verbose configuration to allow for [custom source paths](../../guides/developer-experience/custom-source-paths) in your project. Provide a `src` for each event. + + +## Example + +These configuration examples show how to define events: + + +
+ + +
arc
+
+ +```arc +@app +myapp + +@events +hit-counter +likes +# verbose custom source: +custom-webhook + src custom/source + +``` +
+
+ + +
json
+
+ +```json +{ + "app": "myapp", + "events": [ + "hit-counter", + "likes", + { + "custom-webhook": { + "src": "custom/source" + } + } + ] +} +``` +
+
+ + +
yaml
+
+ +```yaml +--- +app: "myapp" +events: +- hit-counter +- likes +# verbose custom source: +- "custom-webhook": + src: "custom/source" +``` +
+
+ +
+
+ +Running `arc create` generates the following handlers: + +```bash +/ +├── custom/ +│ └── source/ +├── src/events/ +│ ├── hit-counter/ +│ └── likes/ +├── app.arc +└── package.json +``` diff --git a/src/views/docs/en/reference/app.arc/http.md b/src/views/docs/en/reference/project-manifest/http.md similarity index 51% rename from src/views/docs/en/reference/app.arc/http.md rename to src/views/docs/en/reference/project-manifest/http.md index 35001f22..711a508b 100644 --- a/src/views/docs/en/reference/app.arc/http.md +++ b/src/views/docs/en/reference/project-manifest/http.md @@ -1,5 +1,6 @@ --- -title: '@http' +title: '@http' +category: app.arc description: Define HTTP routes --- @@ -7,30 +8,39 @@ Define HTTP routes in API Gateway with Lambda handler functions. ## Syntax -Each route is made up of by two parts: `verb` & `path` +Each route is made up of two parts: HTTP verb and a route path. - HTTP Verb + - `delete` - `get` + - `head` + - `options` + - `patch` - `post` - `put` - - `patch` - - `delete` + - `any`1 -- Path - - Dashes and underscores are not allowed +- Route Path + - Lower + upper case alphanumeric string + - Advised maximum of 100 characters for paths + - Dashes, periods, and underscores are allowed - Must begin with a letter - URL parameters are defined with a leading colon (`:`) + - A trailing asterisk (`*`) denotes a "catchall" (and can only be used as the final character) + +Routes can use more verbose configuration to allow for [custom source paths](../../guides/developer-experience/custom-source-paths) in your project. Provide a `method` and `src` for each route: -### Additional bits +- `method` - HTTP verb +- `src` - path to the function source -- Advised maximum of 100 characters +1. While not an HTTP verb, functions declared with `any` will be called for any valid HTTP method directed at that route. ## Example These configuration examples show how to define `@http` routes: -
+
arc
@@ -46,6 +56,11 @@ get /pages get /pages/:dateID get /contact post /contact +get /widgets/* # catch all unmatched routes +# verbose custom source: +/weather + method get + src custom/source ```
@@ -58,34 +73,24 @@ post /contact { "app": "myapp", "http": [ - ["get", "/"]. + ["get", "/"], ["get", "/pages"], ["get", "/pages/:dateID"], ["get", "/contact"], - ["post", "/contact"] + ["post", "/contact"], + ["get", "/widgets/*"], + { + "/weather": { + "method": "get", + "src": "custom/source", + } + }, ] } ```
- -
toml
-
- -```toml -app="myapp" -http=[ - ["get", "/"], - ["get", "/pages"], - ["get", "/pages/:dateID"], - ["get", "/contact"], - ["post", "/contact"] -] -``` -
-
-
yaml
@@ -99,6 +104,11 @@ http: - get: "/pages/:dateID" - get: "/contact" - post: "/contact" +- get: "/widgets/*" +# verbose custom source: +- "/weather": + method: get + src: "custom/source" ```
@@ -106,20 +116,22 @@ http:
-Which generates the following scaffolding: +Running `arc create` generates the following handlers: ```bash / -├── http +├── custom/ +│ └── source/ +├── src/http/ │ ├── get-index/ │ ├── get-pages/ │ ├── get-pages-000dateID/ │ ├── get-contact/ +│ ├── get-widgets-catchall/ │ └── post-contact/ ├── app.arc └── package.json ``` > ⚠️ Handlers generated from routes with URL parameters i.e. `/pages/:dateID`, substitute `:` for `000`. -> > This is a deliberate convention to ensure valid directory names that correspond with your defined parameterized route. diff --git a/src/views/docs/en/reference/app.arc/macros.md b/src/views/docs/en/reference/project-manifest/macros.md similarity index 77% rename from src/views/docs/en/reference/app.arc/macros.md rename to src/views/docs/en/reference/project-manifest/macros.md index 99122f98..b90835a8 100644 --- a/src/views/docs/en/reference/app.arc/macros.md +++ b/src/views/docs/en/reference/project-manifest/macros.md @@ -1,16 +1,19 @@ --- -title: '@macro' -description: Extend Architect app functionality +title: '@macros' +category: app.arc +description: Customize Architect-generated CloudFormation --- -Extend the functionality of your Architect app with standard CloudFormation. The `@macro` primitive allows developers to add any resources or modify existing ones extending Architect into the entire AWS ecosystem supported by CloudFormation. Macros also allow you to look for custom directives and add pre deploy steps. You can find some examples in our [github](https://github.com/architect/?q=macro-&type=source). +> Notice: `@macros` is no longer the preferred way to extend CloudFormation in Architect, and have been superseded by [`@plugins` `deploy.start`](/docs/en/guides/plugins/deploy#deploy.start). Existing `@macros` extensions will continue to be supported, but are no longer actively improved. + +Extend the functionality of your Architect app with standard CloudFormation. The `@macro` primitive allows developers to add any resources or modify existing ones extending Architect into the entire AWS ecosystem supported by CloudFormation. Macros also allow you to look for custom directives and add pre-deploy steps. You can find some examples in our [GitHub](https://github.com/architect/?q=macro-&type=source). ## Getting started These example configuration files declare a macro saved to `src/macros/my-custom-macro.js` -
+
arc
@@ -41,21 +44,6 @@ my-custom-macro
- -
toml
-
- -```toml -app="testapp" - -macros=[ - "my-custom-macro" -] - -``` -
-
-
yaml
@@ -89,7 +77,7 @@ You deploy a macro by using this syntax: Macros receive the parsed `app.arc` file so custom pragmas and config can be defined. The second argument is the current CloudFormation template. -```js +```javascript /** * @param {object} arc - the parsed app.arc file currently executing * @param {object} cloudformation - the current AWS::Serverless CloudFormation template diff --git a/src/views/docs/en/reference/project-manifest/plugins.md b/src/views/docs/en/reference/project-manifest/plugins.md new file mode 100644 index 00000000..dcf8ebe8 --- /dev/null +++ b/src/views/docs/en/reference/project-manifest/plugins.md @@ -0,0 +1,96 @@ +--- +title: '@plugins' +category: app.arc +description: Extend Architect app functionality and programmatically generate resources +--- + +Extend the functionality of your Architect app with `@plugins`. + +Architect’s plugin API exposes [workflow lifecycle hooks](/docs/en/guides/plugins/overview#workflow-hooks) (such filesystem events in the [Sandbox](/docs/en/reference/cli/sandbox)) and interfaces for [generating cloud resources](/docs/en/guides/plugins/overview#resource-setters) (such as custom Lambdas, or environment variables). + +Plugins can also be used to [customize your AWS deployment via CloudFormation](../../guides/developer-experience/custom-cloudformation), enabling access to cloud resources outside of Architect's built-ins. + + +## Getting started + +Create an inert plugin at `src/plugins/my-plugin.js` by running `npx arc init --plugin my-plugin`. Then add the `@plugins` pragma to your project manifest: + + +
+ + +
arc
+
+ +```arc +@app +testapp + +@plugins +my-plugin +``` +
+
+ + +
json
+
+ +```json +{ + "app": "testapp", + "plugins": [ + "my-plugin" + ] +} +``` +
+
+ + +
yaml
+
+ +```yaml +--- +app: testapp + +plugins: +- my-plugin +``` +
+
+ +
+
+ + +## Plugin locations + +Plugins can be private to your project, or [installed as a dependency from npm](https://www.npmjs.com/search?q=arc-plugin-). Architect will automatically attempt to find each named plugin in the following four locations: + +- `src/plugins/{plugin name}.js` +- `src/plugins/{plugin name}/index.js` +- `node_modules/{plugin name}/` +- `node_modules/@{plugin name}/` + + +## Plugin namespace + +`@plugins` can be used alongside [`@macros`](./macros) in the same project, however they share the same namespace. Consequently, a plugin and macro cannot share the same name. For example, the following project would error upon startup: + +```arc +@app +testapp + +@plugins +s3-events + +@macros +s3-events +``` + + +## Authoring plugins + +Learn more about authoring (and using) plugins [in the Architect plugins guide](/docs/en/guides/plugins/overview). diff --git a/src/views/docs/en/reference/app.arc/proxy.md b/src/views/docs/en/reference/project-manifest/proxy.md similarity index 81% rename from src/views/docs/en/reference/app.arc/proxy.md rename to src/views/docs/en/reference/project-manifest/proxy.md index d74a7f28..5cb177ee 100644 --- a/src/views/docs/en/reference/app.arc/proxy.md +++ b/src/views/docs/en/reference/project-manifest/proxy.md @@ -1,5 +1,6 @@ --- -title: '@proxy' +title: '@proxy' +category: app.arc description: Define a forwarding URL --- @@ -15,7 +16,7 @@ Defines a URL for API Gateway to forward all requests by default. Override with The following configuration file defines a `@proxy` for production that will handle requests for routes not defined in the `@http` section. -
+
arc
@@ -58,31 +59,12 @@ production https://example.biz
- -
toml
-
- -```toml -app="myapp" - -http=[ - ["get", "/v2/*"], - ["post", "/v2/*"] -] - -[proxy] -testing="http://localhost:4000" -staging="https://qa.example.biz" -production="https://example.biz" -``` -
-
-
yaml
```yaml +--- app: myapp http: - get: "/v2/*" @@ -97,6 +79,6 @@ proxy:
- + With the above Architect file, your new app will respond to all get and post requests to `/v2/*`, and forward along requests to `/v1` to your existing API. diff --git a/src/views/docs/en/reference/app.arc/queues.md b/src/views/docs/en/reference/project-manifest/queues.md similarity index 65% rename from src/views/docs/en/reference/app.arc/queues.md rename to src/views/docs/en/reference/project-manifest/queues.md index a78a9cb5..8d7a3b86 100644 --- a/src/views/docs/en/reference/app.arc/queues.md +++ b/src/views/docs/en/reference/project-manifest/queues.md @@ -1,5 +1,6 @@ --- -title: '@queues' +title: '@queues' +category: app.arc description: Define SQS topics --- @@ -7,9 +8,9 @@ Define SQS topics with Lambda handler functions. ### Syntax -- Lowercase alphanumeric string -- Maximum of 50 characters -- Dashes are allowed; underscores are not allowed +- Lower + upper case alphanumeric string +- Maximum of 240 characters +- Dashes, periods, and underscores are allowed - Must begin with a letter ### Example @@ -17,7 +18,7 @@ Define SQS topics with Lambda handler functions. This `app.arc` file defines two `@queues`: -
+
arc
@@ -31,6 +32,7 @@ myapp convert-image publish-log ``` +
@@ -47,21 +49,7 @@ publish-log ] } ``` -
-
- - -
toml
-
-```toml -app="myapp" - -queues=[ - "convert-image" - "publish-log" -] -```
@@ -70,23 +58,26 @@ queues=[
```yaml +--- app: myapp queues: - convert-image - publish-log ``` +
- + -Which generates the corresponding code: +Running `arc create` generates the following handlers: ```bash / -├── queues +├── src/queues/ │ ├── convert-image/ │ └── publish-log/ -└── app.arc +├── app.arc +└── package.json ``` diff --git a/src/views/docs/en/reference/project-manifest/scheduled.md b/src/views/docs/en/reference/project-manifest/scheduled.md new file mode 100644 index 00000000..ca44556e --- /dev/null +++ b/src/views/docs/en/reference/project-manifest/scheduled.md @@ -0,0 +1,100 @@ +--- +title: '@scheduled' +category: app.arc +description: Define EventBridge schedule expressions +--- + +Define EventBridge schedule expressions with Lambda handler functions. + +### Syntax + +- Lower + upper case alphanumeric string +- Maximum of 240 characters +- Dashes, periods, and underscores are allowed +- Must begin with a letter +- Followed by a valid `rate` or `cron` expression ([more info here](https://docs.aws.amazon.com/lambda/latest/dg/tutorial-scheduled-events-schedule-expressions.html)) + +Scheduled functions can use more verbose configuration to allow for [custom source paths](../../guides/developer-experience/custom-source-paths) in your project. Provide a `rate` or `cron` and `src` for each event. + +### Example + +These configuration examples show how to define scheduled functions: + + +
+ + +
arc
+
+ +```arc +@app +myapp + +@scheduled +daily-update-buddy rate(1 day) +friyay-only cron(0 15 ? * FRI *) +# verbose custom source: +annual-review + rate 1 year + src custom/source +``` + +
+
+ + +
json
+
+ +```json +{ + "app": "myapp", + "scheduled": { + "daily-update-buddy": "rate(1 day)", + "friyay-only": "cron(0 15 ? * FRI *)" }, + "annual-review": { + "rate": [1, "year"], + "src": "whatever/schedueld/dir/you/want" + } +} +``` + +
+
+ + +
yaml
+
+ +```yaml +--- +app: myapp +scheduled: + - daily-update-buddy: rate(1 day) + - friyay-only: cron(0 15 ? * FRI *) + # verbose custom source: + - "annual-review": + rate: + - 1, + - year + src: "custom/source" +``` + +
+
+ +
+
+ +Running `arc create` generates the following handlers: + +```bash +/ +├── custom/source/ +├── src/scheduled/ +│ ├── daily-update-buddy/ +│ └── friyay-only/ +├── app.arc +└── package.json +``` diff --git a/src/views/docs/en/reference/app.arc/shared.md b/src/views/docs/en/reference/project-manifest/shared.md similarity index 53% rename from src/views/docs/en/reference/app.arc/shared.md rename to src/views/docs/en/reference/project-manifest/shared.md index 513db5af..079333cf 100644 --- a/src/views/docs/en/reference/app.arc/shared.md +++ b/src/views/docs/en/reference/project-manifest/shared.md @@ -1,23 +1,21 @@ --- -title: '@shared' -description: Configure src/shared code +title: '@shared' +category: app.arc +description: Configure src/shared code --- -Configure the location of shared code. +Configure the location of shared code. For a full example, see [Sharing Code](../../guides/developer-experience/sharing-code). -### Syntax +## Syntax -- Lowercase alphanumeric string -- Maximum of 20 characters -- Dashes are allowed; underscores are not allowed -- Must begin with a letter +- File path of the folder containing shared code -### Example +## Example The following configuration examples define a different folder than the default `src/shared` directory. -
+
arc
@@ -48,24 +46,12 @@ src path/to/code
- -
toml
-
- -```toml -app="myapp" - -[shared] -src="path/to/code" -``` -
-
-
yaml
```yaml +--- app: myapp shared: - src: path/to/code @@ -75,3 +61,7 @@ shared:
+ +## Specific function opt-out + +A function can be [configured with a `config.arc`](../configuration/function-config#%40arc) to not have `@shared` code automatically hydrated. diff --git a/src/views/docs/en/reference/project-manifest/static.md b/src/views/docs/en/reference/project-manifest/static.md new file mode 100644 index 00000000..44521d67 --- /dev/null +++ b/src/views/docs/en/reference/project-manifest/static.md @@ -0,0 +1,108 @@ +--- +title: '@static' +category: app.arc +description: Define S3 bucket +--- + +Configure the static asset S3 bucket deployed by Architect. + +Note: `@static` is implied if `@http` is defined. + +## Syntax + +All parameters are optional. + +- `compression` - **boolean** or **string** (defaults to false) + - Enable static asset compression; `true` or `br` compresses with brotli, or `gzip` compresses with gzip +- `fingerprint` - **boolean** (defaults to false) + - Enable long-lived caching headers by static asset file fingerprinting +- `folder` - **string** (defaults to `./public`) + - Designate the local folder to upload static assets from. +- `ignore` - **list** + - Define which assets in the static `folder` should be ignored during upload +- `prefix` - **string** + - Set a top-level directory in the S3 bucket where files will be deployed +- `prune` - **boolean** (defaults to false) + - Automatically remove assets from S3 bucket not found in the static `folder` +- `spa` - **boolean** (defaults to false) + - Enable "Single Page App" delivery: all page requests route to the root. + +> 📜 The [Frontend Static assets guide](/docs/en/guides/frontend/static-assets) has more information on how to use static assets in your Architect project. + +## Example + +This `app.arc` file uses all `@static` options: + + +
+ + +
arc
+
+ +```arc +@app +test-app + +@static +fingerprint true +folder ./dist +ignore + .tar.gz + tmp + user +prune true +prefix assets +spa true +``` +
+
+ + +
json
+
+ +```json +{ + "app": "test-app", + "static": { + "fingerprint": true, + "folder": "./dist", + "ignore": [ + ".tar.gz", + "tmp", + "user" + ], + "prune": true, + "prefix": "assets", + "spa": true, + } +} +``` +
+
+ + +
yaml
+
+ +```yaml +--- +app: testapp + +static: + fingerprint: true + folder: ./dist + ignore: + - ".tar.gz" + - "tmp" + - "user" + prune: true + prefix: assets + spa: true +``` +
+
+ +
+
diff --git a/src/views/docs/en/reference/project-manifest/tables-indexes.md b/src/views/docs/en/reference/project-manifest/tables-indexes.md new file mode 100644 index 00000000..de347efa --- /dev/null +++ b/src/views/docs/en/reference/project-manifest/tables-indexes.md @@ -0,0 +1,130 @@ +--- +title: @tables-indexes +category: app.arc +description: Define DynamoDB table global secondary indexes. +--- + +Defines [Global Secondary Indexes][gsi] for your project's [DynamoDB][ddb] tables. `@tables-indexes` should only ever be paired with [`@tables`][tables]. + +> ℹ️ As of Architect v9.4, `@tables-indexes` should be used in place of `@indexes`. `@indexes` will be superseded in a future Arc release. + +## Recommended Resources + +[DynamoDB][ddb] is a powerful database, though different from both SQL and NoSQL databases. It is highly recommended to dig into Amazon's resources to familiarize yourself with it: + +- [DynamoDB Core Components (start here!)][core] +- [Global Secondary Indexes in DynamoDB][gsi] +- [Amazon's full DynamoDB documentation][ddb] + +## Syntax + +- `@tables-indexes` is a feature subset of [`@tables`][tables]; as such, the names of your declared indexes must match those of your [`@tables`][tables] +- AWS supports a maximum of 20 indexes per table +- Otherwise, the basic syntax for defining `@tables-indexes` primary keys is the same as [`@tables`][tables]: + - Partition key, defined by a `*`, is required + - Sort key, defined by `**`, is optional + - Currently only `*String`, `**String`, `*Number` and `**Number` are supported +- An optional `name` property can be provided to explicitly name the index. This is helpful when [querying the index with the AWS SDK as you know what to pass to the `IndexName` query parameter](https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB/DocumentClient.html#query-property) +- An optional `projection` property can be provided to explicitly define which [item attributes get projected][projection], or included, in query results. By default, Arc will project _all_ item attributes (the `ALL` projection type as described in the [DynamoDB documentation on attribute projections][projection]) + - Customizing which attributes to project can be helpful when trying to save on [storage costs][pricing] + - Note that once a projection is defined, it cannot be changed; a new index would need to be created + - Acceptable values for `projection` are: + - `all` (default): all item attributes from the table are projected into the index + - `keys`: only the base table and index primary keys (and sort keys, if defined) are projected into the index + - Custom: otherwise, you may define one or more attribute names to explicitly project into the index. Note that the base table and index keys always get projected + +## Deployment Considerations + +⚠️ Be careful when [`arc deploy`](../cli/deploy) indexes! There are a few unique CloudFormation behaviors that happen behind the scenes that you should be aware of, please see the [Important note on the CloudFormation reference page for Global Secondary Indexes](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-dynamodb-table.html#cfn-dynamodb-table-globalsecondaryindexes). In particular: + +- Creating a new index on an existing table can take time. While the index is being populated, you cannot update the table or use the index. Furthermore, CloudFormation will not block while the index is populating, so you should keep tabs on its state after creation. +- CloudFormation generally does not support index updating, aside from a few specific properties. +- You can only add or delete one index in each deploy. + +Given the above, it's generally recommended to silo application updates to just the bare minimum when working with indexes. + +## Example + +The following `app.arc` file defines a [DynamoDB][ddb] table with two named [Global Secondary Indexes][gsi], both with `projection` explicitly defined: + + +
+ +
arc
+
+ +```arc +@app +testapp + +@tables +accounts + accountID *String + +@tables-indexes +accounts + email *String + projection keys # only project base table and index keys (in this example that would be accountID and email) + name byEmail + +accounts + created *String + projection updated lastAccessed # only project base table and index keys plus the updated and lastAccessed attributes + name byDate +``` +
+
+ + +
json
+
+ +```json +{ + "app": "testapp", + "tables": [ + { "accounts": { "accountID": "*String" } } + ], + "indexes": [ + { "accounts": { "email": "*String", "name": "byEmail", "projection": "keys" } }, + { "accounts": { "created": "*String", "name": "byDate", "projection": [ "updated", "lastAccessed" ] } } + ] +} +``` +
+
+ + +
yaml
+
+ +```yaml +--- +app: testapp + +tables: +- accounts: + accountID: "*String" + +tables-indexes: +- accounts: + - email: "*String" + - name: "byEmail" + - projection: "keys" +- accounts: + - created: "*String" + - name: "byDate" + - projection: ["updated", "lastAccessed"] +``` +
+
+ +
+
+ +[tables]: tables +[core]: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.CoreComponents.html +[ddb]: https://aws.amazon.com/documentation/dynamodb/ +[gsi]: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GSI.html +[projection]: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GSI.html#GSI.Projections +[pricing]: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GSI.html#GSI.StorageConsiderations diff --git a/src/views/docs/en/reference/project-manifest/tables-streams.md b/src/views/docs/en/reference/project-manifest/tables-streams.md new file mode 100644 index 00000000..e9ed05a9 --- /dev/null +++ b/src/views/docs/en/reference/project-manifest/tables-streams.md @@ -0,0 +1,107 @@ +--- +title: '@tables-streams' +category: app.arc +description: Define DynamoDB tables with streaming changes +--- + +Define Lambda functions for streaming changes from DynamoDB tables. Respond to `insert`, `update`, and `destroy` events with a handler function. + +## Recommended Resources + +[AWS DynamoDB Streams](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Streams.Lambda.html) + +## Syntax + +- Name + - Lowercase alphanumeric string + - Must match a `@tables` name + +Table streams can use more verbose configuration to allow for [custom source paths](../../guides/developer-experience/custom-source-paths) and names in your project. This is beneficial when creating more than one stream function for a single table. Optionally provide a `name` and/or `src` for each table stream. + +- `name` - a string as defined in `@tables` +- `src` - path to the function source + +## Example + +This `app.arc` file defines a table with a corresponding stream handler: + + +
+ + +
arc
+
+ +```arc +@app +testapp + +@tables +people + pplID *String + +@tables-streams +people +# verbose custom source: +a-named-table-stream + table people + src custom/source + +``` +
+
+ + +
json
+
+ +```json +{ + "app": "testapp", + "tables": [ + { + "people": { + "pplID": "*String" + } + } + ], + "tables-streams": [ + "people", + { + "a-named-table-stream": { + "name": "people", + "src": "custom/source" + } + } + ] +} +``` +
+
+ + +
yaml
+
+ +```yaml +--- +app: testapp + +tables: +- people: + pplID: "*String" + +tables-streams: +- people +# verbose custom source: +- "a-named-table-stream": + name: "people" + src: "custom/source" +``` +
+
+ +
+
+ +> ⚠️ Unfortunately, "dynalite" (used under the hood in Architect [Sandbox](../cli/sandbox)) doesn't support streams, so emulation isn't yet supported for local development. diff --git a/src/views/docs/en/reference/app.arc/tables.md b/src/views/docs/en/reference/project-manifest/tables.md similarity index 78% rename from src/views/docs/en/reference/app.arc/tables.md rename to src/views/docs/en/reference/project-manifest/tables.md index 7df746d8..5f5c1d1b 100644 --- a/src/views/docs/en/reference/app.arc/tables.md +++ b/src/views/docs/en/reference/project-manifest/tables.md @@ -1,18 +1,20 @@ --- -title: '@tables' -description: Define DynamoDB tables +title: '@tables' +category: app.arc +description: Define DynamoDB database tables --- Define [DynamoDB][ddb] tables with optional: -- [streaming DB changes to Lambda functions][stream] - [encryption at rest][encryption] - [time-to-live item expiry][ttl] - [point-in-time recovery][recovery] -You can additionally define [Global Secondary Indexes][gsi] on each table you define using the [`@indexes`][indexes] pragma. +[Global Secondary Indexes][gsi] can be specified on each table you define using the [`@tables-indexes`][tables-indexes] pragma. -## Recommended +Additionally, database changes can be streamed to a function with the [`@tables-streams`][tables-streams] pragma. + +## Recommended Resources [DynamoDB][ddb] is a powerful database, though different from both SQL and NoSQL databases. It is highly recommended to dig into Amazon's resources to familiarize yourself with it: @@ -22,30 +24,32 @@ You can additionally define [Global Secondary Indexes][gsi] on each table you de ## Syntax ### Table name syntax -- Lowercase alphanumeric string + +- Lower + upper case alphanumeric string - Between 3 and 255 characters -- Dashes are allowed -- Underscores are not allowed +- Dashes, periods, and underscores are allowed - Must begin with a letter + ### Table structure syntax + - Keys and Lambdas are defined by indenting two spaces - The required partition key is denoted by `*` - The optional sort key is denoted by `**` - Currently only `*String`, `**String`, `*Number` and `**Number` are supported -- Streaming data has replaced the `insert`, `update`, and `destroy` events. +- `insert`, `update`, and `destroy` events can be handled with [`@tables-streams`][tables-streams] > Note: `app.arc` creates fully isolated tables for `staging` and `production`. ### Streaming Changes to a Lambda -Define a `stream true` property under a table definition to have Architect create a [Lambda function which will receive events whenever items in the table get inserted, updated or deleted][stream]. Architect will create the Lambda for you locally under `src/streams/`. +Use the [`@tables-streams`][tables-streams] pragma to have Architect create a [Lambda function which will receive events whenever items in the table get inserted, updated or deleted][stream]. Architect will create the Lambda for you locally under `src/tables-streams/`. ### Encrypting Tables Define a `encrypt` property under a table definition to enable [encryption at rest][encryption] for your DynamoDB table. If `encrypt` is a boolean (i.e. `encrypt true`), then AWS will manage the encryption key. If a non-boolean is provided to `encrypt` (i.e. `encrypt hithere`), then the parameter is assumed to be an [AWS Key Management Service][kms] custom [master key ID](https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id). -> Note: use of a custom master key will apply additional AWS KMS-related charges +> Note: use of a custom master key will apply additional AWS KMS related charges ### Time To Live @@ -59,10 +63,10 @@ DynamoDB has a feature which lets you [recover your data][recovery] to any point ## Example -This `app.arc` file defines two database tables: +This `app.arc` file defines three database tables: -
+
arc
@@ -75,7 +79,6 @@ testapp @tables people pplID *String - stream true cats pplID *String @@ -100,8 +103,7 @@ fleeting-thoughts "tables": [ { "people": { - "pplID": "*String", - "stream": true + "pplID": "*String" }, "cats": { "pplID": "*String", @@ -120,29 +122,6 @@ fleeting-thoughts
- -
toml
-
- -```toml -app="testapp" - -[tables] -[[tables.people]] -pplID="*String" -stream=true -[[tables.cats]] -pplID="*String" -catID="**String" -encrypt=true -PointInTimeRecovery=true -[[tables.fleeting-thoughts]] -pplID="*String" -expires="TTL" -``` -
-
-
yaml
@@ -154,7 +133,6 @@ app: testapp tables: - people: pplID: "*String" - stream: true - cats: pplID: "*String" catID: "**String" @@ -170,12 +148,13 @@ tables:
-[ddb]: https://aws.amazon.com/documentation/dynamodb/ +[tables-indexes]: tables-indexes +[tables-streams]: tables-streams [core]: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.CoreComponents.html -[gsi]: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GSI.html -[indexes]: indexes +[ddb]: https://aws.amazon.com/documentation/dynamodb/ [encryption]: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/EncryptionAtRest.html +[gsi]: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GSI.html +[kms]: https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html +[recovery]: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/PointInTimeRecovery.html [stream]: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Streams.Lambda.html [ttl]: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/TTL.html -[recovery]: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/PointInTimeRecovery.html -[kms]: https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html diff --git a/src/views/docs/en/reference/app.arc/views.md b/src/views/docs/en/reference/project-manifest/views.md similarity index 64% rename from src/views/docs/en/reference/app.arc/views.md rename to src/views/docs/en/reference/project-manifest/views.md index 7172fcb4..58a9cb28 100644 --- a/src/views/docs/en/reference/app.arc/views.md +++ b/src/views/docs/en/reference/project-manifest/views.md @@ -1,22 +1,25 @@ --- -title: '@views' +title: '@views' +category: app.arc description: Share view code across `@http` functions --- -Configure the location of view code. Architect considers copies view code into HTTP GET handler Lambda functions. +Configure the location of view code. +Architect copies view code to all HTTP GET handler functions by default. +You can also specify only the routes you want views copied to with the `@views` pragma. +For a full example, see [Sharing Code](../../guides/developer-experience/sharing-code). -You can also specify to only copy view code to specific lambda functions by listing them directly. ## Syntax - Routes should be existing `@http` routes. -- Route names follow the same requirements as `@http` routes. [see `@http`](@http) +- Route names follow the same requirements as `@http` routes. [see `@http`](http) ## Example This `app.arc` file defines specific `@http` functions to copy `src/views/` to: -
+
arc
@@ -61,34 +64,11 @@ get /raccoons
- -
toml
-
- -```toml -app="myapp" - -http=[ - [ "get", "/" ], - [ "get", "/kittens" ], - [ "get", "/dogs" ], - [ "get", "/raccoons" ] -] - -views=[ - [ "get", "/kittens" ], - [ "get", "/raccoons" ] -] - -``` -
-
-
yaml
-```yml +```yaml --- app: testapp @@ -107,3 +87,7 @@ views:
+ +## Specific function opt-out + +A function can be [configured with a `config.arc`](../configuration/function-config#%40arc) to not have `@views` code automatically hydrated. diff --git a/src/views/docs/en/reference/project-manifest/ws.md b/src/views/docs/en/reference/project-manifest/ws.md new file mode 100644 index 00000000..7dfedeb8 --- /dev/null +++ b/src/views/docs/en/reference/project-manifest/ws.md @@ -0,0 +1,131 @@ +--- +title: '@ws' +category: app.arc +description: Define WebSocket endpoints +--- + +Define WebSocket endpoint and Lambda handler functions. + +### Example + +This `app.arc` file defines both HTTP and WebSocket endpoints: + + +
+ + +
arc
+
+ +```arc +@app +myapp + +@ws +``` + +
+
+ + +
json
+
+ +```json +{ + "architect": { + "app": "myapp" + "ws": {} + }, + "start": "npx sandbox", + "dependencies": { + "@architect/architect": "latest" + } +} +``` + +
+
+ + +
yaml
+
+ +```yaml +--- +app: testapp + +ws: ~ +``` + +
+
+ +
+
+ +Running `arc create` generates the following WebSocket handlers, each mapping to a required WebSocket event (referred to as an action): + +```bash +/ +├── src/ws/ +│ ├── connect +│ ├── default +│ └── disconnect +├── app.arc +└── package.json +``` + +Each handler responds to WebSocket actions from clients. In the [payload delivered to the function](#function-payload) there is a `connectionId` that uniquely identifies a client. Use this `connectionId` to send messages to the correct client (if needed). + + +### Default actions + +Each action handler created by the `@ws` pragma receives events from WebSocket clients. + +- `connect` - Invoked when a WebSocket client connects to the application +- `default` - Invoked when a WebSocket client sends any (un-routed) message to the application +- `disconnect` - Invoked when a WebSocket client disconnects from your application + + +### Custom actions + +In addition to the three default WebSocket actions (`connect`, `default`, `disconnect`), you can create custom actions to be routed via message payloads like so: + +```arc +@ws +some-custom-action +another-custom-action +``` + +These will generate additional handlers in your `src/ws` dir (e.g. `src/ws/some-custom-action/`). Custom action invocation routing is performed by sending a JSON payload with the corresponding `action` property; for example: + +```javascript +// Assuming the client is already connected +ws.send(JSON.stringify({ + whatever: 'some data' +})) // Invokes `default` + +ws.send(JSON.stringify({ + action: 'some-custom-action', + whatever: 'related data' +})) // Invokes `some-custom-action` +``` + + +#### Payload + +WebSocket event payloads may contain a fair bit of data, but here are a few key bits: + +| Argument | Description | +| --- | --- | +| `req` | The WebSocket request payload | +| `req.requestContext.connectionId` | An ID that uniquely identifies the client | +| `req.body` | Body payload sent by the client (if present) | + + +#### Send messages + +To publish a message to a WebSocket client you can use Arc's runtime library `@architect/functions`' `ws.send` method. You can call this method from any of your application's functions so long as you have a valid `connectionId`. + +Docs: [Node.js](/docs/en/reference/runtime-helpers/node.js#arc.ws) | [Ruby](/docs/en/reference/runtime-helpers/ruby#arc.ws) | [Python](/docs/en/reference/runtime-helpers/python#arc.ws) diff --git a/src/views/docs/en/reference/runtime-helpers/deno.md b/src/views/docs/en/reference/runtime-helpers/deno.md new file mode 100644 index 00000000..79b4603a --- /dev/null +++ b/src/views/docs/en/reference/runtime-helpers/deno.md @@ -0,0 +1,12 @@ +--- +title: Deno runtime helpers +category: Runtime helpers +description: Deno runtime support +--- + +The Architect team has a strong interest in [Deno](https://deno.land/) as a runtime. Ideally, Deno would be supported both locally with Sandbox and in live infrastructure on AWS. + +* Track [runtime helper](https://github.com/architect/functions-deno) progress. +* Join the discussion on [GitHub](https://github.com/architect/architect/discussions) and [Discord](https://discord.gg/y5A2eTsCRX). + +Contributions are always welcome! diff --git a/src/views/docs/en/reference/runtime-helpers/node.js.md b/src/views/docs/en/reference/runtime-helpers/node.js.md new file mode 100644 index 00000000..faca894d --- /dev/null +++ b/src/views/docs/en/reference/runtime-helpers/node.js.md @@ -0,0 +1,787 @@ +--- +title: Node.js runtime helpers +category: Runtime helpers +description: "Node.js runtime utility libraries: `@architect/functions` `@architect/asap`" +--- + +Architect provides optional runtime utility libraries designed to make it significantly easier to work with provisioned resources and related assets. + +Architect has two primary helper libraries for Node.js: + +- [`@architect/functions`](#%40architect%2Ffunctions) - General purpose runtime helpers for various Architect resources, such as `@events`, `@http`, `@tables`, etc. +- [`@architect/asap`](#%40architect%2Fasap) - Helper designed solely for delivering static assets via `@http` endpoints + +--- + +# `@architect/functions` + +[View package source on GitHub](https://github.com/architect/functions/) + + +## Setup + +Install the Architect runtime helpers for Node.js: + +```bash +npm install @architect/functions +``` + +Ensure `arc` is available to your Lambda function code: + +```javascript +let arc = require('@architect/functions') +``` + +## Interfaces + +- [`arc.events`](#arc.events) Publish / subscribe helpers for `@events` functions +- [`arc.http`](#arc.http) Middleware and request/response normalization and session support for `@http` functions +- [`arc.queues`](#arc.queues) Publish/subscribe helpers for `@queues` functions +- [`arc.services`](#arc.services()) Retrieves the Architect service map, exposing metadata for all services making up the application +- [`arc.static`](#arc.static()) Get a `@static` asset path +- [`arc.tables`](#arc.tables()) Generates a DynamoDB client for `@tables` +- [`arc.ws`](#arc.ws) WebSocket helpers for `@ws` functions + +--- + +## `arc.events` + +Publish & subscribe helpers for `@events` functions. Declare events with the [`@events`](/docs/en/reference/project-manifest/events) pragma. + + +### `arc.events.subscribe()` + +Subscribe to events with a handler function. The function will be passed an `event` object, and, if not an `async` function, a callback to be called upon completion. + +```javascript +// async +let arc = require('@architect/functions') + +exports.handler = arc.events.subscribe(handler) + +async function handler (event) { + console.log(event) + return +} +``` + +```javascript +// continuation passing +let arc = require('@architect/functions') + +exports.handler = arc.events.subscribe(handler) + +function handler (event, callback) { + console.log(event) + callback() +} +``` + +### `arc.events.publish()` + +Publish an event to an `@events` function. An object containing two properties is required: +- **`name`** (string) - name of the `@events` function you'd like to publish to +- **`payload`** (object or array) - payload to be published + +```javascript +// async +let arc = require('@architect/functions') + +await arc.events.publish({ + name: 'hit-counter', + payload: { hits: 1 }, +}) +``` + +```javascript +// continuation passing +let arc = require('@architect/functions') + +arc.events.publish({ + name: 'hit-counter', + payload: { hits: 1 }, +}, (err) => console.log) +``` + +--- + +## `arc.http` + +`arc.http` provides middleware and request/response normalization for `@http` functions using your choice of `async` functions or Express-style callbacks. Declare HTTP routes with the [`@http`](/docs/en/reference/project-manifest/http) pragma. + +> A legacy `arc.http.async` middleware interface will remain available exclusively for `async` functions, although we strongly encourage using the unified `arc.http` interface. + + +### Requests + +`arc.http` provides the following: +- Built-in session support +- Added conveniences, such as automatic parsing of `req.body` +- Support for request formats from both AWS API Gateway `HTTP` and `REST` APIs +- Added properties commonplace in other web servers, such as `req.params` and `req.query` (as opposed to the much more verbose `req.pathParameters` and `req.queryStringParameters`, respectively) +- Backward-compatibility for `REST` API properties in `HTTP` APIs, enabling seamless API upgrades + +Handler functions passed to `arc.http` receive a `request` object containing all of the [API Gateway request properties](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html) specific to `HTTP` and `REST` APIs. Additionally, the following properties are added or improved for convenience: + +- `body` - **object** + - Automatically parsed if present; `{}` if request has no body +- `method` (alias of `httpMethod`) - **string** + - HTTP method of the request: `GET`, `POST`, `PATCH`, `PUT`, or `DELETE` +- `params` (alias of `pathParameters`) - **object** + - URL parameters, if defined in your HTTP function's path (e.g. `product` in `/shop/:product`); `{}` if request has none + - Example: `{ product: 'chocolate-chip-cookies' }` +- `path` - **string** + - Root-relative path of the URL being requested + - Example: `/shop/chocolate-chip-cookies` +- `query` (alias of `queryStringParameters`) - **object** + - Parsed query string parameters present in the client request; `{}` if request has none + - Example: `{ someParam: someValue }` +- `session` - **object** + - Automatically parsed from the request cookie; `{}` if no `session` is found for the requesting client + - Example: `{ accountID: 'a1b2c3' }` + - See the [sessions guide for more](/docs/en/guides/frontend/sessions) +- Additional backward-compatible `REST` properties available in `HTTP` APIs via `arc.http`: + - `resource` (an alias of `req.routeKey`) + - `path` (an alias of `req.rawPath`) + + + +> Caveat: Architect Functions does not deal in compatibility with `req.requestContext`; request context semantics are specific to the version of API Gateway in use (`REST` or `HTTP`) + +> Learn more about [API Gateway request payloads here](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html) + + +### Responses + +`arc.http` honors the standard [API Gateway response payload properties](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html) (`statusCode`, `headers`, `body`, etc.), in addition to adding the following convenience properties: + +- `cacheControl` - **string** + - Sets the `cache-control` header (or overrides it if already present) +- `compression` - **string** or **boolean** + - Defaults to Brotli (`br`); sets output compression of non-binary handler responses (e.g. JSON, HTML, etc.) + - If requesting client does not support default (`br`), it automatically falls back to gzip (`gzip`), and then disables compression + - If a compression type is manually specified (e.g. `compression: 'br'`) and the requesting client does not support it, compression is automatically disabled for that request + - To manually disable output compression for non-binary responses, specify `false` +- `cookie` - **string** + - Sets the `set-cookie` header (or overrides it if already present) + - Note: this convenience property predates API Gateway HTTP v2.0's `cookies` property; if using that payload format (which is Architect's default), passing `cookies` (an array) is probably better +- `cors` - **boolean** + - Sets the `access-control-allow-origin` header to `*` (or overrides it if already present) +- `status`, `code` (alias of `statusCode`) - **number** + - Sets the response HTTP status code +- `session` - **object** + - Create or overwrite a client session; see the [sessions guide for more](/docs/en/guides/frontend/sessions) +- `type` - **string** + - Sets the `content-type` header (or overrides it if already present) + +Additionally, you may also pass the following content properties (instead of manually setting `statusCode`, `headers`, and `body`): + +- `css` - **string** + - Sets the `content-type` header to `text/css; charset=utf8` +- `html` - **string** + - Sets the `content-type` header to `text/html; charset=utf8` +- `js` - **string** + - Sets the `content-type` header to `text/javascript; charset=utf8` +- `json` - **object or array** + - JSON-encodes the object or array and sets the `content-type` header to `application/json; charset=utf8` +- `text` - **string** + - Sets the `content-type` header to `text/plain; charset=utf8` +- `xml` - **string** + - Sets the `content-type` header to `text/xml; charset=utf8` + +Finally, you may also return a raw JavaScript Error, which will be interpreted as a status `500`, and output the `message` and `stack` in HTML. + +> Learn more about [API Gateway response payloads here](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html) + + +### Middleware + +Define `arc.http` middleware by passing one or more async or callback functions as parameters. + +In a given handler, all middleware functions must be either [async](#async-middleware) or [callback](#callback-middleware) – they cannot be mixed. + + +#### `async` middleware + +Utilize `async` middleware by passing one or more `async` functions as parameters. + +`async` functions are provided two positional arguments: +- `request` - **object** + - [Request object](#requests); mutations to the `request` object **are** passed along to future middleware functions +- `context` - **object** + - Standard API Gateway context object + +Execution flow is as follows: +- To exit the middleware queue early: + - Return a [valid response payload](#responses), or + - Return an `Error` (to gracefully error out) +- To invoke the following middleware function: + - Return the `request` object, or + - Return nothing, or + - Reach the end of execution + +```javascript +// single function +let arc = require('@architect/functions') + +exports.handler = arc.http(async req => { + return { + json: { ok: true } + } +}) +``` + +```javascript +// middleware +let arc = require('@architect/functions') + +exports.handler = arc.http(auth, handler) + +async function auth (req) { + if (!req.session.accountID) { + return { status: 403 } + } +} + +async function handler (req) { + return { + json: { ok: true } + } +} +``` + + +#### `callback` middleware + +Utilize `callback` middleware by passing one or more non-`async` functions as parameters. + +`callback` functions are provided three positional arguments: +- `request` - **object** + - [Request object](#requests); mutations to the `request` object **are not** passed along to future middleware functions +- `response` - **function** + - Callback that accepts either a [valid response payload](#responses) or `Error` in idiomatic errback style +- `next` - **function** (if not the final middleware) + - Invoke `next` to invoke the following middleware function + +Execution flow is as follows: +- To exit the middleware queue early: + - Invoke the `response` callback with a [valid response payload](#responses), or + - Invoke the `response` callback with an `Error` (to gracefully error out) +- To invoke the following middleware function: + - Invoke the `next` callback + +```javascript +// single function +let arc = require('@architect/functions') + +exports.handler = arc.http(handler) + +function handler (req, res) { + res({ + json: { ok: true } + }) +} +``` + +```javascript +// middleware +let arc = require('@architect/functions') + +exports.handler = arc.http(auth, handler) + +function auth (req, res, next) { + if (!request.session.accountID) { + res({ status: 401 }) + } + else next() +} + +function handler (req, res) { + res({ + json: { ok: true } + }) +} +``` + +--- + +### `arc.http.session` + +Generally we recommend working with sessions via [`arc.http`](#arc.http) by reading them via the `req` object, and writing them via the `session` property. + +However, should you need additional power and flexibility, we expose `arc.http.session` methods for manually reading the current session in an `@http` request, and writing it back to a cookie (that must then be sent back to the client via the `set-cookie` header). + + +#### Methods + +- `read(request[, callback]) → [Promise]` + - Accepts a [request object](#requests) + - Returns `session` object (or `{}` if none is found) + - Must be `await`ed if no callback is provided +- `write(session[, callback]) → [Promise]` + - Returns a `cookie` string to be passed in your `set-cookie` response headers + - If you do not pass the `cookie` in your response, your session may not be properly set or saved + - Must be `await`ed if no callback is provided + +> Please note that session variable encoding and decoding relies on the `ARC_APP_SECRET` [environment variable](../cli/env#security) being set to something secret and not easily guessable. If you use Architect sessions, please be sure to [set the `ARC_APP_SECRET` environment variable](../../guides/frontend/sessions#strong-session-secret)! + +```javascript +let arc = require('@architect/functions') + +exports.handler = async function handler (req) { + // Read the session + let session = await arc.http.session.read(req) + // Modify the state + session.count = (session.count || 0) + 1 + // Save the session state + let cookie = await arc.http.session.write(session) + // Set the client cookie + return { + statusCode: 200, + headers: { 'set-cookie': cookie }, + } +} +``` + +Alternatively, use `arc.http`'s automatic session handling: + +```javascript +let arc = require('@architect/functions') + +exports.handler = arc.http(async req => { + // Session already exists on `req` + let { session } = req + session.count = (session.count || 0) + 1 + // Write the session and set it on the client like so: + return { session } +}) +``` + +--- + +### `arc.queues` + +Publish & subscribe helpers for `@queues` functions. Declare queues with the [`@queues`](/docs/en/reference/project-manifest/queues) pragma. + +#### `arc.queues.subscribe()` + +Subscribe to queues with a handler function. The function will be passed an `event` object, and, if not an `async` function, a callback to be called upon completion. + +```javascript +// async +let arc = require('@architect/functions') + +exports.handler = arc.queues.subscribe(handler) + +async function handler (event) { + console.log(event) + return +} +``` + +```javascript +// continuation passing +let arc = require('@architect/functions') + +exports.handler = arc.queues.subscribe(handler) + +function handler (event, callback) { + console.log(event) + callback() +} +``` + +#### `arc.queues.publish()` + +Publish an event to an `@queues` function. An object containing two properties is required: +- **`name`** (string) - name of the `@queues` function you'd like to publish to +- **`payload`** (object or array) - payload to be published + +```javascript +// async +let arc = require('@architect/functions') + +await arc.queues.publish({ + name: 'hit-counter', + payload: { hits: 1 }, +}) +``` + +```javascript +// continuation passing +let arc = require('@architect/functions') + +arc.queues.publish({ + name: 'hit-counter', + payload: { hits: 1 }, +}, (err) => console.log) +``` + +--- + +### `arc.services()` + +Cloud resources are generated with names more friendly for machines than people. Other frameworks leave resource discovery up to end users, which leads to ad hoc implementations becoming a frequent bug vector. Architect treats service discovery as a first class concern. + +> Amazon Resource Names (ARNs) are available at runtime to all Lambda functions defined in the same Architect project manifest. Things such as DynamoDB tables, SNS topics, SQS queues, API Gateway endpoints, and S3 static bucket ARNs are baked into `@architect/functions` so your runtime program logic interacts with resources using readable, people-friendly names defined in your Architect project manifest. + +`arc.services()` retrieves the Architect service map: an object mapping the plugins and out-of-the-box Architect infrastructure that makes up your application. + +This object is lazily-loaded and cached, and thus the first call may incur a delay as the service map is populated (use of [`arc.events`](#arc.events), [`arc.queues`](#arc.queues) and [`arc.tables`](#arc.tables) transparently uses this method in the background). + +`arc.services()` returns a service map object, with keys equaling any out-of-the-box Architect infrastructure types or plugins used by the Architect application. + +An example service map for an application composed of `@static`, `@events` and an `imagebucket` plugin would have the following structure: + +```javascript +let arc = require('@architect/functions') + +let services = await arc.services() +/* +{ + // a plugin named 'imagebucket' exposing some service discovery variables + imagebucket: { + accessKey: 'someAccessKey', + name: 'arc-plugin-s3-image-bucket-example-image-buket', + secretKey: 'someSecretKey' + }, + // built-in @static service discovery variables + static: { + bucket: 'arcplugins3imagebucketexamplestaging-staticbucket-g8rsuk82ancj', + fingerprint: 'false' + }, + // built-in @events service discovery variables + events: { + myevent: 'https://some-sns-url.amazon.us-east-2.com' + } +} +*/ +``` + +--- + +### `arc.static()` + +Returns the path of a given static asset, intended for use with static asset fingerprinting (`@static fingerprint true`). + +Accepts two parameters: +- `asset` - **string** + - The root-relative path of the asset you'd like to load +- `options` - **object** that may contain the following properties: + - `stagePath` - **boolean** + - `REST` API compatibility option, enables prepending of the API stage + +```javascript +let css = arc.static('/index.css') +// '/_static/index-a1b2c3.css' + +let js = arc.static('/index.js', { stagePath: true }) +// '/staging/_static/index-b2c3d4.js' +``` + +--- + +### `arc.tables()` + +Creates a DynamoDB data layer, with raw client and other helpers, for your application's `@tables`. The client is an object, containing a nested object for each table. Declare tables with the [`@tables`](/docs/en/reference/project-manifest/tables) pragma. + + +#### Options + +`arc.tables()` accepts an optional object of additional options: + +- `awsSdkClient` - **boolean** + - Opt into instantiating and attaching an AWS SDK-based DynamoDB client to the `tables` client + - **Important note:** instantiating AWS SDK DynamoDB clients can take a very long time (>1000 ms), and is generally not advised for user hot paths! [Learn more here](https://aws-lite.org/performance). +- `awsjsonMarshall` - **object** + - Options for overriding default settings for [AWS flavored JSON marshalling](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/Package/-aws-sdk-util-dynamodb/Interface/marshallOptions/) +- `awsjsonUnmarshall` - **object** + - Options for overriding default settings for [AWS flavored JSON unmarshalling](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/Package/-aws-sdk-util-dynamodb/Interface/unmarshallOptions/) + + +```javascript +let arc = require('@architect/functions') +let client = await arc.tables() +client._client // aws-lite DynamoDB client +client._db // undefined +client._doc // undefined + +client = await arc.tables({ + awsSdkClient: true, + awsjsonMarshall: { convertClassInstanceToMap: true }, + awsjsonUnmarshall: { convertWithoutMapWrapper: false }, +}) +client._db // AWS.DynamoDB +client._doc // AWS.DynamoDB.DocumentClient +// ... the above methods are still available +``` + + +#### Client methods + +- `_client() → [Promise]` + - Instance of [`@aws-lite/dynamodb`](https://aws-lite.org/services/dynamodb) +- `_db(params[, callback]) → [Promise]` + - `nodejs16.x` (or lower) - instance of [`AWS.DynamoDB`](https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB.html) + - `nodejs18.x` (or higher) - instance of [`DynamoDB`](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-dynamodb/classes/dynamodb.html) +- `_doc(params[, callback]) → [Promise]` + - `nodejs16.x` (or lower) - instance of [`AWS.DynamoDB.DocumentClient`](https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB/DocumentClient.html) if instantiated with `awsSdkClient: true` + - `nodejs18.x` (or higher) - instance of [`DynamoDBDocument`](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/modules/_aws_sdk_lib_dynamodb.html) if instantiated with `awsSdkClient: true` +- `name(tablename)` + - Helper method that accepts a logical table name string, and returns a physical AWS resource name. Helpful for when you need to go lower level. + - For example use `client.name('my-table')` to get the human-unfriendly AWS name of the `my-table` `@tables` resource +- `reflect() → [object]` + - Returns a dictionary of table names with logical ids + +```arc +@app +testapp + +@tables +widgets + name *String +``` + +```javascript +let arc = require('@architect/functions') +let client = await arc.tables() +client._client // aws-lite DynamoDB client +client.name('widgets') // 'testapp-staging-widgets' +client.reflect() // { widgets: 'testapp-staging-widgets' } +``` + + +#### Instance methods + +Each table has the following methods: + +- `delete(key[, callback]) → [Promise]` + - Delete a record + - [Additional documentation](https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB/DocumentClient.html#delete-property) +- `get(key[, callback]) → [Promise]` + - Get a single row by primary key (and secondary index) + - [Additional documentation](https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB/DocumentClient.html#get-property) +- `put(record[, callback]) → [Promise]` + - Create or replace a record + - [Additional documentation](https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB/DocumentClient.html#put-property) +- `query(query[, callback]) → [Promise]` + - Query a table by passing a full document query object + - [Additional documentation](https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB/DocumentClient.html#query-property) +- `scan([options][, callback]) → [Promise]` + - Scan the table until pagination; accepts document filter object + - [Additional documentation](https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB/DocumentClient.html#scan-property) +- `scanAll([options][, callback]) → [Promise]` + - Scan the entire table with pagination handled automatically; accepts document filter object + - Larger tables generally equate to greater latency as `scanAll` paginates through all contents + - [Additional documentation](https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB/DocumentClient.html#scan-property) +- `update(record[, callback]) → [Promise]` + - Upsert a record; accepts document update object + - [Additional documentation](https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB/DocumentClient.html#update-property) + +> The generated client is facade for `AWS.DynamoDB.DocumentClient`. The `delete` and `get` methods take a single parameter that is passed on to the `params.Key` attribute in the corresponding `DocumentClient` method. The `put` method takes a single parameter that is passed on as the `params.Item` attribute in the `DocumentClient.put` method. The `query`, `scan`, and `update` methods simply pass the `params` argument with the `TableName` parameter prepopulated. [See the official DynamoDB documentation for all available parameters](https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB/DocumentClient.html). + + +#### Examples + +Given the following `app.arc` file: + +```arc +@app +people-app + +@tables +people + email *String + +@tables-indexes +people + job *String + name peopleByJob +``` + +A data access layer will be generated like so: + +```javascript +let arc = require('@architect/functions') +let client = await arc.tables() +let people = client.people + +// create Chuck and Jana +let chuck = await people.put({ + email: 'chuck@example.com', + job: 'Web Developer', + age: 35, +}) +let jana = await people.put({ + email: 'jana@example.com', + job: 'Web Developer', + age: 64, +}) + +// increment Jana's age +await people.update({ + Key: { email: jana.email }, + ExpressionAttributeValues: { ':inc': 1 }, + UpdateExpression: 'ADD age :inc' +}) + +// retrieve Jana's updated record +jana = await people.get({ email: jana.email }) + +// query for Web Developers using a secondary index +let developers = await people.query({ + IndexName: 'peopleByJob', + KeyConditionExpression: 'job = :job', + ExpressionAttributeValues: { ':job': 'Web Developer' }, +}) + +// scan the entire table for people over 64 +let retired = await people.scan({ + FilterExpression : 'age >= :sixtyfive', + ExpressionAttributeValues : {':sixtyfive' : 65}, +}) + +// delete Chuck and Jana +await client._doc.transactWrite({ + TransactItems: [ + { Delete: { TableName: 'people', Key: { email: chuck.email } } }, + { Delete: { TableName: 'people', Key: { email: jana.email } } }, + ] +}) +``` + +--- + +## `arc.ws` + +Interact with WebSocket services. Declare endpoints with the [`@ws`](/docs/en/reference/project-manifest/ws) pragma. + + +### `arc.ws.send()` + +Send a message via WebSocket. An object containing two properties is required: +- **`id`** (string) - API Gateway `connectionId` of the client you'd like to send the message to +- **`payload`** (object or array) - payload to be sent to the WebSocket client (as JSON) + +```javascript +let arc = require('@architect/functions') + +await arc.ws.send({ + id: connectionId, + payload: { + // Invokes @ws greetings, if defined + action: 'greetings', + message: 'Hello, friend!', + } +}) +``` + + +### `arc.ws.close()` + +Close a WebSocket connection with the provided id: +- **`id`** (string) - API Gateway `connectionId` of the client you'd like to close + +```javascript +let arc = require('@architect/functions') + +await arc.ws.close({ id: connectionId }) +``` + + +### `arc.ws.info()` + +A pass-through to the [ApiGatewayManagementApi#getConnection](https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/ApiGatewayManagementApi.html#getConnection-property) method. Retrieve information about the connection with the provided id: +- **`id`** (string) - API Gateway `connectionId` of the client you'd like get information about + +```javascript +let arc = require('@architect/functions') + +let info = await arc.ws.info({ id: connectionId }) +/* +{ + ConnectedAt: , + Identity: { + SourceIp: , + UserAgent: , + }, + LastActiveAt: , +} +*/ +``` + + +### `arc.ws._api()` + +Return the internal [`ApiGatewayManagementApi` client](https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/ApiGatewayManagementApi.html) from [`aws-lite`](https://aws-lite.org). + +```javascript +let arc = require('@architect/functions') + +let wsApi = await arc.ws._api() +``` + +--- + +# `@architect/asap` + +[View package source on GitHub](https://github.com/architect/asap/) + +### Setup + +Install the Architect static asset proxy (ASAP) for Node.js: + +```bash +npm install @architect/asap +``` + +### Parameters + +ASAP takes an optional configuration object with the following properties and returns an `async` Lambda handler: + +- `alias` - **object** + - Map of paths or files to alias to different paths + - Example: `{ '/an-asset.jpg': '/a-different-filename.jpg' }` +- `assets` - **object** + - Map of fingerprinted static assets; defaults to using the Arc-generated `static.json` + - Example: `{ 'some-file.gif': 'some-file-a1b2c3.gif' }` +- `bucket` - **object** containing the following properties: + - `staging` - **string** (required) + - Staging environment bucket name + - `production` - **string** (required) + - Production environment bucket name + - `folder` - **string** (optional) + - Folder path to treat as the root of all requests +- `cacheControl` - **string** + - Sets the cache-control header, overriding ASAP's default, content-aware cache-control header +- `headers` - **object** + - Set response headers + - Example: `{ 'some-header': 'ok=true' }` +- `passthru` - **boolean** (defaults to `false`) + - Return null if asset is not found +- `spa` - **boolean** (defaults to `false`) + - Enable single page app mode, all page requests deliver `/index.html` + +```javascript +// basic usage +let asap = require('@architect/asap') +let params = { cacheControl: 'max-age=0' } +exports.handler = asap(params) +``` + +```javascript +// asap as arc.http middleware +let arc = require('@architect/functions') +let asap = require('@architect/asap') + +exports.handler = arc.http(render, asap()) + +async function render (req) { + // If user is logged in, show them a custom logged in page + if (req.path === '/' && req.session.account) { + return { html: `Hello ${req.session.account.name}!` } + } + // Otherwise, load the logged out static page + return +} +``` diff --git a/src/views/docs/en/reference/runtime-helpers/python.md b/src/views/docs/en/reference/runtime-helpers/python.md new file mode 100644 index 00000000..1faa8db4 --- /dev/null +++ b/src/views/docs/en/reference/runtime-helpers/python.md @@ -0,0 +1,404 @@ +--- +title: Python runtime helpers +category: Runtime helpers +description: "Python runtime utility package: `architect-functions`" +--- + +Architect provides an optional runtime utility package designed to make it significantly easier to work with provisioned resources and related assets: [`architect-functions`](https://pypi.org/project/architect-functions/) + +We strongly suggest making use of this package when developing Python handlers with Architect. + + +## `architect-functions` + +[View package source on GitHub](https://github.com/architect/functions-python/) + + +## Setup + +Install `architect-functions` in your project's root `requirements.txt` file: + +```bash +pip3 install architect-functions -r requirements.txt +``` + +Ensure `arc` is available to your Lambda function code: + +```python +import arc +``` + +## Interfaces + +- [`arc.events`](#arc.events) Publish / subscribe helpers for `@events` functions +- [`arc.http`](#arc.http) Request/response normalization and session support for `@http` functions +- [`arc.queues`](#arc.queues) Publish/subscribe helpers for `@queues` functions +- [`arc.services`](#arc.services()) Retrieves the Architect service map, exposing metadata for all services making up the application +- [`arc.tables`](#arc.tables) Generates a DynamoDB client for `@tables` +- [`arc.ws`](#arc.ws) WebSocket helpers for `@ws` functions + + +--- + +## `arc.events` + +Publish & subscribe helpers for `@events` functions. Declare events with the [`@events`](/docs/en/reference/project-manifest/events) pragma. + + +### `arc.events.parse()` + +Parse the incoming (somewhat complicated, deeply-nested, JSON-encoded) `event`: + +```python +import arc + +def handler(evt): + event = arc.events.parse(evt) + print("incoming event:", event) +``` + + +### `arc.events.publish()` + +Publish an event to an `@events` function. Accepts two required arguments: +- **`name`** (string) - name of the `@events` function you'd like to publish to +- **`payload`** (dict or array) - payload to be published + +```python +import arc + +def handler(event): + payload = {"hello": "there"} + arc.events.publish("some-event", payload) +``` + +--- + +## `arc.http` + +Request, response, and session methods for `@http` functions. Declare HTTP routes with the [`@http`](/docs/en/reference/project-manifest/http) pragma. + + +### `arc.http.parse_body()` + +Parse or un-buffer the incoming `@http` request's body. Supports parsing JSON and form URL-encoded (`application/x-www-form-urlencoded`) data; other data types are merely decoded from base64 buffers. + +```python +import arc + +def handler(request, context): + body = arc.http.parse_body(request) + print("request body contents:", body) +``` + + +### `arc.http.res()` + +`arc.http.res()` provides a variety of conveniences when publishing HTTP responses, including built-in session writes, automatic compression and encoding of the response body, content-type shortcuts, and many more. + +`arc.http.res()` accepts two required positional arguments: +- **`request`** (dict) - the originating handler request parameter +- **`response`** (dict) - response payload (see below) + +A quick example: + +```python +import arc + +def handler(req, context): + return arc.http.res(req, {"hello": "world"}) +``` + +`arc.http.res()` response payloads honor the standard [API Gateway response payload properties](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html) (`statusCode`, `headers`, `body`, etc.), in addition to adding the following convenience properties: + +- `cache_control` - **str** + - Sets the `cache-control` header (or overrides it if already present) +- `compression` - **bool** + - Defaults to `gzip`; sets output compression of non-binary handler responses (e.g. JSON, HTML, etc.) + - If requesting client does not support default (`gzip`), compression is automatically disabled + - To manually disable output compression for non-binary responses, specify `False` +- `cookie` - **str** + - Sets the `set-cookie` header (or overrides it if already present) + - Note: this convenience property predates API Gateway HTTP v2.0's `cookies` property; if using that payload format (which is Architect's default), passing `cookies` (a list) is probably better +- `cors` - **bool** + - Sets the `access-control-allow-origin` header to `*` (or overrides it if already present) +- `status`, `code`, `status_code` (alias of `statusCode`) - **int** + - Sets the response HTTP status code +- `session` - **dict** + - Create or overwrite a client session; see the [sessions guide for more](/docs/en/guides/frontend/sessions) +- `type` - **str** + - Sets the `content-type` header (or overrides it if already present) + +Additionally, you may also pass the following content properties (instead of manually setting `status`, `headers`, and `body`): + +- `css` - **str** + - Sets the `content-type` header to `text/css; charset=utf8` +- `html` - **str** + - Sets the `content-type` header to `text/html; charset=utf8` +- `js` - **str** + - Sets the `content-type` header to `text/javascript; charset=utf8` +- `json` - **dict or list** + - JSON-encodes the object or array and sets the `content-type` header to `application/json; charset=utf8` +- `text` - **str** + - Sets the `content-type` header to `text/plain; charset=utf8` +- `xml` - **str** + - Sets the `content-type` header to `text/xml; charset=utf8` + +Finally, you may also return an Exception, which will be interpreted as a status `500`, and output the Exception `title` in HTML. + +> Learn more about [API Gateway response payloads here](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html) + + +#### Examples + +Respond with an HTML payload + +```python +import arc + +def handler(req, context): + payload = {"html": "

Hello, world!

"} + return arc.http.res(req, payload) +``` + + +Mutate a session property, then persist it via response + +```python +import arc + +def handler(req, context): + session = arc.http.session_read(req) + session["count"] += 1 + return arc.http.res( + req, + { + "session": session, + "json": {"ok": True}, + } + ) +``` + +--- + + +### `arc.http.session_read()` + +Read a client session from an incoming request. Returns the client's session dict (or `{}` if none is found). + +```python +import arc + +def handler(req, context): + session = arc.http.session_read(req) + print("user name:", session.get("name")) + return {"ok": True} +``` + +--- + +### `arc.http.session_write()` + +Manually write a client session. Generally we recommend writing sessions by passing a `session` property via [`arc.http.res()`](#arc.http.res()). + +However, should you need additional power and flexibility when writing sessions, we expose `arc.http.session_write()` for manually writing the session. This method returns a cookie that must be sent back to the client via the `set-cookie` header. + +```python +import arc +import json + +def handler(req, context): + session = arc.http.session_read(req) + session["count"] += 1 + cookie = arc.http.session_write(session) + return { + "statusCode": 200, + "body": json.dumps({"ok": True}), + "headers": { + "set-cookie": cookie, + }, + } +``` + +--- + +## `arc.queues` + +Publish & subscribe helpers for `@queues` functions. Declare queues with the [`@queues`](/docs/en/reference/project-manifest/queues) pragma. + + +### `arc.queues.parse()` + +Parse the incoming (somewhat complicated, deeply-nested, JSON-encoded) `event`: + +```python +import arc + +def handler(evt): + event = arc.queues.parse(evt) + print("incoming event:", event) +``` + + +### `arc.queues.publish()` + +Publish an event to an `@queues` function. Accepts two required arguments: +- **`name`** (string) - name of the `@queues` function you'd like to publish to +- **`payload`** (dict or array) - payload to be published + +```python +import arc + +def handler(event): + payload = {"hello": "there"} + arc.queues.publish("some-event", payload) +``` + +--- + +## `arc.services()` + +Cloud resources are generated with names more friendly for machines than people. Other frameworks leave resource discovery up to end users, which leads to ad hoc implementations becoming a frequent bug vector. Architect treats service discovery as a first class concern. + +> Amazon Resource Names (ARNs) are available at runtime to all Lambda functions defined in the same Architect project manifest. Things such as DynamoDB tables, SNS topics, SQS queues, API Gateway endpoints, and S3 static bucket ARNs are baked into `@architect/functions` so your runtime program logic interacts with resources using readable, people-friendly names defined in your Architect project manifest. + +`arc.services()` retrieves the Architect service map: an object mapping the plugins and out-of-the-box Architect infrastructure that makes up your application. + +This object is lazily-loaded and cached, and thus the first call may incur a delay as the service map is populated (use of [`arc.events`](#arc.events), [`arc.queues`](#arc.queues) and [`arc.tables`](#arc.tables) transparently uses this method in the background). + +`arc.services()` returns a service map object, with keys equaling any out-of-the-box Architect infrastructure types or plugins used by the Architect application. + +An example service map for an application composed of `@static`, `@events` and an `imagebucket` plugin would have the following structure: + +```python +import arc + +def handler(event): + services = arc.services() + print(services) + # { + # # a plugin named 'imagebucket' exposing some service discovery variables + # "imagebucket": { + # "accessKey": "someAccessKey", + # "name": "arc-plugin-s3-image-bucket-example-image-buket", + # "secretKey": "someSecretKey" + # }, + # # built-in @static service discovery variables + # "static": { + # "bucket": "arcplugins3imagebucketexamplestaging-staticbucket-g8rsuk82ancj", + # "fingerprint": "false" + # }, + # # built-in @events service discovery variables + # "events": { + # "myevent": "https://some-sns-url.amazon.us-east-2.com" + # } + # } +``` + +--- + +## `arc.tables` + +Client & resource helpers for DynamoDB tables. Declare tables with the [`@tables`](/docs/en/reference/project-manifest/tables) pragma. + + +### `arc.tables.name()` + +Helper method that accepts a logical table name string, and returns a physical AWS resource name. Helpful for when you need to go lower level than the DynamoDB resource provided by `arc.tables.table()`. + +For example use `arc.tables.name('my-table')` to get the human-unfriendly AWS name of the `my-table` `@tables` resource. + +```python +import arc + +def handler(event): + name = arc.tables.name('my-table') + print(name) + # MyTableStagingABC123 +``` + + +### `arc.tables.table()` + +Accepts a logical table name string and returns a DynamoDB client (specifically, a [DynamoDB resource](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/dynamodb/table/index.html)) for your application's `@tables`. + +```python +import arc + +def handler(event): + data = arc.tables.table('my-table') + items = data.scan() + print("all data from my-table:", items.get("Items")) +``` + +--- + +## `arc.ws` + +Interact with WebSocket services. Declare endpoints with the [`@ws`](/docs/en/reference/project-manifest/ws) pragma. + + +### `arc.ws.send()` + +Send a message via WebSocket. Accepts two required positional parameters: +- **`id`** (string) - API Gateway `ConnectionId` of the client you'd like to send the message to +- **`payload`** (dict or list) - payload to be sent to the WebSocket client (as JSON) + +```python +# src/ws/connect/lambda.py +import arc + +def handler(req, context): + connection_id = req["requestContext"]["connectionId"] + arc.ws.send(connection_id, {"hello": "there"}) +``` + + +### `arc.ws.api()` + +Return the internal [`ApiGatewayManagementApi` client](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/apigatewaymanagementapi.html) from `boto3`. + +```python +import arc + +def handler(req, context): + api = arc.ws.api() +``` + + +### `arc.ws.close()` + +Close a WebSocket connection with the provided id: +- **`id`** (string) - API Gateway `connectionId` of the client you'd like to close + +```python +import arc + +def handler(req, context): + connection_id = req["requestContext"]["connectionId"] + arc.ws.close(connection_id) +``` + + +### `arc.ws.info()` + +A pass-through to the [ApiGatewayManagementApi#get_connection](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/apigatewaymanagementapi/client/get_connection.html) method. Retrieve information about the connection with the provided id: +- **`id`** (string) - API Gateway `ConnectionId` of the client you'd like get information about + +```python +# src/ws/connect/lambda.py +import arc + +def handler(req, context): + connection_id = req["requestContext"]["connectionId"] + info = arc.ws.info(connection_id) + print(info) + # { + # "ConnectedAt": datetime(2023, 1, 1), + # "Identity": { + # "SourceIp": "10.0.0.1", + # "UserAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)..." + # }, + # "LastActiveAt": datetime(2023, 8, 11) + # } +``` diff --git a/src/views/docs/en/reference/runtime/ruby.md b/src/views/docs/en/reference/runtime-helpers/ruby.md similarity index 64% rename from src/views/docs/en/reference/runtime/ruby.md rename to src/views/docs/en/reference/runtime-helpers/ruby.md index c2291b10..09fca7a5 100644 --- a/src/views/docs/en/reference/runtime/ruby.md +++ b/src/views/docs/en/reference/runtime-helpers/ruby.md @@ -1,28 +1,41 @@ --- -title: Ruby +title: Ruby runtime helpers [deprecated] +category: Runtime helpers description: Ruby runtime support --- -[Helpers for working with the Architect generated runtime resources.](https://github.com/architect/functions-ruby) +## ⚠️ Architect's Ruby runtime utility library is now deprecated + +Architect continues to support Ruby Lambda, but no longer actively maintains a Ruby utility library. For more information, please see our [runtime support doc](/docs/en/get-started/runtime-support). + +The information presented below is for reference only. + +--- + + +[View package source on GitHub](https://github.com/architect/functions-ruby/) ## Install ```bash cd path/to/lambda bundle init -bundle install --path vendor/bundle +bundle config set --local path 'vendor/bundle' bundle add architect-functions ``` +See important notes about [deployment configuration for Bundler](../../guides/developer-experience/dependency-management#deployment-configuration). + ## API ```ruby # example lambda function -require 'json' +require 'bundler/setup' require 'architect/functions' +require 'json' def handler - {body: JSON.generate(Arc.reflect)} + { body: JSON.generate(Arc.reflect) } end ``` @@ -74,4 +87,3 @@ Example output: ### `Arc::Tables` - `name(table)` return the CloudFormation name for the given table name - diff --git a/src/views/docs/en/reference/runtime/deno.md b/src/views/docs/en/reference/runtime/deno.md deleted file mode 100644 index 309dfa45..00000000 --- a/src/views/docs/en/reference/runtime/deno.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: Deno -description: Deno runtime support ---- - -Coming soon: and you can help out! [Track the issue here.](https://github.com/architect/architect/issues/1035) diff --git a/src/views/docs/en/reference/runtime/node.md b/src/views/docs/en/reference/runtime/node.md deleted file mode 100644 index e8f5457f..00000000 --- a/src/views/docs/en/reference/runtime/node.md +++ /dev/null @@ -1,235 +0,0 @@ ---- -title: Node -description: Node runtime helpers ---- - -Architect runtime helpers are optional but they do make working with CloudFormation provisioned resources nicer. CloudFormation resources are generated with names more friendly for machines than people. Other frameworks leave resource discovery up to end users which leads to ad hoc implementations becoming a frequent bug vector. Architect treats runtime discovery as a first class concern. - -> Amazon Resource Names (ARNs) are available at runtime to all Lambda functions defined in the same `app.arc`. Things such as DynamoDB tables, SNS topics, SQS queues, API Gateway endpoints, and S3 static bucket ARNs are baked into `@architect/functions` so your runtime program logic interacts with resources using people friendly and readable names defined in the `app.arc` file. - -## Setup - -Install the Architect runtime helpers for Node: - -```bash -npm install @architect/functions -``` - -Ensure `arc` is available to your Lambda function code: - -```javascript -let arc = require('@architect/functions') -``` - -## API - -- [`arc.static`](#arc.static) Get a `@static` asset path -- [`arc.http.async`](#arc.http.async) Middleware for `@http` functions -- [`arc.http.express`](#arc.http.express) Express support for `@http` functions -- [`arc.http.proxy`](#arc.http.proxy) Middleware for `@static` assets -- [`arc.http.session`](#arc.http.session) Sessions for `@http` functions -- [`arc.tables`](#arc.tables) Generates a DynamoDB client for `@tables` -- [`arc.events`](#arc.events) Publish/subscribe helpers for SNS `@events` functions -- [`arc.queues`](#arc.queues) Publish/subscribe helpers for SQS `@queues` functions - -### `arc.static` - -Get a static asset path: - -```javascript -let css = arc.static('/index.css') -``` - -### `arc.http.async` - -Middleware with `async` functions is defined on `arc.http.async` with middleware functions as parameters. The returned function adheres to the expected AWS Lambda function signature. A function can exit the middleware queue early by returning an HTTP response. - -```javascript -let arc = require('@architect/functions') - -exports.handler = arc.http.async(auth, handler) - -async function auth(request) { - if (!request.session.account) { - return { status: 403 } - } -} - -async function handler(request) { - return { - json: { ok: true } - } -} -``` - -#### Request - -The incoming request object is [the standard API Gateway request](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html) with a few enhancements: - -- `body` is automatically parsed -- `session` automatically parsed from the request cookie - -#### Response - -Architect honors the standard API Gateway response payload parameters: - -- `statusCode` -- `headers` -- `body` -- `isBase64Encoded` - -And adds the following clean convenience params: - -- `cacheControl` sets the `Cache-Control` header -- `css` sets the `Content-Type` header to `text/css; charset=utf8` -- `code` alias for `statusCode` -- `cors` sets the `Access-Control-Allow-Origin` header to `*` -- `html` sets the `Content-Type` header to `text/html; charset=utf8` -- `js` sets the `Content-Type` header to `text/javascript; charset=utf8` -- `json` sets the `Content-Type` header to `application/json` -- `session` write a value to the session -- `status` also an alias for `statusCode` -- `text` sets the `Content-Type` header to `text/plain; charset=utf8` -- `type` sets the `Content-Type` header -- `xml` sets the `Content-Type` header to `text/xml; charset=utf8` - -### `arc.http.express` - -[Express](https://expressjs.com) migration helper. - -```javascript -let arc = require('@architect/functions') -let express = require('express') - -let app = express() - -app.get('/', (req, res) => res.send('Hello World!')) -app.get('/cool', (req, res)=> res.send('very cool')) - -exports.handler = arc.http.express(app) -``` - -### `arc.http.proxy` - -Middleware for serving `@static` folder assets. - -```javascript -let arc = require('@architect/functions') - -let asap = arc.http.proxy({ - spa: false, - alias: { - '/playground': '/playground.html' - } -}) - -exports.handler = arc.http.async(asap) -``` - -### `arc.http.session` - -Read the current session in an `@http` request and write it back to a cookie. - -```javascript -async function handler (req) { - // read the session - let session = await arc.http.session.read(req) - // save the session into a cookie string - let cookie = await arc.http.session.write({ count: 1 }) - // write the cookie to the browser - return { - statusCode: 200, - headers: { 'set-cookie': cookie }, - } -} -``` - -### `arc.tables` - -Create a DynamoDB client for `@tables`. - -Given the following `app.arc` file: - -```arc -@app -testapp - -@tables -notes - personID *String - noteID **String -``` - -Generate a data access layer: - -```javascript -let arc = require('@architect/functions') -let data = await arc.tables() -``` - -For the example above the generated API is: - -- `data.notes.get` -- `data.notes.query` -- `data.notes.scan` -- `data.notes.put` -- `data.notes.delete` -- `data.notes.update` - -> The generated client is facade for AWS.DynamoDB.DocumentClient. The `delete` and `get` methods take a single parameter that is passed on to the `params.Key` attribute in the corresponding DocumentClient method. The `put` method takes a single parameter that is passed on as the `params.Item` attribute in the DocumentClient.put method. The `query`, `scan`, and `update` methods simply pass the `params` argument with the `TableName` parameter prepopulated. See the official DynamoDB documentation for all available parameters. - -The generated data layer also allows direct access to DynamoDB through a few methods: - -- `data._db` which returns an instance of [`AWS.DynamoDB`](https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB.html) -- `data._doc` returns an instance of [`AWS.DynamoDB.DocumentClient`](https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB/DocumentClient.html) -- `data._name` helper function that returns a `@table` resource name when you need to go lower level. For example use `data._name("my-table")` to get the name of the "my-table" `@table` resource. - -### `arc.events` - -Subscribe to an SNS topic - -```javascript -let arc = require('@architect/functions') - -exports.handler = arc.events.subscribe(handler) - -async function handler (event) { - console.log(event) -} -``` - -Publish to an SNS topic - -```javascript -let arc = require('@architect/functions') - -await arc.events.publish({ - name: 'hit-counter', - payload: {hits: 1}, -}) -``` - -### `arc.queues` - -Subscribe to an SQS topic - -```javascript -let arc = require('@architect/functions') - -exports.handler = arc.queues.subscribe(handler) - -async function handler (event) { - console.log(event) -} -``` - -Publish to an SNS topic - -```javascript -let arc = require('@architect/functions') - -await arc.queues.publish({ - name: 'hit-counter', - payload: {hits: 1}, -}) -``` diff --git a/src/views/docs/en/reference/runtime/python.md b/src/views/docs/en/reference/runtime/python.md deleted file mode 100644 index 2000c634..00000000 --- a/src/views/docs/en/reference/runtime/python.md +++ /dev/null @@ -1,73 +0,0 @@ ---- -title: Python -description: Python runtime support ---- - -[Python runtime support for Architect provisioned AWS Lambda functions.](https://github.com/architect/functions-python) - -## Install - -```bash -cd path/to/lambda -pip install --target ./vendor architect-functions -``` - -## API - -```python -# example lambda function -import json -import arc - -def handler(event, context): - return {'body': json.dumps(arc.reflect())} -``` - -### `arc` - -`arc.reflect` returns a dict of the current AWS resources. - -Example output: - -```javascript -{ - "events": { - "ping": "arn:aws:sns:us-east-1:555:TestStaging-PingTopic-11111111111", - }, - "queues": { - "continuum": "https://sqs.us-east-1.amazonaws.com/555/TestStaging-ContinuumQueue-8888888888" - }, - "static": { - "bucket": "teststaging-staticbucket-11111111", - "fingerprint": "false" - }, - "tables": { - "noises": "TestStaging-NoisesTable-111111111" - }, - "ws": { - "https": "https://xxx.execute-api.us-east-1.amazonaws.com/production/@connections", - "wss": "wss://xxx.execute-api.us-east-1.amazonaws.com/production" - } -} -``` - -### `arc.http` - -- `arc.http.session_read()` -- `arc.http.session_write()` - -### `arc.ws` - -- `arc.ws.send(id, payload)` - -### `arc.events` - -- `arc.events.publish(name, payload)` - -### `arc.queues` - -- `arc.queues.publish(name, payload)` - -### `arc.tables` - -- `arc.tables.name(tablename)` diff --git a/src/views/docs/en/the-architect-way.md b/src/views/docs/en/the-architect-way.md new file mode 100644 index 00000000..16f6cd9a --- /dev/null +++ b/src/views/docs/en/the-architect-way.md @@ -0,0 +1,19 @@ +--- +title: The Architect Way +category: Architect +description: Building applications with Architect +sections: + - "Excellent developer experience" + - "Cloud function centric development" + - "Local, offline development" # note: possible to work online with real infra + - "Code sharing across functions" + - "Automated dependency management" + - "Discrete environments" + - "Declarative deployment" + - "Runtime resource discovery" + - "Optional helpers" + - "Secured to least privilege by default" + - "Incremental enhancement & development" + - "Open source and open governance" + # - "¿Comparison to other frameworks?" +--- diff --git a/src/views/docs/table-of-contents.js b/src/views/docs/table-of-contents.js deleted file mode 100644 index 9469fd50..00000000 --- a/src/views/docs/table-of-contents.js +++ /dev/null @@ -1,102 +0,0 @@ -let Guides = [ { - 'Get started': [ - 'Why Architect?', - 'Quickstart', - 'Project layout', - 'Detailed AWS setup', - ], - 'Developer experience': [ - 'Local development', // preview, debug and test - 'Dependency management', - 'Sharing code', // src/shared and src/views - 'Custom source paths', - 'Deployment', - 'Logging & monitoring', // cloudwatch - 'Using TypeScript', - ], - 'Frontend': [ - 'Static assets', // fingerprint, ignore, folder, link to cdn - // TODO 'HTTP functions', //inc cors - // TODO 'Sessions', - // TODO 'Middleware', - // TODO 'WebSockets functions', - ], - /* TODO - 'Backend': [ - 'Database tables & indexes', - 'Database stream functions', - 'Event functions', - 'Queue functions', - 'Scheduled functions', - ],*/ - 'Extend': [ - // TODO'Migrate legacy code',// @proxy, arc.http.express - 'Custom CloudFormation', - // TODO'Add a custom domain', - // TODO'Ejecting ' - ] -} ] - -let Reference = [ { - 'Runtime': [ - 'Node', - 'Deno', - 'Ruby', - 'Python' - ], - 'CLI': [ - 'deploy', - 'destroy', - 'env', - 'init', - 'logs', - 'sandbox', - ], - 'app.arc': [ - '@app', - '@aws', - '@events', - '@http', - '@indexes', - '@macros', - '@proxy', - '@queues', - '@scheduled', - '@shared', - '@static', - '@tables', - '@views', - '@ws' - ], - 'config.arc': [ - '@aws', - 'runtime', - 'memory', - 'timeout', - 'concurrency', - 'layers', - 'policies', - ], - 'prefs.arc': [ - '@create', - '@env', - '.env', - '@sandbox', - '@sandbox-startup' - ] -} ] - -let About = [ - 'Mission', - 'Community', - 'Contribute', - 'Upgrade', - 'Playground', -] - -module.exports = { - Guides, - Reference, - About -} - diff --git a/src/views/docs/table-of-contents.mjs b/src/views/docs/table-of-contents.mjs new file mode 100644 index 00000000..28d77cf9 --- /dev/null +++ b/src/views/docs/table-of-contents.mjs @@ -0,0 +1,114 @@ +const GetStarted = [ + 'Why Architect', + 'Quickstart', + 'Project manifest', + 'Detailed AWS setup', + 'Runtime support', +] + +const Guides = [ + { + 'Developer experience': [ + 'Local development', + 'Dependency management', + 'Sharing code', + 'Custom source paths', + 'Deployment', + 'Logging & monitoring', + 'Using ESM', + 'Using TypeScript', + 'Using Deno', + 'Custom CloudFormation', + 'Create AWS credentials', + { + 'Continuous integration': [ + 'GitHub Actions', + 'GitLab Pipelines', + 'AWS EC2', + ], + }, + ], + 'Frontend': [ + 'Sessions', + 'Static assets', + ], + 'Domains': [ + 'Overview', + 'Custom domain', + // { + // 'Registrars': [ + // 'Route53', + // 'Route53 & CloudFront', + // 'Dreamhost', + // 'GoDaddy', + // 'Namecheap', + // 'One', + // ] + // }, + ], + 'Plugins': [ + 'Overview', + 'create', + 'deploy', + 'hydrate', + 'sandbox', + 'set', + 'Inventory', + ], + }, + 'Examples', +] + +const Reference = [ { + 'Project manifest': [ + '@app', + '@aws', + '@events', + '@http', + '@plugins', + '@proxy', + '@queues', + '@scheduled', + '@shared', + '@static', + '@tables', + '@tables-indexes', + '@tables-streams', + '@views', + '@ws', + ], + 'Configuration': [ + 'Function config', + 'Local preferences', + ], + 'CLI': [ + 'deploy', + 'destroy', + 'env', + 'hydrate', + 'init', + 'logs', + 'sandbox', + ], + 'Runtime helpers': [ + 'Node.js', + 'Python', + 'Deno', + ], +} ] + +const About = [ + 'Mission', + 'Community', + 'Contribute', + 'Upgrade guide', + 'Playground', + 'Ejecting from Architect', +] + +export default { + 'Get Started': GetStarted, + Guides, + Reference, + About, +} diff --git a/src/views/landing/elements.mjs b/src/views/landing/elements.mjs new file mode 100644 index 00000000..6aa4b3de --- /dev/null +++ b/src/views/landing/elements.mjs @@ -0,0 +1,27 @@ +import ArcAttribution from './elements/arc-attribution.mjs' +import ArcButtonDivider from './elements/arc-button-divider.mjs' +import ArcContainer from './elements/arc-container.mjs' +import ArcGraphic from './elements/arc-graphic.mjs' +import ArcLanding from './elements/arc-landing.mjs' +import ArcLinkButton from './elements/arc-link-button.mjs' +import ArcLogo from './elements/arc-logo.mjs' +import ArcTerminal from './elements/arc-terminal.mjs' +import Footer from './elements/foot-er.mjs' +import ManifestExamples from './elements/manifest-examples.mjs' +import SeamlessDevelopment from './elements/seamless-development.mjs' +import WhyArchitect from './elements/why-architect.mjs' + +export default { + 'arc-attribution': ArcAttribution, + 'arc-button-divider': ArcButtonDivider, + 'arc-container': ArcContainer, + 'arc-graphic': ArcGraphic, + 'arc-landing': ArcLanding, + 'arc-link-button': ArcLinkButton, + 'arc-logo': ArcLogo, + 'arc-terminal': ArcTerminal, + 'foot-er': Footer, + 'manifest-examples': ManifestExamples, + 'seamless-development': SeamlessDevelopment, + 'why-architect': WhyArchitect, +} diff --git a/src/views/landing/elements/arc-attribution.mjs b/src/views/landing/elements/arc-attribution.mjs new file mode 100644 index 00000000..587e5048 --- /dev/null +++ b/src/views/landing/elements/arc-attribution.mjs @@ -0,0 +1,34 @@ +export default function ArcAttribution ({ html }) { + return html` + + +
+
+

Open governance

+ + ${OpenJSFLogo()} + +
+ +
+

Sponsored development

+ + ${BeginLogo()} + +
+
+
+ ` +} + +function BeginLogo () { + return `` +} + +function OpenJSFLogo () { + return ` ` +} diff --git a/src/views/landing/elements/arc-button-divider.mjs b/src/views/landing/elements/arc-button-divider.mjs new file mode 100644 index 00000000..535eb44b --- /dev/null +++ b/src/views/landing/elements/arc-button-divider.mjs @@ -0,0 +1,19 @@ +export default function ArcButtonDivider ({ html }) { + return html` + +
+ + + +
+ ` +} diff --git a/src/views/landing/elements/arc-container.mjs b/src/views/landing/elements/arc-container.mjs new file mode 100644 index 00000000..ad70d764 --- /dev/null +++ b/src/views/landing/elements/arc-container.mjs @@ -0,0 +1,19 @@ +export default function ArcContainer ({ html }) { + return html` + + + ` +} diff --git a/src/views/landing/elements/arc-graphic.mjs b/src/views/landing/elements/arc-graphic.mjs new file mode 100644 index 00000000..fc11bd14 --- /dev/null +++ b/src/views/landing/elements/arc-graphic.mjs @@ -0,0 +1,598 @@ +export default function ArcGraphic ({ html }) { + return html` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ` +} diff --git a/src/views/landing/elements/arc-landing.mjs b/src/views/landing/elements/arc-landing.mjs new file mode 100644 index 00000000..4ed57b8d --- /dev/null +++ b/src/views/landing/elements/arc-landing.mjs @@ -0,0 +1,121 @@ +export default function ArcLanding ({ html }) { + return html` + + + + + +

+ Cloud infra for humans +

+ +

+ Architect gives you the power to configure, locally build, and globally deploy web apps to the cloud using concise, declarative statements. Built on rock solid AWS foundations, Architect makes advanced web development a breeze. +

+ +

+ + Get started + +

+ + +
+ + + + + + + ` +} diff --git a/src/views/landing/elements/arc-link-button.mjs b/src/views/landing/elements/arc-link-button.mjs new file mode 100644 index 00000000..588e2368 --- /dev/null +++ b/src/views/landing/elements/arc-link-button.mjs @@ -0,0 +1,32 @@ +export default function ArcLinkButton ({ html }) { + return html` + + + + + + + + + ` +} diff --git a/src/views/landing/elements/arc-logo.mjs b/src/views/landing/elements/arc-logo.mjs new file mode 100644 index 00000000..0ca23a22 --- /dev/null +++ b/src/views/landing/elements/arc-logo.mjs @@ -0,0 +1,5 @@ +export default function ArcLogo ({ html }) { + return html` + + ` +} diff --git a/src/views/landing/elements/arc-terminal.mjs b/src/views/landing/elements/arc-terminal.mjs new file mode 100644 index 00000000..17df78f0 --- /dev/null +++ b/src/views/landing/elements/arc-terminal.mjs @@ -0,0 +1,69 @@ +export default function ArcTerminal ({ html }) { + return html` + +
+
+ + + + Node +
+ +
+

+ > +

+ + +
+
+ ` +} diff --git a/src/views/landing/elements/foot-er.mjs b/src/views/landing/elements/foot-er.mjs new file mode 100644 index 00000000..b2c262a8 --- /dev/null +++ b/src/views/landing/elements/foot-er.mjs @@ -0,0 +1,20 @@ +export default function Footer ({ html }) { + return html` + + +
+ +

+ Ditch the complexity and vendor cruft. Get started with Architect today. +

+ + + Read the quick start + +
+ ` +} diff --git a/src/views/landing/elements/manifest-examples.mjs b/src/views/landing/elements/manifest-examples.mjs new file mode 100644 index 00000000..7256cee7 --- /dev/null +++ b/src/views/landing/elements/manifest-examples.mjs @@ -0,0 +1,146 @@ +import { renderHelloWorld, renderArcCodes, renderKitchenSink } from '../support/render-examples.mjs' + +const { + arc: helloWorldArc, + arcLoc: helloWorldArcLoc, + cfLoc: helloWorldCfLoc, + cloudFormation: helloWorldCf, +} = await renderHelloWorld() + +const { + arc: arcCodesArc, + arcLoc: arcCodesArcLoc, + cfLoc: arcCodesCfLoc, + cloudFormation: arcCodesCf, +} = await renderArcCodes() + +const { + arc: kitchenSinkArc, + arcLoc: kitchenSinkArcLoc, + cfLoc: kitchenSinkCfLoc, + cloudFormation: kitchenSinkCf, +} = await renderKitchenSink() + +export default function CloudFormation ({ html }) { + return html` + + + +

+ IaC: NP! +

+ +

+ Infrastructure as Code (IaC) can be intimidating for even the most experienced developers. Architect's manifest file — which can be written in multiple open text formats — codifies cloud infrastructure provisioning as a minimal build artifact, turning formerly complex work into approachable, maintainable code. +

+ +

+ See the difference for yourself by exploring the examples of Architect project manifests and the CloudFormation configurations they generate below. Or try the interactive Architect playground. +

+ + +
+ + + + + +
+ + + + + + + +
+ ` +} diff --git a/src/views/landing/elements/seamless-development.mjs b/src/views/landing/elements/seamless-development.mjs new file mode 100644 index 00000000..761b01d8 --- /dev/null +++ b/src/views/landing/elements/seamless-development.mjs @@ -0,0 +1,74 @@ +export default function SeamlessDevelopment ({ html }) { + return html` + + +

+ Seamless development, from local to cloud +

+ +
+

+ Scaffold a fresh Architect project with a single command. +

+ + + npm init @architect your-app +
+

⚬ Bootstrapping new Architect project

+

| Project name .. your-app

+

| Creating in ... /Users/LouisK/Developer/your-app

+

⚬ Installing Architect...

+

✓ Your Architect project is ready!

+
+
+
+ + + +
+

+ Get a local, production-like environment up and running instantly. +

+ + + npx arc sandbox +
+

   App ⌁ your-app

+

Region ⌁ us-west-2

+

http://localhost:3333

+

✓ Sandbox started in 12ms

+

❤︎ Local environment ready!

+
+
+
+ + + +
+

+ Deploy to identical staging and production environments in seconds. +

+ + + npx arc deploy +
+

   App ⌁ your-app

+

Region ⌁ us-west-2

+

⚬ Creating new private deployment bucket

+

✓ Generated CloudFormation deployment

+

✓ Deployed & built infrastructure

+

✓ Success! Deployed app in 58.984 seconds

+
+
+
+ + + Dive into the docs + +
+ ` +} diff --git a/src/views/landing/elements/why-architect.mjs b/src/views/landing/elements/why-architect.mjs new file mode 100644 index 00000000..c03d3956 --- /dev/null +++ b/src/views/landing/elements/why-architect.mjs @@ -0,0 +1,37 @@ +export default function WhyArchitect ({ html }) { + return html` + +

Why Architect?

+ +

+ Architect provides everything you need to build massively scalable Functional Web Apps on AWS with low code, clear and terse config, and zero ceremony. +

+ +
+
+

Fullstack, for real

+

Architect not only supports cloud functions for HTTP but also web sockets, queues (FIFO), events (fan-out), and scheduled tasks backed by a world class database that boasts millisecond latency no matter how much data you store or how many people concurrently access it. All these capabilities, and more, with terse but determinstic Infra-as-Code (IaC).

+
+ +
+

The best developer experience

+

We prioritize speed with fast local dev, smart configurable defaults and flexible Infrastructure as Code. You can focus on business logic instead of glue code — and only pay for in-use services on-demand, while otherwise scaling to zero.

+
+ +
+

Intelligent, efficient iteration

+

Architect treats local development, staging, and production environments as first class concerns. Architect developers iterate fast with all of these environments and deployments only seconds away, leading to reduced latency in feedback cycles.

+
+ +
+

Infrastructure as code

+

Architect turns formerly complex cloud infrastructure provisioning into a build artifact, so infra and code are always aligned and deterministic. It then compiles manifest code into AWS CloudFormation and deploys it.

+
+
+ + + Learn more about Architect + +
+ ` +} diff --git a/src/views/landing/support/arc-codes.mjs b/src/views/landing/support/arc-codes.mjs new file mode 100644 index 00000000..8be2cac1 --- /dev/null +++ b/src/views/landing/support/arc-codes.mjs @@ -0,0 +1,605 @@ +export const arc = `@app +arc-codes + +@aws +region us-west-2 +profile openjsf + +@static +fingerprint true + +@http +get /docs/:lang/* +get /api/package +any /* +get /landing +` + +export const cloudformation = { + 'AWSTemplateFormatVersion': '2010-09-09', + 'Transform': 'AWS::Serverless-2016-10-31', + 'Description': 'Example: arc.codes website', + 'Resources': { + 'Role': { + 'Type': 'AWS::IAM::Role', + 'Properties': { + 'AssumeRolePolicyDocument': { + 'Version': '2012-10-17', + 'Statement': [ + { + 'Effect': 'Allow', + 'Principal': { + 'Service': 'lambda.amazonaws.com', + }, + 'Action': 'sts:AssumeRole', + }, + ], + }, + 'Policies': [ + { + 'PolicyName': 'ArcGlobalPolicy', + 'PolicyDocument': { + 'Statement': [ + { + 'Effect': 'Allow', + 'Action': [ + 'logs:CreateLogGroup', + 'logs:CreateLogStream', + 'logs:PutLogEvents', + 'logs:DescribeLogStreams', + ], + 'Resource': 'arn:aws:logs:*:*:*', + }, + ], + }, + }, + { + 'PolicyName': 'ArcStaticBucketPolicy', + 'PolicyDocument': { + 'Statement': [ + { + 'Effect': 'Allow', + 'Action': [ + 's3:GetObject', + 's3:PutObject', + 's3:PutObjectAcl', + 's3:DeleteObject', + 's3:ListBucket', + ], + 'Resource': [ + { + 'Fn::Sub': [ + 'arn:aws:s3:::${bukkit}', + { + 'bukkit': { + 'Ref': 'StaticBucket', + }, + }, + ], + }, + { + 'Fn::Sub': [ + 'arn:aws:s3:::${bukkit}/*', + { + 'bukkit': { + 'Ref': 'StaticBucket', + }, + }, + ], + }, + ], + }, + ], + }, + }, + ], + }, + }, + 'StaticBucketParam': { + 'Type': 'AWS::SSM::Parameter', + 'Properties': { + 'Type': 'String', + 'Name': { + 'Fn::Sub': [ + '/${AWS::StackName}/static/${key}', + { + 'key': 'bucket', + }, + ], + }, + 'Value': { + 'Ref': 'StaticBucket', + }, + }, + }, + 'StaticFingerprintParam': { + 'Type': 'AWS::SSM::Parameter', + 'Properties': { + 'Type': 'String', + 'Name': { + 'Fn::Sub': [ + '/${AWS::StackName}/static/${key}', + { + 'key': 'fingerprint', + }, + ], + }, + 'Value': 'true', + }, + }, + 'ParameterStorePolicy': { + 'Type': 'AWS::IAM::Policy', + 'DependsOn': 'Role', + 'Properties': { + 'PolicyName': 'ArcParameterStorePolicy', + 'PolicyDocument': { + 'Statement': [ + { + 'Effect': 'Allow', + 'Action': [ + 'ssm:GetParametersByPath', + 'ssm:GetParameter', + ], + 'Resource': { + 'Fn::Sub': [ + 'arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/${AWS::StackName}', + {}, + ], + }, + }, + { + 'Effect': 'Allow', + 'Action': [ + 'ssm:GetParametersByPath', + 'ssm:GetParameter', + ], + 'Resource': { + 'Fn::Sub': [ + 'arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/${AWS::StackName}/*', + {}, + ], + }, + }, + { + 'Effect': 'Allow', + 'Action': [ + 'ssm:GetParametersByPath', + 'ssm:GetParameter', + ], + 'Resource': { + 'Fn::Sub': [ + 'arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/${AWS::StackName}/*/*', + {}, + ], + }, + }, + ], + }, + 'Roles': [ + { + 'Ref': 'Role', + }, + ], + }, + }, + 'HTTP': { + 'Type': 'AWS::Serverless::HttpApi', + 'Properties': { + 'StageName': '$default', + 'DefinitionBody': { + 'openapi': '3.0.1', + 'info': { + 'title': { + 'Ref': 'AWS::StackName', + }, + }, + 'paths': { + '/docs/{lang}/{proxy+}': { + 'get': { + 'x-amazon-apigateway-integration': { + 'payloadFormatVersion': '2.0', + 'type': 'aws_proxy', + 'httpMethod': 'POST', + 'uri': { + 'Fn::Sub': 'arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${GetDocsLangCatchallHTTPLambda.Arn}/invocations', + }, + 'connectionType': 'INTERNET', + }, + }, + }, + '/api/package': { + 'get': { + 'x-amazon-apigateway-integration': { + 'payloadFormatVersion': '2.0', + 'type': 'aws_proxy', + 'httpMethod': 'POST', + 'uri': { + 'Fn::Sub': 'arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${GetApiPackageHTTPLambda.Arn}/invocations', + }, + 'connectionType': 'INTERNET', + }, + }, + }, + '/landing': { + 'get': { + 'x-amazon-apigateway-integration': { + 'payloadFormatVersion': '2.0', + 'type': 'aws_proxy', + 'httpMethod': 'POST', + 'uri': { + 'Fn::Sub': 'arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${GetLandingHTTPLambda.Arn}/invocations', + }, + 'connectionType': 'INTERNET', + }, + }, + }, + '/{proxy+}': { + 'x-amazon-apigateway-any-method': { + 'x-amazon-apigateway-integration': { + 'payloadFormatVersion': '2.0', + 'type': 'aws_proxy', + 'httpMethod': 'POST', + 'uri': { + 'Fn::Sub': 'arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${AnyCatchallHTTPLambda.Arn}/invocations', + }, + 'connectionType': 'INTERNET', + }, + }, + }, + '/_static/{proxy+}': { + 'get': { + 'x-amazon-apigateway-integration': { + 'payloadFormatVersion': '1.0', + 'type': 'http_proxy', + 'httpMethod': 'GET', + 'uri': { + 'Fn::Sub': [ + 'https://${bukkit}.s3.${AWS::Region}.amazonaws.com/{proxy}', + { + 'bukkit': { + 'Ref': 'StaticBucket', + }, + }, + ], + }, + 'connectionType': 'INTERNET', + 'timeoutInMillis': 30000, + }, + }, + }, + }, + }, + }, + }, + 'GetDocsLangCatchallHTTPLambda': { + 'Type': 'AWS::Serverless::Function', + 'Properties': { + 'Handler': 'index.handler', + 'CodeUri': '/var/task/src/http/get-docs-000lang-catchall', + 'Runtime': 'nodejs20.x', + 'Architectures': [ + 'arm64', + ], + 'MemorySize': 1152, + 'EphemeralStorage': { + 'Size': 512, + }, + 'Timeout': 5, + 'Environment': { + 'Variables': { + 'ARC_APP_NAME': 'arc-codes', + 'ARC_ENV': 'staging', + 'ARC_ROLE': { + 'Ref': 'Role', + }, + 'ARC_SESSION_TABLE_NAME': 'jwe', + 'ARC_STACK_NAME': { + 'Ref': 'AWS::StackName', + }, + 'ARC_STATIC_BUCKET': { + 'Ref': 'StaticBucket', + }, + }, + }, + 'Role': { + 'Fn::Sub': [ + 'arn:aws:iam::${AWS::AccountId}:role/${roleName}', + { + 'roleName': { + 'Ref': 'Role', + }, + }, + ], + }, + 'Events': { + 'GetDocsLangCatchallHTTPEvent': { + 'Type': 'HttpApi', + 'Properties': { + 'Path': '/docs/{lang}/{proxy+}', + 'Method': 'GET', + 'ApiId': { + 'Ref': 'HTTP', + }, + }, + }, + }, + }, + 'ArcMetadata': { + 'pragma': 'http', + 'name': 'get /docs/:lang/*', + 'method': 'get', + 'path': '/docs/:lang/*', + }, + }, + 'GetApiPackageHTTPLambda': { + 'Type': 'AWS::Serverless::Function', + 'Properties': { + 'Handler': 'index.handler', + 'CodeUri': '/var/task/src/http/get-api-package', + 'Runtime': 'nodejs20.x', + 'Architectures': [ + 'arm64', + ], + 'MemorySize': 1152, + 'EphemeralStorage': { + 'Size': 512, + }, + 'Timeout': 5, + 'Environment': { + 'Variables': { + 'ARC_APP_NAME': 'arc-codes', + 'ARC_ENV': 'staging', + 'ARC_ROLE': { + 'Ref': 'Role', + }, + 'ARC_SESSION_TABLE_NAME': 'jwe', + 'ARC_STACK_NAME': { + 'Ref': 'AWS::StackName', + }, + 'ARC_STATIC_BUCKET': { + 'Ref': 'StaticBucket', + }, + }, + }, + 'Role': { + 'Fn::Sub': [ + 'arn:aws:iam::${AWS::AccountId}:role/${roleName}', + { + 'roleName': { + 'Ref': 'Role', + }, + }, + ], + }, + 'Events': { + 'GetApiPackageHTTPEvent': { + 'Type': 'HttpApi', + 'Properties': { + 'Path': '/api/package', + 'Method': 'GET', + 'ApiId': { + 'Ref': 'HTTP', + }, + }, + }, + }, + }, + 'ArcMetadata': { + 'pragma': 'http', + 'name': 'get /api/package', + 'method': 'get', + 'path': '/api/package', + }, + }, + 'GetLandingHTTPLambda': { + 'Type': 'AWS::Serverless::Function', + 'Properties': { + 'Handler': 'index.handler', + 'CodeUri': '/var/task/src/http/get-landing', + 'Runtime': 'nodejs20.x', + 'Architectures': [ + 'arm64', + ], + 'MemorySize': 1152, + 'EphemeralStorage': { + 'Size': 512, + }, + 'Timeout': 5, + 'Environment': { + 'Variables': { + 'ARC_APP_NAME': 'arc-codes', + 'ARC_ENV': 'staging', + 'ARC_ROLE': { + 'Ref': 'Role', + }, + 'ARC_SESSION_TABLE_NAME': 'jwe', + 'ARC_STACK_NAME': { + 'Ref': 'AWS::StackName', + }, + 'ARC_STATIC_BUCKET': { + 'Ref': 'StaticBucket', + }, + }, + }, + 'Role': { + 'Fn::Sub': [ + 'arn:aws:iam::${AWS::AccountId}:role/${roleName}', + { + 'roleName': { + 'Ref': 'Role', + }, + }, + ], + }, + 'Events': { + 'GetLandingHTTPEvent': { + 'Type': 'HttpApi', + 'Properties': { + 'Path': '/landing', + 'Method': 'GET', + 'ApiId': { + 'Ref': 'HTTP', + }, + }, + }, + }, + }, + 'ArcMetadata': { + 'pragma': 'http', + 'name': 'get /landing', + 'method': 'get', + 'path': '/landing', + }, + }, + 'AnyCatchallHTTPLambda': { + 'Type': 'AWS::Serverless::Function', + 'Properties': { + 'Handler': 'index.handler', + 'CodeUri': '/var/task/src/http/any-catchall', + 'Runtime': 'nodejs20.x', + 'Architectures': [ + 'arm64', + ], + 'MemorySize': 1152, + 'EphemeralStorage': { + 'Size': 512, + }, + 'Timeout': 5, + 'Environment': { + 'Variables': { + 'ARC_APP_NAME': 'arc-codes', + 'ARC_ENV': 'staging', + 'ARC_ROLE': { + 'Ref': 'Role', + }, + 'ARC_SESSION_TABLE_NAME': 'jwe', + 'ARC_STACK_NAME': { + 'Ref': 'AWS::StackName', + }, + 'ARC_STATIC_BUCKET': { + 'Ref': 'StaticBucket', + }, + 'ARC_STATIC_SPA': false, + }, + }, + 'Role': { + 'Fn::Sub': [ + 'arn:aws:iam::${AWS::AccountId}:role/${roleName}', + { + 'roleName': { + 'Ref': 'Role', + }, + }, + ], + }, + 'Events': { + 'AnyCatchallHTTPEvent': { + 'Type': 'HttpApi', + 'Properties': { + 'Path': '/{proxy+}', + 'Method': 'ANY', + 'ApiId': { + 'Ref': 'HTTP', + }, + }, + }, + }, + }, + 'ArcMetadata': { + 'pragma': 'http', + 'name': 'any /*', + 'method': 'any', + 'path': '/*', + }, + }, + 'StaticBucket': { + 'Type': 'AWS::S3::Bucket', + 'Properties': { + 'OwnershipControls': { + 'Rules': [ + { + 'ObjectOwnership': 'BucketOwnerEnforced', + }, + ], + }, + 'WebsiteConfiguration': { + 'IndexDocument': 'index.html', + 'ErrorDocument': '404.html', + }, + 'PublicAccessBlockConfiguration': { + 'BlockPublicAcls': false, + 'BlockPublicPolicy': false, + 'IgnorePublicAcls': false, + 'RestrictPublicBuckets': false, + }, + }, + }, + 'StaticBucketPolicy': { + 'Type': 'AWS::S3::BucketPolicy', + 'Properties': { + 'Bucket': { + 'Ref': 'StaticBucket', + }, + 'PolicyDocument': { + 'Version': '2012-10-17', + 'Statement': [ + { + 'Action': [ + 's3:GetObject', + ], + 'Effect': 'Allow', + 'Principal': '*', + 'Resource': [ + { + 'Fn::Sub': [ + 'arn:aws:s3:::${bukkit}/*', + { + 'bukkit': { + 'Ref': 'StaticBucket', + }, + }, + ], + }, + ], + 'Sid': 'PublicReadGetObject', + }, + ], + }, + }, + }, + }, + 'Outputs': { + 'API': { + 'Description': 'API Gateway (HTTP)', + 'Value': { + 'Fn::Sub': [ + 'https://${ApiId}.execute-api.${AWS::Region}.amazonaws.com', + { + 'ApiId': { + 'Ref': 'HTTP', + }, + }, + ], + }, + }, + 'ApiId': { + 'Description': 'API ID (ApiId)', + 'Value': { + 'Ref': 'HTTP', + }, + }, + 'BucketURL': { + 'Description': 'Bucket URL', + 'Value': { + 'Fn::Sub': [ + 'http://${bukkit}.s3-website-${AWS::Region}.amazonaws.com', + { + 'bukkit': { + 'Ref': 'StaticBucket', + }, + }, + ], + }, + }, + }, +} diff --git a/src/views/landing/support/hello-world.mjs b/src/views/landing/support/hello-world.mjs new file mode 100644 index 00000000..c266b6c8 --- /dev/null +++ b/src/views/landing/support/hello-world.mjs @@ -0,0 +1,376 @@ +export const arc = `@app +myapp + +@http +get / +` + +export const cloudformation = { + 'AWSTemplateFormatVersion': '2010-09-09', + 'Transform': 'AWS::Serverless-2016-10-31', + 'Description': 'Example: hello world', + 'Resources': { + 'Role': { + 'Type': 'AWS::IAM::Role', + 'Properties': { + 'AssumeRolePolicyDocument': { + 'Version': '2012-10-17', + 'Statement': [ + { + 'Effect': 'Allow', + 'Principal': { + 'Service': 'lambda.amazonaws.com', + }, + 'Action': 'sts:AssumeRole', + }, + ], + }, + 'Policies': [ + { + 'PolicyName': 'ArcGlobalPolicy', + 'PolicyDocument': { + 'Statement': [ + { + 'Effect': 'Allow', + 'Action': [ + 'logs:CreateLogGroup', + 'logs:CreateLogStream', + 'logs:PutLogEvents', + 'logs:DescribeLogStreams', + ], + 'Resource': 'arn:aws:logs:*:*:*', + }, + ], + }, + }, + { + 'PolicyName': 'ArcStaticBucketPolicy', + 'PolicyDocument': { + 'Statement': [ + { + 'Effect': 'Allow', + 'Action': [ + 's3:GetObject', + 's3:PutObject', + 's3:PutObjectAcl', + 's3:DeleteObject', + 's3:ListBucket', + ], + 'Resource': [ + { + 'Fn::Sub': [ + 'arn:aws:s3:::${bukkit}', + { + 'bukkit': { + 'Ref': 'StaticBucket', + }, + }, + ], + }, + { + 'Fn::Sub': [ + 'arn:aws:s3:::${bukkit}/*', + { + 'bukkit': { + 'Ref': 'StaticBucket', + }, + }, + ], + }, + ], + }, + ], + }, + }, + ], + }, + }, + 'StaticBucketParam': { + 'Type': 'AWS::SSM::Parameter', + 'Properties': { + 'Type': 'String', + 'Name': { + 'Fn::Sub': [ + '/${AWS::StackName}/static/${key}', + { + 'key': 'bucket', + }, + ], + }, + 'Value': { + 'Ref': 'StaticBucket', + }, + }, + }, + 'StaticFingerprintParam': { + 'Type': 'AWS::SSM::Parameter', + 'Properties': { + 'Type': 'String', + 'Name': { + 'Fn::Sub': [ + '/${AWS::StackName}/static/${key}', + { + 'key': 'fingerprint', + }, + ], + }, + 'Value': 'false', + }, + }, + 'ParameterStorePolicy': { + 'Type': 'AWS::IAM::Policy', + 'DependsOn': 'Role', + 'Properties': { + 'PolicyName': 'ArcParameterStorePolicy', + 'PolicyDocument': { + 'Statement': [ + { + 'Effect': 'Allow', + 'Action': [ + 'ssm:GetParametersByPath', + 'ssm:GetParameter', + ], + 'Resource': { + 'Fn::Sub': [ + 'arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/${AWS::StackName}', + {}, + ], + }, + }, + { + 'Effect': 'Allow', + 'Action': [ + 'ssm:GetParametersByPath', + 'ssm:GetParameter', + ], + 'Resource': { + 'Fn::Sub': [ + 'arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/${AWS::StackName}/*', + {}, + ], + }, + }, + { + 'Effect': 'Allow', + 'Action': [ + 'ssm:GetParametersByPath', + 'ssm:GetParameter', + ], + 'Resource': { + 'Fn::Sub': [ + 'arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/${AWS::StackName}/*/*', + {}, + ], + }, + }, + ], + }, + 'Roles': [ + { + 'Ref': 'Role', + }, + ], + }, + }, + 'HTTP': { + 'Type': 'AWS::Serverless::HttpApi', + 'Properties': { + 'StageName': '$default', + 'DefinitionBody': { + 'openapi': '3.0.1', + 'info': { + 'title': { + 'Ref': 'AWS::StackName', + }, + }, + 'paths': { + '/': { + 'get': { + 'x-amazon-apigateway-integration': { + 'payloadFormatVersion': '2.0', + 'type': 'aws_proxy', + 'httpMethod': 'POST', + 'uri': { + 'Fn::Sub': 'arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${GetIndexHTTPLambda.Arn}/invocations', + }, + 'connectionType': 'INTERNET', + }, + }, + }, + '/_static/{proxy+}': { + 'get': { + 'x-amazon-apigateway-integration': { + 'payloadFormatVersion': '1.0', + 'type': 'http_proxy', + 'httpMethod': 'GET', + 'uri': { + 'Fn::Sub': [ + 'https://${bukkit}.s3.${AWS::Region}.amazonaws.com/{proxy}', + { + 'bukkit': { + 'Ref': 'StaticBucket', + }, + }, + ], + }, + 'connectionType': 'INTERNET', + 'timeoutInMillis': 30000, + }, + }, + }, + }, + }, + }, + }, + 'GetIndexHTTPLambda': { + 'Type': 'AWS::Serverless::Function', + 'Properties': { + 'Handler': 'index.handler', + 'CodeUri': '/var/task/src/http/get-index', + 'Runtime': 'nodejs20.x', + 'Architectures': [ + 'arm64', + ], + 'MemorySize': 1152, + 'EphemeralStorage': { + 'Size': 512, + }, + 'Timeout': 5, + 'Environment': { + 'Variables': { + 'ARC_APP_NAME': 'myapp', + 'ARC_ENV': 'staging', + 'ARC_ROLE': { + 'Ref': 'Role', + }, + 'ARC_SESSION_TABLE_NAME': 'jwe', + 'ARC_STACK_NAME': { + 'Ref': 'AWS::StackName', + }, + 'ARC_STATIC_BUCKET': { + 'Ref': 'StaticBucket', + }, + 'ARC_STATIC_SPA': false, + }, + }, + 'Role': { + 'Fn::Sub': [ + 'arn:aws:iam::${AWS::AccountId}:role/${roleName}', + { + 'roleName': { + 'Ref': 'Role', + }, + }, + ], + }, + 'Events': { + 'GetIndexHTTPEvent': { + 'Type': 'HttpApi', + 'Properties': { + 'Path': '/', + 'Method': 'GET', + 'ApiId': { + 'Ref': 'HTTP', + }, + }, + }, + }, + }, + 'ArcMetadata': { + 'pragma': 'http', + 'name': 'get /', + 'method': 'get', + 'path': '/', + }, + }, + 'StaticBucket': { + 'Type': 'AWS::S3::Bucket', + 'Properties': { + 'OwnershipControls': { + 'Rules': [ + { + 'ObjectOwnership': 'BucketOwnerEnforced', + }, + ], + }, + 'WebsiteConfiguration': { + 'IndexDocument': 'index.html', + 'ErrorDocument': '404.html', + }, + 'PublicAccessBlockConfiguration': { + 'BlockPublicAcls': false, + 'BlockPublicPolicy': false, + 'IgnorePublicAcls': false, + 'RestrictPublicBuckets': false, + }, + }, + }, + 'StaticBucketPolicy': { + 'Type': 'AWS::S3::BucketPolicy', + 'Properties': { + 'Bucket': { + 'Ref': 'StaticBucket', + }, + 'PolicyDocument': { + 'Version': '2012-10-17', + 'Statement': [ + { + 'Action': [ + 's3:GetObject', + ], + 'Effect': 'Allow', + 'Principal': '*', + 'Resource': [ + { + 'Fn::Sub': [ + 'arn:aws:s3:::${bukkit}/*', + { + 'bukkit': { + 'Ref': 'StaticBucket', + }, + }, + ], + }, + ], + 'Sid': 'PublicReadGetObject', + }, + ], + }, + }, + }, + }, + 'Outputs': { + 'API': { + 'Description': 'API Gateway (HTTP)', + 'Value': { + 'Fn::Sub': [ + 'https://${ApiId}.execute-api.${AWS::Region}.amazonaws.com', + { + 'ApiId': { + 'Ref': 'HTTP', + }, + }, + ], + }, + }, + 'ApiId': { + 'Description': 'API ID (ApiId)', + 'Value': { + 'Ref': 'HTTP', + }, + }, + 'BucketURL': { + 'Description': 'Bucket URL', + 'Value': { + 'Fn::Sub': [ + 'http://${bukkit}.s3-website-${AWS::Region}.amazonaws.com', + { + 'bukkit': { + 'Ref': 'StaticBucket', + }, + }, + ], + }, + }, + }, +} diff --git a/src/views/landing/support/kitchen-sink.mjs b/src/views/landing/support/kitchen-sink.mjs new file mode 100644 index 00000000..bfa2e56c --- /dev/null +++ b/src/views/landing/support/kitchen-sink.mjs @@ -0,0 +1,1421 @@ +export const arc = `@app +kitchen-sink + +@static +fingerprint true + +@http +get / +get /likes +post /likes + +@tables +likes + likeID *String + +@tables-streams +likes + +@tables-indexes +likes + date *String + +@ws +action +connect +default +disconnect + +@events +hit-counter + +@scheduled +daily-affirmation rate(1 day) +` + + +export const cloudformation = { + 'AWSTemplateFormatVersion': '2010-09-09', + 'Transform': 'AWS::Serverless-2016-10-31', + 'Description': 'Example: kitchen sink', + 'Resources': { + 'Role': { + 'Type': 'AWS::IAM::Role', + 'Properties': { + 'AssumeRolePolicyDocument': { + 'Version': '2012-10-17', + 'Statement': [ + { + 'Effect': 'Allow', + 'Principal': { + 'Service': 'lambda.amazonaws.com', + }, + 'Action': 'sts:AssumeRole', + }, + ], + }, + 'Policies': [ + { + 'PolicyName': 'ArcGlobalPolicy', + 'PolicyDocument': { + 'Statement': [ + { + 'Effect': 'Allow', + 'Action': [ + 'logs:CreateLogGroup', + 'logs:CreateLogStream', + 'logs:PutLogEvents', + 'logs:DescribeLogStreams', + ], + 'Resource': 'arn:aws:logs:*:*:*', + }, + ], + }, + }, + { + 'PolicyName': 'ArcStaticBucketPolicy', + 'PolicyDocument': { + 'Statement': [ + { + 'Effect': 'Allow', + 'Action': [ + 's3:GetObject', + 's3:PutObject', + 's3:PutObjectAcl', + 's3:DeleteObject', + 's3:ListBucket', + ], + 'Resource': [ + { + 'Fn::Sub': [ + 'arn:aws:s3:::${bukkit}', + { + 'bukkit': { + 'Ref': 'StaticBucket', + }, + }, + ], + }, + { + 'Fn::Sub': [ + 'arn:aws:s3:::${bukkit}/*', + { + 'bukkit': { + 'Ref': 'StaticBucket', + }, + }, + ], + }, + ], + }, + ], + }, + }, + { + 'PolicyName': 'ArcDynamoPolicy', + 'PolicyDocument': { + 'Statement': [ + { + 'Effect': 'Allow', + 'Action': 'dynamodb:*', + 'Resource': [ + { + 'Fn::Sub': [ + 'arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${tablename}', + { + 'tablename': { + 'Ref': 'LikesTable', + }, + }, + ], + }, + { + 'Fn::Sub': [ + 'arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${tablename}/*', + { + 'tablename': { + 'Ref': 'LikesTable', + }, + }, + ], + }, + ], + }, + { + 'Effect': 'Deny', + 'Action': 'dynamodb:DeleteTable', + 'Resource': { + 'Fn::Sub': 'arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/*', + }, + }, + ], + }, + }, + { + 'PolicyName': 'ArcSimpleNotificationServicePolicy', + 'PolicyDocument': { + 'Statement': [ + { + 'Effect': 'Allow', + 'Action': [ + 'sns:Publish', + ], + 'Resource': { + 'Fn::Sub': [ + 'arn:aws:sns:${AWS::Region}:${AWS::AccountId}:${AWS::StackName}*', + {}, + ], + }, + }, + ], + }, + }, + ], + }, + }, + 'LikesParam': { + 'Type': 'AWS::SSM::Parameter', + 'Properties': { + 'Type': 'String', + 'Name': { + 'Fn::Sub': [ + '/${AWS::StackName}/tables/${tablename}', + { + 'tablename': 'likes', + }, + ], + }, + 'Value': { + 'Ref': 'LikesTable', + }, + }, + }, + 'HitCounterEventTopicParam': { + 'Type': 'AWS::SSM::Parameter', + 'Properties': { + 'Type': 'String', + 'Name': { + 'Fn::Sub': [ + '/${AWS::StackName}/events/${event}', + { + 'event': 'hit-counter', + }, + ], + }, + 'Value': { + 'Ref': 'HitCounterEventTopic', + }, + }, + }, + 'StaticBucketParam': { + 'Type': 'AWS::SSM::Parameter', + 'Properties': { + 'Type': 'String', + 'Name': { + 'Fn::Sub': [ + '/${AWS::StackName}/static/${key}', + { + 'key': 'bucket', + }, + ], + }, + 'Value': { + 'Ref': 'StaticBucket', + }, + }, + }, + 'StaticFingerprintParam': { + 'Type': 'AWS::SSM::Parameter', + 'Properties': { + 'Type': 'String', + 'Name': { + 'Fn::Sub': [ + '/${AWS::StackName}/static/${key}', + { + 'key': 'fingerprint', + }, + ], + }, + 'Value': 'true', + }, + }, + 'ParameterStorePolicy': { + 'Type': 'AWS::IAM::Policy', + 'DependsOn': 'Role', + 'Properties': { + 'PolicyName': 'ArcParameterStorePolicy', + 'PolicyDocument': { + 'Statement': [ + { + 'Effect': 'Allow', + 'Action': [ + 'ssm:GetParametersByPath', + 'ssm:GetParameter', + ], + 'Resource': { + 'Fn::Sub': [ + 'arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/${AWS::StackName}', + {}, + ], + }, + }, + { + 'Effect': 'Allow', + 'Action': [ + 'ssm:GetParametersByPath', + 'ssm:GetParameter', + ], + 'Resource': { + 'Fn::Sub': [ + 'arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/${AWS::StackName}/*', + {}, + ], + }, + }, + { + 'Effect': 'Allow', + 'Action': [ + 'ssm:GetParametersByPath', + 'ssm:GetParameter', + ], + 'Resource': { + 'Fn::Sub': [ + 'arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/${AWS::StackName}/*/*', + {}, + ], + }, + }, + ], + }, + 'Roles': [ + { + 'Ref': 'Role', + }, + ], + }, + }, + 'HTTP': { + 'Type': 'AWS::Serverless::HttpApi', + 'Properties': { + 'StageName': '$default', + 'DefinitionBody': { + 'openapi': '3.0.1', + 'info': { + 'title': { + 'Ref': 'AWS::StackName', + }, + }, + 'paths': { + '/likes': { + 'get': { + 'x-amazon-apigateway-integration': { + 'payloadFormatVersion': '2.0', + 'type': 'aws_proxy', + 'httpMethod': 'POST', + 'uri': { + 'Fn::Sub': 'arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${GetLikesHTTPLambda.Arn}/invocations', + }, + 'connectionType': 'INTERNET', + }, + }, + 'post': { + 'x-amazon-apigateway-integration': { + 'payloadFormatVersion': '2.0', + 'type': 'aws_proxy', + 'httpMethod': 'POST', + 'uri': { + 'Fn::Sub': 'arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${PostLikesHTTPLambda.Arn}/invocations', + }, + 'connectionType': 'INTERNET', + }, + }, + }, + '/': { + 'get': { + 'x-amazon-apigateway-integration': { + 'payloadFormatVersion': '2.0', + 'type': 'aws_proxy', + 'httpMethod': 'POST', + 'uri': { + 'Fn::Sub': 'arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${GetIndexHTTPLambda.Arn}/invocations', + }, + 'connectionType': 'INTERNET', + }, + }, + }, + '/_static/{proxy+}': { + 'get': { + 'x-amazon-apigateway-integration': { + 'payloadFormatVersion': '1.0', + 'type': 'http_proxy', + 'httpMethod': 'GET', + 'uri': { + 'Fn::Sub': [ + 'https://${bukkit}.s3.${AWS::Region}.amazonaws.com/{proxy}', + { + 'bukkit': { + 'Ref': 'StaticBucket', + }, + }, + ], + }, + 'connectionType': 'INTERNET', + 'timeoutInMillis': 30000, + }, + }, + }, + }, + }, + }, + }, + 'GetLikesHTTPLambda': { + 'Type': 'AWS::Serverless::Function', + 'Properties': { + 'Handler': 'index.handler', + 'CodeUri': '/var/task/src/http/get-likes', + 'Runtime': 'nodejs20.x', + 'Architectures': [ + 'arm64', + ], + 'MemorySize': 1152, + 'EphemeralStorage': { + 'Size': 512, + }, + 'Timeout': 5, + 'Environment': { + 'Variables': { + 'ARC_APP_NAME': 'hello', + 'ARC_ENV': 'staging', + 'ARC_ROLE': { + 'Ref': 'Role', + }, + 'ARC_SESSION_TABLE_NAME': 'jwe', + 'ARC_STACK_NAME': { + 'Ref': 'AWS::StackName', + }, + 'ARC_STATIC_BUCKET': { + 'Ref': 'StaticBucket', + }, + 'ARC_WSS_URL': { + 'Fn::Sub': [ + 'wss://${WS}.execute-api.${AWS::Region}.amazonaws.com/staging', + {}, + ], + }, + }, + }, + 'Role': { + 'Fn::Sub': [ + 'arn:aws:iam::${AWS::AccountId}:role/${roleName}', + { + 'roleName': { + 'Ref': 'Role', + }, + }, + ], + }, + 'Events': { + 'GetLikesHTTPEvent': { + 'Type': 'HttpApi', + 'Properties': { + 'Path': '/likes', + 'Method': 'GET', + 'ApiId': { + 'Ref': 'HTTP', + }, + }, + }, + }, + }, + 'ArcMetadata': { + 'pragma': 'http', + 'name': 'get /likes', + 'method': 'get', + 'path': '/likes', + }, + }, + 'GetIndexHTTPLambda': { + 'Type': 'AWS::Serverless::Function', + 'Properties': { + 'Handler': 'index.handler', + 'CodeUri': '/var/task/src/http/get-index', + 'Runtime': 'nodejs20.x', + 'Architectures': [ + 'arm64', + ], + 'MemorySize': 1152, + 'EphemeralStorage': { + 'Size': 512, + }, + 'Timeout': 5, + 'Environment': { + 'Variables': { + 'ARC_APP_NAME': 'hello', + 'ARC_ENV': 'staging', + 'ARC_ROLE': { + 'Ref': 'Role', + }, + 'ARC_SESSION_TABLE_NAME': 'jwe', + 'ARC_STACK_NAME': { + 'Ref': 'AWS::StackName', + }, + 'ARC_STATIC_BUCKET': { + 'Ref': 'StaticBucket', + }, + 'ARC_WSS_URL': { + 'Fn::Sub': [ + 'wss://${WS}.execute-api.${AWS::Region}.amazonaws.com/staging', + {}, + ], + }, + 'ARC_STATIC_SPA': false, + }, + }, + 'Role': { + 'Fn::Sub': [ + 'arn:aws:iam::${AWS::AccountId}:role/${roleName}', + { + 'roleName': { + 'Ref': 'Role', + }, + }, + ], + }, + 'Events': { + 'GetIndexHTTPEvent': { + 'Type': 'HttpApi', + 'Properties': { + 'Path': '/', + 'Method': 'GET', + 'ApiId': { + 'Ref': 'HTTP', + }, + }, + }, + }, + }, + 'ArcMetadata': { + 'pragma': 'http', + 'name': 'get /', + 'method': 'get', + 'path': '/', + }, + }, + 'PostLikesHTTPLambda': { + 'Type': 'AWS::Serverless::Function', + 'Properties': { + 'Handler': 'index.handler', + 'CodeUri': '/var/task/src/http/post-likes', + 'Runtime': 'nodejs20.x', + 'Architectures': [ + 'arm64', + ], + 'MemorySize': 1152, + 'EphemeralStorage': { + 'Size': 512, + }, + 'Timeout': 5, + 'Environment': { + 'Variables': { + 'ARC_APP_NAME': 'hello', + 'ARC_ENV': 'staging', + 'ARC_ROLE': { + 'Ref': 'Role', + }, + 'ARC_SESSION_TABLE_NAME': 'jwe', + 'ARC_STACK_NAME': { + 'Ref': 'AWS::StackName', + }, + 'ARC_STATIC_BUCKET': { + 'Ref': 'StaticBucket', + }, + 'ARC_WSS_URL': { + 'Fn::Sub': [ + 'wss://${WS}.execute-api.${AWS::Region}.amazonaws.com/staging', + {}, + ], + }, + }, + }, + 'Role': { + 'Fn::Sub': [ + 'arn:aws:iam::${AWS::AccountId}:role/${roleName}', + { + 'roleName': { + 'Ref': 'Role', + }, + }, + ], + }, + 'Events': { + 'PostLikesHTTPEvent': { + 'Type': 'HttpApi', + 'Properties': { + 'Path': '/likes', + 'Method': 'POST', + 'ApiId': { + 'Ref': 'HTTP', + }, + }, + }, + }, + }, + 'ArcMetadata': { + 'pragma': 'http', + 'name': 'post /likes', + 'method': 'post', + 'path': '/likes', + }, + }, + 'LikesTable': { + 'Type': 'AWS::DynamoDB::Table', + 'Properties': { + 'KeySchema': [ + { + 'AttributeName': 'likeID', + 'KeyType': 'HASH', + }, + ], + 'AttributeDefinitions': [ + { + 'AttributeName': 'likeID', + 'AttributeType': 'S', + }, + { + 'AttributeName': 'date', + 'AttributeType': 'S', + }, + ], + 'BillingMode': 'PAY_PER_REQUEST', + 'GlobalSecondaryIndexes': [ + { + 'IndexName': 'date-index', + 'KeySchema': [ + { + 'AttributeName': 'date', + 'KeyType': 'HASH', + }, + ], + 'Projection': { + 'ProjectionType': 'ALL', + }, + }, + ], + 'StreamSpecification': { + 'StreamViewType': 'NEW_AND_OLD_IMAGES', + }, + }, + }, + 'HitCounterEventLambda': { + 'Type': 'AWS::Serverless::Function', + 'Properties': { + 'Handler': 'index.handler', + 'CodeUri': '/var/task/src/events/hit-counter', + 'Runtime': 'nodejs20.x', + 'Architectures': [ + 'arm64', + ], + 'MemorySize': 1152, + 'EphemeralStorage': { + 'Size': 512, + }, + 'Timeout': 5, + 'Environment': { + 'Variables': { + 'ARC_APP_NAME': 'hello', + 'ARC_ENV': 'staging', + 'ARC_ROLE': { + 'Ref': 'Role', + }, + 'ARC_SESSION_TABLE_NAME': 'jwe', + 'ARC_STACK_NAME': { + 'Ref': 'AWS::StackName', + }, + 'ARC_STATIC_BUCKET': { + 'Ref': 'StaticBucket', + }, + 'ARC_WSS_URL': { + 'Fn::Sub': [ + 'wss://${WS}.execute-api.${AWS::Region}.amazonaws.com/staging', + {}, + ], + }, + }, + }, + 'Role': { + 'Fn::Sub': [ + 'arn:aws:iam::${AWS::AccountId}:role/${roleName}', + { + 'roleName': { + 'Ref': 'Role', + }, + }, + ], + }, + 'Events': { + 'HitCounterEvent': { + 'Type': 'SNS', + 'Properties': { + 'Topic': { + 'Ref': 'HitCounterEventTopic', + }, + }, + }, + }, + }, + 'ArcMetadata': { + 'pragma': 'events', + 'name': 'hit-counter', + }, + }, + 'HitCounterEventTopic': { + 'Type': 'AWS::SNS::Topic', + 'Properties': { + 'DisplayName': 'HitCounter', + 'Subscription': [], + }, + }, + 'DailyAffirmationScheduledLambda': { + 'Type': 'AWS::Serverless::Function', + 'Properties': { + 'Handler': 'index.handler', + 'CodeUri': '/var/task/src/scheduled/daily-affirmation', + 'Runtime': 'nodejs20.x', + 'Architectures': [ + 'arm64', + ], + 'MemorySize': 1152, + 'EphemeralStorage': { + 'Size': 512, + }, + 'Timeout': 5, + 'Environment': { + 'Variables': { + 'ARC_APP_NAME': 'hello', + 'ARC_ENV': 'staging', + 'ARC_ROLE': { + 'Ref': 'Role', + }, + 'ARC_SESSION_TABLE_NAME': 'jwe', + 'ARC_STACK_NAME': { + 'Ref': 'AWS::StackName', + }, + 'ARC_STATIC_BUCKET': { + 'Ref': 'StaticBucket', + }, + 'ARC_WSS_URL': { + 'Fn::Sub': [ + 'wss://${WS}.execute-api.${AWS::Region}.amazonaws.com/staging', + {}, + ], + }, + }, + }, + 'Role': { + 'Fn::Sub': [ + 'arn:aws:iam::${AWS::AccountId}:role/${roleName}', + { + 'roleName': { + 'Ref': 'Role', + }, + }, + ], + }, + 'Events': {}, + }, + 'ArcMetadata': { + 'pragma': 'scheduled', + 'name': 'daily-affirmation', + }, + }, + 'DailyAffirmationScheduledEvent': { + 'Type': 'AWS::Events::Rule', + 'Properties': { + 'ScheduleExpression': 'rate(1 day)', + 'Targets': [ + { + 'Arn': { + 'Fn::GetAtt': [ + 'DailyAffirmationScheduledLambda', + 'Arn', + ], + }, + 'Id': 'DailyAffirmationScheduledLambda', + }, + ], + }, + }, + 'DailyAffirmationScheduledPermission': { + 'Type': 'AWS::Lambda::Permission', + 'Properties': { + 'Action': 'lambda:InvokeFunction', + 'FunctionName': { + 'Ref': 'DailyAffirmationScheduledLambda', + }, + 'Principal': 'events.amazonaws.com', + 'SourceArn': { + 'Fn::GetAtt': [ + 'DailyAffirmationScheduledEvent', + 'Arn', + ], + }, + }, + }, + 'StaticBucket': { + 'Type': 'AWS::S3::Bucket', + 'Properties': { + 'OwnershipControls': { + 'Rules': [ + { + 'ObjectOwnership': 'BucketOwnerEnforced', + }, + ], + }, + 'WebsiteConfiguration': { + 'IndexDocument': 'index.html', + 'ErrorDocument': '404.html', + }, + 'PublicAccessBlockConfiguration': { + 'BlockPublicAcls': false, + 'BlockPublicPolicy': false, + 'IgnorePublicAcls': false, + 'RestrictPublicBuckets': false, + }, + }, + }, + 'StaticBucketPolicy': { + 'Type': 'AWS::S3::BucketPolicy', + 'Properties': { + 'Bucket': { + 'Ref': 'StaticBucket', + }, + 'PolicyDocument': { + 'Version': '2012-10-17', + 'Statement': [ + { + 'Action': [ + 's3:GetObject', + ], + 'Effect': 'Allow', + 'Principal': '*', + 'Resource': [ + { + 'Fn::Sub': [ + 'arn:aws:s3:::${bukkit}/*', + { + 'bukkit': { + 'Ref': 'StaticBucket', + }, + }, + ], + }, + ], + 'Sid': 'PublicReadGetObject', + }, + ], + }, + }, + }, + 'LikesTableStreamLambda': { + 'Type': 'AWS::Serverless::Function', + 'Properties': { + 'Handler': 'index.handler', + 'CodeUri': '/var/task/src/tables-streams/likes', + 'Runtime': 'nodejs20.x', + 'Architectures': [ + 'arm64', + ], + 'MemorySize': 1152, + 'EphemeralStorage': { + 'Size': 512, + }, + 'Timeout': 5, + 'Environment': { + 'Variables': { + 'ARC_APP_NAME': 'hello', + 'ARC_ENV': 'staging', + 'ARC_ROLE': { + 'Ref': 'Role', + }, + 'ARC_SESSION_TABLE_NAME': 'jwe', + 'ARC_STACK_NAME': { + 'Ref': 'AWS::StackName', + }, + 'ARC_STATIC_BUCKET': { + 'Ref': 'StaticBucket', + }, + 'ARC_WSS_URL': { + 'Fn::Sub': [ + 'wss://${WS}.execute-api.${AWS::Region}.amazonaws.com/staging', + {}, + ], + }, + }, + }, + 'Role': { + 'Fn::Sub': [ + 'arn:aws:iam::${AWS::AccountId}:role/${roleName}', + { + 'roleName': { + 'Ref': 'Role', + }, + }, + ], + }, + 'Events': {}, + }, + 'ArcMetadata': { + 'pragma': 'tables-streams', + 'name': 'likes', + }, + }, + 'LikesTableStreamEvent': { + 'Type': 'AWS::Lambda::EventSourceMapping', + 'Properties': { + 'BatchSize': 10, + 'EventSourceArn': { + 'Fn::GetAtt': [ + 'LikesTable', + 'StreamArn', + ], + }, + 'FunctionName': { + 'Fn::GetAtt': [ + 'LikesTableStreamLambda', + 'Arn', + ], + }, + 'StartingPosition': 'TRIM_HORIZON', + }, + }, + 'WS': { + 'Type': 'AWS::ApiGatewayV2::Api', + 'Properties': { + 'Name': 'HelloWebsocketStaging', + 'ProtocolType': 'WEBSOCKET', + 'RouteSelectionExpression': '$request.body.action', + }, + }, + 'WebsocketDeployment': { + 'Type': 'AWS::ApiGatewayV2::Deployment', + 'DependsOn': [ + 'ConnectWSRoute', + 'DefaultWSRoute', + 'DisconnectWSRoute', + ], + 'Properties': { + 'ApiId': { + 'Ref': 'WS', + }, + }, + }, + 'WebsocketStage': { + 'Type': 'AWS::ApiGatewayV2::Stage', + 'Properties': { + 'StageName': 'staging', + 'DeploymentId': { + 'Ref': 'WebsocketDeployment', + }, + 'ApiId': { + 'Ref': 'WS', + }, + }, + }, + 'WebSocketPolicy': { + 'Type': 'AWS::IAM::Policy', + 'DependsOn': 'Role', + 'Properties': { + 'PolicyName': 'ArcWebSocketPolicy', + 'PolicyDocument': { + 'Statement': [ + { + 'Effect': 'Allow', + 'Action': [ + 'execute-api:Invoke', + 'execute-api:ManageConnections', + ], + 'Resource': [ + { + 'Fn::Sub': [ + 'arn:aws:execute-api:${AWS::Region}:*:${api}/*', + { + 'api': { + 'Ref': 'WS', + }, + }, + ], + }, + ], + }, + ], + }, + 'Roles': [ + { + 'Ref': 'Role', + }, + ], + }, + }, + 'ActionWSLambda': { + 'Type': 'AWS::Serverless::Function', + 'Properties': { + 'Handler': 'index.handler', + 'CodeUri': '/var/task/src/ws/action', + 'Runtime': 'nodejs20.x', + 'Architectures': [ + 'arm64', + ], + 'MemorySize': 1152, + 'EphemeralStorage': { + 'Size': 512, + }, + 'Timeout': 5, + 'Environment': { + 'Variables': { + 'ARC_APP_NAME': 'hello', + 'ARC_ENV': 'staging', + 'ARC_ROLE': { + 'Ref': 'Role', + }, + 'ARC_SESSION_TABLE_NAME': 'jwe', + 'ARC_STACK_NAME': { + 'Ref': 'AWS::StackName', + }, + 'ARC_STATIC_BUCKET': { + 'Ref': 'StaticBucket', + }, + 'ARC_WSS_URL': { + 'Fn::Sub': [ + 'wss://${WS}.execute-api.${AWS::Region}.amazonaws.com/staging', + {}, + ], + }, + }, + }, + 'Role': { + 'Fn::Sub': [ + 'arn:aws:iam::${AWS::AccountId}:role/${roleName}', + { + 'roleName': { + 'Ref': 'Role', + }, + }, + ], + }, + 'Events': {}, + }, + 'ArcMetadata': { + 'pragma': 'ws', + 'name': 'action', + }, + }, + 'ActionWSRoute': { + 'Type': 'AWS::ApiGatewayV2::Route', + 'Properties': { + 'ApiId': { + 'Ref': 'WS', + }, + 'RouteKey': 'action', + 'OperationName': 'ActionWSRoute', + 'Target': { + 'Fn::Join': [ + '/', + [ + 'integrations', + { + 'Ref': 'ActionWSIntegration', + }, + ], + ], + }, + }, + }, + 'ActionWSIntegration': { + 'Type': 'AWS::ApiGatewayV2::Integration', + 'Properties': { + 'ApiId': { + 'Ref': 'WS', + }, + 'IntegrationType': 'AWS_PROXY', + 'IntegrationUri': { + 'Fn::Sub': [ + 'arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ActionWSLambda.Arn}/invocations', + {}, + ], + }, + }, + }, + 'ActionWSPermission': { + 'Type': 'AWS::Lambda::Permission', + 'DependsOn': [ + 'WS', + 'ActionWSLambda', + ], + 'Properties': { + 'Action': 'lambda:InvokeFunction', + 'FunctionName': { + 'Ref': 'ActionWSLambda', + }, + 'Principal': 'apigateway.amazonaws.com', + }, + }, + 'ConnectWSLambda': { + 'Type': 'AWS::Serverless::Function', + 'Properties': { + 'Handler': 'index.handler', + 'CodeUri': '/var/task/src/ws/connect', + 'Runtime': 'nodejs20.x', + 'Architectures': [ + 'arm64', + ], + 'MemorySize': 1152, + 'EphemeralStorage': { + 'Size': 512, + }, + 'Timeout': 5, + 'Environment': { + 'Variables': { + 'ARC_APP_NAME': 'hello', + 'ARC_ENV': 'staging', + 'ARC_ROLE': { + 'Ref': 'Role', + }, + 'ARC_SESSION_TABLE_NAME': 'jwe', + 'ARC_STACK_NAME': { + 'Ref': 'AWS::StackName', + }, + 'ARC_STATIC_BUCKET': { + 'Ref': 'StaticBucket', + }, + 'ARC_WSS_URL': { + 'Fn::Sub': [ + 'wss://${WS}.execute-api.${AWS::Region}.amazonaws.com/staging', + {}, + ], + }, + }, + }, + 'Role': { + 'Fn::Sub': [ + 'arn:aws:iam::${AWS::AccountId}:role/${roleName}', + { + 'roleName': { + 'Ref': 'Role', + }, + }, + ], + }, + 'Events': {}, + }, + 'ArcMetadata': { + 'pragma': 'ws', + 'name': 'connect', + }, + }, + 'ConnectWSRoute': { + 'Type': 'AWS::ApiGatewayV2::Route', + 'Properties': { + 'ApiId': { + 'Ref': 'WS', + }, + 'RouteKey': '$connect', + 'OperationName': 'ConnectWSRoute', + 'Target': { + 'Fn::Join': [ + '/', + [ + 'integrations', + { + 'Ref': 'ConnectWSIntegration', + }, + ], + ], + }, + }, + }, + 'ConnectWSIntegration': { + 'Type': 'AWS::ApiGatewayV2::Integration', + 'Properties': { + 'ApiId': { + 'Ref': 'WS', + }, + 'IntegrationType': 'AWS_PROXY', + 'IntegrationUri': { + 'Fn::Sub': [ + 'arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ConnectWSLambda.Arn}/invocations', + {}, + ], + }, + }, + }, + 'ConnectWSPermission': { + 'Type': 'AWS::Lambda::Permission', + 'DependsOn': [ + 'WS', + 'ConnectWSLambda', + ], + 'Properties': { + 'Action': 'lambda:InvokeFunction', + 'FunctionName': { + 'Ref': 'ConnectWSLambda', + }, + 'Principal': 'apigateway.amazonaws.com', + }, + }, + 'DefaultWSLambda': { + 'Type': 'AWS::Serverless::Function', + 'Properties': { + 'Handler': 'index.handler', + 'CodeUri': '/var/task/src/ws/default', + 'Runtime': 'nodejs20.x', + 'Architectures': [ + 'arm64', + ], + 'MemorySize': 1152, + 'EphemeralStorage': { + 'Size': 512, + }, + 'Timeout': 5, + 'Environment': { + 'Variables': { + 'ARC_APP_NAME': 'hello', + 'ARC_ENV': 'staging', + 'ARC_ROLE': { + 'Ref': 'Role', + }, + 'ARC_SESSION_TABLE_NAME': 'jwe', + 'ARC_STACK_NAME': { + 'Ref': 'AWS::StackName', + }, + 'ARC_STATIC_BUCKET': { + 'Ref': 'StaticBucket', + }, + 'ARC_WSS_URL': { + 'Fn::Sub': [ + 'wss://${WS}.execute-api.${AWS::Region}.amazonaws.com/staging', + {}, + ], + }, + }, + }, + 'Role': { + 'Fn::Sub': [ + 'arn:aws:iam::${AWS::AccountId}:role/${roleName}', + { + 'roleName': { + 'Ref': 'Role', + }, + }, + ], + }, + 'Events': {}, + }, + 'ArcMetadata': { + 'pragma': 'ws', + 'name': 'default', + }, + }, + 'DefaultWSRoute': { + 'Type': 'AWS::ApiGatewayV2::Route', + 'Properties': { + 'ApiId': { + 'Ref': 'WS', + }, + 'RouteKey': '$default', + 'OperationName': 'DefaultWSRoute', + 'Target': { + 'Fn::Join': [ + '/', + [ + 'integrations', + { + 'Ref': 'DefaultWSIntegration', + }, + ], + ], + }, + }, + }, + 'DefaultWSIntegration': { + 'Type': 'AWS::ApiGatewayV2::Integration', + 'Properties': { + 'ApiId': { + 'Ref': 'WS', + }, + 'IntegrationType': 'AWS_PROXY', + 'IntegrationUri': { + 'Fn::Sub': [ + 'arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${DefaultWSLambda.Arn}/invocations', + {}, + ], + }, + }, + }, + 'DefaultWSPermission': { + 'Type': 'AWS::Lambda::Permission', + 'DependsOn': [ + 'WS', + 'DefaultWSLambda', + ], + 'Properties': { + 'Action': 'lambda:InvokeFunction', + 'FunctionName': { + 'Ref': 'DefaultWSLambda', + }, + 'Principal': 'apigateway.amazonaws.com', + }, + }, + 'DisconnectWSLambda': { + 'Type': 'AWS::Serverless::Function', + 'Properties': { + 'Handler': 'index.handler', + 'CodeUri': '/var/task/src/ws/disconnect', + 'Runtime': 'nodejs20.x', + 'Architectures': [ + 'arm64', + ], + 'MemorySize': 1152, + 'EphemeralStorage': { + 'Size': 512, + }, + 'Timeout': 5, + 'Environment': { + 'Variables': { + 'ARC_APP_NAME': 'hello', + 'ARC_ENV': 'staging', + 'ARC_ROLE': { + 'Ref': 'Role', + }, + 'ARC_SESSION_TABLE_NAME': 'jwe', + 'ARC_STACK_NAME': { + 'Ref': 'AWS::StackName', + }, + 'ARC_STATIC_BUCKET': { + 'Ref': 'StaticBucket', + }, + 'ARC_WSS_URL': { + 'Fn::Sub': [ + 'wss://${WS}.execute-api.${AWS::Region}.amazonaws.com/staging', + {}, + ], + }, + }, + }, + 'Role': { + 'Fn::Sub': [ + 'arn:aws:iam::${AWS::AccountId}:role/${roleName}', + { + 'roleName': { + 'Ref': 'Role', + }, + }, + ], + }, + 'Events': {}, + }, + 'ArcMetadata': { + 'pragma': 'ws', + 'name': 'disconnect', + }, + }, + 'DisconnectWSRoute': { + 'Type': 'AWS::ApiGatewayV2::Route', + 'Properties': { + 'ApiId': { + 'Ref': 'WS', + }, + 'RouteKey': '$disconnect', + 'OperationName': 'DisconnectWSRoute', + 'Target': { + 'Fn::Join': [ + '/', + [ + 'integrations', + { + 'Ref': 'DisconnectWSIntegration', + }, + ], + ], + }, + }, + }, + 'DisconnectWSIntegration': { + 'Type': 'AWS::ApiGatewayV2::Integration', + 'Properties': { + 'ApiId': { + 'Ref': 'WS', + }, + 'IntegrationType': 'AWS_PROXY', + 'IntegrationUri': { + 'Fn::Sub': [ + 'arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${DisconnectWSLambda.Arn}/invocations', + {}, + ], + }, + }, + }, + 'DisconnectWSPermission': { + 'Type': 'AWS::Lambda::Permission', + 'DependsOn': [ + 'WS', + 'DisconnectWSLambda', + ], + 'Properties': { + 'Action': 'lambda:InvokeFunction', + 'FunctionName': { + 'Ref': 'DisconnectWSLambda', + }, + 'Principal': 'apigateway.amazonaws.com', + }, + }, + }, + 'Outputs': { + 'API': { + 'Description': 'API Gateway (HTTP)', + 'Value': { + 'Fn::Sub': [ + 'https://${ApiId}.execute-api.${AWS::Region}.amazonaws.com', + { + 'ApiId': { + 'Ref': 'HTTP', + }, + }, + ], + }, + }, + 'ApiId': { + 'Description': 'API ID (ApiId)', + 'Value': { + 'Ref': 'HTTP', + }, + }, + 'HitCounterEventTopic': { + 'Description': 'An SNS Topic', + 'Value': { + 'Ref': 'HitCounterEventTopic', + }, + }, + 'BucketURL': { + 'Description': 'Bucket URL', + 'Value': { + 'Fn::Sub': [ + 'http://${bukkit}.s3-website-${AWS::Region}.amazonaws.com', + { + 'bukkit': { + 'Ref': 'StaticBucket', + }, + }, + ], + }, + }, + 'WSS': { + 'Description': 'WebSocket Endpoint', + 'Value': { + 'Fn::Sub': [ + 'wss://${WS}.execute-api.${AWS::Region}.amazonaws.com/staging', + {}, + ], + }, + }, + }, +} diff --git a/src/views/landing/support/render-examples.mjs b/src/views/landing/support/render-examples.mjs new file mode 100644 index 00000000..2b08b75d --- /dev/null +++ b/src/views/landing/support/render-examples.mjs @@ -0,0 +1,52 @@ +import { Arcdown } from 'arcdown' + +import { cloudformation as helloWorldCf, arc as helloWorldArc } from './hello-world.mjs' +import { cloudformation as arcCodesCf, arc as arcCodesArc } from './arc-codes.mjs' +import { cloudformation as kitchenSinkCf, arc as kitchenSinkArc } from './kitchen-sink.mjs' + +const hljs = { + classString: 'hljs text-1 p-2', +} + +const arcdown = new Arcdown({ hljs }) + +const helloWorldCfMd = '```json\n' + JSON.stringify(helloWorldCf, null, 2) + '\n```' +const helloWorldArcMd = '```arc\n' + helloWorldArc + '\n```' + +const getLoc = str => str.split('\n').filter(l => l !== '').length + +export async function renderHelloWorld () { + const a = await arcdown.render(helloWorldArcMd) + const cf = await arcdown.render(helloWorldCfMd) + const { html: arc } = a + const { html: cloudFormation } = cf + const arcLoc = getLoc(helloWorldArc) + const cfLoc = getLoc(JSON.stringify(helloWorldCf, null, 2)) + return { arc, arcLoc, cloudFormation, cfLoc } +} + +const arcCodesCfMd = '```json\n' + JSON.stringify(arcCodesCf, null, 2) + '\n```' +const arcCodesArcMd = '```arc\n' + arcCodesArc + '\n```' + +export async function renderArcCodes () { + const a = await arcdown.render(arcCodesArcMd) + const cf = await arcdown.render(arcCodesCfMd) + const { html: arc } = a + const { html: cloudFormation } = cf + const arcLoc = getLoc(arcCodesArc) + const cfLoc = getLoc(JSON.stringify(arcCodesCf, null, 2)) + return { arc, arcLoc, cloudFormation, cfLoc } +} + +const kitchenSinkCfMd = '```json\n' + JSON.stringify(kitchenSinkCf, null, 2) + '\n```' +const kitchenSinkArcMd = '```arc\n' + kitchenSinkArc + '\n```' + +export async function renderKitchenSink () { + const a = await arcdown.render(kitchenSinkArcMd) + const cf = await arcdown.render(kitchenSinkCfMd) + const { html: arc } = a + const { html: cloudFormation } = cf + const arcLoc = getLoc(kitchenSinkArc) + const cfLoc = getLoc(JSON.stringify(kitchenSinkCf, null, 2)) + return { arc, arcLoc, cloudFormation, cfLoc } +} diff --git a/src/views/modules/.eslintrc.js b/src/views/modules/.eslintrc.js deleted file mode 100644 index cc593e88..00000000 --- a/src/views/modules/.eslintrc.js +++ /dev/null @@ -1,6 +0,0 @@ -// eslint-disable-next-line -module.exports = { - parserOptions: { - sourceType: 'module' - } -} diff --git a/src/views/modules/components/algolia.mjs b/src/views/modules/components/algolia.mjs new file mode 100644 index 00000000..29bf0055 --- /dev/null +++ b/src/views/modules/components/algolia.mjs @@ -0,0 +1,25 @@ +// related docsearch config +// https://github.com/algolia/docsearch-configs/blob/master/configs/arc.json + +export default function Algolia (lang) { + return ` + + +` +} diff --git a/src/views/modules/components/anchor.js b/src/views/modules/components/anchor.js deleted file mode 100644 index eb63cd0d..00000000 --- a/src/views/modules/components/anchor.js +++ /dev/null @@ -1,6 +0,0 @@ -export default function Anchor (props = {}) { - let { children, href = '#' } = props - return ` -${children} - ` -} diff --git a/src/views/modules/components/banner.mjs b/src/views/modules/components/banner.mjs new file mode 100644 index 00000000..40949eb4 --- /dev/null +++ b/src/views/modules/components/banner.mjs @@ -0,0 +1,37 @@ +export default function Banner (state = {}) { + const { enabled } = state + + return enabled ? ` +
+ + Community chat has moved to + + Discord + + +
+ ` : '' +} diff --git a/src/views/modules/components/discord-link.mjs b/src/views/modules/components/discord-link.mjs new file mode 100644 index 00000000..e2e8cc14 --- /dev/null +++ b/src/views/modules/components/discord-link.mjs @@ -0,0 +1,21 @@ +import Icon from './icon.mjs' + +export default function DiscordLink (state = {}) { + const { classes } = state + return ` + + ${Icon({ classes: 'discord-icon transition-fill', href: 'discord' })} + + ` +} diff --git a/src/views/modules/components/document-outline.mjs b/src/views/modules/components/document-outline.mjs new file mode 100644 index 00000000..a6eac583 --- /dev/null +++ b/src/views/modules/components/document-outline.mjs @@ -0,0 +1,24 @@ +export default function DocumentOutline (props = {}) { + const { + tocHtml = '', + slug = '', + title = 'Top', + } = props + + return ` + + ` +} + + diff --git a/src/views/modules/components/edit-link.mjs b/src/views/modules/components/edit-link.mjs new file mode 100644 index 00000000..370869c8 --- /dev/null +++ b/src/views/modules/components/edit-link.mjs @@ -0,0 +1,8 @@ +export default function EditLink (state = {}) { + const { editURL } = state + return editURL ? ` + +` : '' +} diff --git a/src/views/modules/components/github-link.js b/src/views/modules/components/github-link.mjs similarity index 72% rename from src/views/modules/components/github-link.js rename to src/views/modules/components/github-link.mjs index 721b4147..7152d296 100644 --- a/src/views/modules/components/github-link.js +++ b/src/views/modules/components/github-link.mjs @@ -1,7 +1,7 @@ -import Icon from './icon.js' +import Icon from './icon.mjs' -export default function GithubButton (state = {}) { - let { classes } = state +export default function GithubLink (state = {}) { + const { classes } = state return ` - ${children} - - ` -} diff --git a/src/views/modules/components/icon.js b/src/views/modules/components/icon.mjs similarity index 81% rename from src/views/modules/components/icon.js rename to src/views/modules/components/icon.mjs index 331a3abe..9a7d61bf 100644 --- a/src/views/modules/components/icon.js +++ b/src/views/modules/components/icon.mjs @@ -1,5 +1,5 @@ export default function Icon (state = {}) { - let { classes, href } = state + const { classes, href } = state return `
diff --git a/src/views/modules/components/item.js b/src/views/modules/components/item.js deleted file mode 100644 index 65d9e075..00000000 --- a/src/views/modules/components/item.js +++ /dev/null @@ -1,8 +0,0 @@ -export default function Item (state = {}) { - let { children, classes } = state - return ` -
  • - ${children} -
  • - ` -} diff --git a/src/views/modules/components/list.js b/src/views/modules/components/list.js deleted file mode 100644 index 1d99f7c6..00000000 --- a/src/views/modules/components/list.js +++ /dev/null @@ -1,8 +0,0 @@ -export default function List (props = {}) { - let { children, classes } = props - return ` -
      - ${children} -
    - ` -} diff --git a/src/views/modules/components/logo.js b/src/views/modules/components/logo.js deleted file mode 100644 index e99a0dbe..00000000 --- a/src/views/modules/components/logo.js +++ /dev/null @@ -1,15 +0,0 @@ -export default function Logo (props = {}) { - let { classes } = props - return ` -
    - - - - - -
    - ` -} - diff --git a/src/views/modules/components/logo.mjs b/src/views/modules/components/logo.mjs new file mode 100644 index 00000000..9eea98a0 --- /dev/null +++ b/src/views/modules/components/logo.mjs @@ -0,0 +1,11 @@ +export default function Logo (props = {}) { + const { classes } = props + return ` +
    + +
    + ` +} + diff --git a/src/views/modules/components/not-found.mjs b/src/views/modules/components/not-found.mjs new file mode 100644 index 00000000..c7e1dc2e --- /dev/null +++ b/src/views/modules/components/not-found.mjs @@ -0,0 +1,13 @@ +export default function FourOFour (state = {}) { + const { term } = state + return ` +
    +

    + 404: ${term ? `"${term}"` : 'that one'} is missing +

    +

    + Try using the search. +

    +
    +` +} diff --git a/src/views/modules/components/search.mjs b/src/views/modules/components/search.mjs new file mode 100644 index 00000000..8caa4c43 --- /dev/null +++ b/src/views/modules/components/search.mjs @@ -0,0 +1,18 @@ +export default function Search (state = {}) { + const { classes = '' } = state + return ` + +` +} diff --git a/src/views/modules/components/sidebar.js b/src/views/modules/components/sidebar.js deleted file mode 100644 index 705a7507..00000000 --- a/src/views/modules/components/sidebar.js +++ /dev/null @@ -1,144 +0,0 @@ -import listFromObject from '../helpers/list.js' -import slugify from '../helpers/slugify.js' -import toc from '../../docs/table-of-contents.js' -const map = { - list: function List (state = {}) { - let { children } = state - return ` -
      - ${children} -
    - ` - }, - item: function Item (state = {}) { - let { child = '', children = [], depth, path, active } = state - let isHeading = children.length - let ml = depth > 1 - ? 'ml-5' - : '' - return ` -
  • - ${ - isHeading - ? Heading({ children: child, depth, path, active }) - : Anchor({ children: child, depth, path, active }) -} - ${children} -
  • - ` - } -} - -function Anchor (state = {}) { - let { children, path, active } = state - let uri = path - .concat([ children ]) - .map(part => slugify(part)) - .join('/') - let href = `/${uri}` - let isActive = active === href - let activeClass = isActive - ? 'active' - : '' - let pointer = isActive - ? '→' - : '' - - return ` -
    ${pointer} ${children} - ` -} - -function Heading3 (state = {}) { - let { children } = state - return ` -

    - ${children} -

    -
    - ` -} - -function Heading4 (state = {}) { - let { children } = state - return ` -

    - ${children} -

    - ` -} - -function Heading5 (state = {}) { - let { children } = state - return ` -
    - ${children} -
    - ` -} - -function Heading (state = {}) { - let { depth } = state - const headings = [ - Heading3, - Heading4, - Heading5 - ] - return headings[depth - 1](state) -} - -export default function Sidebar (props = {}) { - let { active } = props - return ` - - ` -} diff --git a/src/views/modules/components/sidebar.mjs b/src/views/modules/components/sidebar.mjs new file mode 100644 index 00000000..16137d5d --- /dev/null +++ b/src/views/modules/components/sidebar.mjs @@ -0,0 +1,153 @@ +import listFromObject from '../helpers/list.mjs' +import slugify from '../helpers/slugify.mjs' + +const map = { + list: function List (state = {}) { + const { children } = state + return ` +
      + ${children} +
    + ` + }, + item: function Item (state = {}) { + const { child = '', children = [], depth, path, active } = state + const isHeading = children.length + const mb = isHeading ? 'mb1' : 'mb-4' + const ml = path.length > 3 ? 'ml-1' : '' + return ` +
  • + ${ + isHeading + ? Heading({ name: child, depth, path, active, children }) + : Anchor({ name: child, depth, path, active }) +} + ${depth >= 3 ? '' : children} +
  • + ` + }, +} + +function Anchor (state = {}) { + const { name, path, active } = state + const uri = path + .concat([ name ]) + .map((part) => slugify(part)) + .join('/') + const href = `/${uri}` + const isActive = active === href + const activeClass = isActive ? ' active' : '' + const text = isActive ? `→ ${name}` : name + + return ` +${text} + ` +} + +function Heading3 (state = {}) { + const { name } = state + return ` +

    + ${name} +

    +
    + ` +} + +function Heading4 (state = {}) { + const { name } = state + return ` +

    + ${name} +

    + ` +} + +function Group (state = {}) { + const { name, depth, active, children } = state + const slug = slugify(name) + const groupIsActive = + active.replace('/docs/en', '').split('/').indexOf(slug) === depth + + return ` + + ` +} + +function Heading (state = {}) { + const { depth } = state + const headings = [ Heading3, Heading4, Group ] + return headings[depth - 1](state) +} + +export default function Sidebar (props = {}) { + const { active, toc } = props + + return ` + + ` +} diff --git a/src/views/modules/components/slack-link.js b/src/views/modules/components/slack-link.js deleted file mode 100644 index dad655b6..00000000 --- a/src/views/modules/components/slack-link.js +++ /dev/null @@ -1,21 +0,0 @@ -import Icon from './icon.js' - -export default function SlackLink (state = {}) { - let { classes } = state - return ` - - ${Icon({ classes: 'slack-icon transition-fill', href: 'slack' })} - - ` -} diff --git a/src/views/modules/components/star-link.js b/src/views/modules/components/star-link.js deleted file mode 100644 index 481a7981..00000000 --- a/src/views/modules/components/star-link.js +++ /dev/null @@ -1,20 +0,0 @@ -import Icon from './icon.js' - -export default function StarLink (state = {}) { - let { classes } = state - return ` - - ${Icon({ classes: 'icon fill-current transition-fill', href: 'star' })} - - ` -} diff --git a/src/views/modules/components/theme-button.mjs b/src/views/modules/components/theme-button.mjs new file mode 100644 index 00000000..13cb5d22 --- /dev/null +++ b/src/views/modules/components/theme-button.mjs @@ -0,0 +1,21 @@ +import Icon from './icon.mjs' + +export default function ThemeButton (state = {}) { + const { classes } = state + return ` + + ` +} diff --git a/src/views/modules/components/twitter-link.mjs b/src/views/modules/components/twitter-link.mjs new file mode 100644 index 00000000..fb0b6f45 --- /dev/null +++ b/src/views/modules/components/twitter-link.mjs @@ -0,0 +1,21 @@ +import Icon from './icon.mjs' + +export default function TwitterLink (state = {}) { + const { classes } = state + return ` + + ${Icon({ classes: 'icon transition-fill', href: 'twitter' })} + + ` +} diff --git a/src/views/modules/data/store.js b/src/views/modules/data/store.js deleted file mode 100644 index d8d35efe..00000000 --- a/src/views/modules/data/store.js +++ /dev/null @@ -1,42 +0,0 @@ -const listeners = [] -const state = {} -let noop = x => x - -function subscribe (fn) { - listeners.push(fn) -} - -function unsubscribe (fn) { - listeners.splice(listeners.indexOf(fn), 1) -} - -function mutate (mutation) { - mutation = mutation || noop - let i = 0 - let l = listeners.length - let fn - mutation(state) - for (i; i < l; i++) { - fn = listeners[i] - fn(state) - } -} - -function merge (o, n) { - for (let prop in n) { - o[prop] = n[prop] - } -} - -function store (initialState) { - if (initialState) { - merge(state, initialState) - } - return state -} - -store.subscribe = subscribe -store.unsubscribe = unsubscribe -store.mutate = mutate - -export default store diff --git a/src/views/modules/document/ga.mjs b/src/views/modules/document/ga.mjs new file mode 100644 index 00000000..257000db --- /dev/null +++ b/src/views/modules/document/ga.mjs @@ -0,0 +1,21 @@ +export default function GoogleAnalytics () { + const env = process.env.ARC_ENV + const production = 'production' + const productionId = 'UA-74655805-3' + const stagingId = 'G-22723SKNK4' + + function snippet (env) { + return ` + + + ` + } + + return snippet(env == production ? productionId : stagingId) + +} diff --git a/src/views/modules/document/head.js b/src/views/modules/document/head.js deleted file mode 100644 index 63723713..00000000 --- a/src/views/modules/document/head.js +++ /dev/null @@ -1,50 +0,0 @@ -export default function Head (props = {}) { - let { category, description, title } = props - let fullTitle = category && title - ? `${category} > ${title} - Architect documentation` - : 'Architect documentation' - let descriptionContent = description || 'Architect documentation' - - return ` - - - - - -${fullTitle} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -` -} diff --git a/src/views/modules/document/head.mjs b/src/views/modules/document/head.mjs new file mode 100644 index 00000000..e00fbe0f --- /dev/null +++ b/src/views/modules/document/head.mjs @@ -0,0 +1,62 @@ +import arc from '@architect/functions' + +const stripCode = str => str.replace(/\<\/?code\>/g, '') + +export default function Head (props = {}) { + const { category, description, lang = 'en', path, title } = props + const descriptionContent = description || 'Architect documentation' + let fullTitle = '' + if (category && title) + fullTitle += `${category} > ${title} - ` + else if (title) + fullTitle += `${title} - ` + fullTitle += 'Architect documentation' + + return ` + + + + + + +${stripCode(fullTitle)} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +` +} diff --git a/src/views/modules/document/html.js b/src/views/modules/document/html.js deleted file mode 100644 index baf69017..00000000 --- a/src/views/modules/document/html.js +++ /dev/null @@ -1,144 +0,0 @@ -import Head from './head.js' -import Symbols from './symbols.js' -import Script from './script.js' -import State from './state.js' -import Logo from '../components/logo.js' -import Icon from '../components/icon.js' -import Sidebar from '../components/sidebar.js' -import GithubLink from '../components/github-link.js' -import SlackLink from '../components/slack-link.js' - -export default function HTML (props = {}) { - let { - children = [], - editURL = '', - lang = 'en', - scripts = '', - state = {}, - thirdparty = '', - title = '' - } = props - let scriptTags = scripts && - Array.isArray(props.scripts) - ? scripts.map(src => Script({ src })) - : Script(scripts) - let stateTag = state && - State(state) || '' - - return ` - - -${Head(props)} -${Symbols} - -
    -
    - - ${Logo({ classes: 'logo' })} - -
    - ${SlackLink()} - ${GithubLink({ classes: 'ml-2' })} - -
    -
    -
    - ${Sidebar(props)} -
    -
    -

    - ${title} -

    -
    - ${children} - -
    -
    -
    -
    - ${stateTag} - ${scriptTags} - ${thirdparty} - - -` -} diff --git a/src/views/modules/document/html.mjs b/src/views/modules/document/html.mjs new file mode 100644 index 00000000..9ce8b706 --- /dev/null +++ b/src/views/modules/document/html.mjs @@ -0,0 +1,93 @@ +import Banner from '../components/banner.mjs' +import DocumentOutline from '../components/document-outline.mjs' +import EditLink from '../components/edit-link.mjs' +import GoogleAnalytics from './ga.mjs' +import Head from './head.mjs' +import Script from './script.mjs' +import Sidebar from '../components/sidebar.mjs' +import State from './state.mjs' +import Symbols from './symbols.mjs' +import TopNav from './top-nav.mjs' + +export default function HTML (props = {}) { + const { + html = '', + editURL = '', + lang = 'en', + scripts = '', + slug = '', + state = {}, + thirdparty = '', + title = '', + } = props + + const scriptTags = scripts && + Array.isArray(props.scripts) + ? scripts.map(src => Script({ src })).join('') + : Script(scripts) + const stateTag = (state && State(state)) || '' + + return ` + + +${Head(props)} +${Symbols} + +
    + ${TopNav()} + ${Banner({ enabled: false })} + ${Sidebar(props)} +
    +
    +

    + ${title} +

    +
    + ${html} + ${EditLink({ editURL })} +
    +
    + ${DocumentOutline(props)} +
    +
    + ${stateTag} + ${scriptTags} + ${GoogleAnalytics()} + ${thirdparty} + + +` +} diff --git a/src/views/modules/document/script.js b/src/views/modules/document/script.js deleted file mode 100644 index a079c4b9..00000000 --- a/src/views/modules/document/script.js +++ /dev/null @@ -1,8 +0,0 @@ -export default function Script (props) { - props = props || {} - let src = props.src - return src ? ` - - ` - : '' -} diff --git a/src/views/modules/document/script.mjs b/src/views/modules/document/script.mjs new file mode 100644 index 00000000..18eb73f9 --- /dev/null +++ b/src/views/modules/document/script.mjs @@ -0,0 +1,10 @@ +import arc from '@architect/functions' + +export default function Script (props) { + props = props || {} + const src = props.src + return src ? ` + + ` + : '' +} diff --git a/src/views/modules/document/state.js b/src/views/modules/document/state.mjs similarity index 100% rename from src/views/modules/document/state.js rename to src/views/modules/document/state.mjs diff --git a/src/views/modules/document/styles.js b/src/views/modules/document/styles.js deleted file mode 100644 index 43735137..00000000 --- a/src/views/modules/document/styles.js +++ /dev/null @@ -1,1198 +0,0 @@ -export default ` -/* ----- THEME ----- */ -/* VARIABLES */ -:root { - --p0:#2CDD93;/* Medium Aquamarine light */ - --p1:#1F74D6;/* Bright Navy Blue light */ - --p2:hsl(267, 50%, 55%);/* Royal Purple light */ - --p3:#E21893;/* Barbie Pink light */ - --p4:#FF6263;/* Bittersweet Orange light */ - --p5:hsl(267, 75%, 45%);/* Royal purple */ - --g0:#FBFBFB;/* #FBFBFB */ - --g1:#E5E5E5;/* #E5E5E5 */ - --g2:#CFCFCF;/* #CFCFCF */ - --g3:#BABABA;/* #BABABA */ - --g4:#A4A4A4;/* #A4A4A4 */ - --g5:#8E8E8E;/* #8E8E8E */ - --g6:#797979;/* #797979 */ - --g7:#636363;/* #636363 */ - --g8:#4D4D4D;/* #4D4D4D */ - --g9:#383838;/* #383838 */ - --g10:#222222;/* #222222 */ - --h0:#3EE09C;/* Medium Aquamarine */ - --h1:#2a80e0;/* Bright Navy Blue */ - --h2:hsl(267, 90%, 55%);/* Royal Purple Hover */ - --h3:#DE1792;/* Barbie Pink */ - --h4:#FF4747;/* Bittersweet Orange */ - --h5:#FAE05D;/* Minion Yellow */ - --a0:#A4A4A4;/* #A4A4A4 */ - --a1:#8E8E8E;/* #8E8E8E */ - --d0:#797979;/* #797979 */ - --d1:#636363;/* #636363 */ -} - -/* RESET */ -*, -*:before, -*:after { - margin: 0; - padding: 0; - border: none; - box-sizing: border-box; -} -img {max-width: 100%;} -svg { - width: 100%; - height: 100%; - vertical-align: top; -} -select, -textarea, -button, -input {font-family: inherit;} - - -/* TYPEFACE */ -html {font-size: 18px;} -body { - font-weight: normal; - line-height: 1.5; - text-rendering: geometricPrecision; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - - -/* COLOR */ -.text-current{color:currentColor;}/* current color */ -.text-transparent{color:transparent;}/* transparent */ -.text-p0{color:var(--p0);}/* Medium Aquamarine light */ -.text-p1{color:var(--p1);}/* Bright Navy Blue light */ -.text-p2{color:var(--p2);}/* Royal Purple light */ -.text-p3{color:var(--p3);}/* Barbie Pink light */ -.text-p4{color:var(--p4);}/* Bittersweet Orange light */ -.text-p5{color:var(--p5);}/* Royal purple */ -.text-g0{color:var(--g0);}/* #FBFBFB */ -.text-g1{color:var(--g1);}/* #E5E5E5 */ -.text-g2{color:var(--g2);}/* #CFCFCF */ -.text-g3{color:var(--g3);}/* #BABABA */ -.text-g4{color:var(--g4);}/* #A4A4A4 */ -.text-g5{color:var(--g5);}/* #8E8E8E */ -.text-g6{color:var(--g6);}/* #797979 */ -.text-g7{color:var(--g7);}/* #636363 */ -.text-g8{color:var(--g8);}/* #4D4D4D */ -.text-g9{color:var(--g9);}/* #383838 */ -.text-g10{color:var(--g10);}/* #222222 */ -.text-h0:hover{color:var(--h0);}/* Medium Aquamarine */ -.text-h1:hover{color:var(--h1);}/* Bright Navy Blue */ -.text-h2:hover{color:var(--h2);}/* Royal Purple Hover */ -.text-h3:hover{color:var(--h3);}/* Barbie Pink */ -.text-h4:hover{color:var(--h4);}/* Bittersweet Orange */ -.text-h5:hover{color:var(--h5);}/* Minion Yellow */ -.text-a0:active{color:var(--a0);}/* #A4A4A4 */ -.text-a0.active{color:var(--a0);}/* #A4A4A4 */ -.text-a1:active{color:var(--a1);}/* #8E8E8E */ -.text-a1.active{color:var(--a1);}/* #8E8E8E */ -.text-d0:disabled{color:var(--d0);}/* #797979 */ -.text-d1:disabled{color:var(--d1);}/* #636363 */ - - -/* BACKGROUND */ -.bg-fixed{background-attachment:fixed;} -.bg-local{background-attachment:local;} -.bg-scroll{background-attachment:scroll;} -.bg-bottom{background-position:bottom;} -.bg-center{background-position:center;} -.bg-left{background-position:left;} -.bg-left-bottom{background-position:left bottom;} -.bg-left-top{background-position:left top;} -.bg-right{background-position:right;} -.bg-right-bottom{background-position:right bottom;} -.bg-right-top{background-position:right top;} -.bg-top{background-position:top;} -.bg-repeat{background-repeat:repeat;} -.bg-no-repeat{background-repeat:no-repeat;} -.bg-repeat-x{background-repeat:repeat-x;} -.bg-repeat-y{background-repeat:repeat-y;} -.bg-repeat-round{background-repeat:round;} -.bg-repeat-space{background-repeat:space;} -.bg-auto{background-size:auto;} -.bg-cover{background-size:cover;} -.bg-contain{background-size:contain;} -.bg-unset{background-color:unset;} -.bg-p0{background-color:var(--p0);} -.bg-p1{background-color:var(--p1);} -.bg-p2{background-color:var(--p2);} -.bg-p3{background-color:var(--p3);} -.bg-p4{background-color:var(--p4);} -.bg-p5{background-color:var(--p5);} -.bg-g0{background-color:var(--g0);} -.bg-g1{background-color:var(--g1);} -.bg-g2{background-color:var(--g2);} -.bg-g3{background-color:var(--g3);} -.bg-g4{background-color:var(--g4);} -.bg-g5{background-color:var(--g5);} -.bg-g6{background-color:var(--g6);} -.bg-g7{background-color:var(--g7);} -.bg-g8{background-color:var(--g8);} -.bg-g9{background-color:var(--g9);} -.bg-g10{background-color:var(--g10);} -.bg-h0:hover{background-color:var(--h0);} -.bg-h1:hover{background-color:var(--h1);} -.bg-h2:hover{background-color:var(--h2);} -.bg-h3:hover{background-color:var(--h3);} -.bg-h4:hover{background-color:var(--h4);} -.bg-h5:hover{background-color:var(--h5);} -.bg-a0:active{background-color:var(--a0);} -.bg-a0.active{background-color:var(--a0);} -.bg-a1:active{background-color:var(--a1);} -.bg-a1.active{background-color:var(--a1);} -.bg-d0:disabled{background-color:var(--d0);} -.bg-d1:disabled{background-color:var(--d1);} - - - /* GRADIENT */.bg-image0{background-image:linear-gradient(0.4turn, #0100ca, #00e5ff, #f50057);}/* outrun */ - - -/* BORDER */ -.border-solid{border-style:solid;} -.border-dashed{border-style:dashed;} -.border-dotted{border-style:dotted;} -.border-double{border-style:double;} -.border-none{border-style:none;} -.border-t-none{border-top:none;} -.border-r-none{border-right:none;} -.border-b-none{border-bottom:none;} -.border-l-none{border-left:none;} - -.border0{border-width:0px;} -.border-t0{border-top-width:0px;} -.border-r0{border-right-width:0px;} -.border-b0{border-bottom-width:0px;} -.border-l0{border-left-width:0px;} -.border1{border-width:1px;} -.border-t1{border-top-width:1px;} -.border-r1{border-right-width:1px;} -.border-b1{border-bottom-width:1px;} -.border-l1{border-left-width:1px;} -.border2{border-width:2px;} -.border-t2{border-top-width:2px;} -.border-r2{border-right-width:2px;} -.border-b2{border-bottom-width:2px;} -.border-l2{border-left-width:2px;} -.border3{border-width:4px;} -.border-t3{border-top-width:4px;} -.border-r3{border-right-width:4px;} -.border-b3{border-bottom-width:4px;} -.border-l3{border-left-width:4px;} -.border4{border-width:8px;} -.border-t4{border-top-width:8px;} -.border-r4{border-right-width:8px;} -.border-b4{border-bottom-width:8px;} -.border-l4{border-left-width:8px;} -.border5{border-width:16px;} -.border-t5{border-top-width:16px;} -.border-r5{border-right-width:16px;} -.border-b5{border-bottom-width:16px;} -.border-l5{border-left-width:16px;} -.border6{border-width:32px;} -.border-t6{border-top-width:32px;} -.border-r6{border-right-width:32px;} -.border-b6{border-bottom-width:32px;} -.border-l6{border-left-width:32px;}.border-p0{border-color:var(--p0);}/* Medium Aquamarine light */.border-p1{border-color:var(--p1);}/* Bright Navy Blue light */.border-p2{border-color:var(--p2);}/* Royal Purple light */.border-p3{border-color:var(--p3);}/* Barbie Pink light */.border-p4{border-color:var(--p4);}/* Bittersweet Orange light */.border-p5{border-color:var(--p5);}/* Royal purple */.border-g0{border-color:var(--g0);}/* #FBFBFB */.border-g1{border-color:var(--g1);}/* #E5E5E5 */.border-g2{border-color:var(--g2);}/* #CFCFCF */.border-g3{border-color:var(--g3);}/* #BABABA */.border-g4{border-color:var(--g4);}/* #A4A4A4 */.border-g5{border-color:var(--g5);}/* #8E8E8E */.border-g6{border-color:var(--g6);}/* #797979 */.border-g7{border-color:var(--g7);}/* #636363 */.border-g8{border-color:var(--g8);}/* #4D4D4D */.border-g9{border-color:var(--g9);}/* #383838 */.border-g10{border-color:var(--g10);}/* #222222 */.border-h0:hover{border-color:var(--h0);}/* Medium Aquamarine */ -.border-h1:hover{border-color:var(--h1);}/* Bright Navy Blue */ -.border-h2:hover{border-color:var(--h2);}/* Royal Purple Hover */ -.border-h3:hover{border-color:var(--h3);}/* Barbie Pink */ -.border-h4:hover{border-color:var(--h4);}/* Bittersweet Orange */ -.border-h5:hover{border-color:var(--h5);}/* Minion Yellow */ -.border-a0:active{border-color:var(--a0);}/* #A4A4A4 */ -.border-a0.active{border-color:var(--a0);}/* #A4A4A4 */ -.border-a1:active{border-color:var(--a1);}/* #8E8E8E */ -.border-a1.active{border-color:var(--a1);}/* #8E8E8E */ -.border-d0:disabled{border-color:var(--d0);}/* #797979 */ -.border-d1:disabled{border-color:var(--d1);}/* #636363 */ - - -/* RADIUS */ -.radius-none{border-radius:0;} -.radius-100{border-radius:100%;} -.radius-pill{border-radius:9999px;} -.radius-tr-none{border-top-right-radius:0;} -.radius-br-none{border-bottom-right-radius:0;} -.radius-tl-none{border-top-left-radius:0;} -.radius-bl-none{border-bottom-left-radius:0;} -.radius0{border-radius:4px;} -.radius1{border-radius:6px;} -.radius2{border-radius:15px;} -.radius3{border-radius:9999px;} - - -/* FILL */ -.fill-none{fill:none;} -.fill-current{fill:currentColor;} - - -/* STROKE */ -.stroke-none{stroke:none;} -.stroke-current{stroke:currentColor;} - - - -/* CONTAINER */ -.container{max-width:100%;} - - - -/* FAMILY */ -.font-sans{font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";} -.font-serif{font-family: Georgia, Cambria, "Times New Roman", Times, serif;} -.font-mono{font-family: Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;} - - -/* SIZES */ -.text5{font-size:4.209rem;}/* 75.757px */ -.text4{font-size:3.157rem;}/* 56.832px */ -.text3{font-size:2.369rem;}/* 42.635px */ -.text2{font-size:1.777rem;}/* 31.984px */ -.text1{font-size:1.333rem;}/* 23.994px */ -.text0{font-size:1rem;}/* 18px */ -.text-1{font-size:0.75rem;}/* 13.503px */ -.text-2{font-size:0.563rem;}/* 10.13px */ -.text-3{font-size:0.422rem;}/* 7.599px */ -.text-4{font-size:0.317rem;}/* 5.701px */ -.text-5{font-size:0.238rem;}/* 4.277px */ - - -/* Style */ -.italic{font-style:italic;} -.not-italic{font-style:normal;} - - -/* LINE HEIGHT */ -.leading5{line-height: 2;} -.leading4{line-height: 1.625;} -.leading3{line-height: 1.5;} -.leading2{line-height: 1.375;} -.leading1{line-height: 1.25;} -.leading0, -.leading-none{line-height:1;} - - -/* TRACKING */ -.tracking3{letter-spacing: 0.1em;} -.tracking2{letter-spacing: 0.05em;} -.tracking1{letter-spacing: 0.025em;} -.tracking0{letter-spacing: 0;} -.tracking-1{letter-spacing: -0.025em;} -.tracking-2{letter-spacing: -0.05em;} - - -/* WEIGHTS */ -.font-hairline{font-weight:100;} -.font-thin{font-weight:200;} -.font-light{font-weight:300;} -.font-normal{font-weight:400;} -.font-medium{font-weight:500;} -.font-semibold{font-weight:600;} -.font-bold{font-weight:700;} -.font-extrabold{font-weight:800;} -.font-black{font-weight:900;} - - -/* TEXT TRANSFORM */ -.uppercase{text-transform:uppercase;} -.lowercase{text-transform:lowercase;} -.capitalize{text-transform:capitalize;} -.normal-case{text-transform:none;} - - -/* ALIGN */ -.text-inherit{text-align:inherit;} -.text-center{text-align:center;} -.text-left{text-align:left;} -.text-right{text-align:right;} - - -/* DECORATION */ -.no-underline{text-decoration:none;} -.underline{text-decoration:underline;} -.line-through{text-decoration:line-through;} - - -/* LIST */ -.list-none{list-style:none;} -.list-disc{list-style:disc;} -.list-decimal{list-style:decimal;} - - -/* WHITESPACE */ -.whitespace-normal{white-space:normal;} -.truncate, -.whitespace-no-wrap{white-space:nowrap;} -.whitespace-pre{white-space:pre;} -.whitespace-pre-line{white-space:pre-line;} -.whitespace-pre-wrap{white-space:pre-wrap;} - - -/* WORDBREAK */ -.break-normal{word-break:normal;} -.break-normal, -.break-words{overflow-wrap:normal;} -.break-all{word-break:break-all;} -.truncate, -.ellipsis{text-overflow:ellipsis;} - - - -/* ----- LAYOUT ----- */ - - -/* POSITION */ -.sticky{position:sticky;} -.static{position:static;} -.absolute{position:absolute;} -.relative{position:relative;} -.fixed{position:fixed;} - - - /* POSITIONING */ -.top0{top:0;} -.left0{left:0;} -.right0{right:0;} -.bottom0{bottom:0;} - - -/* DISPLAY */ -.hidden{display:none;} -.block{display:block;} -.inline{display:inline;} -.inline-block{display:inline-block;} -.flex{display:flex;} -.inline-flex{display:inline-flex;} -.grid{display:grid;} -.inline-grid{display:inline-grid;} - - -/* WIDTH */ -.w-0{width:0;} -.w-full{width:100%;} -.w-screen{width:100vw;} -.min-w-0{min-width:0;} -.min-w-full{min-width:100%;} -.max-width-none{max-width:none;} -.max-w-full{max-width:100%;} - - -/* HEIGHT */ -.h-0{height:0;} -.h-full{height:100%;} -.h-screen{height:100vh;} -.min-h-0{min-height:0;} -.min-h-full{min-height:100%;} -.min-h-screen{min-height:100vh;} -.max-h-full{max-height:100%;} -.max-h-screen{max-height:100vh;} - - -/* FLEX */ -.flex-1{flex: 1 1 0%;} -.flex-auto{flex: 1 1 auto;} -.flex-initial{flex: 0 1 auto;} -.flex-none{flex:none;} -.flex-row{flex-direction:row;} -.flex-row-reverse{flex-direction:row-reverse;} -.flex-col{flex-direction:column;} -.flex-col-reverse{flex-direction:column-reverse;} -.items-stretch{align-items:stretch;} -.items-start{align-items:flex-start;} -.items-end{align-items:flex-end;} -.items-center{align-items:center;} -.content-start{align-content:start;} -.content-center{align-content:center;} -.content-end{align-content:end;} -.content-between{align-content:space-between;} -.content-around{align-content:space-around;} -.self-auto{align-self:auto;} -.self-start{align-self:flex-start;} -.self-end{align-self:flex-end;} -.self-center{align-self:center;} -.self-stretch{align-self:stretch;} -.justify-start{justify-content:flex-start;} -.justify-end{justify-content: flex-end;} -.justify-around{justify-content:space-around;} -.justify-between{justify-content:space-between;} -.justify-center{justify-content:center;} -.flex-grow{flex-grow:1;} -.flex-grow-0{flex-grow:0;} -.flex-shrink{flex-shrink:1;} -.flex-shrink-0{flex-shrink:0;} -.flex-wrap{flex-wrap:wrap;} -.flex-wrap-reverse{flex-wrap:wrap-reverse;} -.flex-no-wrap{flex-wrap:nowrap;} -.order-first{order:-9999;} -.order-last{order:9999;} -.order-none{order:0;} -.order-1{order:1;} -.order-2{order:2;} -.order-3{order:3;} -.order-4{order:4;} -.order-5{order:5;} -.order-6{order:6;} - - -/* GRID */ -.flow-row{grid-auto-flow:row;} -.flow-col{grid-auto-flow:column;} -.flow-row-dense{grid-auto-flow:row dense;} -.flow-column-dense{grid-auto-flow:column dense;} -.row-auto{grid-row:auto;} -.col-auto{grid-column:auto;} -.col-end-auto{grid-column-end: auto;} -.rows-end-auto{grid-row-end:auto;} -.rows-none{grid-template-rows:none;} -.col-1{grid-template-columns:repeat(1, minmax(0, 1fr));} -.col-span-1{grid-column: span 1 / span 1;} -.col-start-1{grid-column-start: 1;} -.row-start-1{grid-row-start: 1;} -.col-end-1{grid-column-end: 1;} -.row-end-1{grid-row-end: 1;} -.row-1{grid-template-rows: repeat(1, minmax(0, 1fr));} -.col-2{grid-template-columns:repeat(2, minmax(0, 1fr));} -.col-span-2{grid-column: span 2 / span 2;} -.col-start-2{grid-column-start: 2;} -.row-start-2{grid-row-start: 2;} -.col-end-2{grid-column-end: 2;} -.row-end-2{grid-row-end: 2;} -.row-2{grid-template-rows: repeat(2, minmax(0, 1fr));} -.col-3{grid-template-columns:repeat(3, minmax(0, 1fr));} -.col-span-3{grid-column: span 3 / span 3;} -.col-start-3{grid-column-start: 3;} -.row-start-3{grid-row-start: 3;} -.col-end-3{grid-column-end: 3;} -.row-end-3{grid-row-end: 3;} -.row-3{grid-template-rows: repeat(3, minmax(0, 1fr));} -.col-4{grid-template-columns:repeat(4, minmax(0, 1fr));} -.col-span-4{grid-column: span 4 / span 4;} -.col-start-4{grid-column-start: 4;} -.row-start-4{grid-row-start: 4;} -.col-end-4{grid-column-end: 4;} -.row-end-4{grid-row-end: 4;} -.row-4{grid-template-rows: repeat(4, minmax(0, 1fr));} -.col-5{grid-template-columns:repeat(5, minmax(0, 1fr));} -.col-span-5{grid-column: span 5 / span 5;} -.col-start-5{grid-column-start: 5;} -.row-start-5{grid-row-start: 5;} -.col-end-5{grid-column-end: 5;} -.row-end-5{grid-row-end: 5;} -.row-5{grid-template-rows: repeat(5, minmax(0, 1fr));} -.col-6{grid-template-columns:repeat(6, minmax(0, 1fr));} -.col-span-6{grid-column: span 6 / span 6;} -.col-start-6{grid-column-start: 6;} -.row-start-6{grid-row-start: 6;} -.col-end-6{grid-column-end: 6;} -.row-end-6{grid-row-end: 6;} -.row-6{grid-template-rows: repeat(6, minmax(0, 1fr));} -.row-auto-160{grid-auto-rows:minmax(8.889rem, auto);} -.gap4{gap:4.209rem;} -.gap3{gap:3.157rem;} -.gap2{gap:2.369rem;} -.gap1{gap:1.777rem;} -.gap0{gap:1.333rem;} -.gap-1{gap:1rem;} -.gap-2{gap:0.75rem;} -.gap-3{gap:0.563rem;} -.gap-4{gap:0.422rem;} -.gap-5{gap:0.317rem;} -.gap-6{gap:0.238rem;} - - -/* Z-INDEX */ -.z-auto{z-index:auto;} -.z1{z-index:1;} -.z0{z-index:0;} -.z-1{z-index:-1;} - - - -/* MARGIN */ -.m-none{margin:0;} -.mt-none{margin-top:0;} -.mr-none{margin-right:0;} -.mb-none{margin-bottom:0;} -.ml-none{margin-left:0;} -.m-auto{margin-right:auto;margin-left:auto;} -.mr-auto{margin-right:auto;} -.ml-auto{margin-left:auto;} -.m5{margin:4.209rem;} -.mt5{margin-top:4.209rem;} -.mr5{margin-right:4.209rem;} -.mb5{margin-bottom:4.209rem;} -.ml5{margin-left:4.209rem;} -.m4{margin:3.157rem;} -.mt4{margin-top:3.157rem;} -.mr4{margin-right:3.157rem;} -.mb4{margin-bottom:3.157rem;} -.ml4{margin-left:3.157rem;} -.m3{margin:2.369rem;} -.mt3{margin-top:2.369rem;} -.mr3{margin-right:2.369rem;} -.mb3{margin-bottom:2.369rem;} -.ml3{margin-left:2.369rem;} -.m2{margin:1.777rem;} -.mt2{margin-top:1.777rem;} -.mr2{margin-right:1.777rem;} -.mb2{margin-bottom:1.777rem;} -.ml2{margin-left:1.777rem;} -.m1{margin:1.333rem;} -.mt1{margin-top:1.333rem;} -.mr1{margin-right:1.333rem;} -.mb1{margin-bottom:1.333rem;} -.ml1{margin-left:1.333rem;} -.m0{margin:1rem;} -.mt0{margin-top:1rem;} -.mr0{margin-right:1rem;} -.mb0{margin-bottom:1rem;} -.ml0{margin-left:1rem;} -.m-1{margin:0.75rem;} -.mt-1{margin-top:0.75rem;} -.mr-1{margin-right:0.75rem;} -.mb-1{margin-bottom:0.75rem;} -.ml-1{margin-left:0.75rem;} -.m-2{margin:0.563rem;} -.mt-2{margin-top:0.563rem;} -.mr-2{margin-right:0.563rem;} -.mb-2{margin-bottom:0.563rem;} -.ml-2{margin-left:0.563rem;} -.m-3{margin:0.422rem;} -.mt-3{margin-top:0.422rem;} -.mr-3{margin-right:0.422rem;} -.mb-3{margin-bottom:0.422rem;} -.ml-3{margin-left:0.422rem;} -.m-4{margin:0.317rem;} -.mt-4{margin-top:0.317rem;} -.mr-4{margin-right:0.317rem;} -.mb-4{margin-bottom:0.317rem;} -.ml-4{margin-left:0.317rem;} -.m-5{margin:0.238rem;} -.mt-5{margin-top:0.238rem;} -.mr-5{margin-right:0.238rem;} -.mb-5{margin-bottom:0.238rem;} -.ml-5{margin-left:0.238rem;} - - -/* PADDING */ -.p-none{padding:0;} -.pt-none{padding-top:0;} -.pr-none{padding-right:0;} -.pb-none{padding-bottom:0;} -.pl-none{padding-left:0;} -.p5{padding:4.209rem;} -.pt5{padding-top:4.209rem;} -.pr5{padding-right:4.209rem;} -.pb5{padding-bottom:4.209rem;} -.pl5{padding-left:4.209rem;} -.p4{padding:3.157rem;} -.pt4{padding-top:3.157rem;} -.pr4{padding-right:3.157rem;} -.pb4{padding-bottom:3.157rem;} -.pl4{padding-left:3.157rem;} -.p3{padding:2.369rem;} -.pt3{padding-top:2.369rem;} -.pr3{padding-right:2.369rem;} -.pb3{padding-bottom:2.369rem;} -.pl3{padding-left:2.369rem;} -.p2{padding:1.777rem;} -.pt2{padding-top:1.777rem;} -.pr2{padding-right:1.777rem;} -.pb2{padding-bottom:1.777rem;} -.pl2{padding-left:1.777rem;} -.p1{padding:1.333rem;} -.pt1{padding-top:1.333rem;} -.pr1{padding-right:1.333rem;} -.pb1{padding-bottom:1.333rem;} -.pl1{padding-left:1.333rem;} -.p0{padding:1rem;} -.pt0{padding-top:1rem;} -.pr0{padding-right:1rem;} -.pb0{padding-bottom:1rem;} -.pl0{padding-left:1rem;} -.p-1{padding:0.75rem;} -.pt-1{padding-top:0.75rem;} -.pr-1{padding-right:0.75rem;} -.pb-1{padding-bottom:0.75rem;} -.pl-1{padding-left:0.75rem;} -.p-2{padding:0.563rem;} -.pt-2{padding-top:0.563rem;} -.pr-2{padding-right:0.563rem;} -.pb-2{padding-bottom:0.563rem;} -.pl-2{padding-left:0.563rem;} -.p-3{padding:0.422rem;} -.pt-3{padding-top:0.422rem;} -.pr-3{padding-right:0.422rem;} -.pb-3{padding-bottom:0.422rem;} -.pl-3{padding-left:0.422rem;} -.p-4{padding:0.317rem;} -.pt-4{padding-top:0.317rem;} -.pr-4{padding-right:0.317rem;} -.pb-4{padding-bottom:0.317rem;} -.pl-4{padding-left:0.317rem;} -.p-5{padding:0.238rem;} -.pt-5{padding-top:0.238rem;} -.pr-5{padding-right:0.238rem;} -.pb-5{padding-bottom:0.238rem;} -.pl-5{padding-left:0.238rem;} - - -/* OVERFLOW */ -.overflow-auto{overflow:auto;} -.truncate, -.overflow-hidden{overflow:hidden;} -.overflow-visible{overflow:visible;} -.overflow-scroll{overflow:scroll;} -.overflow-x-auto{overflow-x:auto;} -.overflow-y-auto{overflow-y:auto;} -.overflow-x-scroll{overflow-x:scroll;} -.overflow-x-hidden{overflow-x:hidden;} -.overflow-y-scroll{overflow-y:scroll;} -.overflow-y-hidden{overflow-y:hidden;} -.scrolling-touch{webkit-overflow-scrolling:touch;} -.scrolling-auto{webkit-overflow-scrolling:auto;} - - -/* VISIBILITY */ -.invisible{visibility:hidden;} -.visible{visibility:visible;} - - -/* OBJECT FIT */ -.object-contain{object-fit:contain;} -.object-cover{object-fit:cover;} -.object-fill{object-fit:fill;} -.object-none{object-fit:none;} -.object-scale-down{object-fit:scale-down;} - - -/* OBJECT POSITION */ -.object-b{object-position:bottom;} -.object-c{object-position:center;} -.object-t{object-position:top;} -.object-r{object-position:right;} -.object-rt{object-position:right top;} -.object-rb{object-position:right bottom;} -.object-l{object-position:left;} -.object-lt{object-position:left top;} -.object-lb{object-position:left bottom;} - - -/* OUTLINE */ -.outline-none{outline:0;} - - -/* OPACITY */ -.opacity-0{opacity:0;} -.opacity-25{opacity:0.25;} -.opacity-50{opacity:0.5;} -.opacity-75{opacity:0.75;} -.opacity-100{opacity:1.0;} - - -/* CURSOR */ -.cursor-auto{cursor:auto;} -.cursor-default{cursor:default;} -.cursor-pointer{cursor:pointer;} -.cursor-wait{cursor:wait;} -.cursor-text{cursor:text;} -.cursor-move{cursor:move;} -.cursor-not-allowed{cursor:not-allowed;} -.cursor-grab{cursor:grab;} -.cursor-grabbing{cursor:grabbing;} - - -/* USER SELECT */ -.select-none{user-select:none;} -.select-text{user-select:text;} -.select-all{user-select:all;} -.select-auto{user-select:auto;} - - -@media only screen and (min-width:48em) { - - -/* CONTAINER */ -.container-lg{max-width:48em;} - - - -/* FAMILY */ -.font-sans-lg{font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";} -.font-serif-lg{font-family: Georgia, Cambria, "Times New Roman", Times, serif;} -.font-mono-lg{font-family: Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;} - - -/* SIZES */ -.text5-lg{font-size:4.209rem;}/* 75.757px */ -.text4-lg{font-size:3.157rem;}/* 56.832px */ -.text3-lg{font-size:2.369rem;}/* 42.635px */ -.text2-lg{font-size:1.777rem;}/* 31.984px */ -.text1-lg{font-size:1.333rem;}/* 23.994px */ -.text0-lg{font-size:1rem;}/* 18px */ -.text-1-lg{font-size:0.75rem;}/* 13.503px */ -.text-2-lg{font-size:0.563rem;}/* 10.13px */ -.text-3-lg{font-size:0.422rem;}/* 7.599px */ -.text-4-lg{font-size:0.317rem;}/* 5.701px */ -.text-5-lg{font-size:0.238rem;}/* 4.277px */ - - -/* Style */ -.italic-lg{font-style:italic;} -.not-italic-lg{font-style:normal;} - - -/* LINE HEIGHT */ -.leading5-lg{line-height: 2;} -.leading4-lg{line-height: 1.625;} -.leading3-lg{line-height: 1.5;} -.leading2-lg{line-height: 1.375;} -.leading1-lg{line-height: 1.25;} -.leading0-lg, -.leading-none-lg{line-height:1;} - - -/* TRACKING */ -.tracking3-lg{letter-spacing: 0.1em;} -.tracking2-lg{letter-spacing: 0.05em;} -.tracking1-lg{letter-spacing: 0.025em;} -.tracking0-lg{letter-spacing: 0;} -.tracking-1-lg{letter-spacing: -0.025em;} -.tracking-2-lg{letter-spacing: -0.05em;} - - -/* WEIGHTS */ -.font-hairline-lg{font-weight:100;} -.font-thin-lg{font-weight:200;} -.font-light-lg{font-weight:300;} -.font-normal-lg{font-weight:400;} -.font-medium-lg{font-weight:500;} -.font-semibold-lg{font-weight:600;} -.font-bold-lg{font-weight:700;} -.font-extrabold-lg{font-weight:800;} -.font-black-lg{font-weight:900;} - - -/* TEXT TRANSFORM */ -.uppercase-lg{text-transform:uppercase;} -.lowercase-lg{text-transform:lowercase;} -.capitalize-lg{text-transform:capitalize;} -.normal-case-lg{text-transform:none;} - - -/* ALIGN */ -.text-inherit-lg{text-align:inherit;} -.text-center-lg{text-align:center;} -.text-left-lg{text-align:left;} -.text-right-lg{text-align:right;} - - -/* DECORATION */ -.no-underline-lg{text-decoration:none;} -.underline-lg{text-decoration:underline;} -.line-through-lg{text-decoration:line-through;} - - -/* LIST */ -.list-none-lg{list-style:none;} -.list-disc-lg{list-style:disc;} -.list-decimal-lg{list-style:decimal;} - - -/* WHITESPACE */ -.whitespace-normal-lg{white-space:normal;} -.truncate, -.whitespace-no-wrap-lg{white-space:nowrap;} -.whitespace-pre-lg{white-space:pre;} -.whitespace-pre-line-lg{white-space:pre-line;} -.whitespace-pre-wrap-lg{white-space:pre-wrap;} - - -/* WORDBREAK */ -.break-normal-lg{word-break:normal;} -.break-normal, -.break-words-lg{overflow-wrap:normal;} -.break-all-lg{word-break:break-all;} -.truncate, -.ellipsis-lg{text-overflow:ellipsis;} - - - -/* ----- LAYOUT ----- */ - - -/* POSITION */ -.sticky-lg{position:sticky;} -.static-lg{position:static;} -.absolute-lg{position:absolute;} -.relative-lg{position:relative;} -.fixed-lg{position:fixed;} - - - /* POSITIONING */ -.top0-lg{top:0;} -.left0-lg{left:0;} -.right0-lg{right:0;} -.bottom0-lg{bottom:0;} - - -/* DISPLAY */ -.hidden-lg{display:none;} -.block-lg{display:block;} -.inline-lg{display:inline;} -.inline-block-lg{display:inline-block;} -.flex-lg{display:flex;} -.inline-flex-lg{display:inline-flex;} -.grid-lg{display:grid;} -.inline-grid-lg{display:inline-grid;} - - -/* WIDTH */ -.w-0-lg{width:0;} -.w-full-lg{width:100%;} -.w-screen-lg{width:100vw;} -.min-w-0-lg{min-width:0;} -.min-w-full-lg{min-width:100%;} -.max-width-none-lg{max-width:none;} -.max-w-full-lg{max-width:100%;} - - -/* HEIGHT */ -.h-0-lg{height:0;} -.h-full-lg{height:100%;} -.h-screen-lg{height:100vh;} -.min-h-0-lg{min-height:0;} -.min-h-full-lg{min-height:100%;} -.min-h-screen-lg{min-height:100vh;} -.max-h-full-lg{max-height:100%;} -.max-h-screen-lg{max-height:100vh;} - - -/* FLEX */ -.flex-1-lg{flex: 1 1 0%;} -.flex-auto-lg{flex: 1 1 auto;} -.flex-initial-lg{flex: 0 1 auto;} -.flex-none-lg{flex:none;} -.flex-row-lg{flex-direction:row;} -.flex-row-reverse-lg{flex-direction:row-reverse;} -.flex-col-lg{flex-direction:column;} -.flex-col-reverse-lg{flex-direction:column-reverse;} -.items-stretch-lg{align-items:stretch;} -.items-start-lg{align-items:flex-start;} -.items-end-lg{align-items:flex-end;} -.items-center-lg{align-items:center;} -.content-start-lg{align-content:start;} -.content-center-lg{align-content:center;} -.content-end-lg{align-content:end;} -.content-between-lg{align-content:space-between;} -.content-around-lg{align-content:space-around;} -.self-auto-lg{align-self:auto;} -.self-start-lg{align-self:flex-start;} -.self-end-lg{align-self:flex-end;} -.self-center-lg{align-self:center;} -.self-stretch-lg{align-self:stretch;} -.justify-start-lg{justify-content:flex-start;} -.justify-end-lg{justify-content: flex-end;} -.justify-around-lg{justify-content:space-around;} -.justify-between-lg{justify-content:space-between;} -.justify-center-lg{justify-content:center;} -.flex-grow-lg{flex-grow:1;} -.flex-grow-0-lg{flex-grow:0;} -.flex-shrink-lg{flex-shrink:1;} -.flex-shrink-0-lg{flex-shrink:0;} -.flex-wrap-lg{flex-wrap:wrap;} -.flex-wrap-reverse-lg{flex-wrap:wrap-reverse;} -.flex-no-wrap-lg{flex-wrap:nowrap;} -.order-first-lg{order:-9999;} -.order-last-lg{order:9999;} -.order-none-lg{order:0;} -.order-1-lg{order:1;} -.order-2-lg{order:2;} -.order-3-lg{order:3;} -.order-4-lg{order:4;} -.order-5-lg{order:5;} -.order-6-lg{order:6;} - - -/* GRID */ -.flow-row-lg{grid-auto-flow:row;} -.flow-col-lg{grid-auto-flow:column;} -.flow-row-dense-lg{grid-auto-flow:row dense;} -.flow-column-dense-lg{grid-auto-flow:column dense;} -.row-auto-lg{grid-row:auto;} -.col-auto-lg{grid-column:auto;} -.col-end-auto-lg{grid-column-end: auto;} -.rows-end-auto-lg{grid-row-end:auto;} -.rows-none-lg{grid-template-rows:none;} -.col-1-lg{grid-template-columns:repeat(1, minmax(0, 1fr));} -.col-span-1-lg{grid-column: span 1 / span 1;} -.col-start-1-lg{grid-column-start: 1;} -.row-start-1-lg{grid-row-start: 1;} -.col-end-1-lg{grid-column-end: 1;} -.row-end-1-lg{grid-row-end: 1;} -.row-1-lg{grid-template-rows: repeat(1, minmax(0, 1fr));} -.col-2-lg{grid-template-columns:repeat(2, minmax(0, 1fr));} -.col-span-2-lg{grid-column: span 2 / span 2;} -.col-start-2-lg{grid-column-start: 2;} -.row-start-2-lg{grid-row-start: 2;} -.col-end-2-lg{grid-column-end: 2;} -.row-end-2-lg{grid-row-end: 2;} -.row-2-lg{grid-template-rows: repeat(2, minmax(0, 1fr));} -.col-3-lg{grid-template-columns:repeat(3, minmax(0, 1fr));} -.col-span-3-lg{grid-column: span 3 / span 3;} -.col-start-3-lg{grid-column-start: 3;} -.row-start-3-lg{grid-row-start: 3;} -.col-end-3-lg{grid-column-end: 3;} -.row-end-3-lg{grid-row-end: 3;} -.row-3-lg{grid-template-rows: repeat(3, minmax(0, 1fr));} -.col-4-lg{grid-template-columns:repeat(4, minmax(0, 1fr));} -.col-span-4-lg{grid-column: span 4 / span 4;} -.col-start-4-lg{grid-column-start: 4;} -.row-start-4-lg{grid-row-start: 4;} -.col-end-4-lg{grid-column-end: 4;} -.row-end-4-lg{grid-row-end: 4;} -.row-4-lg{grid-template-rows: repeat(4, minmax(0, 1fr));} -.col-5-lg{grid-template-columns:repeat(5, minmax(0, 1fr));} -.col-span-5-lg{grid-column: span 5 / span 5;} -.col-start-5-lg{grid-column-start: 5;} -.row-start-5-lg{grid-row-start: 5;} -.col-end-5-lg{grid-column-end: 5;} -.row-end-5-lg{grid-row-end: 5;} -.row-5-lg{grid-template-rows: repeat(5, minmax(0, 1fr));} -.col-6-lg{grid-template-columns:repeat(6, minmax(0, 1fr));} -.col-span-6-lg{grid-column: span 6 / span 6;} -.col-start-6-lg{grid-column-start: 6;} -.row-start-6-lg{grid-row-start: 6;} -.col-end-6-lg{grid-column-end: 6;} -.row-end-6-lg{grid-row-end: 6;} -.row-6-lg{grid-template-rows: repeat(6, minmax(0, 1fr));} -.row-auto-160-lg{grid-auto-rows:minmax(8.889rem, auto);} -.gap4-lg{gap:4.209rem;} -.gap3-lg{gap:3.157rem;} -.gap2-lg{gap:2.369rem;} -.gap1-lg{gap:1.777rem;} -.gap0-lg{gap:1.333rem;} -.gap-1-lg{gap:1rem;} -.gap-2-lg{gap:0.75rem;} -.gap-3-lg{gap:0.563rem;} -.gap-4-lg{gap:0.422rem;} -.gap-5-lg{gap:0.317rem;} -.gap-6-lg{gap:0.238rem;} - - -/* Z-INDEX */ -.z-auto-lg{z-index:auto;} -.z1-lg{z-index:1;} -.z0-lg{z-index:0;} -.z-1-lg{z-index:-1;} - - - -/* MARGIN */ -.m-none-lg{margin:0;} -.mt-none-lg{margin-top:0;} -.mr-none-lg{margin-right:0;} -.mb-none-lg{margin-bottom:0;} -.ml-none-lg{margin-left:0;} -.m-auto-lg{margin-right:auto;margin-left:auto;} -.mr-auto-lg{margin-right:auto;} -.ml-auto-lg{margin-left:auto;} -.m5-lg{margin:4.209rem;} -.mt5-lg{margin-top:4.209rem;} -.mr5-lg{margin-right:4.209rem;} -.mb5-lg{margin-bottom:4.209rem;} -.ml5-lg{margin-left:4.209rem;} -.m4-lg{margin:3.157rem;} -.mt4-lg{margin-top:3.157rem;} -.mr4-lg{margin-right:3.157rem;} -.mb4-lg{margin-bottom:3.157rem;} -.ml4-lg{margin-left:3.157rem;} -.m3-lg{margin:2.369rem;} -.mt3-lg{margin-top:2.369rem;} -.mr3-lg{margin-right:2.369rem;} -.mb3-lg{margin-bottom:2.369rem;} -.ml3-lg{margin-left:2.369rem;} -.m2-lg{margin:1.777rem;} -.mt2-lg{margin-top:1.777rem;} -.mr2-lg{margin-right:1.777rem;} -.mb2-lg{margin-bottom:1.777rem;} -.ml2-lg{margin-left:1.777rem;} -.m1-lg{margin:1.333rem;} -.mt1-lg{margin-top:1.333rem;} -.mr1-lg{margin-right:1.333rem;} -.mb1-lg{margin-bottom:1.333rem;} -.ml1-lg{margin-left:1.333rem;} -.m0-lg{margin:1rem;} -.mt0-lg{margin-top:1rem;} -.mr0-lg{margin-right:1rem;} -.mb0-lg{margin-bottom:1rem;} -.ml0-lg{margin-left:1rem;} -.m-1-lg{margin:0.75rem;} -.mt-1-lg{margin-top:0.75rem;} -.mr-1-lg{margin-right:0.75rem;} -.mb-1-lg{margin-bottom:0.75rem;} -.ml-1-lg{margin-left:0.75rem;} -.m-2-lg{margin:0.563rem;} -.mt-2-lg{margin-top:0.563rem;} -.mr-2-lg{margin-right:0.563rem;} -.mb-2-lg{margin-bottom:0.563rem;} -.ml-2-lg{margin-left:0.563rem;} -.m-3-lg{margin:0.422rem;} -.mt-3-lg{margin-top:0.422rem;} -.mr-3-lg{margin-right:0.422rem;} -.mb-3-lg{margin-bottom:0.422rem;} -.ml-3-lg{margin-left:0.422rem;} -.m-4-lg{margin:0.317rem;} -.mt-4-lg{margin-top:0.317rem;} -.mr-4-lg{margin-right:0.317rem;} -.mb-4-lg{margin-bottom:0.317rem;} -.ml-4-lg{margin-left:0.317rem;} -.m-5-lg{margin:0.238rem;} -.mt-5-lg{margin-top:0.238rem;} -.mr-5-lg{margin-right:0.238rem;} -.mb-5-lg{margin-bottom:0.238rem;} -.ml-5-lg{margin-left:0.238rem;} - - -/* PADDING */ -.p-none-lg{padding:0;} -.pt-none-lg{padding-top:0;} -.pr-none-lg{padding-right:0;} -.pb-none-lg{padding-bottom:0;} -.pl-none-lg{padding-left:0;} -.p5-lg{padding:4.209rem;} -.pt5-lg{padding-top:4.209rem;} -.pr5-lg{padding-right:4.209rem;} -.pb5-lg{padding-bottom:4.209rem;} -.pl5-lg{padding-left:4.209rem;} -.p4-lg{padding:3.157rem;} -.pt4-lg{padding-top:3.157rem;} -.pr4-lg{padding-right:3.157rem;} -.pb4-lg{padding-bottom:3.157rem;} -.pl4-lg{padding-left:3.157rem;} -.p3-lg{padding:2.369rem;} -.pt3-lg{padding-top:2.369rem;} -.pr3-lg{padding-right:2.369rem;} -.pb3-lg{padding-bottom:2.369rem;} -.pl3-lg{padding-left:2.369rem;} -.p2-lg{padding:1.777rem;} -.pt2-lg{padding-top:1.777rem;} -.pr2-lg{padding-right:1.777rem;} -.pb2-lg{padding-bottom:1.777rem;} -.pl2-lg{padding-left:1.777rem;} -.p1-lg{padding:1.333rem;} -.pt1-lg{padding-top:1.333rem;} -.pr1-lg{padding-right:1.333rem;} -.pb1-lg{padding-bottom:1.333rem;} -.pl1-lg{padding-left:1.333rem;} -.p0-lg{padding:1rem;} -.pt0-lg{padding-top:1rem;} -.pr0-lg{padding-right:1rem;} -.pb0-lg{padding-bottom:1rem;} -.pl0-lg{padding-left:1rem;} -.p-1-lg{padding:0.75rem;} -.pt-1-lg{padding-top:0.75rem;} -.pr-1-lg{padding-right:0.75rem;} -.pb-1-lg{padding-bottom:0.75rem;} -.pl-1-lg{padding-left:0.75rem;} -.p-2-lg{padding:0.563rem;} -.pt-2-lg{padding-top:0.563rem;} -.pr-2-lg{padding-right:0.563rem;} -.pb-2-lg{padding-bottom:0.563rem;} -.pl-2-lg{padding-left:0.563rem;} -.p-3-lg{padding:0.422rem;} -.pt-3-lg{padding-top:0.422rem;} -.pr-3-lg{padding-right:0.422rem;} -.pb-3-lg{padding-bottom:0.422rem;} -.pl-3-lg{padding-left:0.422rem;} -.p-4-lg{padding:0.317rem;} -.pt-4-lg{padding-top:0.317rem;} -.pr-4-lg{padding-right:0.317rem;} -.pb-4-lg{padding-bottom:0.317rem;} -.pl-4-lg{padding-left:0.317rem;} -.p-5-lg{padding:0.238rem;} -.pt-5-lg{padding-top:0.238rem;} -.pr-5-lg{padding-right:0.238rem;} -.pb-5-lg{padding-bottom:0.238rem;} -.pl-5-lg{padding-left:0.238rem;} - - -/* OVERFLOW */ -.overflow-auto-lg{overflow:auto;} -.truncate, -.overflow-hidden-lg{overflow:hidden;} -.overflow-visible-lg{overflow:visible;} -.overflow-scroll-lg{overflow:scroll;} -.overflow-x-auto-lg{overflow-x:auto;} -.overflow-y-auto-lg{overflow-y:auto;} -.overflow-x-scroll-lg{overflow-x:scroll;} -.overflow-x-hidden-lg{overflow-x:hidden;} -.overflow-y-scroll-lg{overflow-y:scroll;} -.overflow-y-hidden-lg{overflow-y:hidden;} -.scrolling-touch-lg{webkit-overflow-scrolling:touch;} -.scrolling-auto-lg{webkit-overflow-scrolling:auto;} - - -/* VISIBILITY */ -.invisible-lg{visibility:hidden;} -.visible-lg{visibility:visible;} - - -/* OBJECT FIT */ -.object-contain-lg{object-fit:contain;} -.object-cover-lg{object-fit:cover;} -.object-fill-lg{object-fit:fill;} -.object-none-lg{object-fit:none;} -.object-scale-down-lg{object-fit:scale-down;} - - -/* OBJECT POSITION */ -.object-b-lg{object-position:bottom;} -.object-c-lg{object-position:center;} -.object-t-lg{object-position:top;} -.object-r-lg{object-position:right;} -.object-rt-lg{object-position:right top;} -.object-rb-lg{object-position:right bottom;} -.object-l-lg{object-position:left;} -.object-lt-lg{object-position:left top;} -.object-lb-lg{object-position:left bottom;} - - -/* OUTLINE */ -.outline-none-lg{outline:0;} - - -/* OPACITY */ -.opacity-0-lg{opacity:0;} -.opacity-25-lg{opacity:0.25;} -.opacity-50-lg{opacity:0.5;} -.opacity-75-lg{opacity:0.75;} -.opacity-100-lg{opacity:1.0;} - - -/* CURSOR */ -.cursor-auto-lg{cursor:auto;} -.cursor-default-lg{cursor:default;} -.cursor-pointer-lg{cursor:pointer;} -.cursor-wait-lg{cursor:wait;} -.cursor-text-lg{cursor:text;} -.cursor-move-lg{cursor:move;} -.cursor-not-allowed-lg{cursor:not-allowed;} -.cursor-grab-lg{cursor:grab;} -.cursor-grabbing-lg{cursor:grabbing;} - - -/* USER SELECT */ -.select-none-lg{user-select:none;} -.select-text-lg{user-select:text;} -.select-all-lg{user-select:all;} -.select-auto-lg{user-select:auto;} - -} -` diff --git a/src/views/modules/document/symbols.js b/src/views/modules/document/symbols.js deleted file mode 100644 index 38f2e8dd..00000000 --- a/src/views/modules/document/symbols.js +++ /dev/null @@ -1,9 +0,0 @@ -export default ` - - - - - - - -` diff --git a/src/views/modules/document/symbols.mjs b/src/views/modules/document/symbols.mjs new file mode 100644 index 00000000..de394ffe --- /dev/null +++ b/src/views/modules/document/symbols.mjs @@ -0,0 +1,13 @@ +export default /* xml */` + + + + + + + + + + + +` diff --git a/src/views/modules/document/top-nav.mjs b/src/views/modules/document/top-nav.mjs new file mode 100644 index 00000000..7c4239e4 --- /dev/null +++ b/src/views/modules/document/top-nav.mjs @@ -0,0 +1,82 @@ +import Logo from '../components/logo.mjs' +import Icon from '../components/icon.mjs' +import Search from '../components/search.mjs' +import GithubLink from '../components/github-link.mjs' +import DiscordLink from '../components/discord-link.mjs' +import TwitterLink from '../components/twitter-link.mjs' +import ThemeButton from '../components/theme-button.mjs' + +export default function TopNav () { + return ` +
    + + ${Logo({ classes: 'logo' })} + + + +
    + ${Search({ classes: 'hidden inline-block-lg mr0' })} + ${DiscordLink()} + ${TwitterLink({ classes: 'ml0' })} + ${GithubLink({ classes: 'ml0' })} + ${ThemeButton({ classes: 'ml0' })} + +
    + +
    + ` +} diff --git a/src/views/modules/helpers/capitalize.js b/src/views/modules/helpers/capitalize.mjs similarity index 100% rename from src/views/modules/helpers/capitalize.js rename to src/views/modules/helpers/capitalize.mjs diff --git a/src/views/modules/helpers/list.js b/src/views/modules/helpers/list.mjs similarity index 69% rename from src/views/modules/helpers/list.js rename to src/views/modules/helpers/list.mjs index 79d5c2d9..d78c1efb 100644 --- a/src/views/modules/helpers/list.js +++ b/src/views/modules/helpers/list.mjs @@ -1,30 +1,31 @@ -export default function listFromObject ({ data = {}, map = {}, path = [], active }) { - let depth = 0 - let { list, item } = map - let children = itemsFromObject({ data, list, item, depth, path, active }) +export default function listFromObject ({ data = {}, map = {}, path = [], active = null }) { + const depth = 0 + const { list, item } = map + const children = itemsFromObject({ data, list, item, depth, path, active }) return list({ - children + children, }) } function itemsFromObject ({ data = {}, list, item, depth, path, active }) { depth = depth + 1 return Object.keys(data).map(child => { - let children = data[child] + const children = data[child] return item({ child, children: ` ${listFromArray({ children, list, item, depth, path: path.concat([ child ]), active })} `, depth, - path + path, + active, }) }).join('') } function listFromArray ({ children = [], list, item, depth, path, active }) { - let gi = getItem.bind(null, list, item, depth, path, active) - let kids = children.map(gi).join('') + const gi = getItem.bind(null, list, item, depth, path, active) + const kids = children.map(gi).join('') return list({ children: kids }) } diff --git a/src/views/modules/helpers/slugify.js b/src/views/modules/helpers/slugify.mjs similarity index 80% rename from src/views/modules/helpers/slugify.js rename to src/views/modules/helpers/slugify.mjs index 55b4db6d..ab388859 100644 --- a/src/views/modules/helpers/slugify.js +++ b/src/views/modules/helpers/slugify.mjs @@ -3,6 +3,6 @@ import slugify from 'slugify' export default function slug (str = '') { return slugify(str, { lower: true, - remove: /[*+~()'"!?:@]/g + remove: /[*+~()'"!?:@]/g, }) } diff --git a/src/views/modules/layouts/one-column.js b/src/views/modules/layouts/one-column.js deleted file mode 100644 index b3071979..00000000 --- a/src/views/modules/layouts/one-column.js +++ /dev/null @@ -1,4 +0,0 @@ -export default function OneColumn (/* props = {} */) { - return ` - ` -} diff --git a/src/views/modules/layouts/two-column.js b/src/views/modules/layouts/two-column.js deleted file mode 100644 index 07f2f600..00000000 --- a/src/views/modules/layouts/two-column.js +++ /dev/null @@ -1,42 +0,0 @@ -import Sidebar from '../components/sidebar.js' - -export default function TwoColumn (props = {}) { - let { children } = props - - return ` -
    -
    -
    - ${Sidebar(props)} -
    - ${children} -
    - ` -} diff --git a/src/views/modules/pages/main.js b/src/views/modules/pages/main.js deleted file mode 100644 index 9f2ba5c9..00000000 --- a/src/views/modules/pages/main.js +++ /dev/null @@ -1,5 +0,0 @@ -export default function Main (/* props = {} */) { - return ` -
    Welcome!
    - ` -} diff --git a/src/views/package.json b/src/views/package.json deleted file mode 100644 index 1076c112..00000000 --- a/src/views/package.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "dependencies": { - "slugify": "^1.4.6" - } -} diff --git a/test/backend/redirect-map-test.mjs b/test/backend/redirect-map-test.mjs new file mode 100644 index 00000000..2387e13f --- /dev/null +++ b/test/backend/redirect-map-test.mjs @@ -0,0 +1,66 @@ +import { stat } from 'fs/promises' +import { join } from 'path' +import test from 'tape' +import { redirect, tempRedirects, permanentRedirects } from '../../src/shared/redirect-map.mjs' + +test('redirect map middleware', async t => { + t.plan(4) + + t.equal(typeof redirect, 'function', 'Redirect middleware is a function') + + const redirectResponse = await redirect({ + requestContext: { + http: { + method: 'GET', + path: '/examples', + } + } + }) + const expectedResponse = { + statusCode: 301, + headers: { + location: '/docs/en/guides/examples', + }, + } + t.deepEqual(redirectResponse, expectedResponse, 'Correctly redirect permanent mapped path') + + const nonRedirectResponse = await redirect({ + requestContext: { + http: { + method: 'get', + path: '/unmapped/path' + } + } + }) + t.notOk(nonRedirectResponse, "Don't respond to unmapped path") + + const postResponse = await redirect({ + requestContext: { + http: { + method: 'POST', + path: '/examples' + } + } + }) + t.notOk(postResponse, "Don't respond to POST method") +}) + +test('all redirect destinations exist', async t => { + t.plan(3) + + t.equal(typeof tempRedirects, 'object', 'tempRedirects map') + t.equal(typeof permanentRedirects, 'object', 'permanentRedirects map') + + const destinations = [ ...Object.values(tempRedirects), ...Object.values(permanentRedirects) ] + for (const destination of destinations) { + const filePath = destination.split('#')[0] + '.md' + try { + await stat(join(new URL('.', import.meta.url).pathname, '../../src/views', filePath)) + } + catch (error) { + t.fail(error) + } + } + + t.pass(`Checked for ${destinations.length} files`) +}) diff --git a/test/backend/sandbox-http-test.mjs b/test/backend/sandbox-http-test.mjs new file mode 100644 index 00000000..1db4adb0 --- /dev/null +++ b/test/backend/sandbox-http-test.mjs @@ -0,0 +1,23 @@ +import test from 'tape' +import { get } from 'tiny-json-http' +import { start, end } from '@architect/sandbox' +import { currentRoot } from '../../src/shared/redirect-map.mjs' + +const host = 'http://localhost:3333' +const root = `${host}${currentRoot}` + +test('check key paths', async (t) => { + await start({ quiet: true }) + t.pass(`sandbox started at ${host}`) + + const quickstart = await get({ url: root }) + t.ok(quickstart.body, 'got quickstart document') + + const playground = await get({ url: `${host}/playground` }) + t.ok(playground.body, 'got static playground document') + + await end() + t.pass('sandbox ended') + + t.end() +}) diff --git a/test/base-test.js b/test/base-test.js deleted file mode 100644 index 480ceec7..00000000 --- a/test/base-test.js +++ /dev/null @@ -1,6 +0,0 @@ -const test = require('tape') - -test('base', t => { - t.ok(true) - t.end() -}) diff --git a/test/frontend/helpers/strip.mjs b/test/frontend/helpers/strip.mjs new file mode 100644 index 00000000..730f8c7e --- /dev/null +++ b/test/frontend/helpers/strip.mjs @@ -0,0 +1,3 @@ +export default function strip (str = '') { + return str.replace(/\s/g, '') +} diff --git a/test/sidebar-test.js b/test/frontend/sidebar-test.mjs similarity index 67% rename from test/sidebar-test.js rename to test/frontend/sidebar-test.mjs index 458db898..11270801 100644 --- a/test/sidebar-test.js +++ b/test/frontend/sidebar-test.mjs @@ -1,73 +1,68 @@ import test from 'tape' -import listFromObject from '../src/views/modules/helpers/list.js' -import strip from './helpers/strip.js' -import slugify from '../src/views/modules/helpers/slugify.js' +import listFromObject from '../../src/views/modules/helpers/list.mjs' +import strip from './helpers/strip.mjs' +import slugify from '../../src/views/modules/helpers/slugify.mjs' -function Ul(state={}) { - let { children } = state +function Ul (state = {}) { + const { children } = state return `
      - ${ children } + ${children}
    ` } -function Li(state={}) { - let { child='', children='' } = state +function Li (state = {}) { + const { child = '', children = '' } = state return `
  • - ${ child } - ${ children } + ${child} + ${children}
  • ` } -function Item(state={}) { - let { child='', children='', depth } = state +function Item (state = {}) { + const { child = '', children = '', depth } = state return Li({ children: ` - ${ child - ? Heading3({ - children: Anchor({ - children: child, - href: slugify(child) - }), - depth - }) - : '' - } - ${ children } + ${child + ? Heading3({ + children: Anchor({ + children: child, + href: slugify(child) + }), + depth + }) + : '' +} + ${children} ` }) } -function Heading(state={}) { - let { depth=0 } = state - return [ Heading3, Heading4 ][depth](state) -} - -function Heading3(state={}) { - let { children } = state +function Heading3 (state = {}) { + const { children } = state return ` -

    ${ children }

    +

    ${children}

    ` } -function Heading4(state={}) { - let { children } = state +function Heading4 (state = {}) { + const { children } = state return ` -

    ${ children }

    +

    ${children}

    ` } -function Anchor(state={}) { - let { children, href } = state +function Anchor (state = {}) { + const { children, href } = state return ` -${ children } +${children} ` } -let map = { +const map = { anchor: Anchor, list: Ul, item: Li, @@ -78,11 +73,11 @@ let map = { } test('render object to list', t => { - let map = { + const map = { list: Ul, item: Li } - let data = { + const data = { 'one': [ 'a', 'b', @@ -94,7 +89,7 @@ test('render object to list', t => { 'f' ] } - let expected = ` + const expected = `
    • one @@ -114,18 +109,18 @@ test('render object to list', t => {
    ` - let actual = listFromObject({ data, map }) + const actual = listFromObject({ data, map }) t.equal(strip(actual), strip(expected), 'Should render object to list', actual) t.end() }) test('render nested object to list', t => { - let map = { + const map = { list: Ul, item: Li } - let data = { + const data = { 'label': [ { 'one': [ @@ -143,7 +138,7 @@ test('render nested object to list', t => { } ] } - let expected = ` + const expected = `
    • label @@ -168,14 +163,14 @@ test('render nested object to list', t => {
    ` - let actual = listFromObject({ data, map }) + const actual = listFromObject({ data, map }) - t.equal(strip(actual), strip(expected),'Should render object to list', actual) + t.equal(strip(actual), strip(expected), 'Should render object to list', actual) t.end() }) test('render deeply nested object to list', t => { - let data = { + const data = { 'label': [ { 'one': [ @@ -199,7 +194,7 @@ test('render deeply nested object to list', t => { } ] } - let expected = ` + const expected = `
    • label @@ -231,14 +226,14 @@ test('render deeply nested object to list', t => {
    ` - let actual = listFromObject({ data, map }) + const actual = listFromObject({ data, map }) - t.equal(strip(actual), strip(expected),'Should render object to list', actual) + t.equal(strip(actual), strip(expected), 'Should render object to list', actual) t.end() }) test('should use custom component map', t => { - let data = { + const data = { 'one': [ 'a', 'b', @@ -250,7 +245,7 @@ test('should use custom component map', t => { 'f' ] } - let expected = ` + const expected = `
    • @@ -314,7 +309,7 @@ test('should use custom component map', t => {

    ` - let actual = listFromObject({ + const actual = listFromObject({ data, map: { list: Ul, @@ -327,15 +322,15 @@ test('should use custom component map', t => { test('Should create correct href', t => { t.plan(8) - let path = [ 'docs', 'en' ] - let map = { - item: function hrefTest({ child, depth, path }) { - let href = slugify(path.join('/')) + const path = [ 'docs', 'en' ] + const map = { + item: function hrefTest ({ path }) { + const href = slugify(path.join('/')) t.ok(href, href) }, - list: function list(params) {} + list: function list () {} } - let data = { + const data = { 'one & done': [ 'a', 'b', diff --git a/test/frontend/slugify-test.mjs b/test/frontend/slugify-test.mjs new file mode 100644 index 00000000..a1a55886 --- /dev/null +++ b/test/frontend/slugify-test.mjs @@ -0,0 +1,10 @@ +import test from 'tape' +import slugify from '../../src/views/modules/helpers/slugify.mjs' + +test('slugify', t => { + const input = 'Architect manifest & config' + const expected = 'architect-manifest-and-config' + const actual = slugify(input) + t.equals(expected, actual, 'slugifies') + t.end() +}) diff --git a/test/helpers/strip.js b/test/helpers/strip.js deleted file mode 100644 index e4aa7648..00000000 --- a/test/helpers/strip.js +++ /dev/null @@ -1,3 +0,0 @@ -export default function strip(str='') { - return str.replace(/\s/g,'') -} diff --git a/test/link-checker.mjs b/test/link-checker.mjs new file mode 100644 index 00000000..d4c8543a --- /dev/null +++ b/test/link-checker.mjs @@ -0,0 +1,44 @@ +// eslint-disable-next-line import/no-unresolved +import { LinkChecker } from 'linkinator' +import test from 'tape' +import sandbox from '@architect/sandbox' + +import { currentRoot } from '../src/shared/redirect-map.mjs' + +const host = 'http://localhost:3333' +const root = `${host}${currentRoot}` + +test('find broken links', async (t) => { + await sandbox.start({ quiet: true }) + t.pass(`sandbox started at ${host}`) + + const checker = new LinkChecker() + + checker.on('link', result => { + if (result.state === 'BROKEN') + console.log(`${result.status} ${result.url} from ${result.parent}`) + }) + + const result = await checker.check({ + concurrency: 5, // default of 100 causes functions to exceed 5s timeout + path: root, + recurse: true, + linksToSkip: [ + 'https://arc.codes/_static/arc.codes.png', // final asset is fingerprinted + 'https://www.godaddy.com', // GoDaddy 403s crawlers + 'https://github.com/architect/arc.codes/edit/', // skip all the "Edit on GitHub" links + 'https://twitter.com/arcdotcodes', // lolnothingmatters + ], + }) + + const brokenCount = result.links.filter(x => x.state === 'BROKEN').length + const okCount = result.links.filter(x => x.state === 'OK').length + + t.ok(brokenCount === 0, `${brokenCount} broken link${brokenCount > 1 || brokenCount === 0 ? 's' : ''}`) + t.pass(`${okCount} working link${okCount > 1 || okCount === 0 ? 's' : ''}`) + + await sandbox.end() + t.pass('sandbox ended') + + t.end() +}) diff --git a/test/slugify-test.js b/test/slugify-test.js deleted file mode 100644 index 40e722ee..00000000 --- a/test/slugify-test.js +++ /dev/null @@ -1,10 +0,0 @@ -import test from 'tape' -import slugify from '../src/views/modules/helpers/slugify.js' - -test('slugify', t=> { - let input = 'Architect manifest & config' - let expected = 'architect-manifest-and-config' - let actual = slugify(input) - t.equals(expected, actual, 'slugifies') - t.end() -}) diff --git a/theme.json b/theme.json new file mode 100644 index 00000000..1895c2ce --- /dev/null +++ b/theme.json @@ -0,0 +1,43 @@ +{ + "typeScale": { + "steps": 9, + "viewportMin": 320, + "viewportMax": 1500, + "baseMin": 16, + "baseMax": 20, + "scaleMin": "minor-third", + "scaleMax": "perfect-fourth" + }, + "spaceScale": { + "steps": 12, + "viewportMin": 320, + "viewportMax": 1500, + "baseMin": 16, + "baseMax": 18, + "scaleMin": "minor-third", + "scaleMax": "perfect-fourth" + }, + "fonts": { + "sans": "Montserrat,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif" + }, + "theme": { + "blue": "#4075bf", + "fore": "#45413a", + "back": "#efefef", + "dark": { + "fore": "#efefef", + "back": "#24231e" + } + }, + "color": { + "arc-blue": "#365c91", + "bright-blue": "#4785ff" + }, + "properties": {}, + "grid": { + "steps": 7 + }, + "queries": { + "lg": "48em" + } +}