diff --git a/.alexignore b/.alexignore new file mode 100644 index 0000000000000..1bf6581c26b1e --- /dev/null +++ b/.alexignore @@ -0,0 +1,2 @@ +CODE_OF_CONDUCT.md +examples/ diff --git a/.alexrc b/.alexrc new file mode 100644 index 0000000000000..1f1de4b4382a9 --- /dev/null +++ b/.alexrc @@ -0,0 +1,23 @@ +{ + "allow": [ + "attacks", + "color", + "dead", + "execute", + "executed", + "executes", + "execution", + "executions", + "failed", + "failure", + "failures", + "fire", + "fires", + "hook", + "hooks", + "host-hostess", + "invalid", + "remains", + "white" + ] +} diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index 3de5647524494..0000000000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,133 +0,0 @@ -version: 2 -jobs: - build: - docker: - - image: circleci/node:8-browsers - working_directory: ~/repo - steps: - - checkout - - run: - name: Installing dependencies - command: yarn install --frozen-lockfile - - run: - name: Linting - command: yarn lint - - persist_to_workspace: - root: ~/repo - paths: ['.'] - test: - parallelism: 4 - docker: - - image: circleci/node:8-browsers - working_directory: ~/repo - steps: - - attach_workspace: - at: . - - run: - name: Tests - command: yarn testall $(circleci tests glob "test/**/*.test.*" | circleci tests split --split-by=timings --timings-type=classname) - environment: - JEST_JUNIT_OUTPUT: 'reports/junit/js-test-results.xml' - JEST_JUNIT_CLASSNAME: '{filepath}' - - store_test_results: - path: ~/repo/reports - test-ie11: - docker: - - image: circleci/node:8-browsers - working_directory: ~/repo - steps: - - attach_workspace: - at: . - - run: - name: Test in ie11 - command: 'if [[ ! -z $BROWSERSTACK_USERNAME ]]; then yarn testall test/integration/production/; else echo "Not running for PR"; fi' - environment: - BROWSERSTACK: 'true' - BROWSER_NAME: 'ie' - test-safari: - docker: - - image: circleci/node:8-browsers - working_directory: ~/repo - steps: - - attach_workspace: - at: . - - run: - name: Test in Safari - command: 'if [[ ! -z $BROWSERSTACK_USERNAME ]]; then yarn testall test/integration/production/; else echo "Not running for PR"; fi' - environment: - BROWSERSTACK: 'true' - BROWSER_NAME: 'safari' - test-firefox: - docker: - - image: circleci/node:8-browsers - working_directory: ~/repo - steps: - - attach_workspace: - at: . - - run: - name: Test in Firefox - command: 'if [[ ! -z $BROWSERSTACK_USERNAME ]]; then yarn testall test/integration/production/; else echo "Not running for PR"; fi' - environment: - BROWSERSTACK: 'true' - BROWSER_NAME: 'firefox' - deploy: - docker: - - image: circleci/node:8-browsers - working_directory: ~/repo - steps: - - attach_workspace: - at: . - - run: - name: Potentially save npm token - command: '([[ ! -z $NPM_TOKEN ]] && echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ~/.npmrc) || echo "Did not write npm token"' - - run: - name: Potentially publish canary release - command: 'if ls ~/.npmrc >/dev/null 2>&1 && [[ $(git describe --exact-match 2> /dev/null || :) =~ -canary ]]; then yarn run lerna publish from-git --npm-tag canary --yes; else echo "Did not publish"; fi' - - run: - name: Potentially publish stable release - command: 'if ls ~/.npmrc >/dev/null 2>&1 && [[ ! $(git describe --exact-match 2> /dev/null || :) =~ -canary ]]; then yarn run lerna publish from-git --yes; else echo "Did not publish"; fi' -workflows: - version: 2 - build-test-and-deploy: - jobs: - - build - - test: - requires: - - build - - test-ie11: - requires: - - build - filters: - branches: - only: - - master - - canary - - test-safari: - requires: - - build - - test - - test-ie11 - filters: - branches: - only: - - master - - canary - - test-firefox: - requires: - - build - - test - - test-ie11 - - test-safari - filters: - branches: - only: - - master - - canary - - deploy: - requires: - - test - filters: - branches: - only: - - master - - canary diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000000000..7e00238ebef0f --- /dev/null +++ b/.eslintignore @@ -0,0 +1,37 @@ +node_modules +**/.next/** +**/_next/** +**/dist/** +e2e-tests/** +examples/with-eslint/** +examples/with-typescript-eslint-jest/** +examples/with-kea/** +examples/with-custom-babel-config/** +examples/with-flow/** +examples/with-jest/** +examples/with-mobx-state-tree/** +examples/with-mobx/** +packages/next/bundles/webpack/packages/*.runtime.js +packages/next/bundles/webpack/packages/lazy-compilation-*.js +packages/next/compiled/**/* +packages/react-refresh-utils/**/*.js +packages/react-dev-overlay/lib/** +**/__tmp__/** +.github/actions/next-stats-action/.work +.github/actions/issue-validator/index.mjs +packages/next-codemod/transforms/__testfixtures__/**/* +packages/next-codemod/transforms/__tests__/**/* +packages/next-codemod/**/*.js +packages/next-codemod/**/*.d.ts +packages/next-env/**/*.d.ts +packages/create-next-app/templates/** +test/integration/eslint/** +test/integration/script-loader/**/* +test/development/basic/legacy-decorators/**/* +test/production/emit-decorator-metadata/**/*.js +test-timings.json +packages/next-swc/crates/** +bench/nested-deps/pages/** +bench/nested-deps/components/** +packages/next-bundle-analyzer/index.d.ts +examples/with-typescript-graphql/lib/gql/ diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000000000..21089d04c71aa --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,338 @@ +{ + "root": true, + "parser": "@babel/eslint-parser", + "plugins": ["react", "react-hooks", "jest", "import"], + "env": { + "browser": true, + "commonjs": true, + "es6": true, + "node": true + }, + "parserOptions": { + "requireConfigFile": false, + "sourceType": "module", + "ecmaFeatures": { + "jsx": true + }, + "babelOptions": { + "presets": ["next/babel"], + "caller": { + // Eslint supports top level await when a parser for it is included. We enable the parser by default for Babel. + "supportsTopLevelAwait": true + } + } + }, + "settings": { + "react": { + "version": "detect" + }, + "import/internal-regex": "^next/" + }, + "overrides": [ + { + "files": ["test/**/*.js", "test/**/*.ts", "**/*.test.ts"], + "extends": ["plugin:jest/recommended"], + "rules": { + "jest/expect-expect": "off", + "jest/no-disabled-tests": "off", + "jest/no-conditional-expect": "off", + "jest/valid-title": "off", + "jest/no-interpolation-in-snapshots": "off", + "jest/no-export": "off" + } + }, + { "files": ["**/__tests__/**"], "env": { "jest": true } }, + { + "files": ["**/*.ts", "**/*.tsx"], + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaVersion": 2018, + "sourceType": "module", + "ecmaFeatures": { + "jsx": true + }, + "warnOnUnsupportedTypeScriptVersion": false + }, + "plugins": ["@typescript-eslint"], + "rules": { + // Already handled by TS + "no-dupe-class-members": "off", + "no-undef": "off", + + // Add TypeScript specific rules (and turn off ESLint equivalents) + "@typescript-eslint/consistent-type-assertions": "warn", + "no-array-constructor": "off", + "@typescript-eslint/no-array-constructor": "warn", + "@typescript-eslint/no-namespace": "error", + "no-use-before-define": "off", + "@typescript-eslint/no-use-before-define": [ + "warn", + { + "functions": true, + "classes": true, + "variables": true, + "enums": true, + "typedefs": true + } + ], + "no-unused-vars": "off", + "@typescript-eslint/no-unused-vars": [ + "warn", + { + "args": "none", + "ignoreRestSiblings": true + } + ], + "no-unused-expressions": "off", + "@typescript-eslint/no-unused-expressions": [ + "error", + { + "allowShortCircuit": true, + "allowTernary": true, + "allowTaggedTemplates": true + } + ], + "no-useless-constructor": "off", + "@typescript-eslint/no-useless-constructor": "warn", + "@typescript-eslint/prefer-literal-enum-member": "error", + "@typescript-eslint/prefer-namespace-keyword": "error" + } + }, + { + "files": [ + "test/**/*", + "examples/**/*", + "packages/create-next-app/templates/**/*" + ], + "rules": { "react/react-in-jsx-scope": "off" } + }, + { + "files": ["examples/**/*"], + "rules": { + "@typescript-eslint/no-use-before-define": [ + "error", + { + "functions": true, + "classes": true, + "variables": true, + "enums": true, + "typedefs": true + } + ], + "import/no-anonymous-default-export": [ + "error", + { + // React components: + "allowArrowFunction": false, + "allowAnonymousClass": false, + "allowAnonymousFunction": false, + + // Non-React stuff: + "allowArray": true, + "allowCallExpression": true, + "allowLiteral": true, + "allowObject": true + } + ] + } + }, + { + "files": ["packages/**"], + "rules": { + "no-shadow": ["warn", { "builtinGlobals": false }], + "import/no-extraneous-dependencies": [ + "error", + { "devDependencies": false } + ] + } + }, + { + "files": ["packages/**/*.tsx", "packages/**/*.ts"], + "rules": { + "@typescript-eslint/no-unused-vars": [ + "warn", + { + "args": "all", + "argsIgnorePattern": "^_", + "ignoreRestSiblings": true + } + ] + } + }, + { + "files": [ + "packages/eslint-plugin-next/**/*.js", + "test/unit/eslint-plugin-next/**/*.test.ts" + ], + "extends": ["plugin:eslint-plugin/recommended"], + "parserOptions": { + "sourceType": "script" + }, + "rules": { + "eslint-plugin/prefer-replace-text": "error", + "eslint-plugin/report-message-format": [ + "error", + ".+\\. See: https://nextjs.org/docs/messages/[a-z\\-]+$" + ], + "eslint-plugin/require-meta-docs-description": [ + "error", + { + "pattern": ".+" + } + ], + "eslint-plugin/require-meta-docs-url": "error" + } + } + ], + "rules": { + "array-callback-return": "warn", + "default-case": ["warn", { "commentPattern": "^no default$" }], + "dot-location": ["warn", "property"], + "eqeqeq": ["warn", "smart"], + "new-parens": "warn", + "no-array-constructor": "warn", + "no-caller": "warn", + "no-cond-assign": ["warn", "except-parens"], + "no-const-assign": "warn", + "no-control-regex": "warn", + "no-delete-var": "warn", + "no-dupe-args": "warn", + "no-dupe-class-members": "warn", + "no-dupe-keys": "warn", + "no-duplicate-case": "warn", + "no-empty-character-class": "warn", + "no-empty-pattern": "warn", + "no-eval": "warn", + "no-ex-assign": "warn", + "no-extend-native": "warn", + "no-extra-bind": "warn", + "no-extra-label": "warn", + "no-fallthrough": "warn", + "no-func-assign": "warn", + "no-implied-eval": "warn", + "no-invalid-regexp": "warn", + "no-iterator": "warn", + "no-label-var": "warn", + "no-labels": ["warn", { "allowLoop": true, "allowSwitch": false }], + "no-lone-blocks": "warn", + "no-loop-func": "warn", + "no-mixed-operators": [ + "warn", + { + "groups": [ + ["&", "|", "^", "~", "<<", ">>", ">>>"], + ["==", "!=", "===", "!==", ">", ">=", "<", "<="], + ["&&", "||"], + ["in", "instanceof"] + ], + "allowSamePrecedence": false + } + ], + "no-multi-str": "warn", + "no-native-reassign": "warn", + "no-negated-in-lhs": "warn", + "no-new-func": "warn", + "no-new-object": "warn", + "no-new-symbol": "warn", + "no-new-wrappers": "warn", + "no-obj-calls": "warn", + "no-octal": "warn", + "no-octal-escape": "warn", + "no-regex-spaces": "warn", + "no-restricted-syntax": [ + "warn", + "WithStatement", + { + "message": "substr() is deprecated, use slice() or substring() instead", + "selector": "MemberExpression > Identifier[name='substr']" + } + ], + "no-script-url": "warn", + "no-self-assign": "warn", + "no-self-compare": "warn", + "no-sequences": "warn", + "no-shadow-restricted-names": "warn", + "no-sparse-arrays": "warn", + "no-template-curly-in-string": "error", + "no-this-before-super": "warn", + "no-throw-literal": "warn", + "no-undef": "error", + "no-unexpected-multiline": "warn", + "no-unreachable": "warn", + "no-unused-expressions": [ + "error", + { + "allowShortCircuit": true, + "allowTernary": true, + "allowTaggedTemplates": true + } + ], + "no-unused-labels": "warn", + "no-unused-vars": [ + "warn", + { + "args": "none", + "ignoreRestSiblings": true + } + ], + "no-use-before-define": [ + "warn", + { + "functions": false, + "classes": false, + "variables": false + } + ], + "no-useless-computed-key": "warn", + "no-useless-concat": "warn", + "no-useless-constructor": "warn", + "no-useless-escape": "warn", + "no-useless-rename": [ + "warn", + { + "ignoreDestructuring": false, + "ignoreImport": false, + "ignoreExport": false + } + ], + "no-with": "warn", + "no-whitespace-before-property": "warn", + "react-hooks/exhaustive-deps": "warn", + "require-yield": "warn", + "rest-spread-spacing": ["warn", "never"], + "strict": ["warn", "never"], + "unicode-bom": ["warn", "never"], + "use-isnan": "warn", + "valid-typeof": "warn", + "getter-return": "warn", + "react/forbid-foreign-prop-types": ["warn", { "allowInPropTypes": true }], + "react/jsx-no-comment-textnodes": "warn", + "react/jsx-no-duplicate-props": "warn", + "react/jsx-no-target-blank": "warn", + "react/jsx-no-undef": "error", + "react/jsx-pascal-case": [ + "warn", + { + "allowAllCaps": true, + "ignore": [] + } + ], + "react/jsx-uses-react": "warn", + "react/jsx-uses-vars": "warn", + "react/no-danger-with-children": "warn", + "react/no-deprecated": "warn", + "react/no-direct-mutation-state": "warn", + "react/no-is-mounted": "warn", + "react/no-typos": "error", + "react/react-in-jsx-scope": "error", + "react/require-render-return": "error", + "react/style-prop-object": "warn", + "react-hooks/rules-of-hooks": "error", + // "@typescript-eslint/non-nullable-type-assertion-style": "warn", + "@typescript-eslint/prefer-as-const": "warn", + "@typescript-eslint/no-redeclare": [ + "warn", + { "builtinGlobals": false, "ignoreDeclarationMerge": true } + ] + } +} diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000000..08f58f77658a7 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +packages/next/bundles/** -text +packages/next/compiled/** -text diff --git a/.github/.kodiak.toml b/.github/.kodiak.toml new file mode 100644 index 0000000000000..ee535afe225c9 --- /dev/null +++ b/.github/.kodiak.toml @@ -0,0 +1,19 @@ +# .kodiak.toml +version = 1 + +[merge] +automerge_label = "ready to land" +require_automerge_label = false +method = "squash" +delete_branch_on_merge = true +optimistic_updates = true +prioritize_ready_to_merge = true +notify_on_conflict = false + +[merge.message] +title = "pull_request_title" +body = "pull_request_body" +include_coauthors= true +include_pr_number = true +body_type = "markdown" +strip_html_comments = true diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index f08ecdf2c734f..9b315dc64a7f2 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,8 +1,23 @@ # Learn how to add code owners here: # https://help.github.com/en/articles/about-code-owners -/packages/ @timneutkens @Timer @dav-is @ijjk @lfades -/examples/ @kheruc @lfades -/test/ @timneutkens @Timer @dav-is @ijjk @lfades -/bench/ @timneutkens @dav-is -/errors/ @kheruc +* @timneutkens @ijjk @shuding @huozhi +/.github/ @timneutkens @ijjk @shuding @styfle @huozhi @padmaia @balazsorban44 +/docs/ @timneutkens @ijjk @shuding @styfle @huozhi @padmaia @leerob @balazsorban44 +/errors/ @timneutkens @ijjk @shuding @styfle @huozhi @padmaia @leerob @balazsorban44 +/examples/ @timneutkens @ijjk @shuding @leerob @steven-tey @balazsorban44 + +# SWC Build & Telemetry (@padmaia) + +/packages/next/build/ @timneutkens @ijjk @shuding @padmaia @huozhi +/packages/next/telemetry/ @timneutkens @ijjk @shuding @padmaia +/packages/next-swc/ @timneutkens @ijjk @shuding @padmaia + +# Image Component (@styfle) + +/**/*image* @timneutkens @ijjk @shuding @styfle +/**/*image*/** @timneutkens @ijjk @shuding @styfle +/packages/next/client/use-intersection.tsx @timneutkens @ijjk @shuding @styfle +/packages/next/server/lib/squoosh/ @timneutkens @ijjk @shuding @styfle +/packages/next/server/serve-static.ts @timneutkens @ijjk @shuding @styfle +/packages/next/server/config.ts @timneutkens @ijjk @shuding @styfle diff --git a/.github/ISSUE_TEMPLATE/1.Bug_report.md b/.github/ISSUE_TEMPLATE/1.Bug_report.md deleted file mode 100644 index 6172fe14ec482..0000000000000 --- a/.github/ISSUE_TEMPLATE/1.Bug_report.md +++ /dev/null @@ -1,33 +0,0 @@ ---- -name: Bug report -about: Create a bug report for the Next.js core ---- - -# Bug report - -## Describe the bug - -A clear and concise description of what the bug is. - -## To Reproduce - -Steps to reproduce the behavior, please provide code snippets or a repository: -1. Go to '...' -2. Click on '....' -3. Scroll down to '....' -4. See error - -## Expected behavior -A clear and concise description of what you expected to happen. - -## Screenshots -If applicable, add screenshots to help explain your problem. - -## System information - - OS: [e.g. macOS, Windows] - - Browser (if applies) [e.g. chrome, safari] - - Version of Next.js: [e.g. 6.0.2] - -## Additional context - -Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/1.bug_report.yml b/.github/ISSUE_TEMPLATE/1.bug_report.yml new file mode 100644 index 0000000000000..f5a4c764022c1 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/1.bug_report.yml @@ -0,0 +1,64 @@ +name: Bug Report +description: Create a bug report for the Next.js core +labels: ['template: bug'] +body: + - type: markdown + attributes: + value: 'NOTE: [examples](https://github.com/vercel/next.js/tree/canary/examples) related issue should be reported using [this](https://github.com/vercel/next.js/issues/new?assignees=&labels=type%3A+example%2Ctemplate%3A+bug&template=2.example_bug_report.yml) issue template instead.' + - type: markdown + attributes: + value: If you leave out sections there is a high likelihood it will be moved to the GitHub Discussions ["Help" section](https://github.com/vercel/next.js/discussions/categories/help). + - type: checkboxes + attributes: + label: Verify canary release + description: '`next@canary` is the canary version of Next.js that ships daily. It includes all features and fixes that have not been released to the stable version yet. Think of canary as a public beta. Some issues may already be fixed in the canary version, so please verify that your issue reproduces before opening a new issue.' + options: + - label: I verified that the issue exists in the latest Next.js canary release + required: true + - type: textarea + attributes: + label: Provide environment information + description: Please run `next info` in the root directory of your project and paste the results. You might need to use `npx --no-install next info` if next is not in the current PATH. + validations: + required: true + - type: input + attributes: + label: What browser are you using? (if relevant) + description: 'Please specify the exact version. For example: Chrome 100.0.4878.0' + - type: input + attributes: + label: How are you deploying your application? (if relevant) + description: 'For example: next start, next export, Vercel, Other platform' + - type: textarea + attributes: + label: Describe the Bug + description: A clear and concise description of what the bug is. + validations: + required: true + - type: textarea + attributes: + label: Expected Behavior + description: A clear and concise description of what you expected to happen. + validations: + required: true + - type: input + attributes: + label: Link to reproduction - Issues with a link to complete (but minimal) reproduction code will be addressed faster + description: A link to a GitHub repository, a [StackBlitz](https://stackblitz.com/fork/github/vercel/next.js/tree/canary/examples/reproduction-template), or a [CodeSandbox](https://codesandbox.io/s/github/vercel/next.js/tree/canary/examples/reproduction-template) minimal reproduction. Minimal reproductions should be created from our [bug report template with `npx create-next-app -e reproduction-template`](https://github.com/vercel/next.js/tree/canary/examples/reproduction-template) and should include only changes that contribute to the issue. + validations: + required: true + - type: textarea + attributes: + label: To Reproduce + description: Steps to reproduce the behavior, please provide a clear description of how to reproduce the issue, based on the linked minimal reproduction. Screenshots can be provided in the issue body below. If using code blocks, make sure that [syntax highlighting is correct](https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/creating-and-highlighting-code-blocks#syntax-highlighting) and double check that the rendered preview is not broken. + validations: + required: true + - type: markdown + attributes: + value: Before posting the issue go through the steps you've written down to make sure the steps provided are detailed and clear. + - type: markdown + attributes: + value: Contributors should be able to follow the steps provided in order to reproduce the bug. + - type: markdown + attributes: + value: These steps are used to add integration tests to ensure the same issue does not happen again. Thanks in advance! diff --git a/.github/ISSUE_TEMPLATE/2.Feature_request.md b/.github/ISSUE_TEMPLATE/2.Feature_request.md deleted file mode 100644 index 6d6da1091c57d..0000000000000 --- a/.github/ISSUE_TEMPLATE/2.Feature_request.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -name: Feature request -about: Create a feature request for the Next.js core ---- - -# Feature request - -## Is your feature request related to a problem? Please describe. -A clear and concise description of what you want and what your use case is. - -## Describe the solution you'd like -A clear and concise description of what you want to happen. - -## Describe alternatives you've considered -A clear and concise description of any alternative solutions or features you've considered. - -## Additional context -Add any other context or screenshots about the feature request here. diff --git a/.github/ISSUE_TEMPLATE/2.example_bug_report.yml b/.github/ISSUE_TEMPLATE/2.example_bug_report.yml new file mode 100644 index 0000000000000..8f5a8e5919a86 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/2.example_bug_report.yml @@ -0,0 +1,64 @@ +name: Bug Report for Examples +description: Create a bug report for one of the Next.js examples +labels: ['area: examples'] +body: + - type: markdown + attributes: + value: Thanks for taking the time to file a bug report for [one of the examples](https://github.com/vercel/next.js/tree/canary/examples)! Please fill out this form as completely as possible. + - type: markdown + attributes: + value: If you leave out sections there is a high likelihood it will be moved to the GitHub Discussions ["Help" section](https://github.com/vercel/next.js/discussions/categories/help). + - type: checkboxes + attributes: + label: Verify canary release + description: '`next@canary` is the canary version of Next.js that ships daily. It includes all features and fixes that have not been released to the stable version yet. Think of canary as a public beta. Some issues may already be fixed in the canary version, so please verify that your issue reproduces before opening a new issue.' + options: + - label: I verified that the issue exists in the latest Next.js canary release + required: true + - type: textarea + attributes: + label: Provide environment information + description: Please run `next info` in the root directory of your project and paste the results. You might need to use `npx --no-install next info` if next is not in the current PATH. + validations: + required: true + - type: input + attributes: + label: Which example does this report relate to? + description: "See a complete list in the [examples folder](https://github.com/vercel/next.js/tree/canary/examples). For example: `with-styled-components`. Note: Examples not in the examples folder might be maintained by the example's library author. Check out their projects before opening the issue on Next.js" + validations: + required: true + - type: input + attributes: + label: What browser are you using? (if relevant) + description: 'Please specify the exact version. For example: Chrome 100.0.4878.0' + - type: input + attributes: + label: How are you deploying your application? (if relevant) + description: 'For example: next start, next export, Vercel, Other platform' + - type: textarea + attributes: + label: Describe the Bug + description: A clear and concise description of what the bug is. + validations: + required: true + - type: textarea + attributes: + label: Expected Behavior + description: A clear and concise description of what you expected to happen. + validations: + required: true + - type: textarea + attributes: + label: To Reproduce + description: Steps to reproduce the behavior, please provide a clear description of how to reproduce the issue, based on the relevant example. Screenshots can be provided in the issue body below. If using code blocks, make sure that [syntax highlighting is correct](https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/creating-and-highlighting-code-blocks#syntax-highlighting) and double check that the rendered preview is not broken. + validations: + required: true + - type: markdown + attributes: + value: Before posting the issue go through the steps you've written down to make sure the steps provided are detailed and clear. + - type: markdown + attributes: + value: Contributors should be able to follow the steps provided in order to reproduce the bug. + - type: markdown + attributes: + value: Thanks in advance! diff --git a/.github/ISSUE_TEMPLATE/3.Example_Bug_report.md b/.github/ISSUE_TEMPLATE/3.Example_Bug_report.md deleted file mode 100644 index be1f0d71892d1..0000000000000 --- a/.github/ISSUE_TEMPLATE/3.Example_Bug_report.md +++ /dev/null @@ -1,34 +0,0 @@ ---- -name: Bug report for examples -about: Create a bug report for one of the Next.js examples ---- - -# Examples bug report - -## Example name -Provide the example name - -## Describe the bug -A clear and concise description of what the bug is. - -## To Reproduce -Steps to reproduce the behavior, please provide code snippets or a repository: -1. Go to '...' -2. Click on '....' -3. Scroll down to '....' -4. See error - -## Expected behavior -A clear and concise description of what you expected to happen. - -## Screenshots -If applicable, add screenshots to help explain your problem. - -## System information - - OS: [e.g. macOS, Windows] - - Browser (if applies) [e.g. chrome, safari] - - Version of Next.js: [e.g. 6.0.2] - -## Additional context - -Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/3.feature_request.yml b/.github/ISSUE_TEMPLATE/3.feature_request.yml new file mode 100644 index 0000000000000..2655aff44d149 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/3.feature_request.yml @@ -0,0 +1,28 @@ +name: Feature Request +description: Create a feature request for the Next.js core +labels: 'template: story' +body: + - type: markdown + attributes: + value: Thanks for taking the time to file a feature request! Please fill out this form as completely as possible. + - type: markdown + attributes: + value: 'Feature requests will be converted to the GitHub Discussions "Ideas" section.' + - type: textarea + attributes: + label: Describe the feature you'd like to request + description: A clear and concise description of what you want and what your use case is. + validations: + required: true + - type: textarea + attributes: + label: Describe the solution you'd like + description: A clear and concise description of what you want to happen. + validations: + required: true + - type: textarea + attributes: + label: Describe alternatives you've considered + description: A clear and concise description of any alternative solutions or features you've considered. + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/4.docs_request.yml b/.github/ISSUE_TEMPLATE/4.docs_request.yml new file mode 100644 index 0000000000000..a2fe06929844e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/4.docs_request.yml @@ -0,0 +1,24 @@ +name: 'Docs Request for an Update or Improvement' +description: A request to update or improve Next.js documentation +title: 'Docs: ' +labels: + - 'template: documentation' +body: + - type: textarea + attributes: + label: What is the improvement or update you wish to see? + description: 'Example: I would like to see more examples of how to use the `` component. Or, the `` component docs are missing information.' + validations: + required: true + - type: textarea + attributes: + label: Is there any context that might help us understand? + description: A clear description of any added context that might help us understand. + validations: + required: true + - type: input + attributes: + label: Does the docs page already exist? Please link to it. + description: 'Example: https://nextjs.org/docs/api-reference/next/link' + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/8.Question_about_next.md b/.github/ISSUE_TEMPLATE/8.Question_about_next.md deleted file mode 100644 index e51fa55a0f239..0000000000000 --- a/.github/ISSUE_TEMPLATE/8.Question_about_next.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: Question about Next.js -about: If you have a question related to Next.js or the examples. Reach out to the community on https://spectrum.chat/next-js ---- - -# Question about Next.js - -Questions should be posted on https://spectrum.chat/next-js diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000000000..5cb26d8d1312c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: false +contact_links: + - name: Ask a question + url: https://github.com/vercel/next.js/discussions + about: Ask questions and discuss with other community members diff --git a/.github/actions/issue-validator/.gitignore b/.github/actions/issue-validator/.gitignore new file mode 100644 index 0000000000000..d123bd5a2eabb --- /dev/null +++ b/.github/actions/issue-validator/.gitignore @@ -0,0 +1,2 @@ +!dist +!package-lock.json \ No newline at end of file diff --git a/.github/actions/issue-validator/index.mjs b/.github/actions/issue-validator/index.mjs new file mode 100644 index 0000000000000..ee098cc6e68a2 --- /dev/null +++ b/.github/actions/issue-validator/index.mjs @@ -0,0 +1,7 @@ +import{createRequire as __WEBPACK_EXTERNAL_createRequire}from"module";var __webpack_modules__={7351:function(e,p,a){var t=this&&this.__createBinding||(Object.create?function(e,p,a,t){if(t===undefined)t=a;Object.defineProperty(e,t,{enumerable:true,get:function(){return p[a]}})}:function(e,p,a,t){if(t===undefined)t=a;e[t]=p[a]});var d=this&&this.__setModuleDefault||(Object.create?function(e,p){Object.defineProperty(e,"default",{enumerable:true,value:p})}:function(e,p){e["default"]=p});var r=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var p={};if(e!=null)for(var a in e)if(a!=="default"&&Object.hasOwnProperty.call(e,a))t(p,e,a);d(p,e);return p};Object.defineProperty(p,"__esModule",{value:true});p.issue=p.issueCommand=void 0;const s=r(a(2037));const i=a(5278);function issueCommand(e,p,a){const t=new Command(e,p,a);process.stdout.write(t.toString()+s.EOL)}p.issueCommand=issueCommand;function issue(e,p=""){issueCommand(e,{},p)}p.issue=issue;const o="::";class Command{constructor(e,p,a){if(!e){e="missing.command"}this.command=e;this.properties=p;this.message=a}toString(){let e=o+this.command;if(this.properties&&Object.keys(this.properties).length>0){e+=" ";let p=true;for(const a in this.properties){if(this.properties.hasOwnProperty(a)){const t=this.properties[a];if(t){if(p){p=false}else{e+=","}e+=`${a}=${escapeProperty(t)}`}}}}e+=`${o}${escapeData(this.message)}`;return e}}function escapeData(e){return i.toCommandValue(e).replace(/%/g,"%25").replace(/\r/g,"%0D").replace(/\n/g,"%0A")}function escapeProperty(e){return i.toCommandValue(e).replace(/%/g,"%25").replace(/\r/g,"%0D").replace(/\n/g,"%0A").replace(/:/g,"%3A").replace(/,/g,"%2C")}},2186:function(e,p,a){var t=this&&this.__createBinding||(Object.create?function(e,p,a,t){if(t===undefined)t=a;Object.defineProperty(e,t,{enumerable:true,get:function(){return p[a]}})}:function(e,p,a,t){if(t===undefined)t=a;e[t]=p[a]});var d=this&&this.__setModuleDefault||(Object.create?function(e,p){Object.defineProperty(e,"default",{enumerable:true,value:p})}:function(e,p){e["default"]=p});var r=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var p={};if(e!=null)for(var a in e)if(a!=="default"&&Object.hasOwnProperty.call(e,a))t(p,e,a);d(p,e);return p};var s=this&&this.__awaiter||function(e,p,a,t){function adopt(e){return e instanceof a?e:new a((function(p){p(e)}))}return new(a||(a=Promise))((function(a,d){function fulfilled(e){try{step(t.next(e))}catch(e){d(e)}}function rejected(e){try{step(t["throw"](e))}catch(e){d(e)}}function step(e){e.done?a(e.value):adopt(e.value).then(fulfilled,rejected)}step((t=t.apply(e,p||[])).next())}))};Object.defineProperty(p,"__esModule",{value:true});p.getIDToken=p.getState=p.saveState=p.group=p.endGroup=p.startGroup=p.info=p.notice=p.warning=p.error=p.debug=p.isDebug=p.setFailed=p.setCommandEcho=p.setOutput=p.getBooleanInput=p.getMultilineInput=p.getInput=p.addPath=p.setSecret=p.exportVariable=p.ExitCode=void 0;const i=a(7351);const o=a(717);const n=a(5278);const l=r(a(2037));const m=r(a(1017));const u=a(8041);var c;(function(e){e[e["Success"]=0]="Success";e[e["Failure"]=1]="Failure"})(c=p.ExitCode||(p.ExitCode={}));function exportVariable(e,p){const a=n.toCommandValue(p);process.env[e]=a;const t=process.env["GITHUB_ENV"]||"";if(t){const p="_GitHubActionsFileCommandDelimeter_";const t=`${e}<<${p}${l.EOL}${a}${l.EOL}${p}`;o.issueCommand("ENV",t)}else{i.issueCommand("set-env",{name:e},a)}}p.exportVariable=exportVariable;function setSecret(e){i.issueCommand("add-mask",{},e)}p.setSecret=setSecret;function addPath(e){const p=process.env["GITHUB_PATH"]||"";if(p){o.issueCommand("PATH",e)}else{i.issueCommand("add-path",{},e)}process.env["PATH"]=`${e}${m.delimiter}${process.env["PATH"]}`}p.addPath=addPath;function getInput(e,p){const a=process.env[`INPUT_${e.replace(/ /g,"_").toUpperCase()}`]||"";if(p&&p.required&&!a){throw new Error(`Input required and not supplied: ${e}`)}if(p&&p.trimWhitespace===false){return a}return a.trim()}p.getInput=getInput;function getMultilineInput(e,p){const a=getInput(e,p).split("\n").filter((e=>e!==""));return a}p.getMultilineInput=getMultilineInput;function getBooleanInput(e,p){const a=["true","True","TRUE"];const t=["false","False","FALSE"];const d=getInput(e,p);if(a.includes(d))return true;if(t.includes(d))return false;throw new TypeError(`Input does not meet YAML 1.2 "Core Schema" specification: ${e}\n`+`Support boolean input list: \`true | True | TRUE | false | False | FALSE\``)}p.getBooleanInput=getBooleanInput;function setOutput(e,p){process.stdout.write(l.EOL);i.issueCommand("set-output",{name:e},p)}p.setOutput=setOutput;function setCommandEcho(e){i.issue("echo",e?"on":"off")}p.setCommandEcho=setCommandEcho;function setFailed(e){process.exitCode=c.Failure;error(e)}p.setFailed=setFailed;function isDebug(){return process.env["RUNNER_DEBUG"]==="1"}p.isDebug=isDebug;function debug(e){i.issueCommand("debug",{},e)}p.debug=debug;function error(e,p={}){i.issueCommand("error",n.toCommandProperties(p),e instanceof Error?e.toString():e)}p.error=error;function warning(e,p={}){i.issueCommand("warning",n.toCommandProperties(p),e instanceof Error?e.toString():e)}p.warning=warning;function notice(e,p={}){i.issueCommand("notice",n.toCommandProperties(p),e instanceof Error?e.toString():e)}p.notice=notice;function info(e){process.stdout.write(e+l.EOL)}p.info=info;function startGroup(e){i.issue("group",e)}p.startGroup=startGroup;function endGroup(){i.issue("endgroup")}p.endGroup=endGroup;function group(e,p){return s(this,void 0,void 0,(function*(){startGroup(e);let a;try{a=yield p()}finally{endGroup()}return a}))}p.group=group;function saveState(e,p){i.issueCommand("save-state",{name:e},p)}p.saveState=saveState;function getState(e){return process.env[`STATE_${e}`]||""}p.getState=getState;function getIDToken(e){return s(this,void 0,void 0,(function*(){return yield u.OidcClient.getIDToken(e)}))}p.getIDToken=getIDToken;var h=a(1327);Object.defineProperty(p,"summary",{enumerable:true,get:function(){return h.summary}});var v=a(1327);Object.defineProperty(p,"markdownSummary",{enumerable:true,get:function(){return v.markdownSummary}});var g=a(2981);Object.defineProperty(p,"toPosixPath",{enumerable:true,get:function(){return g.toPosixPath}});Object.defineProperty(p,"toWin32Path",{enumerable:true,get:function(){return g.toWin32Path}});Object.defineProperty(p,"toPlatformPath",{enumerable:true,get:function(){return g.toPlatformPath}})},717:function(e,p,a){var t=this&&this.__createBinding||(Object.create?function(e,p,a,t){if(t===undefined)t=a;Object.defineProperty(e,t,{enumerable:true,get:function(){return p[a]}})}:function(e,p,a,t){if(t===undefined)t=a;e[t]=p[a]});var d=this&&this.__setModuleDefault||(Object.create?function(e,p){Object.defineProperty(e,"default",{enumerable:true,value:p})}:function(e,p){e["default"]=p});var r=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var p={};if(e!=null)for(var a in e)if(a!=="default"&&Object.hasOwnProperty.call(e,a))t(p,e,a);d(p,e);return p};Object.defineProperty(p,"__esModule",{value:true});p.issueCommand=void 0;const s=r(a(7147));const i=r(a(2037));const o=a(5278);function issueCommand(e,p){const a=process.env[`GITHUB_${e}`];if(!a){throw new Error(`Unable to find environment variable for file command ${e}`)}if(!s.existsSync(a)){throw new Error(`Missing file at path: ${a}`)}s.appendFileSync(a,`${o.toCommandValue(p)}${i.EOL}`,{encoding:"utf8"})}p.issueCommand=issueCommand},8041:function(e,p,a){var t=this&&this.__awaiter||function(e,p,a,t){function adopt(e){return e instanceof a?e:new a((function(p){p(e)}))}return new(a||(a=Promise))((function(a,d){function fulfilled(e){try{step(t.next(e))}catch(e){d(e)}}function rejected(e){try{step(t["throw"](e))}catch(e){d(e)}}function step(e){e.done?a(e.value):adopt(e.value).then(fulfilled,rejected)}step((t=t.apply(e,p||[])).next())}))};Object.defineProperty(p,"__esModule",{value:true});p.OidcClient=void 0;const d=a(6255);const r=a(5526);const s=a(2186);class OidcClient{static createHttpClient(e=true,p=10){const a={allowRetries:e,maxRetries:p};return new d.HttpClient("actions/oidc-client",[new r.BearerCredentialHandler(OidcClient.getRequestToken())],a)}static getRequestToken(){const e=process.env["ACTIONS_ID_TOKEN_REQUEST_TOKEN"];if(!e){throw new Error("Unable to get ACTIONS_ID_TOKEN_REQUEST_TOKEN env variable")}return e}static getIDTokenUrl(){const e=process.env["ACTIONS_ID_TOKEN_REQUEST_URL"];if(!e){throw new Error("Unable to get ACTIONS_ID_TOKEN_REQUEST_URL env variable")}return e}static getCall(e){var p;return t(this,void 0,void 0,(function*(){const a=OidcClient.createHttpClient();const t=yield a.getJson(e).catch((e=>{throw new Error(`Failed to get ID Token. \n \n Error Code : ${e.statusCode}\n \n Error Message: ${e.result.message}`)}));const d=(p=t.result)===null||p===void 0?void 0:p.value;if(!d){throw new Error("Response json body do not have ID Token field")}return d}))}static getIDToken(e){return t(this,void 0,void 0,(function*(){try{let p=OidcClient.getIDTokenUrl();if(e){const a=encodeURIComponent(e);p=`${p}&audience=${a}`}s.debug(`ID token url is ${p}`);const a=yield OidcClient.getCall(p);s.setSecret(a);return a}catch(e){throw new Error(`Error message: ${e.message}`)}}))}}p.OidcClient=OidcClient},2981:function(e,p,a){var t=this&&this.__createBinding||(Object.create?function(e,p,a,t){if(t===undefined)t=a;Object.defineProperty(e,t,{enumerable:true,get:function(){return p[a]}})}:function(e,p,a,t){if(t===undefined)t=a;e[t]=p[a]});var d=this&&this.__setModuleDefault||(Object.create?function(e,p){Object.defineProperty(e,"default",{enumerable:true,value:p})}:function(e,p){e["default"]=p});var r=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var p={};if(e!=null)for(var a in e)if(a!=="default"&&Object.hasOwnProperty.call(e,a))t(p,e,a);d(p,e);return p};Object.defineProperty(p,"__esModule",{value:true});p.toPlatformPath=p.toWin32Path=p.toPosixPath=void 0;const s=r(a(1017));function toPosixPath(e){return e.replace(/[\\]/g,"/")}p.toPosixPath=toPosixPath;function toWin32Path(e){return e.replace(/[/]/g,"\\")}p.toWin32Path=toWin32Path;function toPlatformPath(e){return e.replace(/[/\\]/g,s.sep)}p.toPlatformPath=toPlatformPath},1327:function(e,p,a){var t=this&&this.__awaiter||function(e,p,a,t){function adopt(e){return e instanceof a?e:new a((function(p){p(e)}))}return new(a||(a=Promise))((function(a,d){function fulfilled(e){try{step(t.next(e))}catch(e){d(e)}}function rejected(e){try{step(t["throw"](e))}catch(e){d(e)}}function step(e){e.done?a(e.value):adopt(e.value).then(fulfilled,rejected)}step((t=t.apply(e,p||[])).next())}))};Object.defineProperty(p,"__esModule",{value:true});p.summary=p.markdownSummary=p.SUMMARY_DOCS_URL=p.SUMMARY_ENV_VAR=void 0;const d=a(2037);const r=a(7147);const{access:s,appendFile:i,writeFile:o}=r.promises;p.SUMMARY_ENV_VAR="GITHUB_STEP_SUMMARY";p.SUMMARY_DOCS_URL="https://docs.github.com/actions/using-workflows/workflow-commands-for-github-actions#adding-a-job-summary";class Summary{constructor(){this._buffer=""}filePath(){return t(this,void 0,void 0,(function*(){if(this._filePath){return this._filePath}const e=process.env[p.SUMMARY_ENV_VAR];if(!e){throw new Error(`Unable to find environment variable for $${p.SUMMARY_ENV_VAR}. Check if your runtime environment supports job summaries.`)}try{yield s(e,r.constants.R_OK|r.constants.W_OK)}catch(p){throw new Error(`Unable to access summary file: '${e}'. Check if the file has correct read/write permissions.`)}this._filePath=e;return this._filePath}))}wrap(e,p,a={}){const t=Object.entries(a).map((([e,p])=>` ${e}="${p}"`)).join("");if(!p){return`<${e}${t}>`}return`<${e}${t}>${p}`}write(e){return t(this,void 0,void 0,(function*(){const p=!!(e===null||e===void 0?void 0:e.overwrite);const a=yield this.filePath();const t=p?o:i;yield t(a,this._buffer,{encoding:"utf8"});return this.emptyBuffer()}))}clear(){return t(this,void 0,void 0,(function*(){return this.emptyBuffer().write({overwrite:true})}))}stringify(){return this._buffer}isEmptyBuffer(){return this._buffer.length===0}emptyBuffer(){this._buffer="";return this}addRaw(e,p=false){this._buffer+=e;return p?this.addEOL():this}addEOL(){return this.addRaw(d.EOL)}addCodeBlock(e,p){const a=Object.assign({},p&&{lang:p});const t=this.wrap("pre",this.wrap("code",e),a);return this.addRaw(t).addEOL()}addList(e,p=false){const a=p?"ol":"ul";const t=e.map((e=>this.wrap("li",e))).join("");const d=this.wrap(a,t);return this.addRaw(d).addEOL()}addTable(e){const p=e.map((e=>{const p=e.map((e=>{if(typeof e==="string"){return this.wrap("td",e)}const{header:p,data:a,colspan:t,rowspan:d}=e;const r=p?"th":"td";const s=Object.assign(Object.assign({},t&&{colspan:t}),d&&{rowspan:d});return this.wrap(r,a,s)})).join("");return this.wrap("tr",p)})).join("");const a=this.wrap("table",p);return this.addRaw(a).addEOL()}addDetails(e,p){const a=this.wrap("details",this.wrap("summary",e)+p);return this.addRaw(a).addEOL()}addImage(e,p,a){const{width:t,height:d}=a||{};const r=Object.assign(Object.assign({},t&&{width:t}),d&&{height:d});const s=this.wrap("img",null,Object.assign({src:e,alt:p},r));return this.addRaw(s).addEOL()}addHeading(e,p){const a=`h${p}`;const t=["h1","h2","h3","h4","h5","h6"].includes(a)?a:"h1";const d=this.wrap(t,e);return this.addRaw(d).addEOL()}addSeparator(){const e=this.wrap("hr",null);return this.addRaw(e).addEOL()}addBreak(){const e=this.wrap("br",null);return this.addRaw(e).addEOL()}addQuote(e,p){const a=Object.assign({},p&&{cite:p});const t=this.wrap("blockquote",e,a);return this.addRaw(t).addEOL()}addLink(e,p){const a=this.wrap("a",e,{href:p});return this.addRaw(a).addEOL()}}const n=new Summary;p.markdownSummary=n;p.summary=n},5278:(e,p)=>{Object.defineProperty(p,"__esModule",{value:true});p.toCommandProperties=p.toCommandValue=void 0;function toCommandValue(e){if(e===null||e===undefined){return""}else if(typeof e==="string"||e instanceof String){return e}return JSON.stringify(e)}p.toCommandValue=toCommandValue;function toCommandProperties(e){if(!Object.keys(e).length){return{}}return{title:e.title,file:e.file,line:e.startLine,endLine:e.endLine,col:e.startColumn,endColumn:e.endColumn}}p.toCommandProperties=toCommandProperties},4087:(e,p,a)=>{Object.defineProperty(p,"__esModule",{value:true});p.Context=void 0;const t=a(7147);const d=a(2037);class Context{constructor(){var e,p,a;this.payload={};if(process.env.GITHUB_EVENT_PATH){if(t.existsSync(process.env.GITHUB_EVENT_PATH)){this.payload=JSON.parse(t.readFileSync(process.env.GITHUB_EVENT_PATH,{encoding:"utf8"}))}else{const e=process.env.GITHUB_EVENT_PATH;process.stdout.write(`GITHUB_EVENT_PATH ${e} does not exist${d.EOL}`)}}this.eventName=process.env.GITHUB_EVENT_NAME;this.sha=process.env.GITHUB_SHA;this.ref=process.env.GITHUB_REF;this.workflow=process.env.GITHUB_WORKFLOW;this.action=process.env.GITHUB_ACTION;this.actor=process.env.GITHUB_ACTOR;this.job=process.env.GITHUB_JOB;this.runNumber=parseInt(process.env.GITHUB_RUN_NUMBER,10);this.runId=parseInt(process.env.GITHUB_RUN_ID,10);this.apiUrl=(e=process.env.GITHUB_API_URL)!==null&&e!==void 0?e:`https://api.github.com`;this.serverUrl=(p=process.env.GITHUB_SERVER_URL)!==null&&p!==void 0?p:`https://github.com`;this.graphqlUrl=(a=process.env.GITHUB_GRAPHQL_URL)!==null&&a!==void 0?a:`https://api.github.com/graphql`}get issue(){const e=this.payload;return Object.assign(Object.assign({},this.repo),{number:(e.issue||e.pull_request||e).number})}get repo(){if(process.env.GITHUB_REPOSITORY){const[e,p]=process.env.GITHUB_REPOSITORY.split("/");return{owner:e,repo:p}}if(this.payload.repository){return{owner:this.payload.repository.owner.login,repo:this.payload.repository.name}}throw new Error("context.repo requires a GITHUB_REPOSITORY environment variable like 'owner/repo'")}}p.Context=Context},5438:function(e,p,a){var t=this&&this.__createBinding||(Object.create?function(e,p,a,t){if(t===undefined)t=a;Object.defineProperty(e,t,{enumerable:true,get:function(){return p[a]}})}:function(e,p,a,t){if(t===undefined)t=a;e[t]=p[a]});var d=this&&this.__setModuleDefault||(Object.create?function(e,p){Object.defineProperty(e,"default",{enumerable:true,value:p})}:function(e,p){e["default"]=p});var r=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var p={};if(e!=null)for(var a in e)if(a!=="default"&&Object.hasOwnProperty.call(e,a))t(p,e,a);d(p,e);return p};Object.defineProperty(p,"__esModule",{value:true});p.getOctokit=p.context=void 0;const s=r(a(4087));const i=a(3030);p.context=new s.Context;function getOctokit(e,p){return new i.GitHub(i.getOctokitOptions(e,p))}p.getOctokit=getOctokit},7914:function(e,p,a){var t=this&&this.__createBinding||(Object.create?function(e,p,a,t){if(t===undefined)t=a;Object.defineProperty(e,t,{enumerable:true,get:function(){return p[a]}})}:function(e,p,a,t){if(t===undefined)t=a;e[t]=p[a]});var d=this&&this.__setModuleDefault||(Object.create?function(e,p){Object.defineProperty(e,"default",{enumerable:true,value:p})}:function(e,p){e["default"]=p});var r=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var p={};if(e!=null)for(var a in e)if(a!=="default"&&Object.hasOwnProperty.call(e,a))t(p,e,a);d(p,e);return p};Object.defineProperty(p,"__esModule",{value:true});p.getApiBaseUrl=p.getProxyAgent=p.getAuthString=void 0;const s=r(a(6255));function getAuthString(e,p){if(!e&&!p.auth){throw new Error("Parameter token or opts.auth is required")}else if(e&&p.auth){throw new Error("Parameters token and opts.auth may not both be specified")}return typeof p.auth==="string"?p.auth:`token ${e}`}p.getAuthString=getAuthString;function getProxyAgent(e){const p=new s.HttpClient;return p.getAgent(e)}p.getProxyAgent=getProxyAgent;function getApiBaseUrl(){return process.env["GITHUB_API_URL"]||"https://api.github.com"}p.getApiBaseUrl=getApiBaseUrl},3030:function(e,p,a){var t=this&&this.__createBinding||(Object.create?function(e,p,a,t){if(t===undefined)t=a;Object.defineProperty(e,t,{enumerable:true,get:function(){return p[a]}})}:function(e,p,a,t){if(t===undefined)t=a;e[t]=p[a]});var d=this&&this.__setModuleDefault||(Object.create?function(e,p){Object.defineProperty(e,"default",{enumerable:true,value:p})}:function(e,p){e["default"]=p});var r=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var p={};if(e!=null)for(var a in e)if(a!=="default"&&Object.hasOwnProperty.call(e,a))t(p,e,a);d(p,e);return p};Object.defineProperty(p,"__esModule",{value:true});p.getOctokitOptions=p.GitHub=p.context=void 0;const s=r(a(4087));const i=r(a(7914));const o=a(6762);const n=a(3044);const l=a(4193);p.context=new s.Context;const m=i.getApiBaseUrl();const u={baseUrl:m,request:{agent:i.getProxyAgent(m)}};p.GitHub=o.Octokit.plugin(n.restEndpointMethods,l.paginateRest).defaults(u);function getOctokitOptions(e,p){const a=Object.assign({},p||{});const t=i.getAuthString(e,a);if(t){a.auth=t}return a}p.getOctokitOptions=getOctokitOptions},5526:function(e,p){var a=this&&this.__awaiter||function(e,p,a,t){function adopt(e){return e instanceof a?e:new a((function(p){p(e)}))}return new(a||(a=Promise))((function(a,d){function fulfilled(e){try{step(t.next(e))}catch(e){d(e)}}function rejected(e){try{step(t["throw"](e))}catch(e){d(e)}}function step(e){e.done?a(e.value):adopt(e.value).then(fulfilled,rejected)}step((t=t.apply(e,p||[])).next())}))};Object.defineProperty(p,"__esModule",{value:true});p.PersonalAccessTokenCredentialHandler=p.BearerCredentialHandler=p.BasicCredentialHandler=void 0;class BasicCredentialHandler{constructor(e,p){this.username=e;this.password=p}prepareRequest(e){if(!e.headers){throw Error("The request has no headers")}e.headers["Authorization"]=`Basic ${Buffer.from(`${this.username}:${this.password}`).toString("base64")}`}canHandleAuthentication(){return false}handleAuthentication(){return a(this,void 0,void 0,(function*(){throw new Error("not implemented")}))}}p.BasicCredentialHandler=BasicCredentialHandler;class BearerCredentialHandler{constructor(e){this.token=e}prepareRequest(e){if(!e.headers){throw Error("The request has no headers")}e.headers["Authorization"]=`Bearer ${this.token}`}canHandleAuthentication(){return false}handleAuthentication(){return a(this,void 0,void 0,(function*(){throw new Error("not implemented")}))}}p.BearerCredentialHandler=BearerCredentialHandler;class PersonalAccessTokenCredentialHandler{constructor(e){this.token=e}prepareRequest(e){if(!e.headers){throw Error("The request has no headers")}e.headers["Authorization"]=`Basic ${Buffer.from(`PAT:${this.token}`).toString("base64")}`}canHandleAuthentication(){return false}handleAuthentication(){return a(this,void 0,void 0,(function*(){throw new Error("not implemented")}))}}p.PersonalAccessTokenCredentialHandler=PersonalAccessTokenCredentialHandler},6255:function(e,p,a){var t=this&&this.__createBinding||(Object.create?function(e,p,a,t){if(t===undefined)t=a;Object.defineProperty(e,t,{enumerable:true,get:function(){return p[a]}})}:function(e,p,a,t){if(t===undefined)t=a;e[t]=p[a]});var d=this&&this.__setModuleDefault||(Object.create?function(e,p){Object.defineProperty(e,"default",{enumerable:true,value:p})}:function(e,p){e["default"]=p});var r=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var p={};if(e!=null)for(var a in e)if(a!=="default"&&Object.hasOwnProperty.call(e,a))t(p,e,a);d(p,e);return p};var s=this&&this.__awaiter||function(e,p,a,t){function adopt(e){return e instanceof a?e:new a((function(p){p(e)}))}return new(a||(a=Promise))((function(a,d){function fulfilled(e){try{step(t.next(e))}catch(e){d(e)}}function rejected(e){try{step(t["throw"](e))}catch(e){d(e)}}function step(e){e.done?a(e.value):adopt(e.value).then(fulfilled,rejected)}step((t=t.apply(e,p||[])).next())}))};Object.defineProperty(p,"__esModule",{value:true});p.HttpClient=p.isHttps=p.HttpClientResponse=p.HttpClientError=p.getProxyUrl=p.MediaTypes=p.Headers=p.HttpCodes=void 0;const i=r(a(3685));const o=r(a(5687));const n=r(a(9835));const l=r(a(4294));var m;(function(e){e[e["OK"]=200]="OK";e[e["MultipleChoices"]=300]="MultipleChoices";e[e["MovedPermanently"]=301]="MovedPermanently";e[e["ResourceMoved"]=302]="ResourceMoved";e[e["SeeOther"]=303]="SeeOther";e[e["NotModified"]=304]="NotModified";e[e["UseProxy"]=305]="UseProxy";e[e["SwitchProxy"]=306]="SwitchProxy";e[e["TemporaryRedirect"]=307]="TemporaryRedirect";e[e["PermanentRedirect"]=308]="PermanentRedirect";e[e["BadRequest"]=400]="BadRequest";e[e["Unauthorized"]=401]="Unauthorized";e[e["PaymentRequired"]=402]="PaymentRequired";e[e["Forbidden"]=403]="Forbidden";e[e["NotFound"]=404]="NotFound";e[e["MethodNotAllowed"]=405]="MethodNotAllowed";e[e["NotAcceptable"]=406]="NotAcceptable";e[e["ProxyAuthenticationRequired"]=407]="ProxyAuthenticationRequired";e[e["RequestTimeout"]=408]="RequestTimeout";e[e["Conflict"]=409]="Conflict";e[e["Gone"]=410]="Gone";e[e["TooManyRequests"]=429]="TooManyRequests";e[e["InternalServerError"]=500]="InternalServerError";e[e["NotImplemented"]=501]="NotImplemented";e[e["BadGateway"]=502]="BadGateway";e[e["ServiceUnavailable"]=503]="ServiceUnavailable";e[e["GatewayTimeout"]=504]="GatewayTimeout"})(m=p.HttpCodes||(p.HttpCodes={}));var u;(function(e){e["Accept"]="accept";e["ContentType"]="content-type"})(u=p.Headers||(p.Headers={}));var c;(function(e){e["ApplicationJson"]="application/json"})(c=p.MediaTypes||(p.MediaTypes={}));function getProxyUrl(e){const p=n.getProxyUrl(new URL(e));return p?p.href:""}p.getProxyUrl=getProxyUrl;const h=[m.MovedPermanently,m.ResourceMoved,m.SeeOther,m.TemporaryRedirect,m.PermanentRedirect];const v=[m.BadGateway,m.ServiceUnavailable,m.GatewayTimeout];const g=["OPTIONS","GET","DELETE","HEAD"];const w=10;const E=5;class HttpClientError extends Error{constructor(e,p){super(e);this.name="HttpClientError";this.statusCode=p;Object.setPrototypeOf(this,HttpClientError.prototype)}}p.HttpClientError=HttpClientError;class HttpClientResponse{constructor(e){this.message=e}readBody(){return s(this,void 0,void 0,(function*(){return new Promise((e=>s(this,void 0,void 0,(function*(){let p=Buffer.alloc(0);this.message.on("data",(e=>{p=Buffer.concat([p,e])}));this.message.on("end",(()=>{e(p.toString())}))}))))}))}}p.HttpClientResponse=HttpClientResponse;function isHttps(e){const p=new URL(e);return p.protocol==="https:"}p.isHttps=isHttps;class HttpClient{constructor(e,p,a){this._ignoreSslError=false;this._allowRedirects=true;this._allowRedirectDowngrade=false;this._maxRedirects=50;this._allowRetries=false;this._maxRetries=1;this._keepAlive=false;this._disposed=false;this.userAgent=e;this.handlers=p||[];this.requestOptions=a;if(a){if(a.ignoreSslError!=null){this._ignoreSslError=a.ignoreSslError}this._socketTimeout=a.socketTimeout;if(a.allowRedirects!=null){this._allowRedirects=a.allowRedirects}if(a.allowRedirectDowngrade!=null){this._allowRedirectDowngrade=a.allowRedirectDowngrade}if(a.maxRedirects!=null){this._maxRedirects=Math.max(a.maxRedirects,0)}if(a.keepAlive!=null){this._keepAlive=a.keepAlive}if(a.allowRetries!=null){this._allowRetries=a.allowRetries}if(a.maxRetries!=null){this._maxRetries=a.maxRetries}}}options(e,p){return s(this,void 0,void 0,(function*(){return this.request("OPTIONS",e,null,p||{})}))}get(e,p){return s(this,void 0,void 0,(function*(){return this.request("GET",e,null,p||{})}))}del(e,p){return s(this,void 0,void 0,(function*(){return this.request("DELETE",e,null,p||{})}))}post(e,p,a){return s(this,void 0,void 0,(function*(){return this.request("POST",e,p,a||{})}))}patch(e,p,a){return s(this,void 0,void 0,(function*(){return this.request("PATCH",e,p,a||{})}))}put(e,p,a){return s(this,void 0,void 0,(function*(){return this.request("PUT",e,p,a||{})}))}head(e,p){return s(this,void 0,void 0,(function*(){return this.request("HEAD",e,null,p||{})}))}sendStream(e,p,a,t){return s(this,void 0,void 0,(function*(){return this.request(e,p,a,t)}))}getJson(e,p={}){return s(this,void 0,void 0,(function*(){p[u.Accept]=this._getExistingOrDefaultHeader(p,u.Accept,c.ApplicationJson);const a=yield this.get(e,p);return this._processResponse(a,this.requestOptions)}))}postJson(e,p,a={}){return s(this,void 0,void 0,(function*(){const t=JSON.stringify(p,null,2);a[u.Accept]=this._getExistingOrDefaultHeader(a,u.Accept,c.ApplicationJson);a[u.ContentType]=this._getExistingOrDefaultHeader(a,u.ContentType,c.ApplicationJson);const d=yield this.post(e,t,a);return this._processResponse(d,this.requestOptions)}))}putJson(e,p,a={}){return s(this,void 0,void 0,(function*(){const t=JSON.stringify(p,null,2);a[u.Accept]=this._getExistingOrDefaultHeader(a,u.Accept,c.ApplicationJson);a[u.ContentType]=this._getExistingOrDefaultHeader(a,u.ContentType,c.ApplicationJson);const d=yield this.put(e,t,a);return this._processResponse(d,this.requestOptions)}))}patchJson(e,p,a={}){return s(this,void 0,void 0,(function*(){const t=JSON.stringify(p,null,2);a[u.Accept]=this._getExistingOrDefaultHeader(a,u.Accept,c.ApplicationJson);a[u.ContentType]=this._getExistingOrDefaultHeader(a,u.ContentType,c.ApplicationJson);const d=yield this.patch(e,t,a);return this._processResponse(d,this.requestOptions)}))}request(e,p,a,t){return s(this,void 0,void 0,(function*(){if(this._disposed){throw new Error("Client has already been disposed.")}const d=new URL(p);let r=this._prepareRequest(e,d,t);const s=this._allowRetries&&g.includes(e)?this._maxRetries+1:1;let i=0;let o;do{o=yield this.requestRaw(r,a);if(o&&o.message&&o.message.statusCode===m.Unauthorized){let e;for(const p of this.handlers){if(p.canHandleAuthentication(o)){e=p;break}}if(e){return e.handleAuthentication(this,r,a)}else{return o}}let p=this._maxRedirects;while(o.message.statusCode&&h.includes(o.message.statusCode)&&this._allowRedirects&&p>0){const s=o.message.headers["location"];if(!s){break}const i=new URL(s);if(d.protocol==="https:"&&d.protocol!==i.protocol&&!this._allowRedirectDowngrade){throw new Error("Redirect from HTTPS to HTTP protocol. This downgrade is not allowed for security reasons. If you want to allow this behavior, set the allowRedirectDowngrade option to true.")}yield o.readBody();if(i.hostname!==d.hostname){for(const e in t){if(e.toLowerCase()==="authorization"){delete t[e]}}}r=this._prepareRequest(e,i,t);o=yield this.requestRaw(r,a);p--}if(!o.message.statusCode||!v.includes(o.message.statusCode)){return o}i+=1;if(i{function callbackForResult(e,p){if(e){t(e)}else if(!p){t(new Error("Unknown error"))}else{a(p)}}this.requestRawWithCallback(e,p,callbackForResult)}))}))}requestRawWithCallback(e,p,a){if(typeof p==="string"){if(!e.options.headers){e.options.headers={}}e.options.headers["Content-Length"]=Buffer.byteLength(p,"utf8")}let t=false;function handleResult(e,p){if(!t){t=true;a(e,p)}}const d=e.httpModule.request(e.options,(e=>{const p=new HttpClientResponse(e);handleResult(undefined,p)}));let r;d.on("socket",(e=>{r=e}));d.setTimeout(this._socketTimeout||3*6e4,(()=>{if(r){r.end()}handleResult(new Error(`Request timeout: ${e.options.path}`))}));d.on("error",(function(e){handleResult(e)}));if(p&&typeof p==="string"){d.write(p,"utf8")}if(p&&typeof p!=="string"){p.on("close",(function(){d.end()}));p.pipe(d)}else{d.end()}}getAgent(e){const p=new URL(e);return this._getAgent(p)}_prepareRequest(e,p,a){const t={};t.parsedUrl=p;const d=t.parsedUrl.protocol==="https:";t.httpModule=d?o:i;const r=d?443:80;t.options={};t.options.host=t.parsedUrl.hostname;t.options.port=t.parsedUrl.port?parseInt(t.parsedUrl.port):r;t.options.path=(t.parsedUrl.pathname||"")+(t.parsedUrl.search||"");t.options.method=e;t.options.headers=this._mergeHeaders(a);if(this.userAgent!=null){t.options.headers["user-agent"]=this.userAgent}t.options.agent=this._getAgent(t.parsedUrl);if(this.handlers){for(const e of this.handlers){e.prepareRequest(t.options)}}return t}_mergeHeaders(e){if(this.requestOptions&&this.requestOptions.headers){return Object.assign({},lowercaseKeys(this.requestOptions.headers),lowercaseKeys(e||{}))}return lowercaseKeys(e||{})}_getExistingOrDefaultHeader(e,p,a){let t;if(this.requestOptions&&this.requestOptions.headers){t=lowercaseKeys(this.requestOptions.headers)[p]}return e[p]||t||a}_getAgent(e){let p;const a=n.getProxyUrl(e);const t=a&&a.hostname;if(this._keepAlive&&t){p=this._proxyAgent}if(this._keepAlive&&!t){p=this._agent}if(p){return p}const d=e.protocol==="https:";let r=100;if(this.requestOptions){r=this.requestOptions.maxSockets||i.globalAgent.maxSockets}if(a&&a.hostname){const e={maxSockets:r,keepAlive:this._keepAlive,proxy:Object.assign(Object.assign({},(a.username||a.password)&&{proxyAuth:`${a.username}:${a.password}`}),{host:a.hostname,port:a.port})};let t;const s=a.protocol==="https:";if(d){t=s?l.httpsOverHttps:l.httpsOverHttp}else{t=s?l.httpOverHttps:l.httpOverHttp}p=t(e);this._proxyAgent=p}if(this._keepAlive&&!p){const e={keepAlive:this._keepAlive,maxSockets:r};p=d?new o.Agent(e):new i.Agent(e);this._agent=p}if(!p){p=d?o.globalAgent:i.globalAgent}if(d&&this._ignoreSslError){p.options=Object.assign(p.options||{},{rejectUnauthorized:false})}return p}_performExponentialBackoff(e){return s(this,void 0,void 0,(function*(){e=Math.min(w,e);const p=E*Math.pow(2,e);return new Promise((e=>setTimeout((()=>e()),p)))}))}_processResponse(e,p){return s(this,void 0,void 0,(function*(){return new Promise(((a,t)=>s(this,void 0,void 0,(function*(){const d=e.message.statusCode||0;const r={statusCode:d,result:null,headers:{}};if(d===m.NotFound){a(r)}function dateTimeDeserializer(e,p){if(typeof p==="string"){const e=new Date(p);if(!isNaN(e.valueOf())){return e}}return p}let s;let i;try{i=yield e.readBody();if(i&&i.length>0){if(p&&p.deserializeDates){s=JSON.parse(i,dateTimeDeserializer)}else{s=JSON.parse(i)}r.result=s}r.headers=e.message.headers}catch(e){}if(d>299){let e;if(s&&s.message){e=s.message}else if(i&&i.length>0){e=i}else{e=`Failed request: (${d})`}const p=new HttpClientError(e,d);p.result=r.result;t(p)}else{a(r)}}))))}))}}p.HttpClient=HttpClient;const lowercaseKeys=e=>Object.keys(e).reduce(((p,a)=>(p[a.toLowerCase()]=e[a],p)),{})},9835:(e,p)=>{Object.defineProperty(p,"__esModule",{value:true});p.checkBypass=p.getProxyUrl=void 0;function getProxyUrl(e){const p=e.protocol==="https:";if(checkBypass(e)){return undefined}const a=(()=>{if(p){return process.env["https_proxy"]||process.env["HTTPS_PROXY"]}else{return process.env["http_proxy"]||process.env["HTTP_PROXY"]}})();if(a){return new URL(a)}else{return undefined}}p.getProxyUrl=getProxyUrl;function checkBypass(e){if(!e.hostname){return false}const p=process.env["no_proxy"]||process.env["NO_PROXY"]||"";if(!p){return false}let a;if(e.port){a=Number(e.port)}else if(e.protocol==="http:"){a=80}else if(e.protocol==="https:"){a=443}const t=[e.hostname.toUpperCase()];if(typeof a==="number"){t.push(`${t[0]}:${a}`)}for(const e of p.split(",").map((e=>e.trim().toUpperCase())).filter((e=>e))){if(t.some((p=>p===e))){return true}}return false}p.checkBypass=checkBypass},334:(e,p)=>{Object.defineProperty(p,"__esModule",{value:true});const a=/^v1\./;const t=/^ghs_/;const d=/^ghu_/;async function auth(e){const p=e.split(/\./).length===3;const r=a.test(e)||t.test(e);const s=d.test(e);const i=p?"app":r?"installation":s?"user-to-server":"oauth";return{type:"token",token:e,tokenType:i}}function withAuthorizationPrefix(e){if(e.split(/\./).length===3){return`bearer ${e}`}return`token ${e}`}async function hook(e,p,a,t){const d=p.endpoint.merge(a,t);d.headers.authorization=withAuthorizationPrefix(e);return p(d)}const r=function createTokenAuth(e){if(!e){throw new Error("[@octokit/auth-token] No token passed to createTokenAuth")}if(typeof e!=="string"){throw new Error("[@octokit/auth-token] Token passed to createTokenAuth is not a string")}e=e.replace(/^(token|bearer) +/i,"");return Object.assign(auth.bind(null,e),{hook:hook.bind(null,e)})};p.createTokenAuth=r},6762:(e,p,a)=>{Object.defineProperty(p,"__esModule",{value:true});var t=a(5030);var d=a(3682);var r=a(6234);var s=a(8467);var i=a(334);function _objectWithoutPropertiesLoose(e,p){if(e==null)return{};var a={};var t=Object.keys(e);var d,r;for(r=0;r=0)continue;a[d]=e[d]}return a}function _objectWithoutProperties(e,p){if(e==null)return{};var a=_objectWithoutPropertiesLoose(e,p);var t,d;if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(d=0;d=0)continue;if(!Object.prototype.propertyIsEnumerable.call(e,t))continue;a[t]=e[t]}}return a}const o="3.6.0";const n=["authStrategy"];class Octokit{constructor(e={}){const p=new d.Collection;const a={baseUrl:r.request.endpoint.DEFAULTS.baseUrl,headers:{},request:Object.assign({},e.request,{hook:p.bind(null,"request")}),mediaType:{previews:[],format:""}};a.headers["user-agent"]=[e.userAgent,`octokit-core.js/${o} ${t.getUserAgent()}`].filter(Boolean).join(" ");if(e.baseUrl){a.baseUrl=e.baseUrl}if(e.previews){a.mediaType.previews=e.previews}if(e.timeZone){a.headers["time-zone"]=e.timeZone}this.request=r.request.defaults(a);this.graphql=s.withCustomRequest(this.request).defaults(a);this.log=Object.assign({debug:()=>{},info:()=>{},warn:console.warn.bind(console),error:console.error.bind(console)},e.log);this.hook=p;if(!e.authStrategy){if(!e.auth){this.auth=async()=>({type:"unauthenticated"})}else{const a=i.createTokenAuth(e.auth);p.wrap("request",a.hook);this.auth=a}}else{const{authStrategy:a}=e,t=_objectWithoutProperties(e,n);const d=a(Object.assign({request:this.request,log:this.log,octokit:this,octokitOptions:t},e.auth));p.wrap("request",d.hook);this.auth=d}const l=this.constructor;l.plugins.forEach((p=>{Object.assign(this,p(this,e))}))}static defaults(e){const p=class extends(this){constructor(...p){const a=p[0]||{};if(typeof e==="function"){super(e(a));return}super(Object.assign({},e,a,a.userAgent&&e.userAgent?{userAgent:`${a.userAgent} ${e.userAgent}`}:null))}};return p}static plugin(...e){var p;const a=this.plugins;const t=(p=class extends(this){},p.plugins=a.concat(e.filter((e=>!a.includes(e)))),p);return t}}Octokit.VERSION=o;Octokit.plugins=[];p.Octokit=Octokit},9440:(e,p,a)=>{Object.defineProperty(p,"__esModule",{value:true});var t=a(3287);var d=a(5030);function lowercaseKeys(e){if(!e){return{}}return Object.keys(e).reduce(((p,a)=>{p[a.toLowerCase()]=e[a];return p}),{})}function mergeDeep(e,p){const a=Object.assign({},e);Object.keys(p).forEach((d=>{if(t.isPlainObject(p[d])){if(!(d in e))Object.assign(a,{[d]:p[d]});else a[d]=mergeDeep(e[d],p[d])}else{Object.assign(a,{[d]:p[d]})}}));return a}function removeUndefinedProperties(e){for(const p in e){if(e[p]===undefined){delete e[p]}}return e}function merge(e,p,a){if(typeof p==="string"){let[e,t]=p.split(" ");a=Object.assign(t?{method:e,url:t}:{url:e},a)}else{a=Object.assign({},p)}a.headers=lowercaseKeys(a.headers);removeUndefinedProperties(a);removeUndefinedProperties(a.headers);const t=mergeDeep(e||{},a);if(e&&e.mediaType.previews.length){t.mediaType.previews=e.mediaType.previews.filter((e=>!t.mediaType.previews.includes(e))).concat(t.mediaType.previews)}t.mediaType.previews=t.mediaType.previews.map((e=>e.replace(/-preview/,"")));return t}function addQueryParameters(e,p){const a=/\?/.test(e)?"&":"?";const t=Object.keys(p);if(t.length===0){return e}return e+a+t.map((e=>{if(e==="q"){return"q="+p.q.split("+").map(encodeURIComponent).join("+")}return`${e}=${encodeURIComponent(p[e])}`})).join("&")}const r=/\{[^}]+\}/g;function removeNonChars(e){return e.replace(/^\W+|\W+$/g,"").split(/,/)}function extractUrlVariableNames(e){const p=e.match(r);if(!p){return[]}return p.map(removeNonChars).reduce(((e,p)=>e.concat(p)),[])}function omit(e,p){return Object.keys(e).filter((e=>!p.includes(e))).reduce(((p,a)=>{p[a]=e[a];return p}),{})}function encodeReserved(e){return e.split(/(%[0-9A-Fa-f]{2})/g).map((function(e){if(!/%[0-9A-Fa-f]/.test(e)){e=encodeURI(e).replace(/%5B/g,"[").replace(/%5D/g,"]")}return e})).join("")}function encodeUnreserved(e){return encodeURIComponent(e).replace(/[!'()*]/g,(function(e){return"%"+e.charCodeAt(0).toString(16).toUpperCase()}))}function encodeValue(e,p,a){p=e==="+"||e==="#"?encodeReserved(p):encodeUnreserved(p);if(a){return encodeUnreserved(a)+"="+p}else{return p}}function isDefined(e){return e!==undefined&&e!==null}function isKeyOperator(e){return e===";"||e==="&"||e==="?"}function getValues(e,p,a,t){var d=e[a],r=[];if(isDefined(d)&&d!==""){if(typeof d==="string"||typeof d==="number"||typeof d==="boolean"){d=d.toString();if(t&&t!=="*"){d=d.substring(0,parseInt(t,10))}r.push(encodeValue(p,d,isKeyOperator(p)?a:""))}else{if(t==="*"){if(Array.isArray(d)){d.filter(isDefined).forEach((function(e){r.push(encodeValue(p,e,isKeyOperator(p)?a:""))}))}else{Object.keys(d).forEach((function(e){if(isDefined(d[e])){r.push(encodeValue(p,d[e],e))}}))}}else{const e=[];if(Array.isArray(d)){d.filter(isDefined).forEach((function(a){e.push(encodeValue(p,a))}))}else{Object.keys(d).forEach((function(a){if(isDefined(d[a])){e.push(encodeUnreserved(a));e.push(encodeValue(p,d[a].toString()))}}))}if(isKeyOperator(p)){r.push(encodeUnreserved(a)+"="+e.join(","))}else if(e.length!==0){r.push(e.join(","))}}}}else{if(p===";"){if(isDefined(d)){r.push(encodeUnreserved(a))}}else if(d===""&&(p==="&"||p==="?")){r.push(encodeUnreserved(a)+"=")}else if(d===""){r.push("")}}return r}function parseUrl(e){return{expand:expand.bind(null,e)}}function expand(e,p){var a=["+","#",".","/",";","?","&"];return e.replace(/\{([^\{\}]+)\}|([^\{\}]+)/g,(function(e,t,d){if(t){let e="";const d=[];if(a.indexOf(t.charAt(0))!==-1){e=t.charAt(0);t=t.substr(1)}t.split(/,/g).forEach((function(a){var t=/([^:\*]*)(?::(\d+)|(\*))?/.exec(a);d.push(getValues(p,e,t[1],t[2]||t[3]))}));if(e&&e!=="+"){var r=",";if(e==="?"){r="&"}else if(e!=="#"){r=e}return(d.length!==0?e:"")+d.join(r)}else{return d.join(",")}}else{return encodeReserved(d)}}))}function parse(e){let p=e.method.toUpperCase();let a=(e.url||"/").replace(/:([a-z]\w+)/g,"{$1}");let t=Object.assign({},e.headers);let d;let r=omit(e,["method","baseUrl","url","headers","request","mediaType"]);const s=extractUrlVariableNames(a);a=parseUrl(a).expand(r);if(!/^http/.test(a)){a=e.baseUrl+a}const i=Object.keys(e).filter((e=>s.includes(e))).concat("baseUrl");const o=omit(r,i);const n=/application\/octet-stream/i.test(t.accept);if(!n){if(e.mediaType.format){t.accept=t.accept.split(/,/).map((p=>p.replace(/application\/vnd(\.\w+)(\.v3)?(\.\w+)?(\+json)?$/,`application/vnd$1$2.${e.mediaType.format}`))).join(",")}if(e.mediaType.previews.length){const p=t.accept.match(/[\w-]+(?=-preview)/g)||[];t.accept=p.concat(e.mediaType.previews).map((p=>{const a=e.mediaType.format?`.${e.mediaType.format}`:"+json";return`application/vnd.github.${p}-preview${a}`})).join(",")}}if(["GET","HEAD"].includes(p)){a=addQueryParameters(a,o)}else{if("data"in o){d=o.data}else{if(Object.keys(o).length){d=o}else{t["content-length"]=0}}}if(!t["content-type"]&&typeof d!=="undefined"){t["content-type"]="application/json; charset=utf-8"}if(["PATCH","PUT"].includes(p)&&typeof d==="undefined"){d=""}return Object.assign({method:p,url:a,headers:t},typeof d!=="undefined"?{body:d}:null,e.request?{request:e.request}:null)}function endpointWithDefaults(e,p,a){return parse(merge(e,p,a))}function withDefaults(e,p){const a=merge(e,p);const t=endpointWithDefaults.bind(null,a);return Object.assign(t,{DEFAULTS:a,defaults:withDefaults.bind(null,a),merge:merge.bind(null,a),parse:parse})}const s="6.0.12";const i=`octokit-endpoint.js/${s} ${d.getUserAgent()}`;const o={method:"GET",baseUrl:"https://api.github.com",headers:{accept:"application/vnd.github.v3+json","user-agent":i},mediaType:{format:"",previews:[]}};const n=withDefaults(null,o);p.endpoint=n},8467:(e,p,a)=>{Object.defineProperty(p,"__esModule",{value:true});var t=a(6234);var d=a(5030);const r="4.8.0";function _buildMessageForResponseErrors(e){return`Request failed due to following response errors:\n`+e.errors.map((e=>` - ${e.message}`)).join("\n")}class GraphqlResponseError extends Error{constructor(e,p,a){super(_buildMessageForResponseErrors(a));this.request=e;this.headers=p;this.response=a;this.name="GraphqlResponseError";this.errors=a.errors;this.data=a.data;if(Error.captureStackTrace){Error.captureStackTrace(this,this.constructor)}}}const s=["method","baseUrl","url","headers","request","query","mediaType"];const i=["query","method","url"];const o=/\/api\/v3\/?$/;function graphql(e,p,a){if(a){if(typeof p==="string"&&"query"in a){return Promise.reject(new Error(`[@octokit/graphql] "query" cannot be used as variable name`))}for(const e in a){if(!i.includes(e))continue;return Promise.reject(new Error(`[@octokit/graphql] "${e}" cannot be used as variable name`))}}const t=typeof p==="string"?Object.assign({query:p},a):p;const d=Object.keys(t).reduce(((e,p)=>{if(s.includes(p)){e[p]=t[p];return e}if(!e.variables){e.variables={}}e.variables[p]=t[p];return e}),{});const r=t.baseUrl||e.endpoint.DEFAULTS.baseUrl;if(o.test(r)){d.url=r.replace(o,"/api/graphql")}return e(d).then((e=>{if(e.data.errors){const p={};for(const a of Object.keys(e.headers)){p[a]=e.headers[a]}throw new GraphqlResponseError(d,p,e.data)}return e.data.data}))}function withDefaults(e,p){const a=e.defaults(p);const newApi=(e,p)=>graphql(a,e,p);return Object.assign(newApi,{defaults:withDefaults.bind(null,a),endpoint:t.request.endpoint})}const n=withDefaults(t.request,{headers:{"user-agent":`octokit-graphql.js/${r} ${d.getUserAgent()}`},method:"POST",url:"/graphql"});function withCustomRequest(e){return withDefaults(e,{method:"POST",url:"/graphql"})}p.GraphqlResponseError=GraphqlResponseError;p.graphql=n;p.withCustomRequest=withCustomRequest},4193:(e,p)=>{Object.defineProperty(p,"__esModule",{value:true});const a="2.21.3";function ownKeys(e,p){var a=Object.keys(e);if(Object.getOwnPropertySymbols){var t=Object.getOwnPropertySymbols(e);p&&(t=t.filter((function(p){return Object.getOwnPropertyDescriptor(e,p).enumerable}))),a.push.apply(a,t)}return a}function _objectSpread2(e){for(var p=1;p({async next(){if(!i)return{done:true};try{const e=await d({method:r,url:i,headers:s});const p=normalizePaginatedListResponse(e);i=((p.headers.link||"").match(/<([^>]+)>;\s*rel="next"/)||[])[1];return{value:p}}catch(e){if(e.status!==409)throw e;i="";return{value:{status:200,headers:{},data:[]}}}}})}}function paginate(e,p,a,t){if(typeof a==="function"){t=a;a=undefined}return gather(e,[],iterator(e,p,a)[Symbol.asyncIterator](),t)}function gather(e,p,a,t){return a.next().then((d=>{if(d.done){return p}let r=false;function done(){r=true}p=p.concat(t?t(d.value,done):d.value.data);if(r){return p}return gather(e,p,a,t)}))}const t=Object.assign(paginate,{iterator:iterator});const d=["GET /app/hook/deliveries","GET /app/installations","GET /applications/grants","GET /authorizations","GET /enterprises/{enterprise}/actions/permissions/organizations","GET /enterprises/{enterprise}/actions/runner-groups","GET /enterprises/{enterprise}/actions/runner-groups/{runner_group_id}/organizations","GET /enterprises/{enterprise}/actions/runner-groups/{runner_group_id}/runners","GET /enterprises/{enterprise}/actions/runners","GET /enterprises/{enterprise}/audit-log","GET /enterprises/{enterprise}/secret-scanning/alerts","GET /enterprises/{enterprise}/settings/billing/advanced-security","GET /events","GET /gists","GET /gists/public","GET /gists/starred","GET /gists/{gist_id}/comments","GET /gists/{gist_id}/commits","GET /gists/{gist_id}/forks","GET /installation/repositories","GET /issues","GET /licenses","GET /marketplace_listing/plans","GET /marketplace_listing/plans/{plan_id}/accounts","GET /marketplace_listing/stubbed/plans","GET /marketplace_listing/stubbed/plans/{plan_id}/accounts","GET /networks/{owner}/{repo}/events","GET /notifications","GET /organizations","GET /orgs/{org}/actions/cache/usage-by-repository","GET /orgs/{org}/actions/permissions/repositories","GET /orgs/{org}/actions/runner-groups","GET /orgs/{org}/actions/runner-groups/{runner_group_id}/repositories","GET /orgs/{org}/actions/runner-groups/{runner_group_id}/runners","GET /orgs/{org}/actions/runners","GET /orgs/{org}/actions/secrets","GET /orgs/{org}/actions/secrets/{secret_name}/repositories","GET /orgs/{org}/audit-log","GET /orgs/{org}/blocks","GET /orgs/{org}/code-scanning/alerts","GET /orgs/{org}/codespaces","GET /orgs/{org}/credential-authorizations","GET /orgs/{org}/dependabot/secrets","GET /orgs/{org}/dependabot/secrets/{secret_name}/repositories","GET /orgs/{org}/events","GET /orgs/{org}/external-groups","GET /orgs/{org}/failed_invitations","GET /orgs/{org}/hooks","GET /orgs/{org}/hooks/{hook_id}/deliveries","GET /orgs/{org}/installations","GET /orgs/{org}/invitations","GET /orgs/{org}/invitations/{invitation_id}/teams","GET /orgs/{org}/issues","GET /orgs/{org}/members","GET /orgs/{org}/migrations","GET /orgs/{org}/migrations/{migration_id}/repositories","GET /orgs/{org}/outside_collaborators","GET /orgs/{org}/packages","GET /orgs/{org}/packages/{package_type}/{package_name}/versions","GET /orgs/{org}/projects","GET /orgs/{org}/public_members","GET /orgs/{org}/repos","GET /orgs/{org}/secret-scanning/alerts","GET /orgs/{org}/settings/billing/advanced-security","GET /orgs/{org}/team-sync/groups","GET /orgs/{org}/teams","GET /orgs/{org}/teams/{team_slug}/discussions","GET /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/comments","GET /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/comments/{comment_number}/reactions","GET /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/reactions","GET /orgs/{org}/teams/{team_slug}/invitations","GET /orgs/{org}/teams/{team_slug}/members","GET /orgs/{org}/teams/{team_slug}/projects","GET /orgs/{org}/teams/{team_slug}/repos","GET /orgs/{org}/teams/{team_slug}/teams","GET /projects/columns/{column_id}/cards","GET /projects/{project_id}/collaborators","GET /projects/{project_id}/columns","GET /repos/{owner}/{repo}/actions/artifacts","GET /repos/{owner}/{repo}/actions/caches","GET /repos/{owner}/{repo}/actions/runners","GET /repos/{owner}/{repo}/actions/runs","GET /repos/{owner}/{repo}/actions/runs/{run_id}/artifacts","GET /repos/{owner}/{repo}/actions/runs/{run_id}/attempts/{attempt_number}/jobs","GET /repos/{owner}/{repo}/actions/runs/{run_id}/jobs","GET /repos/{owner}/{repo}/actions/secrets","GET /repos/{owner}/{repo}/actions/workflows","GET /repos/{owner}/{repo}/actions/workflows/{workflow_id}/runs","GET /repos/{owner}/{repo}/assignees","GET /repos/{owner}/{repo}/branches","GET /repos/{owner}/{repo}/check-runs/{check_run_id}/annotations","GET /repos/{owner}/{repo}/check-suites/{check_suite_id}/check-runs","GET /repos/{owner}/{repo}/code-scanning/alerts","GET /repos/{owner}/{repo}/code-scanning/alerts/{alert_number}/instances","GET /repos/{owner}/{repo}/code-scanning/analyses","GET /repos/{owner}/{repo}/codespaces","GET /repos/{owner}/{repo}/codespaces/devcontainers","GET /repos/{owner}/{repo}/codespaces/secrets","GET /repos/{owner}/{repo}/collaborators","GET /repos/{owner}/{repo}/comments","GET /repos/{owner}/{repo}/comments/{comment_id}/reactions","GET /repos/{owner}/{repo}/commits","GET /repos/{owner}/{repo}/commits/{commit_sha}/comments","GET /repos/{owner}/{repo}/commits/{commit_sha}/pulls","GET /repos/{owner}/{repo}/commits/{ref}/check-runs","GET /repos/{owner}/{repo}/commits/{ref}/check-suites","GET /repos/{owner}/{repo}/commits/{ref}/status","GET /repos/{owner}/{repo}/commits/{ref}/statuses","GET /repos/{owner}/{repo}/contributors","GET /repos/{owner}/{repo}/dependabot/secrets","GET /repos/{owner}/{repo}/deployments","GET /repos/{owner}/{repo}/deployments/{deployment_id}/statuses","GET /repos/{owner}/{repo}/environments","GET /repos/{owner}/{repo}/events","GET /repos/{owner}/{repo}/forks","GET /repos/{owner}/{repo}/git/matching-refs/{ref}","GET /repos/{owner}/{repo}/hooks","GET /repos/{owner}/{repo}/hooks/{hook_id}/deliveries","GET /repos/{owner}/{repo}/invitations","GET /repos/{owner}/{repo}/issues","GET /repos/{owner}/{repo}/issues/comments","GET /repos/{owner}/{repo}/issues/comments/{comment_id}/reactions","GET /repos/{owner}/{repo}/issues/events","GET /repos/{owner}/{repo}/issues/{issue_number}/comments","GET /repos/{owner}/{repo}/issues/{issue_number}/events","GET /repos/{owner}/{repo}/issues/{issue_number}/labels","GET /repos/{owner}/{repo}/issues/{issue_number}/reactions","GET /repos/{owner}/{repo}/issues/{issue_number}/timeline","GET /repos/{owner}/{repo}/keys","GET /repos/{owner}/{repo}/labels","GET /repos/{owner}/{repo}/milestones","GET /repos/{owner}/{repo}/milestones/{milestone_number}/labels","GET /repos/{owner}/{repo}/notifications","GET /repos/{owner}/{repo}/pages/builds","GET /repos/{owner}/{repo}/projects","GET /repos/{owner}/{repo}/pulls","GET /repos/{owner}/{repo}/pulls/comments","GET /repos/{owner}/{repo}/pulls/comments/{comment_id}/reactions","GET /repos/{owner}/{repo}/pulls/{pull_number}/comments","GET /repos/{owner}/{repo}/pulls/{pull_number}/commits","GET /repos/{owner}/{repo}/pulls/{pull_number}/files","GET /repos/{owner}/{repo}/pulls/{pull_number}/requested_reviewers","GET /repos/{owner}/{repo}/pulls/{pull_number}/reviews","GET /repos/{owner}/{repo}/pulls/{pull_number}/reviews/{review_id}/comments","GET /repos/{owner}/{repo}/releases","GET /repos/{owner}/{repo}/releases/{release_id}/assets","GET /repos/{owner}/{repo}/releases/{release_id}/reactions","GET /repos/{owner}/{repo}/secret-scanning/alerts","GET /repos/{owner}/{repo}/secret-scanning/alerts/{alert_number}/locations","GET /repos/{owner}/{repo}/stargazers","GET /repos/{owner}/{repo}/subscribers","GET /repos/{owner}/{repo}/tags","GET /repos/{owner}/{repo}/teams","GET /repos/{owner}/{repo}/topics","GET /repositories","GET /repositories/{repository_id}/environments/{environment_name}/secrets","GET /search/code","GET /search/commits","GET /search/issues","GET /search/labels","GET /search/repositories","GET /search/topics","GET /search/users","GET /teams/{team_id}/discussions","GET /teams/{team_id}/discussions/{discussion_number}/comments","GET /teams/{team_id}/discussions/{discussion_number}/comments/{comment_number}/reactions","GET /teams/{team_id}/discussions/{discussion_number}/reactions","GET /teams/{team_id}/invitations","GET /teams/{team_id}/members","GET /teams/{team_id}/projects","GET /teams/{team_id}/repos","GET /teams/{team_id}/teams","GET /user/blocks","GET /user/codespaces","GET /user/codespaces/secrets","GET /user/emails","GET /user/followers","GET /user/following","GET /user/gpg_keys","GET /user/installations","GET /user/installations/{installation_id}/repositories","GET /user/issues","GET /user/keys","GET /user/marketplace_purchases","GET /user/marketplace_purchases/stubbed","GET /user/memberships/orgs","GET /user/migrations","GET /user/migrations/{migration_id}/repositories","GET /user/orgs","GET /user/packages","GET /user/packages/{package_type}/{package_name}/versions","GET /user/public_emails","GET /user/repos","GET /user/repository_invitations","GET /user/starred","GET /user/subscriptions","GET /user/teams","GET /users","GET /users/{username}/events","GET /users/{username}/events/orgs/{org}","GET /users/{username}/events/public","GET /users/{username}/followers","GET /users/{username}/following","GET /users/{username}/gists","GET /users/{username}/gpg_keys","GET /users/{username}/keys","GET /users/{username}/orgs","GET /users/{username}/packages","GET /users/{username}/projects","GET /users/{username}/received_events","GET /users/{username}/received_events/public","GET /users/{username}/repos","GET /users/{username}/starred","GET /users/{username}/subscriptions"];function isPaginatingEndpoint(e){if(typeof e==="string"){return d.includes(e)}else{return false}}function paginateRest(e){return{paginate:Object.assign(paginate.bind(null,e),{iterator:iterator.bind(null,e)})}}paginateRest.VERSION=a;p.composePaginateRest=t;p.isPaginatingEndpoint=isPaginatingEndpoint;p.paginateRest=paginateRest;p.paginatingEndpoints=d},3044:(e,p)=>{Object.defineProperty(p,"__esModule",{value:true});function ownKeys(e,p){var a=Object.keys(e);if(Object.getOwnPropertySymbols){var t=Object.getOwnPropertySymbols(e);if(p){t=t.filter((function(p){return Object.getOwnPropertyDescriptor(e,p).enumerable}))}a.push.apply(a,t)}return a}function _objectSpread2(e){for(var p=1;p{Object.defineProperty(p,"__esModule",{value:true});function _interopDefault(e){return e&&typeof e==="object"&&"default"in e?e["default"]:e}var t=a(8932);var d=_interopDefault(a(1223));const r=d((e=>console.warn(e)));const s=d((e=>console.warn(e)));class RequestError extends Error{constructor(e,p,a){super(e);if(Error.captureStackTrace){Error.captureStackTrace(this,this.constructor)}this.name="HttpError";this.status=p;let d;if("headers"in a&&typeof a.headers!=="undefined"){d=a.headers}if("response"in a){this.response=a.response;d=a.response.headers}const i=Object.assign({},a.request);if(a.request.headers.authorization){i.headers=Object.assign({},a.request.headers,{authorization:a.request.headers.authorization.replace(/ .*$/," [REDACTED]")})}i.url=i.url.replace(/\bclient_secret=\w+/g,"client_secret=[REDACTED]").replace(/\baccess_token=\w+/g,"access_token=[REDACTED]");this.request=i;Object.defineProperty(this,"code",{get(){r(new t.Deprecation("[@octokit/request-error] `error.code` is deprecated, use `error.status`."));return p}});Object.defineProperty(this,"headers",{get(){s(new t.Deprecation("[@octokit/request-error] `error.headers` is deprecated, use `error.response.headers`."));return d||{}}})}}p.RequestError=RequestError},6234:(e,p,a)=>{Object.defineProperty(p,"__esModule",{value:true});function _interopDefault(e){return e&&typeof e==="object"&&"default"in e?e["default"]:e}var t=a(9440);var d=a(5030);var r=a(3287);var s=_interopDefault(a(467));var i=a(537);const o="5.6.3";function getBufferResponse(e){return e.arrayBuffer()}function fetchWrapper(e){const p=e.request&&e.request.log?e.request.log:console;if(r.isPlainObject(e.body)||Array.isArray(e.body)){e.body=JSON.stringify(e.body)}let a={};let t;let d;const o=e.request&&e.request.fetch||s;return o(e.url,Object.assign({method:e.method,body:e.body,headers:e.headers,redirect:e.redirect},e.request)).then((async r=>{d=r.url;t=r.status;for(const e of r.headers){a[e[0]]=e[1]}if("deprecation"in a){const t=a.link&&a.link.match(/<([^>]+)>; rel="deprecation"/);const d=t&&t.pop();p.warn(`[@octokit/request] "${e.method} ${e.url}" is deprecated. It is scheduled to be removed on ${a.sunset}${d?`. See ${d}`:""}`)}if(t===204||t===205){return}if(e.method==="HEAD"){if(t<400){return}throw new i.RequestError(r.statusText,t,{response:{url:d,status:t,headers:a,data:undefined},request:e})}if(t===304){throw new i.RequestError("Not modified",t,{response:{url:d,status:t,headers:a,data:await getResponseData(r)},request:e})}if(t>=400){const p=await getResponseData(r);const s=new i.RequestError(toErrorMessage(p),t,{response:{url:d,status:t,headers:a,data:p},request:e});throw s}return getResponseData(r)})).then((e=>({status:t,url:d,headers:a,data:e}))).catch((p=>{if(p instanceof i.RequestError)throw p;throw new i.RequestError(p.message,500,{request:e})}))}async function getResponseData(e){const p=e.headers.get("content-type");if(/application\/json/.test(p)){return e.json()}if(!p||/^text\/|charset=utf-8$/.test(p)){return e.text()}return getBufferResponse(e)}function toErrorMessage(e){if(typeof e==="string")return e;if("message"in e){if(Array.isArray(e.errors)){return`${e.message}: ${e.errors.map(JSON.stringify).join(", ")}`}return e.message}return`Unknown error: ${JSON.stringify(e)}`}function withDefaults(e,p){const a=e.defaults(p);const newApi=function(e,p){const t=a.merge(e,p);if(!t.request||!t.request.hook){return fetchWrapper(a.parse(t))}const request=(e,p)=>fetchWrapper(a.parse(a.merge(e,p)));Object.assign(request,{endpoint:a,defaults:withDefaults.bind(null,a)});return t.request.hook(request,t)};return Object.assign(newApi,{endpoint:a,defaults:withDefaults.bind(null,a)})}const n=withDefaults(t.endpoint,{headers:{"user-agent":`octokit-request.js/${o} ${d.getUserAgent()}`}});p.request=n},3682:(e,p,a)=>{var t=a(4670);var d=a(5549);var r=a(6819);var s=Function.bind;var i=s.bind(s);function bindApi(e,p,a){var t=i(r,null).apply(null,a?[p,a]:[p]);e.api={remove:t};e.remove=t;["before","error","after","wrap"].forEach((function(t){var r=a?[p,t,a]:[p,t];e[t]=e.api[t]=i(d,null).apply(null,r)}))}function HookSingular(){var e="h";var p={registry:{}};var a=t.bind(null,p,e);bindApi(a,p,e);return a}function HookCollection(){var e={registry:{}};var p=t.bind(null,e);bindApi(p,e);return p}var o=false;function Hook(){if(!o){console.warn('[before-after-hook]: "Hook()" repurposing warning, use "Hook.Collection()". Read more: https://git.io/upgrade-before-after-hook-to-1.4');o=true}return HookCollection()}Hook.Singular=HookSingular.bind();Hook.Collection=HookCollection.bind();e.exports=Hook;e.exports.Hook=Hook;e.exports.Singular=Hook.Singular;e.exports.Collection=Hook.Collection},5549:e=>{e.exports=addHook;function addHook(e,p,a,t){var d=t;if(!e.registry[a]){e.registry[a]=[]}if(p==="before"){t=function(e,p){return Promise.resolve().then(d.bind(null,p)).then(e.bind(null,p))}}if(p==="after"){t=function(e,p){var a;return Promise.resolve().then(e.bind(null,p)).then((function(e){a=e;return d(a,p)})).then((function(){return a}))}}if(p==="error"){t=function(e,p){return Promise.resolve().then(e.bind(null,p)).catch((function(e){return d(e,p)}))}}e.registry[a].push({hook:t,orig:d})}},4670:e=>{e.exports=register;function register(e,p,a,t){if(typeof a!=="function"){throw new Error("method for before hook must be a function")}if(!t){t={}}if(Array.isArray(p)){return p.reverse().reduce((function(p,a){return register.bind(null,e,a,p,t)}),a)()}return Promise.resolve().then((function(){if(!e.registry[p]){return a(t)}return e.registry[p].reduce((function(e,p){return p.hook.bind(null,e,t)}),a)()}))}},6819:e=>{e.exports=removeHook;function removeHook(e,p,a){if(!e.registry[p]){return}var t=e.registry[p].map((function(e){return e.orig})).indexOf(a);if(t===-1){return}e.registry[p].splice(t,1)}},8932:(e,p)=>{Object.defineProperty(p,"__esModule",{value:true});class Deprecation extends Error{constructor(e){super(e);if(Error.captureStackTrace){Error.captureStackTrace(this,this.constructor)}this.name="Deprecation"}}p.Deprecation=Deprecation},3287:(e,p)=>{Object.defineProperty(p,"__esModule",{value:true}); +/*! + * is-plain-object + * + * Copyright (c) 2014-2017, Jon Schlinkert. + * Released under the MIT License. + */function isObject(e){return Object.prototype.toString.call(e)==="[object Object]"}function isPlainObject(e){var p,a;if(isObject(e)===false)return false;p=e.constructor;if(p===undefined)return true;a=p.prototype;if(isObject(a)===false)return false;if(a.hasOwnProperty("isPrototypeOf")===false){return false}return true}p.isPlainObject=isPlainObject},7129:(e,p,a)=>{const t=a(665);const d=Symbol("max");const r=Symbol("length");const s=Symbol("lengthCalculator");const i=Symbol("allowStale");const o=Symbol("maxAge");const n=Symbol("dispose");const l=Symbol("noDisposeOnSet");const m=Symbol("lruList");const u=Symbol("cache");const c=Symbol("updateAgeOnGet");const naiveLength=()=>1;class LRUCache{constructor(e){if(typeof e==="number")e={max:e};if(!e)e={};if(e.max&&(typeof e.max!=="number"||e.max<0))throw new TypeError("max must be a non-negative number");const p=this[d]=e.max||Infinity;const a=e.length||naiveLength;this[s]=typeof a!=="function"?naiveLength:a;this[i]=e.stale||false;if(e.maxAge&&typeof e.maxAge!=="number")throw new TypeError("maxAge must be a number");this[o]=e.maxAge||0;this[n]=e.dispose;this[l]=e.noDisposeOnSet||false;this[c]=e.updateAgeOnGet||false;this.reset()}set max(e){if(typeof e!=="number"||e<0)throw new TypeError("max must be a non-negative number");this[d]=e||Infinity;trim(this)}get max(){return this[d]}set allowStale(e){this[i]=!!e}get allowStale(){return this[i]}set maxAge(e){if(typeof e!=="number")throw new TypeError("maxAge must be a non-negative number");this[o]=e;trim(this)}get maxAge(){return this[o]}set lengthCalculator(e){if(typeof e!=="function")e=naiveLength;if(e!==this[s]){this[s]=e;this[r]=0;this[m].forEach((e=>{e.length=this[s](e.value,e.key);this[r]+=e.length}))}trim(this)}get lengthCalculator(){return this[s]}get length(){return this[r]}get itemCount(){return this[m].length}rforEach(e,p){p=p||this;for(let a=this[m].tail;a!==null;){const t=a.prev;forEachStep(this,e,a,p);a=t}}forEach(e,p){p=p||this;for(let a=this[m].head;a!==null;){const t=a.next;forEachStep(this,e,a,p);a=t}}keys(){return this[m].toArray().map((e=>e.key))}values(){return this[m].toArray().map((e=>e.value))}reset(){if(this[n]&&this[m]&&this[m].length){this[m].forEach((e=>this[n](e.key,e.value)))}this[u]=new Map;this[m]=new t;this[r]=0}dump(){return this[m].map((e=>isStale(this,e)?false:{k:e.key,v:e.value,e:e.now+(e.maxAge||0)})).toArray().filter((e=>e))}dumpLru(){return this[m]}set(e,p,a){a=a||this[o];if(a&&typeof a!=="number")throw new TypeError("maxAge must be a number");const t=a?Date.now():0;const i=this[s](p,e);if(this[u].has(e)){if(i>this[d]){del(this,this[u].get(e));return false}const s=this[u].get(e);const o=s.value;if(this[n]){if(!this[l])this[n](e,o.value)}o.now=t;o.maxAge=a;o.value=p;this[r]+=i-o.length;o.length=i;this.get(e);trim(this);return true}const c=new Entry(e,p,i,t,a);if(c.length>this[d]){if(this[n])this[n](e,p);return false}this[r]+=c.length;this[m].unshift(c);this[u].set(e,this[m].head);trim(this);return true}has(e){if(!this[u].has(e))return false;const p=this[u].get(e).value;return!isStale(this,p)}get(e){return get(this,e,true)}peek(e){return get(this,e,false)}pop(){const e=this[m].tail;if(!e)return null;del(this,e);return e.value}del(e){del(this,this[u].get(e))}load(e){this.reset();const p=Date.now();for(let a=e.length-1;a>=0;a--){const t=e[a];const d=t.e||0;if(d===0)this.set(t.k,t.v);else{const e=d-p;if(e>0){this.set(t.k,t.v,e)}}}}prune(){this[u].forEach(((e,p)=>get(this,p,false)))}}const get=(e,p,a)=>{const t=e[u].get(p);if(t){const p=t.value;if(isStale(e,p)){del(e,t);if(!e[i])return undefined}else{if(a){if(e[c])t.value.now=Date.now();e[m].unshiftNode(t)}}return p.value}};const isStale=(e,p)=>{if(!p||!p.maxAge&&!e[o])return false;const a=Date.now()-p.now;return p.maxAge?a>p.maxAge:e[o]&&a>e[o]};const trim=e=>{if(e[r]>e[d]){for(let p=e[m].tail;e[r]>e[d]&&p!==null;){const a=p.prev;del(e,p);p=a}}};const del=(e,p)=>{if(p){const a=p.value;if(e[n])e[n](a.key,a.value);e[r]-=a.length;e[u].delete(a.key);e[m].removeNode(p)}};class Entry{constructor(e,p,a,t,d){this.key=e;this.value=p;this.length=a;this.now=t;this.maxAge=d||0}}const forEachStep=(e,p,a,t)=>{let d=a.value;if(isStale(e,d)){del(e,a);if(!e[i])d=undefined}if(d)p.call(t,d.value,d.key,e)};e.exports=LRUCache},467:(e,p,a)=>{Object.defineProperty(p,"__esModule",{value:true});function _interopDefault(e){return e&&typeof e==="object"&&"default"in e?e["default"]:e}var t=_interopDefault(a(2781));var d=_interopDefault(a(3685));var r=_interopDefault(a(7310));var s=_interopDefault(a(8665));var i=_interopDefault(a(5687));var o=_interopDefault(a(9796));const n=t.Readable;const l=Symbol("buffer");const m=Symbol("type");class Blob{constructor(){this[m]="";const e=arguments[0];const p=arguments[1];const a=[];let t=0;if(e){const p=e;const d=Number(p.length);for(let e=0;e1&&arguments[1]!==undefined?arguments[1]:{},d=a.size;let r=d===undefined?0:d;var s=a.timeout;let i=s===undefined?0:s;if(e==null){e=null}else if(isURLSearchParams(e)){e=Buffer.from(e.toString())}else if(isBlob(e));else if(Buffer.isBuffer(e));else if(Object.prototype.toString.call(e)==="[object ArrayBuffer]"){e=Buffer.from(e)}else if(ArrayBuffer.isView(e)){e=Buffer.from(e.buffer,e.byteOffset,e.byteLength)}else if(e instanceof t);else{e=Buffer.from(String(e))}this[c]={body:e,disturbed:false,error:null};this.size=r;this.timeout=i;if(e instanceof t){e.on("error",(function(e){const a=e.name==="AbortError"?e:new FetchError(`Invalid response body while trying to fetch ${p.url}: ${e.message}`,"system",e);p[c].error=a}))}}Body.prototype={get body(){return this[c].body},get bodyUsed(){return this[c].disturbed},arrayBuffer(){return consumeBody.call(this).then((function(e){return e.buffer.slice(e.byteOffset,e.byteOffset+e.byteLength)}))},blob(){let e=this.headers&&this.headers.get("content-type")||"";return consumeBody.call(this).then((function(p){return Object.assign(new Blob([],{type:e.toLowerCase()}),{[l]:p})}))},json(){var e=this;return consumeBody.call(this).then((function(p){try{return JSON.parse(p.toString())}catch(p){return Body.Promise.reject(new FetchError(`invalid json response body at ${e.url} reason: ${p.message}`,"invalid-json"))}}))},text(){return consumeBody.call(this).then((function(e){return e.toString()}))},buffer(){return consumeBody.call(this)},textConverted(){var e=this;return consumeBody.call(this).then((function(p){return convertBody(p,e.headers)}))}};Object.defineProperties(Body.prototype,{body:{enumerable:true},bodyUsed:{enumerable:true},arrayBuffer:{enumerable:true},blob:{enumerable:true},json:{enumerable:true},text:{enumerable:true}});Body.mixIn=function(e){for(const p of Object.getOwnPropertyNames(Body.prototype)){if(!(p in e)){const a=Object.getOwnPropertyDescriptor(Body.prototype,p);Object.defineProperty(e,p,a)}}};function consumeBody(){var e=this;if(this[c].disturbed){return Body.Promise.reject(new TypeError(`body used already for: ${this.url}`))}this[c].disturbed=true;if(this[c].error){return Body.Promise.reject(this[c].error)}let p=this.body;if(p===null){return Body.Promise.resolve(Buffer.alloc(0))}if(isBlob(p)){p=p.stream()}if(Buffer.isBuffer(p)){return Body.Promise.resolve(p)}if(!(p instanceof t)){return Body.Promise.resolve(Buffer.alloc(0))}let a=[];let d=0;let r=false;return new Body.Promise((function(t,s){let i;if(e.timeout){i=setTimeout((function(){r=true;s(new FetchError(`Response timeout while trying to fetch ${e.url} (over ${e.timeout}ms)`,"body-timeout"))}),e.timeout)}p.on("error",(function(p){if(p.name==="AbortError"){r=true;s(p)}else{s(new FetchError(`Invalid response body while trying to fetch ${e.url}: ${p.message}`,"system",p))}}));p.on("data",(function(p){if(r||p===null){return}if(e.size&&d+p.length>e.size){r=true;s(new FetchError(`content size at ${e.url} over limit: ${e.size}`,"max-size"));return}d+=p.length;a.push(p)}));p.on("end",(function(){if(r){return}clearTimeout(i);try{t(Buffer.concat(a,d))}catch(p){s(new FetchError(`Could not create Buffer from response body for ${e.url}: ${p.message}`,"system",p))}}))}))}function convertBody(e,p){if(typeof u!=="function"){throw new Error("The package `encoding` must be installed to use the textConverted() function")}const a=p.get("content-type");let t="utf-8";let d,r;if(a){d=/charset=([^;]*)/i.exec(a)}r=e.slice(0,1024).toString();if(!d&&r){d=/0&&arguments[0]!==undefined?arguments[0]:undefined;this[w]=Object.create(null);if(e instanceof Headers){const p=e.raw();const a=Object.keys(p);for(const e of a){for(const a of p[e]){this.append(e,a)}}return}if(e==null);else if(typeof e==="object"){const p=e[Symbol.iterator];if(p!=null){if(typeof p!=="function"){throw new TypeError("Header pairs must be iterable")}const a=[];for(const p of e){if(typeof p!=="object"||typeof p[Symbol.iterator]!=="function"){throw new TypeError("Each header pair must be iterable")}a.push(Array.from(p))}for(const e of a){if(e.length!==2){throw new TypeError("Each header pair must be a name/value tuple")}this.append(e[0],e[1])}}else{for(const p of Object.keys(e)){const a=e[p];this.append(p,a)}}}else{throw new TypeError("Provided initializer must be an object")}}get(e){e=`${e}`;validateName(e);const p=find(this[w],e);if(p===undefined){return null}return this[w][p].join(", ")}forEach(e){let p=arguments.length>1&&arguments[1]!==undefined?arguments[1]:undefined;let a=getHeaders(this);let t=0;while(t1&&arguments[1]!==undefined?arguments[1]:"key+value";const a=Object.keys(e[w]).sort();return a.map(p==="key"?function(e){return e.toLowerCase()}:p==="value"?function(p){return e[w][p].join(", ")}:function(p){return[p.toLowerCase(),e[w][p].join(", ")]})}const E=Symbol("internal");function createHeadersIterator(e,p){const a=Object.create(T);a[E]={target:e,kind:p,index:0};return a}const T=Object.setPrototypeOf({next(){if(!this||Object.getPrototypeOf(this)!==T){throw new TypeError("Value of `this` is not a HeadersIterator")}var e=this[E];const p=e.target,a=e.kind,t=e.index;const d=getHeaders(p,a);const r=d.length;if(t>=r){return{value:undefined,done:true}}this[E].index=t+1;return{value:d[t],done:false}}},Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]())));Object.defineProperty(T,Symbol.toStringTag,{value:"HeadersIterator",writable:false,enumerable:false,configurable:true});function exportNodeCompatibleHeaders(e){const p=Object.assign({__proto__:null},e[w]);const a=find(e[w],"Host");if(a!==undefined){p[a]=p[a][0]}return p}function createHeadersLenient(e){const p=new Headers;for(const a of Object.keys(e)){if(v.test(a)){continue}if(Array.isArray(e[a])){for(const t of e[a]){if(g.test(t)){continue}if(p[w][a]===undefined){p[w][a]=[t]}else{p[w][a].push(t)}}}else if(!g.test(e[a])){p[w][a]=[e[a]]}}return p}const _=Symbol("Response internals");const b=d.STATUS_CODES;class Response{constructor(){let e=arguments.length>0&&arguments[0]!==undefined?arguments[0]:null;let p=arguments.length>1&&arguments[1]!==undefined?arguments[1]:{};Body.call(this,e,p);const a=p.status||200;const t=new Headers(p.headers);if(e!=null&&!t.has("Content-Type")){const p=extractContentType(e);if(p){t.append("Content-Type",p)}}this[_]={url:p.url,status:a,statusText:p.statusText||b[a],headers:t,counter:p.counter}}get url(){return this[_].url||""}get status(){return this[_].status}get ok(){return this[_].status>=200&&this[_].status<300}get redirected(){return this[_].counter>0}get statusText(){return this[_].statusText}get headers(){return this[_].headers}clone(){return new Response(clone(this),{url:this.url,status:this.status,statusText:this.statusText,headers:this.headers,ok:this.ok,redirected:this.redirected})}}Body.mixIn(Response.prototype);Object.defineProperties(Response.prototype,{url:{enumerable:true},status:{enumerable:true},ok:{enumerable:true},redirected:{enumerable:true},statusText:{enumerable:true},headers:{enumerable:true},clone:{enumerable:true}});Object.defineProperty(Response.prototype,Symbol.toStringTag,{value:"Response",writable:false,enumerable:false,configurable:true});const y=Symbol("Request internals");const S=r.URL||s.URL;const A=r.parse;const N=r.format;function parseURL(e){if(/^[a-zA-Z][a-zA-Z\d+\-.]*:/.exec(e)){e=new S(e).toString()}return A(e)}const D="destroy"in t.Readable.prototype;function isRequest(e){return typeof e==="object"&&typeof e[y]==="object"}function isAbortSignal(e){const p=e&&typeof e==="object"&&Object.getPrototypeOf(e);return!!(p&&p.constructor.name==="AbortSignal")}class Request{constructor(e){let p=arguments.length>1&&arguments[1]!==undefined?arguments[1]:{};let a;if(!isRequest(e)){if(e&&e.href){a=parseURL(e.href)}else{a=parseURL(`${e}`)}e={}}else{a=parseURL(e.url)}let t=p.method||e.method||"GET";t=t.toUpperCase();if((p.body!=null||isRequest(e)&&e.body!==null)&&(t==="GET"||t==="HEAD")){throw new TypeError("Request with GET/HEAD method cannot have body")}let d=p.body!=null?p.body:isRequest(e)&&e.body!==null?clone(e):null;Body.call(this,d,{timeout:p.timeout||e.timeout||0,size:p.size||e.size||0});const r=new Headers(p.headers||e.headers||{});if(d!=null&&!r.has("Content-Type")){const e=extractContentType(d);if(e){r.append("Content-Type",e)}}let s=isRequest(e)?e.signal:null;if("signal"in p)s=p.signal;if(s!=null&&!isAbortSignal(s)){throw new TypeError("Expected signal to be an instanceof AbortSignal")}this[y]={method:t,redirect:p.redirect||e.redirect||"follow",headers:r,parsedURL:a,signal:s};this.follow=p.follow!==undefined?p.follow:e.follow!==undefined?e.follow:20;this.compress=p.compress!==undefined?p.compress:e.compress!==undefined?e.compress:true;this.counter=p.counter||e.counter||0;this.agent=p.agent||e.agent}get method(){return this[y].method}get url(){return N(this[y].parsedURL)}get headers(){return this[y].headers}get redirect(){return this[y].redirect}get signal(){return this[y].signal}clone(){return new Request(this)}}Body.mixIn(Request.prototype);Object.defineProperty(Request.prototype,Symbol.toStringTag,{value:"Request",writable:false,enumerable:false,configurable:true});Object.defineProperties(Request.prototype,{method:{enumerable:true},url:{enumerable:true},headers:{enumerable:true},redirect:{enumerable:true},clone:{enumerable:true},signal:{enumerable:true}});function getNodeRequestOptions(e){const p=e[y].parsedURL;const a=new Headers(e[y].headers);if(!a.has("Accept")){a.set("Accept","*/*")}if(!p.protocol||!p.hostname){throw new TypeError("Only absolute URLs are supported")}if(!/^https?:$/.test(p.protocol)){throw new TypeError("Only HTTP(S) protocols are supported")}if(e.signal&&e.body instanceof t.Readable&&!D){throw new Error("Cancellation of streamed requests with AbortSignal is not supported in node < 8")}let d=null;if(e.body==null&&/^(POST|PUT)$/i.test(e.method)){d="0"}if(e.body!=null){const p=getTotalBytes(e);if(typeof p==="number"){d=String(p)}}if(d){a.set("Content-Length",d)}if(!a.has("User-Agent")){a.set("User-Agent","node-fetch/1.0 (+https://github.com/bitinn/node-fetch)")}if(e.compress&&!a.has("Accept-Encoding")){a.set("Accept-Encoding","gzip,deflate")}let r=e.agent;if(typeof r==="function"){r=r(p)}if(!a.has("Connection")&&!r){a.set("Connection","close")}return Object.assign({},p,{method:e.method,headers:exportNodeCompatibleHeaders(a),agent:r})}function AbortError(e){Error.call(this,e);this.type="aborted";this.message=e;Error.captureStackTrace(this,this.constructor)}AbortError.prototype=Object.create(Error.prototype);AbortError.prototype.constructor=AbortError;AbortError.prototype.name="AbortError";const O=r.URL||s.URL;const P=t.PassThrough;const R=function isDomainOrSubdomain(e,p){const a=new O(p).hostname;const t=new O(e).hostname;return a===t||a[a.length-t.length-1]==="."&&a.endsWith(t)};function fetch(e,p){if(!fetch.Promise){throw new Error("native promise missing, set fetch.Promise to your favorite alternative")}Body.Promise=fetch.Promise;return new fetch.Promise((function(a,r){const s=new Request(e,p);const n=getNodeRequestOptions(s);const l=(n.protocol==="https:"?i:d).request;const m=s.signal;let u=null;const c=function abort(){let e=new AbortError("The user aborted a request.");r(e);if(s.body&&s.body instanceof t.Readable){s.body.destroy(e)}if(!u||!u.body)return;u.body.emit("error",e)};if(m&&m.aborted){c();return}const h=function abortAndFinalize(){c();finalize()};const v=l(n);let g;if(m){m.addEventListener("abort",h)}function finalize(){v.abort();if(m)m.removeEventListener("abort",h);clearTimeout(g)}if(s.timeout){v.once("socket",(function(e){g=setTimeout((function(){r(new FetchError(`network timeout at: ${s.url}`,"request-timeout"));finalize()}),s.timeout)}))}v.on("error",(function(e){r(new FetchError(`request to ${s.url} failed, reason: ${e.message}`,"system",e));finalize()}));v.on("response",(function(e){clearTimeout(g);const p=createHeadersLenient(e.headers);if(fetch.isRedirect(e.statusCode)){const t=p.get("Location");let d=null;try{d=t===null?null:new O(t,s.url).toString()}catch(e){if(s.redirect!=="manual"){r(new FetchError(`uri requested responds with an invalid redirect URL: ${t}`,"invalid-redirect"));finalize();return}}switch(s.redirect){case"error":r(new FetchError(`uri requested responds with a redirect, redirect mode is set to error: ${s.url}`,"no-redirect"));finalize();return;case"manual":if(d!==null){try{p.set("Location",d)}catch(e){r(e)}}break;case"follow":if(d===null){break}if(s.counter>=s.follow){r(new FetchError(`maximum redirect reached at: ${s.url}`,"max-redirect"));finalize();return}const t={headers:new Headers(s.headers),follow:s.follow,counter:s.counter+1,agent:s.agent,compress:s.compress,method:s.method,body:s.body,signal:s.signal,timeout:s.timeout,size:s.size};if(!R(s.url,d)){for(const e of["authorization","www-authenticate","cookie","cookie2"]){t.headers.delete(e)}}if(e.statusCode!==303&&s.body&&getTotalBytes(s)===null){r(new FetchError("Cannot follow redirect with body being a readable stream","unsupported-redirect"));finalize();return}if(e.statusCode===303||(e.statusCode===301||e.statusCode===302)&&s.method==="POST"){t.method="GET";t.body=undefined;t.headers.delete("content-length")}a(fetch(new Request(d,t)));finalize();return}}e.once("end",(function(){if(m)m.removeEventListener("abort",h)}));let t=e.pipe(new P);const d={url:s.url,status:e.statusCode,statusText:e.statusMessage,headers:p,size:s.size,timeout:s.timeout,counter:s.counter};const i=p.get("Content-Encoding");if(!s.compress||s.method==="HEAD"||i===null||e.statusCode===204||e.statusCode===304){u=new Response(t,d);a(u);return}const n={flush:o.Z_SYNC_FLUSH,finishFlush:o.Z_SYNC_FLUSH};if(i=="gzip"||i=="x-gzip"){t=t.pipe(o.createGunzip(n));u=new Response(t,d);a(u);return}if(i=="deflate"||i=="x-deflate"){const p=e.pipe(new P);p.once("data",(function(e){if((e[0]&15)===8){t=t.pipe(o.createInflate())}else{t=t.pipe(o.createInflateRaw())}u=new Response(t,d);a(u)}));return}if(i=="br"&&typeof o.createBrotliDecompress==="function"){t=t.pipe(o.createBrotliDecompress());u=new Response(t,d);a(u);return}u=new Response(t,d);a(u)}));writeToStream(v,s)}))}fetch.isRedirect=function(e){return e===301||e===302||e===303||e===307||e===308};fetch.Promise=global.Promise;e.exports=p=fetch;Object.defineProperty(p,"__esModule",{value:true});p["default"]=p;p.Headers=Headers;p.Request=Request;p.Response=Response;p.FetchError=FetchError},1223:(e,p,a)=>{var t=a(2940);e.exports=t(once);e.exports.strict=t(onceStrict);once.proto=once((function(){Object.defineProperty(Function.prototype,"once",{value:function(){return once(this)},configurable:true});Object.defineProperty(Function.prototype,"onceStrict",{value:function(){return onceStrict(this)},configurable:true})}));function once(e){var f=function(){if(f.called)return f.value;f.called=true;return f.value=e.apply(this,arguments)};f.called=false;return f}function onceStrict(e){var f=function(){if(f.called)throw new Error(f.onceError);f.called=true;return f.value=e.apply(this,arguments)};var p=e.name||"Function wrapped with `once`";f.onceError=p+" shouldn't be called more than once";f.called=false;return f}},1532:(e,p,a)=>{const t=Symbol("SemVer ANY");class Comparator{static get ANY(){return t}constructor(e,p){p=d(p);if(e instanceof Comparator){if(e.loose===!!p.loose){return e}else{e=e.value}}o("comparator",e,p);this.options=p;this.loose=!!p.loose;this.parse(e);if(this.semver===t){this.value=""}else{this.value=this.operator+this.semver.version}o("comp",this)}parse(e){const p=this.options.loose?r[s.COMPARATORLOOSE]:r[s.COMPARATOR];const a=e.match(p);if(!a){throw new TypeError(`Invalid comparator: ${e}`)}this.operator=a[1]!==undefined?a[1]:"";if(this.operator==="="){this.operator=""}if(!a[2]){this.semver=t}else{this.semver=new n(a[2],this.options.loose)}}toString(){return this.value}test(e){o("Comparator.test",e,this.options.loose);if(this.semver===t||e===t){return true}if(typeof e==="string"){try{e=new n(e,this.options)}catch(e){return false}}return i(e,this.operator,this.semver,this.options)}intersects(e,p){if(!(e instanceof Comparator)){throw new TypeError("a Comparator is required")}if(!p||typeof p!=="object"){p={loose:!!p,includePrerelease:false}}if(this.operator===""){if(this.value===""){return true}return new l(e.value,p).test(this.value)}else if(e.operator===""){if(e.value===""){return true}return new l(this.value,p).test(e.semver)}const a=(this.operator===">="||this.operator===">")&&(e.operator===">="||e.operator===">");const t=(this.operator==="<="||this.operator==="<")&&(e.operator==="<="||e.operator==="<");const d=this.semver.version===e.semver.version;const r=(this.operator===">="||this.operator==="<=")&&(e.operator===">="||e.operator==="<=");const s=i(this.semver,"<",e.semver,p)&&(this.operator===">="||this.operator===">")&&(e.operator==="<="||e.operator==="<");const o=i(this.semver,">",e.semver,p)&&(this.operator==="<="||this.operator==="<")&&(e.operator===">="||e.operator===">");return a||t||d&&r||s||o}}e.exports=Comparator;const d=a(785);const{re:r,t:s}=a(9523);const i=a(5098);const o=a(427);const n=a(8088);const l=a(9828)},9828:(e,p,a)=>{class Range{constructor(e,p){p=r(p);if(e instanceof Range){if(e.loose===!!p.loose&&e.includePrerelease===!!p.includePrerelease){return e}else{return new Range(e.raw,p)}}if(e instanceof s){this.raw=e.value;this.set=[[e]];this.format();return this}this.options=p;this.loose=!!p.loose;this.includePrerelease=!!p.includePrerelease;this.raw=e;this.set=e.split("||").map((e=>this.parseRange(e.trim()))).filter((e=>e.length));if(!this.set.length){throw new TypeError(`Invalid SemVer Range: ${e}`)}if(this.set.length>1){const e=this.set[0];this.set=this.set.filter((e=>!isNullSet(e[0])));if(this.set.length===0){this.set=[e]}else if(this.set.length>1){for(const e of this.set){if(e.length===1&&isAny(e[0])){this.set=[e];break}}}}this.format()}format(){this.range=this.set.map((e=>e.join(" ").trim())).join("||").trim();return this.range}toString(){return this.range}parseRange(e){e=e.trim();const p=Object.keys(this.options).join(",");const a=`parseRange:${p}:${e}`;const t=d.get(a);if(t){return t}const r=this.options.loose;const o=r?n[l.HYPHENRANGELOOSE]:n[l.HYPHENRANGE];e=e.replace(o,hyphenReplace(this.options.includePrerelease));i("hyphen replace",e);e=e.replace(n[l.COMPARATORTRIM],m);i("comparator trim",e);e=e.replace(n[l.TILDETRIM],u);e=e.replace(n[l.CARETTRIM],c);e=e.split(/\s+/).join(" ");let h=e.split(" ").map((e=>parseComparator(e,this.options))).join(" ").split(/\s+/).map((e=>replaceGTE0(e,this.options)));if(r){h=h.filter((e=>{i("loose invalid filter",e,this.options);return!!e.match(n[l.COMPARATORLOOSE])}))}i("range list",h);const v=new Map;const g=h.map((e=>new s(e,this.options)));for(const e of g){if(isNullSet(e)){return[e]}v.set(e.value,e)}if(v.size>1&&v.has("")){v.delete("")}const w=[...v.values()];d.set(a,w);return w}intersects(e,p){if(!(e instanceof Range)){throw new TypeError("a Range is required")}return this.set.some((a=>isSatisfiable(a,p)&&e.set.some((e=>isSatisfiable(e,p)&&a.every((a=>e.every((e=>a.intersects(e,p)))))))))}test(e){if(!e){return false}if(typeof e==="string"){try{e=new o(e,this.options)}catch(e){return false}}for(let p=0;pe.value==="<0.0.0-0";const isAny=e=>e.value==="";const isSatisfiable=(e,p)=>{let a=true;const t=e.slice();let d=t.pop();while(a&&t.length){a=t.every((e=>d.intersects(e,p)));d=t.pop()}return a};const parseComparator=(e,p)=>{i("comp",e,p);e=replaceCarets(e,p);i("caret",e);e=replaceTildes(e,p);i("tildes",e);e=replaceXRanges(e,p);i("xrange",e);e=replaceStars(e,p);i("stars",e);return e};const isX=e=>!e||e.toLowerCase()==="x"||e==="*";const replaceTildes=(e,p)=>e.trim().split(/\s+/).map((e=>replaceTilde(e,p))).join(" ");const replaceTilde=(e,p)=>{const a=p.loose?n[l.TILDELOOSE]:n[l.TILDE];return e.replace(a,((p,a,t,d,r)=>{i("tilde",e,p,a,t,d,r);let s;if(isX(a)){s=""}else if(isX(t)){s=`>=${a}.0.0 <${+a+1}.0.0-0`}else if(isX(d)){s=`>=${a}.${t}.0 <${a}.${+t+1}.0-0`}else if(r){i("replaceTilde pr",r);s=`>=${a}.${t}.${d}-${r} <${a}.${+t+1}.0-0`}else{s=`>=${a}.${t}.${d} <${a}.${+t+1}.0-0`}i("tilde return",s);return s}))};const replaceCarets=(e,p)=>e.trim().split(/\s+/).map((e=>replaceCaret(e,p))).join(" ");const replaceCaret=(e,p)=>{i("caret",e,p);const a=p.loose?n[l.CARETLOOSE]:n[l.CARET];const t=p.includePrerelease?"-0":"";return e.replace(a,((p,a,d,r,s)=>{i("caret",e,p,a,d,r,s);let o;if(isX(a)){o=""}else if(isX(d)){o=`>=${a}.0.0${t} <${+a+1}.0.0-0`}else if(isX(r)){if(a==="0"){o=`>=${a}.${d}.0${t} <${a}.${+d+1}.0-0`}else{o=`>=${a}.${d}.0${t} <${+a+1}.0.0-0`}}else if(s){i("replaceCaret pr",s);if(a==="0"){if(d==="0"){o=`>=${a}.${d}.${r}-${s} <${a}.${d}.${+r+1}-0`}else{o=`>=${a}.${d}.${r}-${s} <${a}.${+d+1}.0-0`}}else{o=`>=${a}.${d}.${r}-${s} <${+a+1}.0.0-0`}}else{i("no pr");if(a==="0"){if(d==="0"){o=`>=${a}.${d}.${r}${t} <${a}.${d}.${+r+1}-0`}else{o=`>=${a}.${d}.${r}${t} <${a}.${+d+1}.0-0`}}else{o=`>=${a}.${d}.${r} <${+a+1}.0.0-0`}}i("caret return",o);return o}))};const replaceXRanges=(e,p)=>{i("replaceXRanges",e,p);return e.split(/\s+/).map((e=>replaceXRange(e,p))).join(" ")};const replaceXRange=(e,p)=>{e=e.trim();const a=p.loose?n[l.XRANGELOOSE]:n[l.XRANGE];return e.replace(a,((a,t,d,r,s,o)=>{i("xRange",e,a,t,d,r,s,o);const n=isX(d);const l=n||isX(r);const m=l||isX(s);const u=m;if(t==="="&&u){t=""}o=p.includePrerelease?"-0":"";if(n){if(t===">"||t==="<"){a="<0.0.0-0"}else{a="*"}}else if(t&&u){if(l){r=0}s=0;if(t===">"){t=">=";if(l){d=+d+1;r=0;s=0}else{r=+r+1;s=0}}else if(t==="<="){t="<";if(l){d=+d+1}else{r=+r+1}}if(t==="<"){o="-0"}a=`${t+d}.${r}.${s}${o}`}else if(l){a=`>=${d}.0.0${o} <${+d+1}.0.0-0`}else if(m){a=`>=${d}.${r}.0${o} <${d}.${+r+1}.0-0`}i("xRange return",a);return a}))};const replaceStars=(e,p)=>{i("replaceStars",e,p);return e.trim().replace(n[l.STAR],"")};const replaceGTE0=(e,p)=>{i("replaceGTE0",e,p);return e.trim().replace(n[p.includePrerelease?l.GTE0PRE:l.GTE0],"")};const hyphenReplace=e=>(p,a,t,d,r,s,i,o,n,l,m,u,c)=>{if(isX(t)){a=""}else if(isX(d)){a=`>=${t}.0.0${e?"-0":""}`}else if(isX(r)){a=`>=${t}.${d}.0${e?"-0":""}`}else if(s){a=`>=${a}`}else{a=`>=${a}${e?"-0":""}`}if(isX(n)){o=""}else if(isX(l)){o=`<${+n+1}.0.0-0`}else if(isX(m)){o=`<${n}.${+l+1}.0-0`}else if(u){o=`<=${n}.${l}.${m}-${u}`}else if(e){o=`<${n}.${l}.${+m+1}-0`}else{o=`<=${o}`}return`${a} ${o}`.trim()};const testSet=(e,p,a)=>{for(let a=0;a0){const t=e[a].semver;if(t.major===p.major&&t.minor===p.minor&&t.patch===p.patch){return true}}}return false}return true}},8088:(e,p,a)=>{const t=a(427);const{MAX_LENGTH:d,MAX_SAFE_INTEGER:r}=a(2293);const{re:s,t:i}=a(9523);const o=a(785);const{compareIdentifiers:n}=a(2463);class SemVer{constructor(e,p){p=o(p);if(e instanceof SemVer){if(e.loose===!!p.loose&&e.includePrerelease===!!p.includePrerelease){return e}else{e=e.version}}else if(typeof e!=="string"){throw new TypeError(`Invalid Version: ${e}`)}if(e.length>d){throw new TypeError(`version is longer than ${d} characters`)}t("SemVer",e,p);this.options=p;this.loose=!!p.loose;this.includePrerelease=!!p.includePrerelease;const a=e.trim().match(p.loose?s[i.LOOSE]:s[i.FULL]);if(!a){throw new TypeError(`Invalid Version: ${e}`)}this.raw=e;this.major=+a[1];this.minor=+a[2];this.patch=+a[3];if(this.major>r||this.major<0){throw new TypeError("Invalid major version")}if(this.minor>r||this.minor<0){throw new TypeError("Invalid minor version")}if(this.patch>r||this.patch<0){throw new TypeError("Invalid patch version")}if(!a[4]){this.prerelease=[]}else{this.prerelease=a[4].split(".").map((e=>{if(/^[0-9]+$/.test(e)){const p=+e;if(p>=0&&p=0){if(typeof this.prerelease[e]==="number"){this.prerelease[e]++;e=-2}}if(e===-1){this.prerelease.push(0)}}if(p){if(n(this.prerelease[0],p)===0){if(isNaN(this.prerelease[1])){this.prerelease=[p,0]}}else{this.prerelease=[p,0]}}break;default:throw new Error(`invalid increment argument: ${e}`)}this.format();this.raw=this.version;return this}}e.exports=SemVer},8848:(e,p,a)=>{const t=a(5925);const clean=(e,p)=>{const a=t(e.trim().replace(/^[=v]+/,""),p);return a?a.version:null};e.exports=clean},5098:(e,p,a)=>{const t=a(1898);const d=a(6017);const r=a(4123);const s=a(5522);const i=a(194);const o=a(7520);const cmp=(e,p,a,n)=>{switch(p){case"===":if(typeof e==="object"){e=e.version}if(typeof a==="object"){a=a.version}return e===a;case"!==":if(typeof e==="object"){e=e.version}if(typeof a==="object"){a=a.version}return e!==a;case"":case"=":case"==":return t(e,a,n);case"!=":return d(e,a,n);case">":return r(e,a,n);case">=":return s(e,a,n);case"<":return i(e,a,n);case"<=":return o(e,a,n);default:throw new TypeError(`Invalid operator: ${p}`)}};e.exports=cmp},3466:(e,p,a)=>{const t=a(8088);const d=a(5925);const{re:r,t:s}=a(9523);const coerce=(e,p)=>{if(e instanceof t){return e}if(typeof e==="number"){e=String(e)}if(typeof e!=="string"){return null}p=p||{};let a=null;if(!p.rtl){a=e.match(r[s.COERCE])}else{let p;while((p=r[s.COERCERTL].exec(e))&&(!a||a.index+a[0].length!==e.length)){if(!a||p.index+p[0].length!==a.index+a[0].length){a=p}r[s.COERCERTL].lastIndex=p.index+p[1].length+p[2].length}r[s.COERCERTL].lastIndex=-1}if(a===null){return null}return d(`${a[2]}.${a[3]||"0"}.${a[4]||"0"}`,p)};e.exports=coerce},2156:(e,p,a)=>{const t=a(8088);const compareBuild=(e,p,a)=>{const d=new t(e,a);const r=new t(p,a);return d.compare(r)||d.compareBuild(r)};e.exports=compareBuild},2804:(e,p,a)=>{const t=a(4309);const compareLoose=(e,p)=>t(e,p,true);e.exports=compareLoose},4309:(e,p,a)=>{const t=a(8088);const compare=(e,p,a)=>new t(e,a).compare(new t(p,a));e.exports=compare},4297:(e,p,a)=>{const t=a(5925);const d=a(1898);const diff=(e,p)=>{if(d(e,p)){return null}else{const a=t(e);const d=t(p);const r=a.prerelease.length||d.prerelease.length;const s=r?"pre":"";const i=r?"prerelease":"";for(const e in a){if(e==="major"||e==="minor"||e==="patch"){if(a[e]!==d[e]){return s+e}}}return i}};e.exports=diff},1898:(e,p,a)=>{const t=a(4309);const eq=(e,p,a)=>t(e,p,a)===0;e.exports=eq},4123:(e,p,a)=>{const t=a(4309);const gt=(e,p,a)=>t(e,p,a)>0;e.exports=gt},5522:(e,p,a)=>{const t=a(4309);const gte=(e,p,a)=>t(e,p,a)>=0;e.exports=gte},900:(e,p,a)=>{const t=a(8088);const inc=(e,p,a,d)=>{if(typeof a==="string"){d=a;a=undefined}try{return new t(e instanceof t?e.version:e,a).inc(p,d).version}catch(e){return null}};e.exports=inc},194:(e,p,a)=>{const t=a(4309);const lt=(e,p,a)=>t(e,p,a)<0;e.exports=lt},7520:(e,p,a)=>{const t=a(4309);const lte=(e,p,a)=>t(e,p,a)<=0;e.exports=lte},6688:(e,p,a)=>{const t=a(8088);const major=(e,p)=>new t(e,p).major;e.exports=major},8447:(e,p,a)=>{const t=a(8088);const minor=(e,p)=>new t(e,p).minor;e.exports=minor},6017:(e,p,a)=>{const t=a(4309);const neq=(e,p,a)=>t(e,p,a)!==0;e.exports=neq},5925:(e,p,a)=>{const{MAX_LENGTH:t}=a(2293);const{re:d,t:r}=a(9523);const s=a(8088);const i=a(785);const parse=(e,p)=>{p=i(p);if(e instanceof s){return e}if(typeof e!=="string"){return null}if(e.length>t){return null}const a=p.loose?d[r.LOOSE]:d[r.FULL];if(!a.test(e)){return null}try{return new s(e,p)}catch(e){return null}};e.exports=parse},2866:(e,p,a)=>{const t=a(8088);const patch=(e,p)=>new t(e,p).patch;e.exports=patch},4016:(e,p,a)=>{const t=a(5925);const prerelease=(e,p)=>{const a=t(e,p);return a&&a.prerelease.length?a.prerelease:null};e.exports=prerelease},6417:(e,p,a)=>{const t=a(4309);const rcompare=(e,p,a)=>t(p,e,a);e.exports=rcompare},8701:(e,p,a)=>{const t=a(2156);const rsort=(e,p)=>e.sort(((e,a)=>t(a,e,p)));e.exports=rsort},6055:(e,p,a)=>{const t=a(9828);const satisfies=(e,p,a)=>{try{p=new t(p,a)}catch(e){return false}return p.test(e)};e.exports=satisfies},1426:(e,p,a)=>{const t=a(2156);const sort=(e,p)=>e.sort(((e,a)=>t(e,a,p)));e.exports=sort},9601:(e,p,a)=>{const t=a(5925);const valid=(e,p)=>{const a=t(e,p);return a?a.version:null};e.exports=valid},1383:(e,p,a)=>{const t=a(9523);e.exports={re:t.re,src:t.src,tokens:t.t,SEMVER_SPEC_VERSION:a(2293).SEMVER_SPEC_VERSION,SemVer:a(8088),compareIdentifiers:a(2463).compareIdentifiers,rcompareIdentifiers:a(2463).rcompareIdentifiers,parse:a(5925),valid:a(9601),clean:a(8848),inc:a(900),diff:a(4297),major:a(6688),minor:a(8447),patch:a(2866),prerelease:a(4016),compare:a(4309),rcompare:a(6417),compareLoose:a(2804),compareBuild:a(2156),sort:a(1426),rsort:a(8701),gt:a(4123),lt:a(194),eq:a(1898),neq:a(6017),gte:a(5522),lte:a(7520),cmp:a(5098),coerce:a(3466),Comparator:a(1532),Range:a(9828),satisfies:a(6055),toComparators:a(2706),maxSatisfying:a(579),minSatisfying:a(832),minVersion:a(4179),validRange:a(2098),outside:a(420),gtr:a(9380),ltr:a(3323),intersects:a(7008),simplifyRange:a(5297),subset:a(7863)}},2293:e=>{const p="2.0.0";const a=256;const t=Number.MAX_SAFE_INTEGER||9007199254740991;const d=16;e.exports={SEMVER_SPEC_VERSION:p,MAX_LENGTH:a,MAX_SAFE_INTEGER:t,MAX_SAFE_COMPONENT_LENGTH:d}},427:e=>{const p=typeof process==="object"&&process.env&&process.env.NODE_DEBUG&&/\bsemver\b/i.test(process.env.NODE_DEBUG)?(...e)=>console.error("SEMVER",...e):()=>{};e.exports=p},2463:e=>{const p=/^[0-9]+$/;const compareIdentifiers=(e,a)=>{const t=p.test(e);const d=p.test(a);if(t&&d){e=+e;a=+a}return e===a?0:t&&!d?-1:d&&!t?1:ecompareIdentifiers(p,e);e.exports={compareIdentifiers:compareIdentifiers,rcompareIdentifiers:rcompareIdentifiers}},785:e=>{const p=["includePrerelease","loose","rtl"];const parseOptions=e=>!e?{}:typeof e!=="object"?{loose:true}:p.filter((p=>e[p])).reduce(((e,p)=>{e[p]=true;return e}),{});e.exports=parseOptions},9523:(e,p,a)=>{const{MAX_SAFE_COMPONENT_LENGTH:t}=a(2293);const d=a(427);p=e.exports={};const r=p.re=[];const s=p.src=[];const i=p.t={};let o=0;const createToken=(e,p,a)=>{const t=o++;d(e,t,p);i[e]=t;s[t]=p;r[t]=new RegExp(p,a?"g":undefined)};createToken("NUMERICIDENTIFIER","0|[1-9]\\d*");createToken("NUMERICIDENTIFIERLOOSE","[0-9]+");createToken("NONNUMERICIDENTIFIER","\\d*[a-zA-Z-][a-zA-Z0-9-]*");createToken("MAINVERSION",`(${s[i.NUMERICIDENTIFIER]})\\.`+`(${s[i.NUMERICIDENTIFIER]})\\.`+`(${s[i.NUMERICIDENTIFIER]})`);createToken("MAINVERSIONLOOSE",`(${s[i.NUMERICIDENTIFIERLOOSE]})\\.`+`(${s[i.NUMERICIDENTIFIERLOOSE]})\\.`+`(${s[i.NUMERICIDENTIFIERLOOSE]})`);createToken("PRERELEASEIDENTIFIER",`(?:${s[i.NUMERICIDENTIFIER]}|${s[i.NONNUMERICIDENTIFIER]})`);createToken("PRERELEASEIDENTIFIERLOOSE",`(?:${s[i.NUMERICIDENTIFIERLOOSE]}|${s[i.NONNUMERICIDENTIFIER]})`);createToken("PRERELEASE",`(?:-(${s[i.PRERELEASEIDENTIFIER]}(?:\\.${s[i.PRERELEASEIDENTIFIER]})*))`);createToken("PRERELEASELOOSE",`(?:-?(${s[i.PRERELEASEIDENTIFIERLOOSE]}(?:\\.${s[i.PRERELEASEIDENTIFIERLOOSE]})*))`);createToken("BUILDIDENTIFIER","[0-9A-Za-z-]+");createToken("BUILD",`(?:\\+(${s[i.BUILDIDENTIFIER]}(?:\\.${s[i.BUILDIDENTIFIER]})*))`);createToken("FULLPLAIN",`v?${s[i.MAINVERSION]}${s[i.PRERELEASE]}?${s[i.BUILD]}?`);createToken("FULL",`^${s[i.FULLPLAIN]}$`);createToken("LOOSEPLAIN",`[v=\\s]*${s[i.MAINVERSIONLOOSE]}${s[i.PRERELEASELOOSE]}?${s[i.BUILD]}?`);createToken("LOOSE",`^${s[i.LOOSEPLAIN]}$`);createToken("GTLT","((?:<|>)?=?)");createToken("XRANGEIDENTIFIERLOOSE",`${s[i.NUMERICIDENTIFIERLOOSE]}|x|X|\\*`);createToken("XRANGEIDENTIFIER",`${s[i.NUMERICIDENTIFIER]}|x|X|\\*`);createToken("XRANGEPLAIN",`[v=\\s]*(${s[i.XRANGEIDENTIFIER]})`+`(?:\\.(${s[i.XRANGEIDENTIFIER]})`+`(?:\\.(${s[i.XRANGEIDENTIFIER]})`+`(?:${s[i.PRERELEASE]})?${s[i.BUILD]}?`+`)?)?`);createToken("XRANGEPLAINLOOSE",`[v=\\s]*(${s[i.XRANGEIDENTIFIERLOOSE]})`+`(?:\\.(${s[i.XRANGEIDENTIFIERLOOSE]})`+`(?:\\.(${s[i.XRANGEIDENTIFIERLOOSE]})`+`(?:${s[i.PRERELEASELOOSE]})?${s[i.BUILD]}?`+`)?)?`);createToken("XRANGE",`^${s[i.GTLT]}\\s*${s[i.XRANGEPLAIN]}$`);createToken("XRANGELOOSE",`^${s[i.GTLT]}\\s*${s[i.XRANGEPLAINLOOSE]}$`);createToken("COERCE",`${"(^|[^\\d])"+"(\\d{1,"}${t}})`+`(?:\\.(\\d{1,${t}}))?`+`(?:\\.(\\d{1,${t}}))?`+`(?:$|[^\\d])`);createToken("COERCERTL",s[i.COERCE],true);createToken("LONETILDE","(?:~>?)");createToken("TILDETRIM",`(\\s*)${s[i.LONETILDE]}\\s+`,true);p.tildeTrimReplace="$1~";createToken("TILDE",`^${s[i.LONETILDE]}${s[i.XRANGEPLAIN]}$`);createToken("TILDELOOSE",`^${s[i.LONETILDE]}${s[i.XRANGEPLAINLOOSE]}$`);createToken("LONECARET","(?:\\^)");createToken("CARETTRIM",`(\\s*)${s[i.LONECARET]}\\s+`,true);p.caretTrimReplace="$1^";createToken("CARET",`^${s[i.LONECARET]}${s[i.XRANGEPLAIN]}$`);createToken("CARETLOOSE",`^${s[i.LONECARET]}${s[i.XRANGEPLAINLOOSE]}$`);createToken("COMPARATORLOOSE",`^${s[i.GTLT]}\\s*(${s[i.LOOSEPLAIN]})$|^$`);createToken("COMPARATOR",`^${s[i.GTLT]}\\s*(${s[i.FULLPLAIN]})$|^$`);createToken("COMPARATORTRIM",`(\\s*)${s[i.GTLT]}\\s*(${s[i.LOOSEPLAIN]}|${s[i.XRANGEPLAIN]})`,true);p.comparatorTrimReplace="$1$2$3";createToken("HYPHENRANGE",`^\\s*(${s[i.XRANGEPLAIN]})`+`\\s+-\\s+`+`(${s[i.XRANGEPLAIN]})`+`\\s*$`);createToken("HYPHENRANGELOOSE",`^\\s*(${s[i.XRANGEPLAINLOOSE]})`+`\\s+-\\s+`+`(${s[i.XRANGEPLAINLOOSE]})`+`\\s*$`);createToken("STAR","(<|>)?=?\\s*\\*");createToken("GTE0","^\\s*>=\\s*0\\.0\\.0\\s*$");createToken("GTE0PRE","^\\s*>=\\s*0\\.0\\.0-0\\s*$")},9380:(e,p,a)=>{const t=a(420);const gtr=(e,p,a)=>t(e,p,">",a);e.exports=gtr},7008:(e,p,a)=>{const t=a(9828);const intersects=(e,p,a)=>{e=new t(e,a);p=new t(p,a);return e.intersects(p)};e.exports=intersects},3323:(e,p,a)=>{const t=a(420);const ltr=(e,p,a)=>t(e,p,"<",a);e.exports=ltr},579:(e,p,a)=>{const t=a(8088);const d=a(9828);const maxSatisfying=(e,p,a)=>{let r=null;let s=null;let i=null;try{i=new d(p,a)}catch(e){return null}e.forEach((e=>{if(i.test(e)){if(!r||s.compare(e)===-1){r=e;s=new t(r,a)}}}));return r};e.exports=maxSatisfying},832:(e,p,a)=>{const t=a(8088);const d=a(9828);const minSatisfying=(e,p,a)=>{let r=null;let s=null;let i=null;try{i=new d(p,a)}catch(e){return null}e.forEach((e=>{if(i.test(e)){if(!r||s.compare(e)===1){r=e;s=new t(r,a)}}}));return r};e.exports=minSatisfying},4179:(e,p,a)=>{const t=a(8088);const d=a(9828);const r=a(4123);const minVersion=(e,p)=>{e=new d(e,p);let a=new t("0.0.0");if(e.test(a)){return a}a=new t("0.0.0-0");if(e.test(a)){return a}a=null;for(let p=0;p{const p=new t(e.semver.version);switch(e.operator){case">":if(p.prerelease.length===0){p.patch++}else{p.prerelease.push(0)}p.raw=p.format();case"":case">=":if(!s||r(p,s)){s=p}break;case"<":case"<=":break;default:throw new Error(`Unexpected operation: ${e.operator}`)}}));if(s&&(!a||r(a,s))){a=s}}if(a&&e.test(a)){return a}return null};e.exports=minVersion},420:(e,p,a)=>{const t=a(8088);const d=a(1532);const{ANY:r}=d;const s=a(9828);const i=a(6055);const o=a(4123);const n=a(194);const l=a(7520);const m=a(5522);const outside=(e,p,a,u)=>{e=new t(e,u);p=new s(p,u);let c,h,v,g,w;switch(a){case">":c=o;h=l;v=n;g=">";w=">=";break;case"<":c=n;h=m;v=o;g="<";w="<=";break;default:throw new TypeError('Must provide a hilo val of "<" or ">"')}if(i(e,p,u)){return false}for(let a=0;a{if(e.semver===r){e=new d(">=0.0.0")}s=s||e;i=i||e;if(c(e.semver,s.semver,u)){s=e}else if(v(e.semver,i.semver,u)){i=e}}));if(s.operator===g||s.operator===w){return false}if((!i.operator||i.operator===g)&&h(e,i.semver)){return false}else if(i.operator===w&&v(e,i.semver)){return false}}return true};e.exports=outside},5297:(e,p,a)=>{const t=a(6055);const d=a(4309);e.exports=(e,p,a)=>{const r=[];let s=null;let i=null;const o=e.sort(((e,p)=>d(e,p,a)));for(const e of o){const d=t(e,p,a);if(d){i=e;if(!s){s=e}}else{if(i){r.push([s,i])}i=null;s=null}}if(s){r.push([s,null])}const n=[];for(const[e,p]of r){if(e===p){n.push(e)}else if(!p&&e===o[0]){n.push("*")}else if(!p){n.push(`>=${e}`)}else if(e===o[0]){n.push(`<=${p}`)}else{n.push(`${e} - ${p}`)}}const l=n.join(" || ");const m=typeof p.raw==="string"?p.raw:String(p);return l.length{const t=a(9828);const d=a(1532);const{ANY:r}=d;const s=a(6055);const i=a(4309);const subset=(e,p,a={})=>{if(e===p){return true}e=new t(e,a);p=new t(p,a);let d=false;e:for(const t of e.set){for(const e of p.set){const p=simpleSubset(t,e,a);d=d||p!==null;if(p){continue e}}if(d){return false}}return true};const simpleSubset=(e,p,a)=>{if(e===p){return true}if(e.length===1&&e[0].semver===r){if(p.length===1&&p[0].semver===r){return true}else if(a.includePrerelease){e=[new d(">=0.0.0-0")]}else{e=[new d(">=0.0.0")]}}if(p.length===1&&p[0].semver===r){if(a.includePrerelease){return true}else{p=[new d(">=0.0.0")]}}const t=new Set;let o,n;for(const p of e){if(p.operator===">"||p.operator===">="){o=higherGT(o,p,a)}else if(p.operator==="<"||p.operator==="<="){n=lowerLT(n,p,a)}else{t.add(p.semver)}}if(t.size>1){return null}let l;if(o&&n){l=i(o.semver,n.semver,a);if(l>0){return null}else if(l===0&&(o.operator!==">="||n.operator!=="<=")){return null}}for(const e of t){if(o&&!s(e,String(o),a)){return null}if(n&&!s(e,String(n),a)){return null}for(const t of p){if(!s(e,String(t),a)){return false}}return true}let m,u;let c,h;let v=n&&!a.includePrerelease&&n.semver.prerelease.length?n.semver:false;let g=o&&!a.includePrerelease&&o.semver.prerelease.length?o.semver:false;if(v&&v.prerelease.length===1&&n.operator==="<"&&v.prerelease[0]===0){v=false}for(const e of p){h=h||e.operator===">"||e.operator===">=";c=c||e.operator==="<"||e.operator==="<=";if(o){if(g){if(e.semver.prerelease&&e.semver.prerelease.length&&e.semver.major===g.major&&e.semver.minor===g.minor&&e.semver.patch===g.patch){g=false}}if(e.operator===">"||e.operator===">="){m=higherGT(o,e,a);if(m===e&&m!==o){return false}}else if(o.operator===">="&&!s(o.semver,String(e),a)){return false}}if(n){if(v){if(e.semver.prerelease&&e.semver.prerelease.length&&e.semver.major===v.major&&e.semver.minor===v.minor&&e.semver.patch===v.patch){v=false}}if(e.operator==="<"||e.operator==="<="){u=lowerLT(n,e,a);if(u===e&&u!==n){return false}}else if(n.operator==="<="&&!s(n.semver,String(e),a)){return false}}if(!e.operator&&(n||o)&&l!==0){return false}}if(o&&c&&!n&&l!==0){return false}if(n&&h&&!o&&l!==0){return false}if(g||v){return false}return true};const higherGT=(e,p,a)=>{if(!e){return p}const t=i(e.semver,p.semver,a);return t>0?e:t<0?p:p.operator===">"&&e.operator===">="?p:e};const lowerLT=(e,p,a)=>{if(!e){return p}const t=i(e.semver,p.semver,a);return t<0?e:t>0?p:p.operator==="<"&&e.operator==="<="?p:e};e.exports=subset},2706:(e,p,a)=>{const t=a(9828);const toComparators=(e,p)=>new t(e,p).set.map((e=>e.map((e=>e.value)).join(" ").trim().split(" ")));e.exports=toComparators},2098:(e,p,a)=>{const t=a(9828);const validRange=(e,p)=>{try{return new t(e,p).range||"*"}catch(e){return null}};e.exports=validRange},4256:(e,p,a)=>{var t=a(5477);var d=a(2020);var r={TRANSITIONAL:0,NONTRANSITIONAL:1};function normalize(e){return e.split("\0").map((function(e){return e.normalize("NFC")})).join("\0")}function findStatus(e){var p=0;var a=d.length-1;while(p<=a){var t=Math.floor((p+a)/2);var r=d[t];if(r[0][0]<=e&&r[0][1]>=e){return r}else if(r[0][0]>e){a=t-1}else{p=t+1}}return null}var s=/[\uD800-\uDBFF][\uDC00-\uDFFF]/g;function countSymbols(e){return e.replace(s,"_").length}function mapChars(e,p,a){var t=false;var d="";var s=countSymbols(e);for(var i=0;i253||i.length===0){r.error=true}for(var o=0;o63||s.length===0){r.error=true;break}}}if(r.error)return null;return s.join(".")};e.exports.toUnicode=function(e,p){var a=processing(e,p,r.NONTRANSITIONAL);return{domain:a.string,error:a.error}};e.exports.PROCESSING_OPTIONS=r},4294:(e,p,a)=>{e.exports=a(4219)},4219:(e,p,a)=>{var t=a(1808);var d=a(4404);var r=a(3685);var s=a(5687);var i=a(2361);var o=a(9491);var n=a(3837);p.httpOverHttp=httpOverHttp;p.httpsOverHttp=httpsOverHttp;p.httpOverHttps=httpOverHttps;p.httpsOverHttps=httpsOverHttps;function httpOverHttp(e){var p=new TunnelingAgent(e);p.request=r.request;return p}function httpsOverHttp(e){var p=new TunnelingAgent(e);p.request=r.request;p.createSocket=createSecureSocket;p.defaultPort=443;return p}function httpOverHttps(e){var p=new TunnelingAgent(e);p.request=s.request;return p}function httpsOverHttps(e){var p=new TunnelingAgent(e);p.request=s.request;p.createSocket=createSecureSocket;p.defaultPort=443;return p}function TunnelingAgent(e){var p=this;p.options=e||{};p.proxyOptions=p.options.proxy||{};p.maxSockets=p.options.maxSockets||r.Agent.defaultMaxSockets;p.requests=[];p.sockets=[];p.on("free",(function onFree(e,a,t,d){var r=toOptions(a,t,d);for(var s=0,i=p.requests.length;s=this.maxSockets){d.requests.push(r);return}d.createSocket(r,(function(p){p.on("free",onFree);p.on("close",onCloseOrRemove);p.on("agentRemove",onCloseOrRemove);e.onSocket(p);function onFree(){d.emit("free",p,r)}function onCloseOrRemove(e){d.removeSocket(p);p.removeListener("free",onFree);p.removeListener("close",onCloseOrRemove);p.removeListener("agentRemove",onCloseOrRemove)}}))};TunnelingAgent.prototype.createSocket=function createSocket(e,p){var a=this;var t={};a.sockets.push(t);var d=mergeOptions({},a.proxyOptions,{method:"CONNECT",path:e.host+":"+e.port,agent:false,headers:{host:e.host+":"+e.port}});if(e.localAddress){d.localAddress=e.localAddress}if(d.proxyAuth){d.headers=d.headers||{};d.headers["Proxy-Authorization"]="Basic "+new Buffer(d.proxyAuth).toString("base64")}l("making CONNECT request");var r=a.request(d);r.useChunkedEncodingByDefault=false;r.once("response",onResponse);r.once("upgrade",onUpgrade);r.once("connect",onConnect);r.once("error",onError);r.end();function onResponse(e){e.upgrade=true}function onUpgrade(e,p,a){process.nextTick((function(){onConnect(e,p,a)}))}function onConnect(d,s,i){r.removeAllListeners();s.removeAllListeners();if(d.statusCode!==200){l("tunneling socket could not be established, statusCode=%d",d.statusCode);s.destroy();var o=new Error("tunneling socket could not be established, "+"statusCode="+d.statusCode);o.code="ECONNRESET";e.request.emit("error",o);a.removeSocket(t);return}if(i.length>0){l("got illegal response body from proxy");s.destroy();var o=new Error("got illegal response body from proxy");o.code="ECONNRESET";e.request.emit("error",o);a.removeSocket(t);return}l("tunneling connection has established");a.sockets[a.sockets.indexOf(t)]=s;return p(s)}function onError(p){r.removeAllListeners();l("tunneling socket could not be established, cause=%s\n",p.message,p.stack);var d=new Error("tunneling socket could not be established, "+"cause="+p.message);d.code="ECONNRESET";e.request.emit("error",d);a.removeSocket(t)}};TunnelingAgent.prototype.removeSocket=function removeSocket(e){var p=this.sockets.indexOf(e);if(p===-1){return}this.sockets.splice(p,1);var a=this.requests.shift();if(a){this.createSocket(a,(function(e){a.request.onSocket(e)}))}};function createSecureSocket(e,p){var a=this;TunnelingAgent.prototype.createSocket.call(a,e,(function(t){var r=e.request.getHeader("host");var s=mergeOptions({},a.options,{socket:t,servername:r?r.replace(/:.*$/,""):e.host});var i=d.connect(0,s);a.sockets[a.sockets.indexOf(t)]=i;p(i)}))}function toOptions(e,p,a){if(typeof e==="string"){return{host:e,port:p,localAddress:a}}return e}function mergeOptions(e){for(var p=1,a=arguments.length;p{Object.defineProperty(p,"__esModule",{value:true});function getUserAgent(){if(typeof navigator==="object"&&"userAgent"in navigator){return navigator.userAgent}if(typeof process==="object"&&"version"in process){return`Node.js/${process.version.substr(1)} (${process.platform}; ${process.arch})`}return""}p.getUserAgent=getUserAgent},4886:e=>{var p={};e.exports=p;function sign(e){return e<0?-1:1}function evenRound(e){if(e%1===.5&&(e&1)===0){return Math.floor(e)}else{return Math.round(e)}}function createNumberConversion(e,p){if(!p.unsigned){--e}const a=p.unsigned?0:-Math.pow(2,e);const t=Math.pow(2,e)-1;const d=p.moduloBitLength?Math.pow(2,p.moduloBitLength):Math.pow(2,e);const r=p.moduloBitLength?Math.pow(2,p.moduloBitLength-1):Math.pow(2,e-1);return function(e,s){if(!s)s={};let i=+e;if(s.enforceRange){if(!Number.isFinite(i)){throw new TypeError("Argument is not a finite number")}i=sign(i)*Math.floor(Math.abs(i));if(it){throw new TypeError("Argument is not in byte range")}return i}if(!isNaN(i)&&s.clamp){i=evenRound(i);if(it)i=t;return i}if(!Number.isFinite(i)||i===0){return 0}i=sign(i)*Math.floor(Math.abs(i));i=i%d;if(!p.unsigned&&i>=r){return i-d}else if(p.unsigned){if(i<0){i+=d}else if(i===-0){return 0}}return i}}p["void"]=function(){return undefined};p["boolean"]=function(e){return!!e};p["byte"]=createNumberConversion(8,{unsigned:false});p["octet"]=createNumberConversion(8,{unsigned:true});p["short"]=createNumberConversion(16,{unsigned:false});p["unsigned short"]=createNumberConversion(16,{unsigned:true});p["long"]=createNumberConversion(32,{unsigned:false});p["unsigned long"]=createNumberConversion(32,{unsigned:true});p["long long"]=createNumberConversion(32,{unsigned:false,moduloBitLength:64});p["unsigned long long"]=createNumberConversion(32,{unsigned:true,moduloBitLength:64});p["double"]=function(e){const p=+e;if(!Number.isFinite(p)){throw new TypeError("Argument is not a finite floating-point value")}return p};p["unrestricted double"]=function(e){const p=+e;if(isNaN(p)){throw new TypeError("Argument is NaN")}return p};p["float"]=p["double"];p["unrestricted float"]=p["unrestricted double"];p["DOMString"]=function(e,p){if(!p)p={};if(p.treatNullAsEmptyString&&e===null){return""}return String(e)};p["ByteString"]=function(e,p){const a=String(e);let t=undefined;for(let e=0;(t=a.codePointAt(e))!==undefined;++e){if(t>255){throw new TypeError("Argument is not a valid bytestring")}}return a};p["USVString"]=function(e){const p=String(e);const a=p.length;const t=[];for(let e=0;e57343){t.push(String.fromCodePoint(d))}else if(56320<=d&&d<=57343){t.push(String.fromCodePoint(65533))}else{if(e===a-1){t.push(String.fromCodePoint(65533))}else{const a=p.charCodeAt(e+1);if(56320<=a&&a<=57343){const p=d&1023;const r=a&1023;t.push(String.fromCodePoint((2<<15)+(2<<9)*p+r));++e}else{t.push(String.fromCodePoint(65533))}}}}return t.join("")};p["Date"]=function(e,p){if(!(e instanceof Date)){throw new TypeError("Argument is not a Date object")}if(isNaN(e)){return undefined}return e};p["RegExp"]=function(e,p){if(!(e instanceof RegExp)){e=new RegExp(e)}return e}},7537:(e,p,a)=>{const t=a(2158);p.implementation=class URLImpl{constructor(e){const p=e[0];const a=e[1];let d=null;if(a!==undefined){d=t.basicURLParse(a);if(d==="failure"){throw new TypeError("Invalid base URL")}}const r=t.basicURLParse(p,{baseURL:d});if(r==="failure"){throw new TypeError("Invalid URL")}this._url=r}get href(){return t.serializeURL(this._url)}set href(e){const p=t.basicURLParse(e);if(p==="failure"){throw new TypeError("Invalid URL")}this._url=p}get origin(){return t.serializeURLOrigin(this._url)}get protocol(){return this._url.scheme+":"}set protocol(e){t.basicURLParse(e+":",{url:this._url,stateOverride:"scheme start"})}get username(){return this._url.username}set username(e){if(t.cannotHaveAUsernamePasswordPort(this._url)){return}t.setTheUsername(this._url,e)}get password(){return this._url.password}set password(e){if(t.cannotHaveAUsernamePasswordPort(this._url)){return}t.setThePassword(this._url,e)}get host(){const e=this._url;if(e.host===null){return""}if(e.port===null){return t.serializeHost(e.host)}return t.serializeHost(e.host)+":"+t.serializeInteger(e.port)}set host(e){if(this._url.cannotBeABaseURL){return}t.basicURLParse(e,{url:this._url,stateOverride:"host"})}get hostname(){if(this._url.host===null){return""}return t.serializeHost(this._url.host)}set hostname(e){if(this._url.cannotBeABaseURL){return}t.basicURLParse(e,{url:this._url,stateOverride:"hostname"})}get port(){if(this._url.port===null){return""}return t.serializeInteger(this._url.port)}set port(e){if(t.cannotHaveAUsernamePasswordPort(this._url)){return}if(e===""){this._url.port=null}else{t.basicURLParse(e,{url:this._url,stateOverride:"port"})}}get pathname(){if(this._url.cannotBeABaseURL){return this._url.path[0]}if(this._url.path.length===0){return""}return"/"+this._url.path.join("/")}set pathname(e){if(this._url.cannotBeABaseURL){return}this._url.path=[];t.basicURLParse(e,{url:this._url,stateOverride:"path start"})}get search(){if(this._url.query===null||this._url.query===""){return""}return"?"+this._url.query}set search(e){const p=this._url;if(e===""){p.query=null;return}const a=e[0]==="?"?e.substring(1):e;p.query="";t.basicURLParse(a,{url:p,stateOverride:"query"})}get hash(){if(this._url.fragment===null||this._url.fragment===""){return""}return"#"+this._url.fragment}set hash(e){if(e===""){this._url.fragment=null;return}const p=e[0]==="#"?e.substring(1):e;this._url.fragment="";t.basicURLParse(p,{url:this._url,stateOverride:"fragment"})}toJSON(){return this.href}}},3394:(e,p,a)=>{const t=a(4886);const d=a(3185);const r=a(7537);const s=d.implSymbol;function URL(p){if(!this||this[s]||!(this instanceof URL)){throw new TypeError("Failed to construct 'URL': Please use the 'new' operator, this DOM object constructor cannot be called as a function.")}if(arguments.length<1){throw new TypeError("Failed to construct 'URL': 1 argument required, but only "+arguments.length+" present.")}const a=[];for(let e=0;e{p.URL=a(3394)["interface"];p.serializeURL=a(2158).serializeURL;p.serializeURLOrigin=a(2158).serializeURLOrigin;p.basicURLParse=a(2158).basicURLParse;p.setTheUsername=a(2158).setTheUsername;p.setThePassword=a(2158).setThePassword;p.serializeHost=a(2158).serializeHost;p.serializeInteger=a(2158).serializeInteger;p.parseURL=a(2158).parseURL},2158:(e,p,a)=>{const t=a(5477);const d=a(4256);const r={ftp:21,file:null,gopher:70,http:80,https:443,ws:80,wss:443};const s=Symbol("failure");function countSymbols(e){return t.ucs2.decode(e).length}function at(e,p){const a=e[p];return isNaN(a)?undefined:String.fromCodePoint(a)}function isASCIIDigit(e){return e>=48&&e<=57}function isASCIIAlpha(e){return e>=65&&e<=90||e>=97&&e<=122}function isASCIIAlphanumeric(e){return isASCIIAlpha(e)||isASCIIDigit(e)}function isASCIIHex(e){return isASCIIDigit(e)||e>=65&&e<=70||e>=97&&e<=102}function isSingleDot(e){return e==="."||e.toLowerCase()==="%2e"}function isDoubleDot(e){e=e.toLowerCase();return e===".."||e==="%2e."||e===".%2e"||e==="%2e%2e"}function isWindowsDriveLetterCodePoints(e,p){return isASCIIAlpha(e)&&(p===58||p===124)}function isWindowsDriveLetterString(e){return e.length===2&&isASCIIAlpha(e.codePointAt(0))&&(e[1]===":"||e[1]==="|")}function isNormalizedWindowsDriveLetterString(e){return e.length===2&&isASCIIAlpha(e.codePointAt(0))&&e[1]===":"}function containsForbiddenHostCodePoint(e){return e.search(/\u0000|\u0009|\u000A|\u000D|\u0020|#|%|\/|:|\?|@|\[|\\|\]/)!==-1}function containsForbiddenHostCodePointExcludingPercent(e){return e.search(/\u0000|\u0009|\u000A|\u000D|\u0020|#|\/|:|\?|@|\[|\\|\]/)!==-1}function isSpecialScheme(e){return r[e]!==undefined}function isSpecial(e){return isSpecialScheme(e.scheme)}function defaultPort(e){return r[e]}function percentEncode(e){let p=e.toString(16).toUpperCase();if(p.length===1){p="0"+p}return"%"+p}function utf8PercentEncode(e){const p=new Buffer(e);let a="";for(let e=0;e126}const i=new Set([32,34,35,60,62,63,96,123,125]);function isPathPercentEncode(e){return isC0ControlPercentEncode(e)||i.has(e)}const o=new Set([47,58,59,61,64,91,92,93,94,124]);function isUserinfoPercentEncode(e){return isPathPercentEncode(e)||o.has(e)}function percentEncodeChar(e,p){const a=String.fromCodePoint(e);if(p(e)){return utf8PercentEncode(a)}return a}function parseIPv4Number(e){let p=10;if(e.length>=2&&e.charAt(0)==="0"&&e.charAt(1).toLowerCase()==="x"){e=e.substring(2);p=16}else if(e.length>=2&&e.charAt(0)==="0"){e=e.substring(1);p=8}if(e===""){return 0}const a=p===10?/[^0-9]/:p===16?/[^0-9A-Fa-f]/:/[^0-7]/;if(a.test(e)){return s}return parseInt(e,p)}function parseIPv4(e){const p=e.split(".");if(p[p.length-1]===""){if(p.length>1){p.pop()}}if(p.length>4){return e}const a=[];for(const t of p){if(t===""){return e}const p=parseIPv4Number(t);if(p===s){return e}a.push(p)}for(let e=0;e255){return s}}if(a[a.length-1]>=Math.pow(256,5-a.length)){return s}let t=a.pop();let d=0;for(const e of a){t+=e*Math.pow(256,3-d);++d}return t}function serializeIPv4(e){let p="";let a=e;for(let e=1;e<=4;++e){p=String(a%256)+p;if(e!==4){p="."+p}a=Math.floor(a/256)}return p}function parseIPv6(e){const p=[0,0,0,0,0,0,0,0];let a=0;let d=null;let r=0;e=t.ucs2.decode(e);if(e[r]===58){if(e[r+1]!==58){return s}r+=2;++a;d=a}while(r6){return s}let t=0;while(e[r]!==undefined){let d=null;if(t>0){if(e[r]===46&&t<4){++r}else{return s}}if(!isASCIIDigit(e[r])){return s}while(isASCIIDigit(e[r])){const p=parseInt(at(e,r));if(d===null){d=p}else if(d===0){return s}else{d=d*10+p}if(d>255){return s}++r}p[a]=p[a]*256+d;++t;if(t===2||t===4){++a}}if(t!==4){return s}break}else if(e[r]===58){++r;if(e[r]===undefined){return s}}else if(e[r]!==undefined){return s}p[a]=t;++a}if(d!==null){let e=a-d;a=7;while(a!==0&&e>0){const t=p[d+e-1];p[d+e-1]=p[a];p[a]=t;--a;--e}}else if(d===null&&a!==8){return s}return p}function serializeIPv6(e){let p="";const a=findLongestZeroSequence(e);const t=a.idx;let d=false;for(let a=0;a<=7;++a){if(d&&e[a]===0){continue}else if(d){d=false}if(t===a){const e=a===0?"::":":";p+=e;d=true;continue}p+=e[a].toString(16);if(a!==7){p+=":"}}return p}function parseHost(e,p){if(e[0]==="["){if(e[e.length-1]!=="]"){return s}return parseIPv6(e.substring(1,e.length-1))}if(!p){return parseOpaqueHost(e)}const a=utf8PercentDecode(e);const t=d.toASCII(a,false,d.PROCESSING_OPTIONS.NONTRANSITIONAL,false);if(t===null){return s}if(containsForbiddenHostCodePoint(t)){return s}const r=parseIPv4(t);if(typeof r==="number"||r===s){return r}return t}function parseOpaqueHost(e){if(containsForbiddenHostCodePointExcludingPercent(e)){return s}let p="";const a=t.ucs2.decode(e);for(let e=0;ea){p=t;a=d}t=null;d=0}else{if(t===null){t=r}++d}}if(d>a){p=t;a=d}return{idx:p,len:a}}function serializeHost(e){if(typeof e==="number"){return serializeIPv4(e)}if(e instanceof Array){return"["+serializeIPv6(e)+"]"}return e}function trimControlChars(e){return e.replace(/^[\u0000-\u001F\u0020]+|[\u0000-\u001F\u0020]+$/g,"")}function trimTabAndNewline(e){return e.replace(/\u0009|\u000A|\u000D/g,"")}function shortenPath(e){const p=e.path;if(p.length===0){return}if(e.scheme==="file"&&p.length===1&&isNormalizedWindowsDriveLetter(p[0])){return}p.pop()}function includesCredentials(e){return e.username!==""||e.password!==""}function cannotHaveAUsernamePasswordPort(e){return e.host===null||e.host===""||e.cannotBeABaseURL||e.scheme==="file"}function isNormalizedWindowsDriveLetter(e){return/^[A-Za-z]:$/.test(e)}function URLStateMachine(e,p,a,d,r){this.pointer=0;this.input=e;this.base=p||null;this.encodingOverride=a||"utf-8";this.stateOverride=r;this.url=d;this.failure=false;this.parseError=false;if(!this.url){this.url={scheme:"",username:"",password:"",host:null,port:null,path:[],query:null,fragment:null,cannotBeABaseURL:false};const e=trimControlChars(this.input);if(e!==this.input){this.parseError=true}this.input=e}const i=trimTabAndNewline(this.input);if(i!==this.input){this.parseError=true}this.input=i;this.state=r||"scheme start";this.buffer="";this.atFlag=false;this.arrFlag=false;this.passwordTokenSeenFlag=false;this.input=t.ucs2.decode(this.input);for(;this.pointer<=this.input.length;++this.pointer){const e=this.input[this.pointer];const p=isNaN(e)?undefined:String.fromCodePoint(e);const a=this["parse "+this.state](e,p);if(!a){break}else if(a===s){this.failure=true;break}}}URLStateMachine.prototype["parse scheme start"]=function parseSchemeStart(e,p){if(isASCIIAlpha(e)){this.buffer+=p.toLowerCase();this.state="scheme"}else if(!this.stateOverride){this.state="no scheme";--this.pointer}else{this.parseError=true;return s}return true};URLStateMachine.prototype["parse scheme"]=function parseScheme(e,p){if(isASCIIAlphanumeric(e)||e===43||e===45||e===46){this.buffer+=p.toLowerCase()}else if(e===58){if(this.stateOverride){if(isSpecial(this.url)&&!isSpecialScheme(this.buffer)){return false}if(!isSpecial(this.url)&&isSpecialScheme(this.buffer)){return false}if((includesCredentials(this.url)||this.url.port!==null)&&this.buffer==="file"){return false}if(this.url.scheme==="file"&&(this.url.host===""||this.url.host===null)){return false}}this.url.scheme=this.buffer;this.buffer="";if(this.stateOverride){return false}if(this.url.scheme==="file"){if(this.input[this.pointer+1]!==47||this.input[this.pointer+2]!==47){this.parseError=true}this.state="file"}else if(isSpecial(this.url)&&this.base!==null&&this.base.scheme===this.url.scheme){this.state="special relative or authority"}else if(isSpecial(this.url)){this.state="special authority slashes"}else if(this.input[this.pointer+1]===47){this.state="path or authority";++this.pointer}else{this.url.cannotBeABaseURL=true;this.url.path.push("");this.state="cannot-be-a-base-URL path"}}else if(!this.stateOverride){this.buffer="";this.state="no scheme";this.pointer=-1}else{this.parseError=true;return s}return true};URLStateMachine.prototype["parse no scheme"]=function parseNoScheme(e){if(this.base===null||this.base.cannotBeABaseURL&&e!==35){return s}else if(this.base.cannotBeABaseURL&&e===35){this.url.scheme=this.base.scheme;this.url.path=this.base.path.slice();this.url.query=this.base.query;this.url.fragment="";this.url.cannotBeABaseURL=true;this.state="fragment"}else if(this.base.scheme==="file"){this.state="file";--this.pointer}else{this.state="relative";--this.pointer}return true};URLStateMachine.prototype["parse special relative or authority"]=function parseSpecialRelativeOrAuthority(e){if(e===47&&this.input[this.pointer+1]===47){this.state="special authority ignore slashes";++this.pointer}else{this.parseError=true;this.state="relative";--this.pointer}return true};URLStateMachine.prototype["parse path or authority"]=function parsePathOrAuthority(e){if(e===47){this.state="authority"}else{this.state="path";--this.pointer}return true};URLStateMachine.prototype["parse relative"]=function parseRelative(e){this.url.scheme=this.base.scheme;if(isNaN(e)){this.url.username=this.base.username;this.url.password=this.base.password;this.url.host=this.base.host;this.url.port=this.base.port;this.url.path=this.base.path.slice();this.url.query=this.base.query}else if(e===47){this.state="relative slash"}else if(e===63){this.url.username=this.base.username;this.url.password=this.base.password;this.url.host=this.base.host;this.url.port=this.base.port;this.url.path=this.base.path.slice();this.url.query="";this.state="query"}else if(e===35){this.url.username=this.base.username;this.url.password=this.base.password;this.url.host=this.base.host;this.url.port=this.base.port;this.url.path=this.base.path.slice();this.url.query=this.base.query;this.url.fragment="";this.state="fragment"}else if(isSpecial(this.url)&&e===92){this.parseError=true;this.state="relative slash"}else{this.url.username=this.base.username;this.url.password=this.base.password;this.url.host=this.base.host;this.url.port=this.base.port;this.url.path=this.base.path.slice(0,this.base.path.length-1);this.state="path";--this.pointer}return true};URLStateMachine.prototype["parse relative slash"]=function parseRelativeSlash(e){if(isSpecial(this.url)&&(e===47||e===92)){if(e===92){this.parseError=true}this.state="special authority ignore slashes"}else if(e===47){this.state="authority"}else{this.url.username=this.base.username;this.url.password=this.base.password;this.url.host=this.base.host;this.url.port=this.base.port;this.state="path";--this.pointer}return true};URLStateMachine.prototype["parse special authority slashes"]=function parseSpecialAuthoritySlashes(e){if(e===47&&this.input[this.pointer+1]===47){this.state="special authority ignore slashes";++this.pointer}else{this.parseError=true;this.state="special authority ignore slashes";--this.pointer}return true};URLStateMachine.prototype["parse special authority ignore slashes"]=function parseSpecialAuthorityIgnoreSlashes(e){if(e!==47&&e!==92){this.state="authority";--this.pointer}else{this.parseError=true}return true};URLStateMachine.prototype["parse authority"]=function parseAuthority(e,p){if(e===64){this.parseError=true;if(this.atFlag){this.buffer="%40"+this.buffer}this.atFlag=true;const e=countSymbols(this.buffer);for(let p=0;pMath.pow(2,16)-1){this.parseError=true;return s}this.url.port=e===defaultPort(this.url.scheme)?null:e;this.buffer=""}if(this.stateOverride){return false}this.state="path start";--this.pointer}else{this.parseError=true;return s}return true};const n=new Set([47,92,63,35]);URLStateMachine.prototype["parse file"]=function parseFile(e){this.url.scheme="file";if(e===47||e===92){if(e===92){this.parseError=true}this.state="file slash"}else if(this.base!==null&&this.base.scheme==="file"){if(isNaN(e)){this.url.host=this.base.host;this.url.path=this.base.path.slice();this.url.query=this.base.query}else if(e===63){this.url.host=this.base.host;this.url.path=this.base.path.slice();this.url.query="";this.state="query"}else if(e===35){this.url.host=this.base.host;this.url.path=this.base.path.slice();this.url.query=this.base.query;this.url.fragment="";this.state="fragment"}else{if(this.input.length-this.pointer-1===0||!isWindowsDriveLetterCodePoints(e,this.input[this.pointer+1])||this.input.length-this.pointer-1>=2&&!n.has(this.input[this.pointer+2])){this.url.host=this.base.host;this.url.path=this.base.path.slice();shortenPath(this.url)}else{this.parseError=true}this.state="path";--this.pointer}}else{this.state="path";--this.pointer}return true};URLStateMachine.prototype["parse file slash"]=function parseFileSlash(e){if(e===47||e===92){if(e===92){this.parseError=true}this.state="file host"}else{if(this.base!==null&&this.base.scheme==="file"){if(isNormalizedWindowsDriveLetterString(this.base.path[0])){this.url.path.push(this.base.path[0])}else{this.url.host=this.base.host}}this.state="path";--this.pointer}return true};URLStateMachine.prototype["parse file host"]=function parseFileHost(e,p){if(isNaN(e)||e===47||e===92||e===63||e===35){--this.pointer;if(!this.stateOverride&&isWindowsDriveLetterString(this.buffer)){this.parseError=true;this.state="path"}else if(this.buffer===""){this.url.host="";if(this.stateOverride){return false}this.state="path start"}else{let e=parseHost(this.buffer,isSpecial(this.url));if(e===s){return s}if(e==="localhost"){e=""}this.url.host=e;if(this.stateOverride){return false}this.buffer="";this.state="path start"}}else{this.buffer+=p}return true};URLStateMachine.prototype["parse path start"]=function parsePathStart(e){if(isSpecial(this.url)){if(e===92){this.parseError=true}this.state="path";if(e!==47&&e!==92){--this.pointer}}else if(!this.stateOverride&&e===63){this.url.query="";this.state="query"}else if(!this.stateOverride&&e===35){this.url.fragment="";this.state="fragment"}else if(e!==undefined){this.state="path";if(e!==47){--this.pointer}}return true};URLStateMachine.prototype["parse path"]=function parsePath(e){if(isNaN(e)||e===47||isSpecial(this.url)&&e===92||!this.stateOverride&&(e===63||e===35)){if(isSpecial(this.url)&&e===92){this.parseError=true}if(isDoubleDot(this.buffer)){shortenPath(this.url);if(e!==47&&!(isSpecial(this.url)&&e===92)){this.url.path.push("")}}else if(isSingleDot(this.buffer)&&e!==47&&!(isSpecial(this.url)&&e===92)){this.url.path.push("")}else if(!isSingleDot(this.buffer)){if(this.url.scheme==="file"&&this.url.path.length===0&&isWindowsDriveLetterString(this.buffer)){if(this.url.host!==""&&this.url.host!==null){this.parseError=true;this.url.host=""}this.buffer=this.buffer[0]+":"}this.url.path.push(this.buffer)}this.buffer="";if(this.url.scheme==="file"&&(e===undefined||e===63||e===35)){while(this.url.path.length>1&&this.url.path[0]===""){this.parseError=true;this.url.path.shift()}}if(e===63){this.url.query="";this.state="query"}if(e===35){this.url.fragment="";this.state="fragment"}}else{if(e===37&&(!isASCIIHex(this.input[this.pointer+1])||!isASCIIHex(this.input[this.pointer+2]))){this.parseError=true}this.buffer+=percentEncodeChar(e,isPathPercentEncode)}return true};URLStateMachine.prototype["parse cannot-be-a-base-URL path"]=function parseCannotBeABaseURLPath(e){if(e===63){this.url.query="";this.state="query"}else if(e===35){this.url.fragment="";this.state="fragment"}else{if(!isNaN(e)&&e!==37){this.parseError=true}if(e===37&&(!isASCIIHex(this.input[this.pointer+1])||!isASCIIHex(this.input[this.pointer+2]))){this.parseError=true}if(!isNaN(e)){this.url.path[0]=this.url.path[0]+percentEncodeChar(e,isC0ControlPercentEncode)}}return true};URLStateMachine.prototype["parse query"]=function parseQuery(e,p){if(isNaN(e)||!this.stateOverride&&e===35){if(!isSpecial(this.url)||this.url.scheme==="ws"||this.url.scheme==="wss"){this.encodingOverride="utf-8"}const p=new Buffer(this.buffer);for(let e=0;e126||p[e]===34||p[e]===35||p[e]===60||p[e]===62){this.url.query+=percentEncode(p[e])}else{this.url.query+=String.fromCodePoint(p[e])}}this.buffer="";if(e===35){this.url.fragment="";this.state="fragment"}}else{if(e===37&&(!isASCIIHex(this.input[this.pointer+1])||!isASCIIHex(this.input[this.pointer+2]))){this.parseError=true}this.buffer+=p}return true};URLStateMachine.prototype["parse fragment"]=function parseFragment(e){if(isNaN(e)){}else if(e===0){this.parseError=true}else{if(e===37&&(!isASCIIHex(this.input[this.pointer+1])||!isASCIIHex(this.input[this.pointer+2]))){this.parseError=true}this.url.fragment+=percentEncodeChar(e,isC0ControlPercentEncode)}return true};function serializeURL(e,p){let a=e.scheme+":";if(e.host!==null){a+="//";if(e.username!==""||e.password!==""){a+=e.username;if(e.password!==""){a+=":"+e.password}a+="@"}a+=serializeHost(e.host);if(e.port!==null){a+=":"+e.port}}else if(e.host===null&&e.scheme==="file"){a+="//"}if(e.cannotBeABaseURL){a+=e.path[0]}else{for(const p of e.path){a+="/"+p}}if(e.query!==null){a+="?"+e.query}if(!p&&e.fragment!==null){a+="#"+e.fragment}return a}function serializeOrigin(e){let p=e.scheme+"://";p+=serializeHost(e.host);if(e.port!==null){p+=":"+e.port}return p}e.exports.serializeURL=serializeURL;e.exports.serializeURLOrigin=function(p){switch(p.scheme){case"blob":try{return e.exports.serializeURLOrigin(e.exports.parseURL(p.path[0]))}catch(e){return"null"}case"ftp":case"gopher":case"http":case"https":case"ws":case"wss":return serializeOrigin({scheme:p.scheme,host:p.host,port:p.port});case"file":return"file://";default:return"null"}};e.exports.basicURLParse=function(e,p){if(p===undefined){p={}}const a=new URLStateMachine(e,p.baseURL,p.encodingOverride,p.url,p.stateOverride);if(a.failure){return"failure"}return a.url};e.exports.setTheUsername=function(e,p){e.username="";const a=t.ucs2.decode(p);for(let p=0;p{e.exports.mixin=function mixin(e,p){const a=Object.getOwnPropertyNames(p);for(let t=0;t{e.exports=wrappy;function wrappy(e,p){if(e&&p)return wrappy(e)(p);if(typeof e!=="function")throw new TypeError("need wrapper function");Object.keys(e).forEach((function(p){wrapper[p]=e[p]}));return wrapper;function wrapper(){var p=new Array(arguments.length);for(var a=0;a{e.exports=function(e){e.prototype[Symbol.iterator]=function*(){for(let e=this.head;e;e=e.next){yield e.value}}}},665:(e,p,a)=>{e.exports=Yallist;Yallist.Node=Node;Yallist.create=Yallist;function Yallist(e){var p=this;if(!(p instanceof Yallist)){p=new Yallist}p.tail=null;p.head=null;p.length=0;if(e&&typeof e.forEach==="function"){e.forEach((function(e){p.push(e)}))}else if(arguments.length>0){for(var a=0,t=arguments.length;a1){a=p}else if(this.head){t=this.head.next;a=this.head.value}else{throw new TypeError("Reduce of empty list with no initial value")}for(var d=0;t!==null;d++){a=e(a,t.value,d);t=t.next}return a};Yallist.prototype.reduceReverse=function(e,p){var a;var t=this.tail;if(arguments.length>1){a=p}else if(this.tail){t=this.tail.prev;a=this.tail.value}else{throw new TypeError("Reduce of empty list with no initial value")}for(var d=this.length-1;t!==null;d--){a=e(a,t.value,d);t=t.prev}return a};Yallist.prototype.toArray=function(){var e=new Array(this.length);for(var p=0,a=this.head;a!==null;p++){e[p]=a.value;a=a.next}return e};Yallist.prototype.toArrayReverse=function(){var e=new Array(this.length);for(var p=0,a=this.tail;a!==null;p++){e[p]=a.value;a=a.prev}return e};Yallist.prototype.slice=function(e,p){p=p||this.length;if(p<0){p+=this.length}e=e||0;if(e<0){e+=this.length}var a=new Yallist;if(pthis.length){p=this.length}for(var t=0,d=this.head;d!==null&&tthis.length){p=this.length}for(var t=this.length,d=this.tail;d!==null&&t>p;t--){d=d.prev}for(;d!==null&&t>e;t--,d=d.prev){a.push(d.value)}return a};Yallist.prototype.splice=function(e,p,...a){if(e>this.length){e=this.length-1}if(e<0){e=this.length+e}for(var t=0,d=this.head;d!==null&&t{module.exports=eval("require")("encoding")},9491:e=>{e.exports=__WEBPACK_EXTERNAL_createRequire(import.meta.url)("assert")},2361:e=>{e.exports=__WEBPACK_EXTERNAL_createRequire(import.meta.url)("events")},7147:e=>{e.exports=__WEBPACK_EXTERNAL_createRequire(import.meta.url)("fs")},3685:e=>{e.exports=__WEBPACK_EXTERNAL_createRequire(import.meta.url)("http")},5687:e=>{e.exports=__WEBPACK_EXTERNAL_createRequire(import.meta.url)("https")},1808:e=>{e.exports=__WEBPACK_EXTERNAL_createRequire(import.meta.url)("net")},2037:e=>{e.exports=__WEBPACK_EXTERNAL_createRequire(import.meta.url)("os")},1017:e=>{e.exports=__WEBPACK_EXTERNAL_createRequire(import.meta.url)("path")},5477:e=>{e.exports=__WEBPACK_EXTERNAL_createRequire(import.meta.url)("punycode")},2781:e=>{e.exports=__WEBPACK_EXTERNAL_createRequire(import.meta.url)("stream")},4404:e=>{e.exports=__WEBPACK_EXTERNAL_createRequire(import.meta.url)("tls")},7310:e=>{e.exports=__WEBPACK_EXTERNAL_createRequire(import.meta.url)("url")},3837:e=>{e.exports=__WEBPACK_EXTERNAL_createRequire(import.meta.url)("util")},9796:e=>{e.exports=__WEBPACK_EXTERNAL_createRequire(import.meta.url)("zlib")},2020:e=>{e.exports=JSON.parse('[[[0,44],"disallowed_STD3_valid"],[[45,46],"valid"],[[47,47],"disallowed_STD3_valid"],[[48,57],"valid"],[[58,64],"disallowed_STD3_valid"],[[65,65],"mapped",[97]],[[66,66],"mapped",[98]],[[67,67],"mapped",[99]],[[68,68],"mapped",[100]],[[69,69],"mapped",[101]],[[70,70],"mapped",[102]],[[71,71],"mapped",[103]],[[72,72],"mapped",[104]],[[73,73],"mapped",[105]],[[74,74],"mapped",[106]],[[75,75],"mapped",[107]],[[76,76],"mapped",[108]],[[77,77],"mapped",[109]],[[78,78],"mapped",[110]],[[79,79],"mapped",[111]],[[80,80],"mapped",[112]],[[81,81],"mapped",[113]],[[82,82],"mapped",[114]],[[83,83],"mapped",[115]],[[84,84],"mapped",[116]],[[85,85],"mapped",[117]],[[86,86],"mapped",[118]],[[87,87],"mapped",[119]],[[88,88],"mapped",[120]],[[89,89],"mapped",[121]],[[90,90],"mapped",[122]],[[91,96],"disallowed_STD3_valid"],[[97,122],"valid"],[[123,127],"disallowed_STD3_valid"],[[128,159],"disallowed"],[[160,160],"disallowed_STD3_mapped",[32]],[[161,167],"valid",[],"NV8"],[[168,168],"disallowed_STD3_mapped",[32,776]],[[169,169],"valid",[],"NV8"],[[170,170],"mapped",[97]],[[171,172],"valid",[],"NV8"],[[173,173],"ignored"],[[174,174],"valid",[],"NV8"],[[175,175],"disallowed_STD3_mapped",[32,772]],[[176,177],"valid",[],"NV8"],[[178,178],"mapped",[50]],[[179,179],"mapped",[51]],[[180,180],"disallowed_STD3_mapped",[32,769]],[[181,181],"mapped",[956]],[[182,182],"valid",[],"NV8"],[[183,183],"valid"],[[184,184],"disallowed_STD3_mapped",[32,807]],[[185,185],"mapped",[49]],[[186,186],"mapped",[111]],[[187,187],"valid",[],"NV8"],[[188,188],"mapped",[49,8260,52]],[[189,189],"mapped",[49,8260,50]],[[190,190],"mapped",[51,8260,52]],[[191,191],"valid",[],"NV8"],[[192,192],"mapped",[224]],[[193,193],"mapped",[225]],[[194,194],"mapped",[226]],[[195,195],"mapped",[227]],[[196,196],"mapped",[228]],[[197,197],"mapped",[229]],[[198,198],"mapped",[230]],[[199,199],"mapped",[231]],[[200,200],"mapped",[232]],[[201,201],"mapped",[233]],[[202,202],"mapped",[234]],[[203,203],"mapped",[235]],[[204,204],"mapped",[236]],[[205,205],"mapped",[237]],[[206,206],"mapped",[238]],[[207,207],"mapped",[239]],[[208,208],"mapped",[240]],[[209,209],"mapped",[241]],[[210,210],"mapped",[242]],[[211,211],"mapped",[243]],[[212,212],"mapped",[244]],[[213,213],"mapped",[245]],[[214,214],"mapped",[246]],[[215,215],"valid",[],"NV8"],[[216,216],"mapped",[248]],[[217,217],"mapped",[249]],[[218,218],"mapped",[250]],[[219,219],"mapped",[251]],[[220,220],"mapped",[252]],[[221,221],"mapped",[253]],[[222,222],"mapped",[254]],[[223,223],"deviation",[115,115]],[[224,246],"valid"],[[247,247],"valid",[],"NV8"],[[248,255],"valid"],[[256,256],"mapped",[257]],[[257,257],"valid"],[[258,258],"mapped",[259]],[[259,259],"valid"],[[260,260],"mapped",[261]],[[261,261],"valid"],[[262,262],"mapped",[263]],[[263,263],"valid"],[[264,264],"mapped",[265]],[[265,265],"valid"],[[266,266],"mapped",[267]],[[267,267],"valid"],[[268,268],"mapped",[269]],[[269,269],"valid"],[[270,270],"mapped",[271]],[[271,271],"valid"],[[272,272],"mapped",[273]],[[273,273],"valid"],[[274,274],"mapped",[275]],[[275,275],"valid"],[[276,276],"mapped",[277]],[[277,277],"valid"],[[278,278],"mapped",[279]],[[279,279],"valid"],[[280,280],"mapped",[281]],[[281,281],"valid"],[[282,282],"mapped",[283]],[[283,283],"valid"],[[284,284],"mapped",[285]],[[285,285],"valid"],[[286,286],"mapped",[287]],[[287,287],"valid"],[[288,288],"mapped",[289]],[[289,289],"valid"],[[290,290],"mapped",[291]],[[291,291],"valid"],[[292,292],"mapped",[293]],[[293,293],"valid"],[[294,294],"mapped",[295]],[[295,295],"valid"],[[296,296],"mapped",[297]],[[297,297],"valid"],[[298,298],"mapped",[299]],[[299,299],"valid"],[[300,300],"mapped",[301]],[[301,301],"valid"],[[302,302],"mapped",[303]],[[303,303],"valid"],[[304,304],"mapped",[105,775]],[[305,305],"valid"],[[306,307],"mapped",[105,106]],[[308,308],"mapped",[309]],[[309,309],"valid"],[[310,310],"mapped",[311]],[[311,312],"valid"],[[313,313],"mapped",[314]],[[314,314],"valid"],[[315,315],"mapped",[316]],[[316,316],"valid"],[[317,317],"mapped",[318]],[[318,318],"valid"],[[319,320],"mapped",[108,183]],[[321,321],"mapped",[322]],[[322,322],"valid"],[[323,323],"mapped",[324]],[[324,324],"valid"],[[325,325],"mapped",[326]],[[326,326],"valid"],[[327,327],"mapped",[328]],[[328,328],"valid"],[[329,329],"mapped",[700,110]],[[330,330],"mapped",[331]],[[331,331],"valid"],[[332,332],"mapped",[333]],[[333,333],"valid"],[[334,334],"mapped",[335]],[[335,335],"valid"],[[336,336],"mapped",[337]],[[337,337],"valid"],[[338,338],"mapped",[339]],[[339,339],"valid"],[[340,340],"mapped",[341]],[[341,341],"valid"],[[342,342],"mapped",[343]],[[343,343],"valid"],[[344,344],"mapped",[345]],[[345,345],"valid"],[[346,346],"mapped",[347]],[[347,347],"valid"],[[348,348],"mapped",[349]],[[349,349],"valid"],[[350,350],"mapped",[351]],[[351,351],"valid"],[[352,352],"mapped",[353]],[[353,353],"valid"],[[354,354],"mapped",[355]],[[355,355],"valid"],[[356,356],"mapped",[357]],[[357,357],"valid"],[[358,358],"mapped",[359]],[[359,359],"valid"],[[360,360],"mapped",[361]],[[361,361],"valid"],[[362,362],"mapped",[363]],[[363,363],"valid"],[[364,364],"mapped",[365]],[[365,365],"valid"],[[366,366],"mapped",[367]],[[367,367],"valid"],[[368,368],"mapped",[369]],[[369,369],"valid"],[[370,370],"mapped",[371]],[[371,371],"valid"],[[372,372],"mapped",[373]],[[373,373],"valid"],[[374,374],"mapped",[375]],[[375,375],"valid"],[[376,376],"mapped",[255]],[[377,377],"mapped",[378]],[[378,378],"valid"],[[379,379],"mapped",[380]],[[380,380],"valid"],[[381,381],"mapped",[382]],[[382,382],"valid"],[[383,383],"mapped",[115]],[[384,384],"valid"],[[385,385],"mapped",[595]],[[386,386],"mapped",[387]],[[387,387],"valid"],[[388,388],"mapped",[389]],[[389,389],"valid"],[[390,390],"mapped",[596]],[[391,391],"mapped",[392]],[[392,392],"valid"],[[393,393],"mapped",[598]],[[394,394],"mapped",[599]],[[395,395],"mapped",[396]],[[396,397],"valid"],[[398,398],"mapped",[477]],[[399,399],"mapped",[601]],[[400,400],"mapped",[603]],[[401,401],"mapped",[402]],[[402,402],"valid"],[[403,403],"mapped",[608]],[[404,404],"mapped",[611]],[[405,405],"valid"],[[406,406],"mapped",[617]],[[407,407],"mapped",[616]],[[408,408],"mapped",[409]],[[409,411],"valid"],[[412,412],"mapped",[623]],[[413,413],"mapped",[626]],[[414,414],"valid"],[[415,415],"mapped",[629]],[[416,416],"mapped",[417]],[[417,417],"valid"],[[418,418],"mapped",[419]],[[419,419],"valid"],[[420,420],"mapped",[421]],[[421,421],"valid"],[[422,422],"mapped",[640]],[[423,423],"mapped",[424]],[[424,424],"valid"],[[425,425],"mapped",[643]],[[426,427],"valid"],[[428,428],"mapped",[429]],[[429,429],"valid"],[[430,430],"mapped",[648]],[[431,431],"mapped",[432]],[[432,432],"valid"],[[433,433],"mapped",[650]],[[434,434],"mapped",[651]],[[435,435],"mapped",[436]],[[436,436],"valid"],[[437,437],"mapped",[438]],[[438,438],"valid"],[[439,439],"mapped",[658]],[[440,440],"mapped",[441]],[[441,443],"valid"],[[444,444],"mapped",[445]],[[445,451],"valid"],[[452,454],"mapped",[100,382]],[[455,457],"mapped",[108,106]],[[458,460],"mapped",[110,106]],[[461,461],"mapped",[462]],[[462,462],"valid"],[[463,463],"mapped",[464]],[[464,464],"valid"],[[465,465],"mapped",[466]],[[466,466],"valid"],[[467,467],"mapped",[468]],[[468,468],"valid"],[[469,469],"mapped",[470]],[[470,470],"valid"],[[471,471],"mapped",[472]],[[472,472],"valid"],[[473,473],"mapped",[474]],[[474,474],"valid"],[[475,475],"mapped",[476]],[[476,477],"valid"],[[478,478],"mapped",[479]],[[479,479],"valid"],[[480,480],"mapped",[481]],[[481,481],"valid"],[[482,482],"mapped",[483]],[[483,483],"valid"],[[484,484],"mapped",[485]],[[485,485],"valid"],[[486,486],"mapped",[487]],[[487,487],"valid"],[[488,488],"mapped",[489]],[[489,489],"valid"],[[490,490],"mapped",[491]],[[491,491],"valid"],[[492,492],"mapped",[493]],[[493,493],"valid"],[[494,494],"mapped",[495]],[[495,496],"valid"],[[497,499],"mapped",[100,122]],[[500,500],"mapped",[501]],[[501,501],"valid"],[[502,502],"mapped",[405]],[[503,503],"mapped",[447]],[[504,504],"mapped",[505]],[[505,505],"valid"],[[506,506],"mapped",[507]],[[507,507],"valid"],[[508,508],"mapped",[509]],[[509,509],"valid"],[[510,510],"mapped",[511]],[[511,511],"valid"],[[512,512],"mapped",[513]],[[513,513],"valid"],[[514,514],"mapped",[515]],[[515,515],"valid"],[[516,516],"mapped",[517]],[[517,517],"valid"],[[518,518],"mapped",[519]],[[519,519],"valid"],[[520,520],"mapped",[521]],[[521,521],"valid"],[[522,522],"mapped",[523]],[[523,523],"valid"],[[524,524],"mapped",[525]],[[525,525],"valid"],[[526,526],"mapped",[527]],[[527,527],"valid"],[[528,528],"mapped",[529]],[[529,529],"valid"],[[530,530],"mapped",[531]],[[531,531],"valid"],[[532,532],"mapped",[533]],[[533,533],"valid"],[[534,534],"mapped",[535]],[[535,535],"valid"],[[536,536],"mapped",[537]],[[537,537],"valid"],[[538,538],"mapped",[539]],[[539,539],"valid"],[[540,540],"mapped",[541]],[[541,541],"valid"],[[542,542],"mapped",[543]],[[543,543],"valid"],[[544,544],"mapped",[414]],[[545,545],"valid"],[[546,546],"mapped",[547]],[[547,547],"valid"],[[548,548],"mapped",[549]],[[549,549],"valid"],[[550,550],"mapped",[551]],[[551,551],"valid"],[[552,552],"mapped",[553]],[[553,553],"valid"],[[554,554],"mapped",[555]],[[555,555],"valid"],[[556,556],"mapped",[557]],[[557,557],"valid"],[[558,558],"mapped",[559]],[[559,559],"valid"],[[560,560],"mapped",[561]],[[561,561],"valid"],[[562,562],"mapped",[563]],[[563,563],"valid"],[[564,566],"valid"],[[567,569],"valid"],[[570,570],"mapped",[11365]],[[571,571],"mapped",[572]],[[572,572],"valid"],[[573,573],"mapped",[410]],[[574,574],"mapped",[11366]],[[575,576],"valid"],[[577,577],"mapped",[578]],[[578,578],"valid"],[[579,579],"mapped",[384]],[[580,580],"mapped",[649]],[[581,581],"mapped",[652]],[[582,582],"mapped",[583]],[[583,583],"valid"],[[584,584],"mapped",[585]],[[585,585],"valid"],[[586,586],"mapped",[587]],[[587,587],"valid"],[[588,588],"mapped",[589]],[[589,589],"valid"],[[590,590],"mapped",[591]],[[591,591],"valid"],[[592,680],"valid"],[[681,685],"valid"],[[686,687],"valid"],[[688,688],"mapped",[104]],[[689,689],"mapped",[614]],[[690,690],"mapped",[106]],[[691,691],"mapped",[114]],[[692,692],"mapped",[633]],[[693,693],"mapped",[635]],[[694,694],"mapped",[641]],[[695,695],"mapped",[119]],[[696,696],"mapped",[121]],[[697,705],"valid"],[[706,709],"valid",[],"NV8"],[[710,721],"valid"],[[722,727],"valid",[],"NV8"],[[728,728],"disallowed_STD3_mapped",[32,774]],[[729,729],"disallowed_STD3_mapped",[32,775]],[[730,730],"disallowed_STD3_mapped",[32,778]],[[731,731],"disallowed_STD3_mapped",[32,808]],[[732,732],"disallowed_STD3_mapped",[32,771]],[[733,733],"disallowed_STD3_mapped",[32,779]],[[734,734],"valid",[],"NV8"],[[735,735],"valid",[],"NV8"],[[736,736],"mapped",[611]],[[737,737],"mapped",[108]],[[738,738],"mapped",[115]],[[739,739],"mapped",[120]],[[740,740],"mapped",[661]],[[741,745],"valid",[],"NV8"],[[746,747],"valid",[],"NV8"],[[748,748],"valid"],[[749,749],"valid",[],"NV8"],[[750,750],"valid"],[[751,767],"valid",[],"NV8"],[[768,831],"valid"],[[832,832],"mapped",[768]],[[833,833],"mapped",[769]],[[834,834],"valid"],[[835,835],"mapped",[787]],[[836,836],"mapped",[776,769]],[[837,837],"mapped",[953]],[[838,846],"valid"],[[847,847],"ignored"],[[848,855],"valid"],[[856,860],"valid"],[[861,863],"valid"],[[864,865],"valid"],[[866,866],"valid"],[[867,879],"valid"],[[880,880],"mapped",[881]],[[881,881],"valid"],[[882,882],"mapped",[883]],[[883,883],"valid"],[[884,884],"mapped",[697]],[[885,885],"valid"],[[886,886],"mapped",[887]],[[887,887],"valid"],[[888,889],"disallowed"],[[890,890],"disallowed_STD3_mapped",[32,953]],[[891,893],"valid"],[[894,894],"disallowed_STD3_mapped",[59]],[[895,895],"mapped",[1011]],[[896,899],"disallowed"],[[900,900],"disallowed_STD3_mapped",[32,769]],[[901,901],"disallowed_STD3_mapped",[32,776,769]],[[902,902],"mapped",[940]],[[903,903],"mapped",[183]],[[904,904],"mapped",[941]],[[905,905],"mapped",[942]],[[906,906],"mapped",[943]],[[907,907],"disallowed"],[[908,908],"mapped",[972]],[[909,909],"disallowed"],[[910,910],"mapped",[973]],[[911,911],"mapped",[974]],[[912,912],"valid"],[[913,913],"mapped",[945]],[[914,914],"mapped",[946]],[[915,915],"mapped",[947]],[[916,916],"mapped",[948]],[[917,917],"mapped",[949]],[[918,918],"mapped",[950]],[[919,919],"mapped",[951]],[[920,920],"mapped",[952]],[[921,921],"mapped",[953]],[[922,922],"mapped",[954]],[[923,923],"mapped",[955]],[[924,924],"mapped",[956]],[[925,925],"mapped",[957]],[[926,926],"mapped",[958]],[[927,927],"mapped",[959]],[[928,928],"mapped",[960]],[[929,929],"mapped",[961]],[[930,930],"disallowed"],[[931,931],"mapped",[963]],[[932,932],"mapped",[964]],[[933,933],"mapped",[965]],[[934,934],"mapped",[966]],[[935,935],"mapped",[967]],[[936,936],"mapped",[968]],[[937,937],"mapped",[969]],[[938,938],"mapped",[970]],[[939,939],"mapped",[971]],[[940,961],"valid"],[[962,962],"deviation",[963]],[[963,974],"valid"],[[975,975],"mapped",[983]],[[976,976],"mapped",[946]],[[977,977],"mapped",[952]],[[978,978],"mapped",[965]],[[979,979],"mapped",[973]],[[980,980],"mapped",[971]],[[981,981],"mapped",[966]],[[982,982],"mapped",[960]],[[983,983],"valid"],[[984,984],"mapped",[985]],[[985,985],"valid"],[[986,986],"mapped",[987]],[[987,987],"valid"],[[988,988],"mapped",[989]],[[989,989],"valid"],[[990,990],"mapped",[991]],[[991,991],"valid"],[[992,992],"mapped",[993]],[[993,993],"valid"],[[994,994],"mapped",[995]],[[995,995],"valid"],[[996,996],"mapped",[997]],[[997,997],"valid"],[[998,998],"mapped",[999]],[[999,999],"valid"],[[1000,1000],"mapped",[1001]],[[1001,1001],"valid"],[[1002,1002],"mapped",[1003]],[[1003,1003],"valid"],[[1004,1004],"mapped",[1005]],[[1005,1005],"valid"],[[1006,1006],"mapped",[1007]],[[1007,1007],"valid"],[[1008,1008],"mapped",[954]],[[1009,1009],"mapped",[961]],[[1010,1010],"mapped",[963]],[[1011,1011],"valid"],[[1012,1012],"mapped",[952]],[[1013,1013],"mapped",[949]],[[1014,1014],"valid",[],"NV8"],[[1015,1015],"mapped",[1016]],[[1016,1016],"valid"],[[1017,1017],"mapped",[963]],[[1018,1018],"mapped",[1019]],[[1019,1019],"valid"],[[1020,1020],"valid"],[[1021,1021],"mapped",[891]],[[1022,1022],"mapped",[892]],[[1023,1023],"mapped",[893]],[[1024,1024],"mapped",[1104]],[[1025,1025],"mapped",[1105]],[[1026,1026],"mapped",[1106]],[[1027,1027],"mapped",[1107]],[[1028,1028],"mapped",[1108]],[[1029,1029],"mapped",[1109]],[[1030,1030],"mapped",[1110]],[[1031,1031],"mapped",[1111]],[[1032,1032],"mapped",[1112]],[[1033,1033],"mapped",[1113]],[[1034,1034],"mapped",[1114]],[[1035,1035],"mapped",[1115]],[[1036,1036],"mapped",[1116]],[[1037,1037],"mapped",[1117]],[[1038,1038],"mapped",[1118]],[[1039,1039],"mapped",[1119]],[[1040,1040],"mapped",[1072]],[[1041,1041],"mapped",[1073]],[[1042,1042],"mapped",[1074]],[[1043,1043],"mapped",[1075]],[[1044,1044],"mapped",[1076]],[[1045,1045],"mapped",[1077]],[[1046,1046],"mapped",[1078]],[[1047,1047],"mapped",[1079]],[[1048,1048],"mapped",[1080]],[[1049,1049],"mapped",[1081]],[[1050,1050],"mapped",[1082]],[[1051,1051],"mapped",[1083]],[[1052,1052],"mapped",[1084]],[[1053,1053],"mapped",[1085]],[[1054,1054],"mapped",[1086]],[[1055,1055],"mapped",[1087]],[[1056,1056],"mapped",[1088]],[[1057,1057],"mapped",[1089]],[[1058,1058],"mapped",[1090]],[[1059,1059],"mapped",[1091]],[[1060,1060],"mapped",[1092]],[[1061,1061],"mapped",[1093]],[[1062,1062],"mapped",[1094]],[[1063,1063],"mapped",[1095]],[[1064,1064],"mapped",[1096]],[[1065,1065],"mapped",[1097]],[[1066,1066],"mapped",[1098]],[[1067,1067],"mapped",[1099]],[[1068,1068],"mapped",[1100]],[[1069,1069],"mapped",[1101]],[[1070,1070],"mapped",[1102]],[[1071,1071],"mapped",[1103]],[[1072,1103],"valid"],[[1104,1104],"valid"],[[1105,1116],"valid"],[[1117,1117],"valid"],[[1118,1119],"valid"],[[1120,1120],"mapped",[1121]],[[1121,1121],"valid"],[[1122,1122],"mapped",[1123]],[[1123,1123],"valid"],[[1124,1124],"mapped",[1125]],[[1125,1125],"valid"],[[1126,1126],"mapped",[1127]],[[1127,1127],"valid"],[[1128,1128],"mapped",[1129]],[[1129,1129],"valid"],[[1130,1130],"mapped",[1131]],[[1131,1131],"valid"],[[1132,1132],"mapped",[1133]],[[1133,1133],"valid"],[[1134,1134],"mapped",[1135]],[[1135,1135],"valid"],[[1136,1136],"mapped",[1137]],[[1137,1137],"valid"],[[1138,1138],"mapped",[1139]],[[1139,1139],"valid"],[[1140,1140],"mapped",[1141]],[[1141,1141],"valid"],[[1142,1142],"mapped",[1143]],[[1143,1143],"valid"],[[1144,1144],"mapped",[1145]],[[1145,1145],"valid"],[[1146,1146],"mapped",[1147]],[[1147,1147],"valid"],[[1148,1148],"mapped",[1149]],[[1149,1149],"valid"],[[1150,1150],"mapped",[1151]],[[1151,1151],"valid"],[[1152,1152],"mapped",[1153]],[[1153,1153],"valid"],[[1154,1154],"valid",[],"NV8"],[[1155,1158],"valid"],[[1159,1159],"valid"],[[1160,1161],"valid",[],"NV8"],[[1162,1162],"mapped",[1163]],[[1163,1163],"valid"],[[1164,1164],"mapped",[1165]],[[1165,1165],"valid"],[[1166,1166],"mapped",[1167]],[[1167,1167],"valid"],[[1168,1168],"mapped",[1169]],[[1169,1169],"valid"],[[1170,1170],"mapped",[1171]],[[1171,1171],"valid"],[[1172,1172],"mapped",[1173]],[[1173,1173],"valid"],[[1174,1174],"mapped",[1175]],[[1175,1175],"valid"],[[1176,1176],"mapped",[1177]],[[1177,1177],"valid"],[[1178,1178],"mapped",[1179]],[[1179,1179],"valid"],[[1180,1180],"mapped",[1181]],[[1181,1181],"valid"],[[1182,1182],"mapped",[1183]],[[1183,1183],"valid"],[[1184,1184],"mapped",[1185]],[[1185,1185],"valid"],[[1186,1186],"mapped",[1187]],[[1187,1187],"valid"],[[1188,1188],"mapped",[1189]],[[1189,1189],"valid"],[[1190,1190],"mapped",[1191]],[[1191,1191],"valid"],[[1192,1192],"mapped",[1193]],[[1193,1193],"valid"],[[1194,1194],"mapped",[1195]],[[1195,1195],"valid"],[[1196,1196],"mapped",[1197]],[[1197,1197],"valid"],[[1198,1198],"mapped",[1199]],[[1199,1199],"valid"],[[1200,1200],"mapped",[1201]],[[1201,1201],"valid"],[[1202,1202],"mapped",[1203]],[[1203,1203],"valid"],[[1204,1204],"mapped",[1205]],[[1205,1205],"valid"],[[1206,1206],"mapped",[1207]],[[1207,1207],"valid"],[[1208,1208],"mapped",[1209]],[[1209,1209],"valid"],[[1210,1210],"mapped",[1211]],[[1211,1211],"valid"],[[1212,1212],"mapped",[1213]],[[1213,1213],"valid"],[[1214,1214],"mapped",[1215]],[[1215,1215],"valid"],[[1216,1216],"disallowed"],[[1217,1217],"mapped",[1218]],[[1218,1218],"valid"],[[1219,1219],"mapped",[1220]],[[1220,1220],"valid"],[[1221,1221],"mapped",[1222]],[[1222,1222],"valid"],[[1223,1223],"mapped",[1224]],[[1224,1224],"valid"],[[1225,1225],"mapped",[1226]],[[1226,1226],"valid"],[[1227,1227],"mapped",[1228]],[[1228,1228],"valid"],[[1229,1229],"mapped",[1230]],[[1230,1230],"valid"],[[1231,1231],"valid"],[[1232,1232],"mapped",[1233]],[[1233,1233],"valid"],[[1234,1234],"mapped",[1235]],[[1235,1235],"valid"],[[1236,1236],"mapped",[1237]],[[1237,1237],"valid"],[[1238,1238],"mapped",[1239]],[[1239,1239],"valid"],[[1240,1240],"mapped",[1241]],[[1241,1241],"valid"],[[1242,1242],"mapped",[1243]],[[1243,1243],"valid"],[[1244,1244],"mapped",[1245]],[[1245,1245],"valid"],[[1246,1246],"mapped",[1247]],[[1247,1247],"valid"],[[1248,1248],"mapped",[1249]],[[1249,1249],"valid"],[[1250,1250],"mapped",[1251]],[[1251,1251],"valid"],[[1252,1252],"mapped",[1253]],[[1253,1253],"valid"],[[1254,1254],"mapped",[1255]],[[1255,1255],"valid"],[[1256,1256],"mapped",[1257]],[[1257,1257],"valid"],[[1258,1258],"mapped",[1259]],[[1259,1259],"valid"],[[1260,1260],"mapped",[1261]],[[1261,1261],"valid"],[[1262,1262],"mapped",[1263]],[[1263,1263],"valid"],[[1264,1264],"mapped",[1265]],[[1265,1265],"valid"],[[1266,1266],"mapped",[1267]],[[1267,1267],"valid"],[[1268,1268],"mapped",[1269]],[[1269,1269],"valid"],[[1270,1270],"mapped",[1271]],[[1271,1271],"valid"],[[1272,1272],"mapped",[1273]],[[1273,1273],"valid"],[[1274,1274],"mapped",[1275]],[[1275,1275],"valid"],[[1276,1276],"mapped",[1277]],[[1277,1277],"valid"],[[1278,1278],"mapped",[1279]],[[1279,1279],"valid"],[[1280,1280],"mapped",[1281]],[[1281,1281],"valid"],[[1282,1282],"mapped",[1283]],[[1283,1283],"valid"],[[1284,1284],"mapped",[1285]],[[1285,1285],"valid"],[[1286,1286],"mapped",[1287]],[[1287,1287],"valid"],[[1288,1288],"mapped",[1289]],[[1289,1289],"valid"],[[1290,1290],"mapped",[1291]],[[1291,1291],"valid"],[[1292,1292],"mapped",[1293]],[[1293,1293],"valid"],[[1294,1294],"mapped",[1295]],[[1295,1295],"valid"],[[1296,1296],"mapped",[1297]],[[1297,1297],"valid"],[[1298,1298],"mapped",[1299]],[[1299,1299],"valid"],[[1300,1300],"mapped",[1301]],[[1301,1301],"valid"],[[1302,1302],"mapped",[1303]],[[1303,1303],"valid"],[[1304,1304],"mapped",[1305]],[[1305,1305],"valid"],[[1306,1306],"mapped",[1307]],[[1307,1307],"valid"],[[1308,1308],"mapped",[1309]],[[1309,1309],"valid"],[[1310,1310],"mapped",[1311]],[[1311,1311],"valid"],[[1312,1312],"mapped",[1313]],[[1313,1313],"valid"],[[1314,1314],"mapped",[1315]],[[1315,1315],"valid"],[[1316,1316],"mapped",[1317]],[[1317,1317],"valid"],[[1318,1318],"mapped",[1319]],[[1319,1319],"valid"],[[1320,1320],"mapped",[1321]],[[1321,1321],"valid"],[[1322,1322],"mapped",[1323]],[[1323,1323],"valid"],[[1324,1324],"mapped",[1325]],[[1325,1325],"valid"],[[1326,1326],"mapped",[1327]],[[1327,1327],"valid"],[[1328,1328],"disallowed"],[[1329,1329],"mapped",[1377]],[[1330,1330],"mapped",[1378]],[[1331,1331],"mapped",[1379]],[[1332,1332],"mapped",[1380]],[[1333,1333],"mapped",[1381]],[[1334,1334],"mapped",[1382]],[[1335,1335],"mapped",[1383]],[[1336,1336],"mapped",[1384]],[[1337,1337],"mapped",[1385]],[[1338,1338],"mapped",[1386]],[[1339,1339],"mapped",[1387]],[[1340,1340],"mapped",[1388]],[[1341,1341],"mapped",[1389]],[[1342,1342],"mapped",[1390]],[[1343,1343],"mapped",[1391]],[[1344,1344],"mapped",[1392]],[[1345,1345],"mapped",[1393]],[[1346,1346],"mapped",[1394]],[[1347,1347],"mapped",[1395]],[[1348,1348],"mapped",[1396]],[[1349,1349],"mapped",[1397]],[[1350,1350],"mapped",[1398]],[[1351,1351],"mapped",[1399]],[[1352,1352],"mapped",[1400]],[[1353,1353],"mapped",[1401]],[[1354,1354],"mapped",[1402]],[[1355,1355],"mapped",[1403]],[[1356,1356],"mapped",[1404]],[[1357,1357],"mapped",[1405]],[[1358,1358],"mapped",[1406]],[[1359,1359],"mapped",[1407]],[[1360,1360],"mapped",[1408]],[[1361,1361],"mapped",[1409]],[[1362,1362],"mapped",[1410]],[[1363,1363],"mapped",[1411]],[[1364,1364],"mapped",[1412]],[[1365,1365],"mapped",[1413]],[[1366,1366],"mapped",[1414]],[[1367,1368],"disallowed"],[[1369,1369],"valid"],[[1370,1375],"valid",[],"NV8"],[[1376,1376],"disallowed"],[[1377,1414],"valid"],[[1415,1415],"mapped",[1381,1410]],[[1416,1416],"disallowed"],[[1417,1417],"valid",[],"NV8"],[[1418,1418],"valid",[],"NV8"],[[1419,1420],"disallowed"],[[1421,1422],"valid",[],"NV8"],[[1423,1423],"valid",[],"NV8"],[[1424,1424],"disallowed"],[[1425,1441],"valid"],[[1442,1442],"valid"],[[1443,1455],"valid"],[[1456,1465],"valid"],[[1466,1466],"valid"],[[1467,1469],"valid"],[[1470,1470],"valid",[],"NV8"],[[1471,1471],"valid"],[[1472,1472],"valid",[],"NV8"],[[1473,1474],"valid"],[[1475,1475],"valid",[],"NV8"],[[1476,1476],"valid"],[[1477,1477],"valid"],[[1478,1478],"valid",[],"NV8"],[[1479,1479],"valid"],[[1480,1487],"disallowed"],[[1488,1514],"valid"],[[1515,1519],"disallowed"],[[1520,1524],"valid"],[[1525,1535],"disallowed"],[[1536,1539],"disallowed"],[[1540,1540],"disallowed"],[[1541,1541],"disallowed"],[[1542,1546],"valid",[],"NV8"],[[1547,1547],"valid",[],"NV8"],[[1548,1548],"valid",[],"NV8"],[[1549,1551],"valid",[],"NV8"],[[1552,1557],"valid"],[[1558,1562],"valid"],[[1563,1563],"valid",[],"NV8"],[[1564,1564],"disallowed"],[[1565,1565],"disallowed"],[[1566,1566],"valid",[],"NV8"],[[1567,1567],"valid",[],"NV8"],[[1568,1568],"valid"],[[1569,1594],"valid"],[[1595,1599],"valid"],[[1600,1600],"valid",[],"NV8"],[[1601,1618],"valid"],[[1619,1621],"valid"],[[1622,1624],"valid"],[[1625,1630],"valid"],[[1631,1631],"valid"],[[1632,1641],"valid"],[[1642,1645],"valid",[],"NV8"],[[1646,1647],"valid"],[[1648,1652],"valid"],[[1653,1653],"mapped",[1575,1652]],[[1654,1654],"mapped",[1608,1652]],[[1655,1655],"mapped",[1735,1652]],[[1656,1656],"mapped",[1610,1652]],[[1657,1719],"valid"],[[1720,1721],"valid"],[[1722,1726],"valid"],[[1727,1727],"valid"],[[1728,1742],"valid"],[[1743,1743],"valid"],[[1744,1747],"valid"],[[1748,1748],"valid",[],"NV8"],[[1749,1756],"valid"],[[1757,1757],"disallowed"],[[1758,1758],"valid",[],"NV8"],[[1759,1768],"valid"],[[1769,1769],"valid",[],"NV8"],[[1770,1773],"valid"],[[1774,1775],"valid"],[[1776,1785],"valid"],[[1786,1790],"valid"],[[1791,1791],"valid"],[[1792,1805],"valid",[],"NV8"],[[1806,1806],"disallowed"],[[1807,1807],"disallowed"],[[1808,1836],"valid"],[[1837,1839],"valid"],[[1840,1866],"valid"],[[1867,1868],"disallowed"],[[1869,1871],"valid"],[[1872,1901],"valid"],[[1902,1919],"valid"],[[1920,1968],"valid"],[[1969,1969],"valid"],[[1970,1983],"disallowed"],[[1984,2037],"valid"],[[2038,2042],"valid",[],"NV8"],[[2043,2047],"disallowed"],[[2048,2093],"valid"],[[2094,2095],"disallowed"],[[2096,2110],"valid",[],"NV8"],[[2111,2111],"disallowed"],[[2112,2139],"valid"],[[2140,2141],"disallowed"],[[2142,2142],"valid",[],"NV8"],[[2143,2207],"disallowed"],[[2208,2208],"valid"],[[2209,2209],"valid"],[[2210,2220],"valid"],[[2221,2226],"valid"],[[2227,2228],"valid"],[[2229,2274],"disallowed"],[[2275,2275],"valid"],[[2276,2302],"valid"],[[2303,2303],"valid"],[[2304,2304],"valid"],[[2305,2307],"valid"],[[2308,2308],"valid"],[[2309,2361],"valid"],[[2362,2363],"valid"],[[2364,2381],"valid"],[[2382,2382],"valid"],[[2383,2383],"valid"],[[2384,2388],"valid"],[[2389,2389],"valid"],[[2390,2391],"valid"],[[2392,2392],"mapped",[2325,2364]],[[2393,2393],"mapped",[2326,2364]],[[2394,2394],"mapped",[2327,2364]],[[2395,2395],"mapped",[2332,2364]],[[2396,2396],"mapped",[2337,2364]],[[2397,2397],"mapped",[2338,2364]],[[2398,2398],"mapped",[2347,2364]],[[2399,2399],"mapped",[2351,2364]],[[2400,2403],"valid"],[[2404,2405],"valid",[],"NV8"],[[2406,2415],"valid"],[[2416,2416],"valid",[],"NV8"],[[2417,2418],"valid"],[[2419,2423],"valid"],[[2424,2424],"valid"],[[2425,2426],"valid"],[[2427,2428],"valid"],[[2429,2429],"valid"],[[2430,2431],"valid"],[[2432,2432],"valid"],[[2433,2435],"valid"],[[2436,2436],"disallowed"],[[2437,2444],"valid"],[[2445,2446],"disallowed"],[[2447,2448],"valid"],[[2449,2450],"disallowed"],[[2451,2472],"valid"],[[2473,2473],"disallowed"],[[2474,2480],"valid"],[[2481,2481],"disallowed"],[[2482,2482],"valid"],[[2483,2485],"disallowed"],[[2486,2489],"valid"],[[2490,2491],"disallowed"],[[2492,2492],"valid"],[[2493,2493],"valid"],[[2494,2500],"valid"],[[2501,2502],"disallowed"],[[2503,2504],"valid"],[[2505,2506],"disallowed"],[[2507,2509],"valid"],[[2510,2510],"valid"],[[2511,2518],"disallowed"],[[2519,2519],"valid"],[[2520,2523],"disallowed"],[[2524,2524],"mapped",[2465,2492]],[[2525,2525],"mapped",[2466,2492]],[[2526,2526],"disallowed"],[[2527,2527],"mapped",[2479,2492]],[[2528,2531],"valid"],[[2532,2533],"disallowed"],[[2534,2545],"valid"],[[2546,2554],"valid",[],"NV8"],[[2555,2555],"valid",[],"NV8"],[[2556,2560],"disallowed"],[[2561,2561],"valid"],[[2562,2562],"valid"],[[2563,2563],"valid"],[[2564,2564],"disallowed"],[[2565,2570],"valid"],[[2571,2574],"disallowed"],[[2575,2576],"valid"],[[2577,2578],"disallowed"],[[2579,2600],"valid"],[[2601,2601],"disallowed"],[[2602,2608],"valid"],[[2609,2609],"disallowed"],[[2610,2610],"valid"],[[2611,2611],"mapped",[2610,2620]],[[2612,2612],"disallowed"],[[2613,2613],"valid"],[[2614,2614],"mapped",[2616,2620]],[[2615,2615],"disallowed"],[[2616,2617],"valid"],[[2618,2619],"disallowed"],[[2620,2620],"valid"],[[2621,2621],"disallowed"],[[2622,2626],"valid"],[[2627,2630],"disallowed"],[[2631,2632],"valid"],[[2633,2634],"disallowed"],[[2635,2637],"valid"],[[2638,2640],"disallowed"],[[2641,2641],"valid"],[[2642,2648],"disallowed"],[[2649,2649],"mapped",[2582,2620]],[[2650,2650],"mapped",[2583,2620]],[[2651,2651],"mapped",[2588,2620]],[[2652,2652],"valid"],[[2653,2653],"disallowed"],[[2654,2654],"mapped",[2603,2620]],[[2655,2661],"disallowed"],[[2662,2676],"valid"],[[2677,2677],"valid"],[[2678,2688],"disallowed"],[[2689,2691],"valid"],[[2692,2692],"disallowed"],[[2693,2699],"valid"],[[2700,2700],"valid"],[[2701,2701],"valid"],[[2702,2702],"disallowed"],[[2703,2705],"valid"],[[2706,2706],"disallowed"],[[2707,2728],"valid"],[[2729,2729],"disallowed"],[[2730,2736],"valid"],[[2737,2737],"disallowed"],[[2738,2739],"valid"],[[2740,2740],"disallowed"],[[2741,2745],"valid"],[[2746,2747],"disallowed"],[[2748,2757],"valid"],[[2758,2758],"disallowed"],[[2759,2761],"valid"],[[2762,2762],"disallowed"],[[2763,2765],"valid"],[[2766,2767],"disallowed"],[[2768,2768],"valid"],[[2769,2783],"disallowed"],[[2784,2784],"valid"],[[2785,2787],"valid"],[[2788,2789],"disallowed"],[[2790,2799],"valid"],[[2800,2800],"valid",[],"NV8"],[[2801,2801],"valid",[],"NV8"],[[2802,2808],"disallowed"],[[2809,2809],"valid"],[[2810,2816],"disallowed"],[[2817,2819],"valid"],[[2820,2820],"disallowed"],[[2821,2828],"valid"],[[2829,2830],"disallowed"],[[2831,2832],"valid"],[[2833,2834],"disallowed"],[[2835,2856],"valid"],[[2857,2857],"disallowed"],[[2858,2864],"valid"],[[2865,2865],"disallowed"],[[2866,2867],"valid"],[[2868,2868],"disallowed"],[[2869,2869],"valid"],[[2870,2873],"valid"],[[2874,2875],"disallowed"],[[2876,2883],"valid"],[[2884,2884],"valid"],[[2885,2886],"disallowed"],[[2887,2888],"valid"],[[2889,2890],"disallowed"],[[2891,2893],"valid"],[[2894,2901],"disallowed"],[[2902,2903],"valid"],[[2904,2907],"disallowed"],[[2908,2908],"mapped",[2849,2876]],[[2909,2909],"mapped",[2850,2876]],[[2910,2910],"disallowed"],[[2911,2913],"valid"],[[2914,2915],"valid"],[[2916,2917],"disallowed"],[[2918,2927],"valid"],[[2928,2928],"valid",[],"NV8"],[[2929,2929],"valid"],[[2930,2935],"valid",[],"NV8"],[[2936,2945],"disallowed"],[[2946,2947],"valid"],[[2948,2948],"disallowed"],[[2949,2954],"valid"],[[2955,2957],"disallowed"],[[2958,2960],"valid"],[[2961,2961],"disallowed"],[[2962,2965],"valid"],[[2966,2968],"disallowed"],[[2969,2970],"valid"],[[2971,2971],"disallowed"],[[2972,2972],"valid"],[[2973,2973],"disallowed"],[[2974,2975],"valid"],[[2976,2978],"disallowed"],[[2979,2980],"valid"],[[2981,2983],"disallowed"],[[2984,2986],"valid"],[[2987,2989],"disallowed"],[[2990,2997],"valid"],[[2998,2998],"valid"],[[2999,3001],"valid"],[[3002,3005],"disallowed"],[[3006,3010],"valid"],[[3011,3013],"disallowed"],[[3014,3016],"valid"],[[3017,3017],"disallowed"],[[3018,3021],"valid"],[[3022,3023],"disallowed"],[[3024,3024],"valid"],[[3025,3030],"disallowed"],[[3031,3031],"valid"],[[3032,3045],"disallowed"],[[3046,3046],"valid"],[[3047,3055],"valid"],[[3056,3058],"valid",[],"NV8"],[[3059,3066],"valid",[],"NV8"],[[3067,3071],"disallowed"],[[3072,3072],"valid"],[[3073,3075],"valid"],[[3076,3076],"disallowed"],[[3077,3084],"valid"],[[3085,3085],"disallowed"],[[3086,3088],"valid"],[[3089,3089],"disallowed"],[[3090,3112],"valid"],[[3113,3113],"disallowed"],[[3114,3123],"valid"],[[3124,3124],"valid"],[[3125,3129],"valid"],[[3130,3132],"disallowed"],[[3133,3133],"valid"],[[3134,3140],"valid"],[[3141,3141],"disallowed"],[[3142,3144],"valid"],[[3145,3145],"disallowed"],[[3146,3149],"valid"],[[3150,3156],"disallowed"],[[3157,3158],"valid"],[[3159,3159],"disallowed"],[[3160,3161],"valid"],[[3162,3162],"valid"],[[3163,3167],"disallowed"],[[3168,3169],"valid"],[[3170,3171],"valid"],[[3172,3173],"disallowed"],[[3174,3183],"valid"],[[3184,3191],"disallowed"],[[3192,3199],"valid",[],"NV8"],[[3200,3200],"disallowed"],[[3201,3201],"valid"],[[3202,3203],"valid"],[[3204,3204],"disallowed"],[[3205,3212],"valid"],[[3213,3213],"disallowed"],[[3214,3216],"valid"],[[3217,3217],"disallowed"],[[3218,3240],"valid"],[[3241,3241],"disallowed"],[[3242,3251],"valid"],[[3252,3252],"disallowed"],[[3253,3257],"valid"],[[3258,3259],"disallowed"],[[3260,3261],"valid"],[[3262,3268],"valid"],[[3269,3269],"disallowed"],[[3270,3272],"valid"],[[3273,3273],"disallowed"],[[3274,3277],"valid"],[[3278,3284],"disallowed"],[[3285,3286],"valid"],[[3287,3293],"disallowed"],[[3294,3294],"valid"],[[3295,3295],"disallowed"],[[3296,3297],"valid"],[[3298,3299],"valid"],[[3300,3301],"disallowed"],[[3302,3311],"valid"],[[3312,3312],"disallowed"],[[3313,3314],"valid"],[[3315,3328],"disallowed"],[[3329,3329],"valid"],[[3330,3331],"valid"],[[3332,3332],"disallowed"],[[3333,3340],"valid"],[[3341,3341],"disallowed"],[[3342,3344],"valid"],[[3345,3345],"disallowed"],[[3346,3368],"valid"],[[3369,3369],"valid"],[[3370,3385],"valid"],[[3386,3386],"valid"],[[3387,3388],"disallowed"],[[3389,3389],"valid"],[[3390,3395],"valid"],[[3396,3396],"valid"],[[3397,3397],"disallowed"],[[3398,3400],"valid"],[[3401,3401],"disallowed"],[[3402,3405],"valid"],[[3406,3406],"valid"],[[3407,3414],"disallowed"],[[3415,3415],"valid"],[[3416,3422],"disallowed"],[[3423,3423],"valid"],[[3424,3425],"valid"],[[3426,3427],"valid"],[[3428,3429],"disallowed"],[[3430,3439],"valid"],[[3440,3445],"valid",[],"NV8"],[[3446,3448],"disallowed"],[[3449,3449],"valid",[],"NV8"],[[3450,3455],"valid"],[[3456,3457],"disallowed"],[[3458,3459],"valid"],[[3460,3460],"disallowed"],[[3461,3478],"valid"],[[3479,3481],"disallowed"],[[3482,3505],"valid"],[[3506,3506],"disallowed"],[[3507,3515],"valid"],[[3516,3516],"disallowed"],[[3517,3517],"valid"],[[3518,3519],"disallowed"],[[3520,3526],"valid"],[[3527,3529],"disallowed"],[[3530,3530],"valid"],[[3531,3534],"disallowed"],[[3535,3540],"valid"],[[3541,3541],"disallowed"],[[3542,3542],"valid"],[[3543,3543],"disallowed"],[[3544,3551],"valid"],[[3552,3557],"disallowed"],[[3558,3567],"valid"],[[3568,3569],"disallowed"],[[3570,3571],"valid"],[[3572,3572],"valid",[],"NV8"],[[3573,3584],"disallowed"],[[3585,3634],"valid"],[[3635,3635],"mapped",[3661,3634]],[[3636,3642],"valid"],[[3643,3646],"disallowed"],[[3647,3647],"valid",[],"NV8"],[[3648,3662],"valid"],[[3663,3663],"valid",[],"NV8"],[[3664,3673],"valid"],[[3674,3675],"valid",[],"NV8"],[[3676,3712],"disallowed"],[[3713,3714],"valid"],[[3715,3715],"disallowed"],[[3716,3716],"valid"],[[3717,3718],"disallowed"],[[3719,3720],"valid"],[[3721,3721],"disallowed"],[[3722,3722],"valid"],[[3723,3724],"disallowed"],[[3725,3725],"valid"],[[3726,3731],"disallowed"],[[3732,3735],"valid"],[[3736,3736],"disallowed"],[[3737,3743],"valid"],[[3744,3744],"disallowed"],[[3745,3747],"valid"],[[3748,3748],"disallowed"],[[3749,3749],"valid"],[[3750,3750],"disallowed"],[[3751,3751],"valid"],[[3752,3753],"disallowed"],[[3754,3755],"valid"],[[3756,3756],"disallowed"],[[3757,3762],"valid"],[[3763,3763],"mapped",[3789,3762]],[[3764,3769],"valid"],[[3770,3770],"disallowed"],[[3771,3773],"valid"],[[3774,3775],"disallowed"],[[3776,3780],"valid"],[[3781,3781],"disallowed"],[[3782,3782],"valid"],[[3783,3783],"disallowed"],[[3784,3789],"valid"],[[3790,3791],"disallowed"],[[3792,3801],"valid"],[[3802,3803],"disallowed"],[[3804,3804],"mapped",[3755,3737]],[[3805,3805],"mapped",[3755,3745]],[[3806,3807],"valid"],[[3808,3839],"disallowed"],[[3840,3840],"valid"],[[3841,3850],"valid",[],"NV8"],[[3851,3851],"valid"],[[3852,3852],"mapped",[3851]],[[3853,3863],"valid",[],"NV8"],[[3864,3865],"valid"],[[3866,3871],"valid",[],"NV8"],[[3872,3881],"valid"],[[3882,3892],"valid",[],"NV8"],[[3893,3893],"valid"],[[3894,3894],"valid",[],"NV8"],[[3895,3895],"valid"],[[3896,3896],"valid",[],"NV8"],[[3897,3897],"valid"],[[3898,3901],"valid",[],"NV8"],[[3902,3906],"valid"],[[3907,3907],"mapped",[3906,4023]],[[3908,3911],"valid"],[[3912,3912],"disallowed"],[[3913,3916],"valid"],[[3917,3917],"mapped",[3916,4023]],[[3918,3921],"valid"],[[3922,3922],"mapped",[3921,4023]],[[3923,3926],"valid"],[[3927,3927],"mapped",[3926,4023]],[[3928,3931],"valid"],[[3932,3932],"mapped",[3931,4023]],[[3933,3944],"valid"],[[3945,3945],"mapped",[3904,4021]],[[3946,3946],"valid"],[[3947,3948],"valid"],[[3949,3952],"disallowed"],[[3953,3954],"valid"],[[3955,3955],"mapped",[3953,3954]],[[3956,3956],"valid"],[[3957,3957],"mapped",[3953,3956]],[[3958,3958],"mapped",[4018,3968]],[[3959,3959],"mapped",[4018,3953,3968]],[[3960,3960],"mapped",[4019,3968]],[[3961,3961],"mapped",[4019,3953,3968]],[[3962,3968],"valid"],[[3969,3969],"mapped",[3953,3968]],[[3970,3972],"valid"],[[3973,3973],"valid",[],"NV8"],[[3974,3979],"valid"],[[3980,3983],"valid"],[[3984,3986],"valid"],[[3987,3987],"mapped",[3986,4023]],[[3988,3989],"valid"],[[3990,3990],"valid"],[[3991,3991],"valid"],[[3992,3992],"disallowed"],[[3993,3996],"valid"],[[3997,3997],"mapped",[3996,4023]],[[3998,4001],"valid"],[[4002,4002],"mapped",[4001,4023]],[[4003,4006],"valid"],[[4007,4007],"mapped",[4006,4023]],[[4008,4011],"valid"],[[4012,4012],"mapped",[4011,4023]],[[4013,4013],"valid"],[[4014,4016],"valid"],[[4017,4023],"valid"],[[4024,4024],"valid"],[[4025,4025],"mapped",[3984,4021]],[[4026,4028],"valid"],[[4029,4029],"disallowed"],[[4030,4037],"valid",[],"NV8"],[[4038,4038],"valid"],[[4039,4044],"valid",[],"NV8"],[[4045,4045],"disallowed"],[[4046,4046],"valid",[],"NV8"],[[4047,4047],"valid",[],"NV8"],[[4048,4049],"valid",[],"NV8"],[[4050,4052],"valid",[],"NV8"],[[4053,4056],"valid",[],"NV8"],[[4057,4058],"valid",[],"NV8"],[[4059,4095],"disallowed"],[[4096,4129],"valid"],[[4130,4130],"valid"],[[4131,4135],"valid"],[[4136,4136],"valid"],[[4137,4138],"valid"],[[4139,4139],"valid"],[[4140,4146],"valid"],[[4147,4149],"valid"],[[4150,4153],"valid"],[[4154,4159],"valid"],[[4160,4169],"valid"],[[4170,4175],"valid",[],"NV8"],[[4176,4185],"valid"],[[4186,4249],"valid"],[[4250,4253],"valid"],[[4254,4255],"valid",[],"NV8"],[[4256,4293],"disallowed"],[[4294,4294],"disallowed"],[[4295,4295],"mapped",[11559]],[[4296,4300],"disallowed"],[[4301,4301],"mapped",[11565]],[[4302,4303],"disallowed"],[[4304,4342],"valid"],[[4343,4344],"valid"],[[4345,4346],"valid"],[[4347,4347],"valid",[],"NV8"],[[4348,4348],"mapped",[4316]],[[4349,4351],"valid"],[[4352,4441],"valid",[],"NV8"],[[4442,4446],"valid",[],"NV8"],[[4447,4448],"disallowed"],[[4449,4514],"valid",[],"NV8"],[[4515,4519],"valid",[],"NV8"],[[4520,4601],"valid",[],"NV8"],[[4602,4607],"valid",[],"NV8"],[[4608,4614],"valid"],[[4615,4615],"valid"],[[4616,4678],"valid"],[[4679,4679],"valid"],[[4680,4680],"valid"],[[4681,4681],"disallowed"],[[4682,4685],"valid"],[[4686,4687],"disallowed"],[[4688,4694],"valid"],[[4695,4695],"disallowed"],[[4696,4696],"valid"],[[4697,4697],"disallowed"],[[4698,4701],"valid"],[[4702,4703],"disallowed"],[[4704,4742],"valid"],[[4743,4743],"valid"],[[4744,4744],"valid"],[[4745,4745],"disallowed"],[[4746,4749],"valid"],[[4750,4751],"disallowed"],[[4752,4782],"valid"],[[4783,4783],"valid"],[[4784,4784],"valid"],[[4785,4785],"disallowed"],[[4786,4789],"valid"],[[4790,4791],"disallowed"],[[4792,4798],"valid"],[[4799,4799],"disallowed"],[[4800,4800],"valid"],[[4801,4801],"disallowed"],[[4802,4805],"valid"],[[4806,4807],"disallowed"],[[4808,4814],"valid"],[[4815,4815],"valid"],[[4816,4822],"valid"],[[4823,4823],"disallowed"],[[4824,4846],"valid"],[[4847,4847],"valid"],[[4848,4878],"valid"],[[4879,4879],"valid"],[[4880,4880],"valid"],[[4881,4881],"disallowed"],[[4882,4885],"valid"],[[4886,4887],"disallowed"],[[4888,4894],"valid"],[[4895,4895],"valid"],[[4896,4934],"valid"],[[4935,4935],"valid"],[[4936,4954],"valid"],[[4955,4956],"disallowed"],[[4957,4958],"valid"],[[4959,4959],"valid"],[[4960,4960],"valid",[],"NV8"],[[4961,4988],"valid",[],"NV8"],[[4989,4991],"disallowed"],[[4992,5007],"valid"],[[5008,5017],"valid",[],"NV8"],[[5018,5023],"disallowed"],[[5024,5108],"valid"],[[5109,5109],"valid"],[[5110,5111],"disallowed"],[[5112,5112],"mapped",[5104]],[[5113,5113],"mapped",[5105]],[[5114,5114],"mapped",[5106]],[[5115,5115],"mapped",[5107]],[[5116,5116],"mapped",[5108]],[[5117,5117],"mapped",[5109]],[[5118,5119],"disallowed"],[[5120,5120],"valid",[],"NV8"],[[5121,5740],"valid"],[[5741,5742],"valid",[],"NV8"],[[5743,5750],"valid"],[[5751,5759],"valid"],[[5760,5760],"disallowed"],[[5761,5786],"valid"],[[5787,5788],"valid",[],"NV8"],[[5789,5791],"disallowed"],[[5792,5866],"valid"],[[5867,5872],"valid",[],"NV8"],[[5873,5880],"valid"],[[5881,5887],"disallowed"],[[5888,5900],"valid"],[[5901,5901],"disallowed"],[[5902,5908],"valid"],[[5909,5919],"disallowed"],[[5920,5940],"valid"],[[5941,5942],"valid",[],"NV8"],[[5943,5951],"disallowed"],[[5952,5971],"valid"],[[5972,5983],"disallowed"],[[5984,5996],"valid"],[[5997,5997],"disallowed"],[[5998,6000],"valid"],[[6001,6001],"disallowed"],[[6002,6003],"valid"],[[6004,6015],"disallowed"],[[6016,6067],"valid"],[[6068,6069],"disallowed"],[[6070,6099],"valid"],[[6100,6102],"valid",[],"NV8"],[[6103,6103],"valid"],[[6104,6107],"valid",[],"NV8"],[[6108,6108],"valid"],[[6109,6109],"valid"],[[6110,6111],"disallowed"],[[6112,6121],"valid"],[[6122,6127],"disallowed"],[[6128,6137],"valid",[],"NV8"],[[6138,6143],"disallowed"],[[6144,6149],"valid",[],"NV8"],[[6150,6150],"disallowed"],[[6151,6154],"valid",[],"NV8"],[[6155,6157],"ignored"],[[6158,6158],"disallowed"],[[6159,6159],"disallowed"],[[6160,6169],"valid"],[[6170,6175],"disallowed"],[[6176,6263],"valid"],[[6264,6271],"disallowed"],[[6272,6313],"valid"],[[6314,6314],"valid"],[[6315,6319],"disallowed"],[[6320,6389],"valid"],[[6390,6399],"disallowed"],[[6400,6428],"valid"],[[6429,6430],"valid"],[[6431,6431],"disallowed"],[[6432,6443],"valid"],[[6444,6447],"disallowed"],[[6448,6459],"valid"],[[6460,6463],"disallowed"],[[6464,6464],"valid",[],"NV8"],[[6465,6467],"disallowed"],[[6468,6469],"valid",[],"NV8"],[[6470,6509],"valid"],[[6510,6511],"disallowed"],[[6512,6516],"valid"],[[6517,6527],"disallowed"],[[6528,6569],"valid"],[[6570,6571],"valid"],[[6572,6575],"disallowed"],[[6576,6601],"valid"],[[6602,6607],"disallowed"],[[6608,6617],"valid"],[[6618,6618],"valid",[],"XV8"],[[6619,6621],"disallowed"],[[6622,6623],"valid",[],"NV8"],[[6624,6655],"valid",[],"NV8"],[[6656,6683],"valid"],[[6684,6685],"disallowed"],[[6686,6687],"valid",[],"NV8"],[[6688,6750],"valid"],[[6751,6751],"disallowed"],[[6752,6780],"valid"],[[6781,6782],"disallowed"],[[6783,6793],"valid"],[[6794,6799],"disallowed"],[[6800,6809],"valid"],[[6810,6815],"disallowed"],[[6816,6822],"valid",[],"NV8"],[[6823,6823],"valid"],[[6824,6829],"valid",[],"NV8"],[[6830,6831],"disallowed"],[[6832,6845],"valid"],[[6846,6846],"valid",[],"NV8"],[[6847,6911],"disallowed"],[[6912,6987],"valid"],[[6988,6991],"disallowed"],[[6992,7001],"valid"],[[7002,7018],"valid",[],"NV8"],[[7019,7027],"valid"],[[7028,7036],"valid",[],"NV8"],[[7037,7039],"disallowed"],[[7040,7082],"valid"],[[7083,7085],"valid"],[[7086,7097],"valid"],[[7098,7103],"valid"],[[7104,7155],"valid"],[[7156,7163],"disallowed"],[[7164,7167],"valid",[],"NV8"],[[7168,7223],"valid"],[[7224,7226],"disallowed"],[[7227,7231],"valid",[],"NV8"],[[7232,7241],"valid"],[[7242,7244],"disallowed"],[[7245,7293],"valid"],[[7294,7295],"valid",[],"NV8"],[[7296,7359],"disallowed"],[[7360,7367],"valid",[],"NV8"],[[7368,7375],"disallowed"],[[7376,7378],"valid"],[[7379,7379],"valid",[],"NV8"],[[7380,7410],"valid"],[[7411,7414],"valid"],[[7415,7415],"disallowed"],[[7416,7417],"valid"],[[7418,7423],"disallowed"],[[7424,7467],"valid"],[[7468,7468],"mapped",[97]],[[7469,7469],"mapped",[230]],[[7470,7470],"mapped",[98]],[[7471,7471],"valid"],[[7472,7472],"mapped",[100]],[[7473,7473],"mapped",[101]],[[7474,7474],"mapped",[477]],[[7475,7475],"mapped",[103]],[[7476,7476],"mapped",[104]],[[7477,7477],"mapped",[105]],[[7478,7478],"mapped",[106]],[[7479,7479],"mapped",[107]],[[7480,7480],"mapped",[108]],[[7481,7481],"mapped",[109]],[[7482,7482],"mapped",[110]],[[7483,7483],"valid"],[[7484,7484],"mapped",[111]],[[7485,7485],"mapped",[547]],[[7486,7486],"mapped",[112]],[[7487,7487],"mapped",[114]],[[7488,7488],"mapped",[116]],[[7489,7489],"mapped",[117]],[[7490,7490],"mapped",[119]],[[7491,7491],"mapped",[97]],[[7492,7492],"mapped",[592]],[[7493,7493],"mapped",[593]],[[7494,7494],"mapped",[7426]],[[7495,7495],"mapped",[98]],[[7496,7496],"mapped",[100]],[[7497,7497],"mapped",[101]],[[7498,7498],"mapped",[601]],[[7499,7499],"mapped",[603]],[[7500,7500],"mapped",[604]],[[7501,7501],"mapped",[103]],[[7502,7502],"valid"],[[7503,7503],"mapped",[107]],[[7504,7504],"mapped",[109]],[[7505,7505],"mapped",[331]],[[7506,7506],"mapped",[111]],[[7507,7507],"mapped",[596]],[[7508,7508],"mapped",[7446]],[[7509,7509],"mapped",[7447]],[[7510,7510],"mapped",[112]],[[7511,7511],"mapped",[116]],[[7512,7512],"mapped",[117]],[[7513,7513],"mapped",[7453]],[[7514,7514],"mapped",[623]],[[7515,7515],"mapped",[118]],[[7516,7516],"mapped",[7461]],[[7517,7517],"mapped",[946]],[[7518,7518],"mapped",[947]],[[7519,7519],"mapped",[948]],[[7520,7520],"mapped",[966]],[[7521,7521],"mapped",[967]],[[7522,7522],"mapped",[105]],[[7523,7523],"mapped",[114]],[[7524,7524],"mapped",[117]],[[7525,7525],"mapped",[118]],[[7526,7526],"mapped",[946]],[[7527,7527],"mapped",[947]],[[7528,7528],"mapped",[961]],[[7529,7529],"mapped",[966]],[[7530,7530],"mapped",[967]],[[7531,7531],"valid"],[[7532,7543],"valid"],[[7544,7544],"mapped",[1085]],[[7545,7578],"valid"],[[7579,7579],"mapped",[594]],[[7580,7580],"mapped",[99]],[[7581,7581],"mapped",[597]],[[7582,7582],"mapped",[240]],[[7583,7583],"mapped",[604]],[[7584,7584],"mapped",[102]],[[7585,7585],"mapped",[607]],[[7586,7586],"mapped",[609]],[[7587,7587],"mapped",[613]],[[7588,7588],"mapped",[616]],[[7589,7589],"mapped",[617]],[[7590,7590],"mapped",[618]],[[7591,7591],"mapped",[7547]],[[7592,7592],"mapped",[669]],[[7593,7593],"mapped",[621]],[[7594,7594],"mapped",[7557]],[[7595,7595],"mapped",[671]],[[7596,7596],"mapped",[625]],[[7597,7597],"mapped",[624]],[[7598,7598],"mapped",[626]],[[7599,7599],"mapped",[627]],[[7600,7600],"mapped",[628]],[[7601,7601],"mapped",[629]],[[7602,7602],"mapped",[632]],[[7603,7603],"mapped",[642]],[[7604,7604],"mapped",[643]],[[7605,7605],"mapped",[427]],[[7606,7606],"mapped",[649]],[[7607,7607],"mapped",[650]],[[7608,7608],"mapped",[7452]],[[7609,7609],"mapped",[651]],[[7610,7610],"mapped",[652]],[[7611,7611],"mapped",[122]],[[7612,7612],"mapped",[656]],[[7613,7613],"mapped",[657]],[[7614,7614],"mapped",[658]],[[7615,7615],"mapped",[952]],[[7616,7619],"valid"],[[7620,7626],"valid"],[[7627,7654],"valid"],[[7655,7669],"valid"],[[7670,7675],"disallowed"],[[7676,7676],"valid"],[[7677,7677],"valid"],[[7678,7679],"valid"],[[7680,7680],"mapped",[7681]],[[7681,7681],"valid"],[[7682,7682],"mapped",[7683]],[[7683,7683],"valid"],[[7684,7684],"mapped",[7685]],[[7685,7685],"valid"],[[7686,7686],"mapped",[7687]],[[7687,7687],"valid"],[[7688,7688],"mapped",[7689]],[[7689,7689],"valid"],[[7690,7690],"mapped",[7691]],[[7691,7691],"valid"],[[7692,7692],"mapped",[7693]],[[7693,7693],"valid"],[[7694,7694],"mapped",[7695]],[[7695,7695],"valid"],[[7696,7696],"mapped",[7697]],[[7697,7697],"valid"],[[7698,7698],"mapped",[7699]],[[7699,7699],"valid"],[[7700,7700],"mapped",[7701]],[[7701,7701],"valid"],[[7702,7702],"mapped",[7703]],[[7703,7703],"valid"],[[7704,7704],"mapped",[7705]],[[7705,7705],"valid"],[[7706,7706],"mapped",[7707]],[[7707,7707],"valid"],[[7708,7708],"mapped",[7709]],[[7709,7709],"valid"],[[7710,7710],"mapped",[7711]],[[7711,7711],"valid"],[[7712,7712],"mapped",[7713]],[[7713,7713],"valid"],[[7714,7714],"mapped",[7715]],[[7715,7715],"valid"],[[7716,7716],"mapped",[7717]],[[7717,7717],"valid"],[[7718,7718],"mapped",[7719]],[[7719,7719],"valid"],[[7720,7720],"mapped",[7721]],[[7721,7721],"valid"],[[7722,7722],"mapped",[7723]],[[7723,7723],"valid"],[[7724,7724],"mapped",[7725]],[[7725,7725],"valid"],[[7726,7726],"mapped",[7727]],[[7727,7727],"valid"],[[7728,7728],"mapped",[7729]],[[7729,7729],"valid"],[[7730,7730],"mapped",[7731]],[[7731,7731],"valid"],[[7732,7732],"mapped",[7733]],[[7733,7733],"valid"],[[7734,7734],"mapped",[7735]],[[7735,7735],"valid"],[[7736,7736],"mapped",[7737]],[[7737,7737],"valid"],[[7738,7738],"mapped",[7739]],[[7739,7739],"valid"],[[7740,7740],"mapped",[7741]],[[7741,7741],"valid"],[[7742,7742],"mapped",[7743]],[[7743,7743],"valid"],[[7744,7744],"mapped",[7745]],[[7745,7745],"valid"],[[7746,7746],"mapped",[7747]],[[7747,7747],"valid"],[[7748,7748],"mapped",[7749]],[[7749,7749],"valid"],[[7750,7750],"mapped",[7751]],[[7751,7751],"valid"],[[7752,7752],"mapped",[7753]],[[7753,7753],"valid"],[[7754,7754],"mapped",[7755]],[[7755,7755],"valid"],[[7756,7756],"mapped",[7757]],[[7757,7757],"valid"],[[7758,7758],"mapped",[7759]],[[7759,7759],"valid"],[[7760,7760],"mapped",[7761]],[[7761,7761],"valid"],[[7762,7762],"mapped",[7763]],[[7763,7763],"valid"],[[7764,7764],"mapped",[7765]],[[7765,7765],"valid"],[[7766,7766],"mapped",[7767]],[[7767,7767],"valid"],[[7768,7768],"mapped",[7769]],[[7769,7769],"valid"],[[7770,7770],"mapped",[7771]],[[7771,7771],"valid"],[[7772,7772],"mapped",[7773]],[[7773,7773],"valid"],[[7774,7774],"mapped",[7775]],[[7775,7775],"valid"],[[7776,7776],"mapped",[7777]],[[7777,7777],"valid"],[[7778,7778],"mapped",[7779]],[[7779,7779],"valid"],[[7780,7780],"mapped",[7781]],[[7781,7781],"valid"],[[7782,7782],"mapped",[7783]],[[7783,7783],"valid"],[[7784,7784],"mapped",[7785]],[[7785,7785],"valid"],[[7786,7786],"mapped",[7787]],[[7787,7787],"valid"],[[7788,7788],"mapped",[7789]],[[7789,7789],"valid"],[[7790,7790],"mapped",[7791]],[[7791,7791],"valid"],[[7792,7792],"mapped",[7793]],[[7793,7793],"valid"],[[7794,7794],"mapped",[7795]],[[7795,7795],"valid"],[[7796,7796],"mapped",[7797]],[[7797,7797],"valid"],[[7798,7798],"mapped",[7799]],[[7799,7799],"valid"],[[7800,7800],"mapped",[7801]],[[7801,7801],"valid"],[[7802,7802],"mapped",[7803]],[[7803,7803],"valid"],[[7804,7804],"mapped",[7805]],[[7805,7805],"valid"],[[7806,7806],"mapped",[7807]],[[7807,7807],"valid"],[[7808,7808],"mapped",[7809]],[[7809,7809],"valid"],[[7810,7810],"mapped",[7811]],[[7811,7811],"valid"],[[7812,7812],"mapped",[7813]],[[7813,7813],"valid"],[[7814,7814],"mapped",[7815]],[[7815,7815],"valid"],[[7816,7816],"mapped",[7817]],[[7817,7817],"valid"],[[7818,7818],"mapped",[7819]],[[7819,7819],"valid"],[[7820,7820],"mapped",[7821]],[[7821,7821],"valid"],[[7822,7822],"mapped",[7823]],[[7823,7823],"valid"],[[7824,7824],"mapped",[7825]],[[7825,7825],"valid"],[[7826,7826],"mapped",[7827]],[[7827,7827],"valid"],[[7828,7828],"mapped",[7829]],[[7829,7833],"valid"],[[7834,7834],"mapped",[97,702]],[[7835,7835],"mapped",[7777]],[[7836,7837],"valid"],[[7838,7838],"mapped",[115,115]],[[7839,7839],"valid"],[[7840,7840],"mapped",[7841]],[[7841,7841],"valid"],[[7842,7842],"mapped",[7843]],[[7843,7843],"valid"],[[7844,7844],"mapped",[7845]],[[7845,7845],"valid"],[[7846,7846],"mapped",[7847]],[[7847,7847],"valid"],[[7848,7848],"mapped",[7849]],[[7849,7849],"valid"],[[7850,7850],"mapped",[7851]],[[7851,7851],"valid"],[[7852,7852],"mapped",[7853]],[[7853,7853],"valid"],[[7854,7854],"mapped",[7855]],[[7855,7855],"valid"],[[7856,7856],"mapped",[7857]],[[7857,7857],"valid"],[[7858,7858],"mapped",[7859]],[[7859,7859],"valid"],[[7860,7860],"mapped",[7861]],[[7861,7861],"valid"],[[7862,7862],"mapped",[7863]],[[7863,7863],"valid"],[[7864,7864],"mapped",[7865]],[[7865,7865],"valid"],[[7866,7866],"mapped",[7867]],[[7867,7867],"valid"],[[7868,7868],"mapped",[7869]],[[7869,7869],"valid"],[[7870,7870],"mapped",[7871]],[[7871,7871],"valid"],[[7872,7872],"mapped",[7873]],[[7873,7873],"valid"],[[7874,7874],"mapped",[7875]],[[7875,7875],"valid"],[[7876,7876],"mapped",[7877]],[[7877,7877],"valid"],[[7878,7878],"mapped",[7879]],[[7879,7879],"valid"],[[7880,7880],"mapped",[7881]],[[7881,7881],"valid"],[[7882,7882],"mapped",[7883]],[[7883,7883],"valid"],[[7884,7884],"mapped",[7885]],[[7885,7885],"valid"],[[7886,7886],"mapped",[7887]],[[7887,7887],"valid"],[[7888,7888],"mapped",[7889]],[[7889,7889],"valid"],[[7890,7890],"mapped",[7891]],[[7891,7891],"valid"],[[7892,7892],"mapped",[7893]],[[7893,7893],"valid"],[[7894,7894],"mapped",[7895]],[[7895,7895],"valid"],[[7896,7896],"mapped",[7897]],[[7897,7897],"valid"],[[7898,7898],"mapped",[7899]],[[7899,7899],"valid"],[[7900,7900],"mapped",[7901]],[[7901,7901],"valid"],[[7902,7902],"mapped",[7903]],[[7903,7903],"valid"],[[7904,7904],"mapped",[7905]],[[7905,7905],"valid"],[[7906,7906],"mapped",[7907]],[[7907,7907],"valid"],[[7908,7908],"mapped",[7909]],[[7909,7909],"valid"],[[7910,7910],"mapped",[7911]],[[7911,7911],"valid"],[[7912,7912],"mapped",[7913]],[[7913,7913],"valid"],[[7914,7914],"mapped",[7915]],[[7915,7915],"valid"],[[7916,7916],"mapped",[7917]],[[7917,7917],"valid"],[[7918,7918],"mapped",[7919]],[[7919,7919],"valid"],[[7920,7920],"mapped",[7921]],[[7921,7921],"valid"],[[7922,7922],"mapped",[7923]],[[7923,7923],"valid"],[[7924,7924],"mapped",[7925]],[[7925,7925],"valid"],[[7926,7926],"mapped",[7927]],[[7927,7927],"valid"],[[7928,7928],"mapped",[7929]],[[7929,7929],"valid"],[[7930,7930],"mapped",[7931]],[[7931,7931],"valid"],[[7932,7932],"mapped",[7933]],[[7933,7933],"valid"],[[7934,7934],"mapped",[7935]],[[7935,7935],"valid"],[[7936,7943],"valid"],[[7944,7944],"mapped",[7936]],[[7945,7945],"mapped",[7937]],[[7946,7946],"mapped",[7938]],[[7947,7947],"mapped",[7939]],[[7948,7948],"mapped",[7940]],[[7949,7949],"mapped",[7941]],[[7950,7950],"mapped",[7942]],[[7951,7951],"mapped",[7943]],[[7952,7957],"valid"],[[7958,7959],"disallowed"],[[7960,7960],"mapped",[7952]],[[7961,7961],"mapped",[7953]],[[7962,7962],"mapped",[7954]],[[7963,7963],"mapped",[7955]],[[7964,7964],"mapped",[7956]],[[7965,7965],"mapped",[7957]],[[7966,7967],"disallowed"],[[7968,7975],"valid"],[[7976,7976],"mapped",[7968]],[[7977,7977],"mapped",[7969]],[[7978,7978],"mapped",[7970]],[[7979,7979],"mapped",[7971]],[[7980,7980],"mapped",[7972]],[[7981,7981],"mapped",[7973]],[[7982,7982],"mapped",[7974]],[[7983,7983],"mapped",[7975]],[[7984,7991],"valid"],[[7992,7992],"mapped",[7984]],[[7993,7993],"mapped",[7985]],[[7994,7994],"mapped",[7986]],[[7995,7995],"mapped",[7987]],[[7996,7996],"mapped",[7988]],[[7997,7997],"mapped",[7989]],[[7998,7998],"mapped",[7990]],[[7999,7999],"mapped",[7991]],[[8000,8005],"valid"],[[8006,8007],"disallowed"],[[8008,8008],"mapped",[8000]],[[8009,8009],"mapped",[8001]],[[8010,8010],"mapped",[8002]],[[8011,8011],"mapped",[8003]],[[8012,8012],"mapped",[8004]],[[8013,8013],"mapped",[8005]],[[8014,8015],"disallowed"],[[8016,8023],"valid"],[[8024,8024],"disallowed"],[[8025,8025],"mapped",[8017]],[[8026,8026],"disallowed"],[[8027,8027],"mapped",[8019]],[[8028,8028],"disallowed"],[[8029,8029],"mapped",[8021]],[[8030,8030],"disallowed"],[[8031,8031],"mapped",[8023]],[[8032,8039],"valid"],[[8040,8040],"mapped",[8032]],[[8041,8041],"mapped",[8033]],[[8042,8042],"mapped",[8034]],[[8043,8043],"mapped",[8035]],[[8044,8044],"mapped",[8036]],[[8045,8045],"mapped",[8037]],[[8046,8046],"mapped",[8038]],[[8047,8047],"mapped",[8039]],[[8048,8048],"valid"],[[8049,8049],"mapped",[940]],[[8050,8050],"valid"],[[8051,8051],"mapped",[941]],[[8052,8052],"valid"],[[8053,8053],"mapped",[942]],[[8054,8054],"valid"],[[8055,8055],"mapped",[943]],[[8056,8056],"valid"],[[8057,8057],"mapped",[972]],[[8058,8058],"valid"],[[8059,8059],"mapped",[973]],[[8060,8060],"valid"],[[8061,8061],"mapped",[974]],[[8062,8063],"disallowed"],[[8064,8064],"mapped",[7936,953]],[[8065,8065],"mapped",[7937,953]],[[8066,8066],"mapped",[7938,953]],[[8067,8067],"mapped",[7939,953]],[[8068,8068],"mapped",[7940,953]],[[8069,8069],"mapped",[7941,953]],[[8070,8070],"mapped",[7942,953]],[[8071,8071],"mapped",[7943,953]],[[8072,8072],"mapped",[7936,953]],[[8073,8073],"mapped",[7937,953]],[[8074,8074],"mapped",[7938,953]],[[8075,8075],"mapped",[7939,953]],[[8076,8076],"mapped",[7940,953]],[[8077,8077],"mapped",[7941,953]],[[8078,8078],"mapped",[7942,953]],[[8079,8079],"mapped",[7943,953]],[[8080,8080],"mapped",[7968,953]],[[8081,8081],"mapped",[7969,953]],[[8082,8082],"mapped",[7970,953]],[[8083,8083],"mapped",[7971,953]],[[8084,8084],"mapped",[7972,953]],[[8085,8085],"mapped",[7973,953]],[[8086,8086],"mapped",[7974,953]],[[8087,8087],"mapped",[7975,953]],[[8088,8088],"mapped",[7968,953]],[[8089,8089],"mapped",[7969,953]],[[8090,8090],"mapped",[7970,953]],[[8091,8091],"mapped",[7971,953]],[[8092,8092],"mapped",[7972,953]],[[8093,8093],"mapped",[7973,953]],[[8094,8094],"mapped",[7974,953]],[[8095,8095],"mapped",[7975,953]],[[8096,8096],"mapped",[8032,953]],[[8097,8097],"mapped",[8033,953]],[[8098,8098],"mapped",[8034,953]],[[8099,8099],"mapped",[8035,953]],[[8100,8100],"mapped",[8036,953]],[[8101,8101],"mapped",[8037,953]],[[8102,8102],"mapped",[8038,953]],[[8103,8103],"mapped",[8039,953]],[[8104,8104],"mapped",[8032,953]],[[8105,8105],"mapped",[8033,953]],[[8106,8106],"mapped",[8034,953]],[[8107,8107],"mapped",[8035,953]],[[8108,8108],"mapped",[8036,953]],[[8109,8109],"mapped",[8037,953]],[[8110,8110],"mapped",[8038,953]],[[8111,8111],"mapped",[8039,953]],[[8112,8113],"valid"],[[8114,8114],"mapped",[8048,953]],[[8115,8115],"mapped",[945,953]],[[8116,8116],"mapped",[940,953]],[[8117,8117],"disallowed"],[[8118,8118],"valid"],[[8119,8119],"mapped",[8118,953]],[[8120,8120],"mapped",[8112]],[[8121,8121],"mapped",[8113]],[[8122,8122],"mapped",[8048]],[[8123,8123],"mapped",[940]],[[8124,8124],"mapped",[945,953]],[[8125,8125],"disallowed_STD3_mapped",[32,787]],[[8126,8126],"mapped",[953]],[[8127,8127],"disallowed_STD3_mapped",[32,787]],[[8128,8128],"disallowed_STD3_mapped",[32,834]],[[8129,8129],"disallowed_STD3_mapped",[32,776,834]],[[8130,8130],"mapped",[8052,953]],[[8131,8131],"mapped",[951,953]],[[8132,8132],"mapped",[942,953]],[[8133,8133],"disallowed"],[[8134,8134],"valid"],[[8135,8135],"mapped",[8134,953]],[[8136,8136],"mapped",[8050]],[[8137,8137],"mapped",[941]],[[8138,8138],"mapped",[8052]],[[8139,8139],"mapped",[942]],[[8140,8140],"mapped",[951,953]],[[8141,8141],"disallowed_STD3_mapped",[32,787,768]],[[8142,8142],"disallowed_STD3_mapped",[32,787,769]],[[8143,8143],"disallowed_STD3_mapped",[32,787,834]],[[8144,8146],"valid"],[[8147,8147],"mapped",[912]],[[8148,8149],"disallowed"],[[8150,8151],"valid"],[[8152,8152],"mapped",[8144]],[[8153,8153],"mapped",[8145]],[[8154,8154],"mapped",[8054]],[[8155,8155],"mapped",[943]],[[8156,8156],"disallowed"],[[8157,8157],"disallowed_STD3_mapped",[32,788,768]],[[8158,8158],"disallowed_STD3_mapped",[32,788,769]],[[8159,8159],"disallowed_STD3_mapped",[32,788,834]],[[8160,8162],"valid"],[[8163,8163],"mapped",[944]],[[8164,8167],"valid"],[[8168,8168],"mapped",[8160]],[[8169,8169],"mapped",[8161]],[[8170,8170],"mapped",[8058]],[[8171,8171],"mapped",[973]],[[8172,8172],"mapped",[8165]],[[8173,8173],"disallowed_STD3_mapped",[32,776,768]],[[8174,8174],"disallowed_STD3_mapped",[32,776,769]],[[8175,8175],"disallowed_STD3_mapped",[96]],[[8176,8177],"disallowed"],[[8178,8178],"mapped",[8060,953]],[[8179,8179],"mapped",[969,953]],[[8180,8180],"mapped",[974,953]],[[8181,8181],"disallowed"],[[8182,8182],"valid"],[[8183,8183],"mapped",[8182,953]],[[8184,8184],"mapped",[8056]],[[8185,8185],"mapped",[972]],[[8186,8186],"mapped",[8060]],[[8187,8187],"mapped",[974]],[[8188,8188],"mapped",[969,953]],[[8189,8189],"disallowed_STD3_mapped",[32,769]],[[8190,8190],"disallowed_STD3_mapped",[32,788]],[[8191,8191],"disallowed"],[[8192,8202],"disallowed_STD3_mapped",[32]],[[8203,8203],"ignored"],[[8204,8205],"deviation",[]],[[8206,8207],"disallowed"],[[8208,8208],"valid",[],"NV8"],[[8209,8209],"mapped",[8208]],[[8210,8214],"valid",[],"NV8"],[[8215,8215],"disallowed_STD3_mapped",[32,819]],[[8216,8227],"valid",[],"NV8"],[[8228,8230],"disallowed"],[[8231,8231],"valid",[],"NV8"],[[8232,8238],"disallowed"],[[8239,8239],"disallowed_STD3_mapped",[32]],[[8240,8242],"valid",[],"NV8"],[[8243,8243],"mapped",[8242,8242]],[[8244,8244],"mapped",[8242,8242,8242]],[[8245,8245],"valid",[],"NV8"],[[8246,8246],"mapped",[8245,8245]],[[8247,8247],"mapped",[8245,8245,8245]],[[8248,8251],"valid",[],"NV8"],[[8252,8252],"disallowed_STD3_mapped",[33,33]],[[8253,8253],"valid",[],"NV8"],[[8254,8254],"disallowed_STD3_mapped",[32,773]],[[8255,8262],"valid",[],"NV8"],[[8263,8263],"disallowed_STD3_mapped",[63,63]],[[8264,8264],"disallowed_STD3_mapped",[63,33]],[[8265,8265],"disallowed_STD3_mapped",[33,63]],[[8266,8269],"valid",[],"NV8"],[[8270,8274],"valid",[],"NV8"],[[8275,8276],"valid",[],"NV8"],[[8277,8278],"valid",[],"NV8"],[[8279,8279],"mapped",[8242,8242,8242,8242]],[[8280,8286],"valid",[],"NV8"],[[8287,8287],"disallowed_STD3_mapped",[32]],[[8288,8288],"ignored"],[[8289,8291],"disallowed"],[[8292,8292],"ignored"],[[8293,8293],"disallowed"],[[8294,8297],"disallowed"],[[8298,8303],"disallowed"],[[8304,8304],"mapped",[48]],[[8305,8305],"mapped",[105]],[[8306,8307],"disallowed"],[[8308,8308],"mapped",[52]],[[8309,8309],"mapped",[53]],[[8310,8310],"mapped",[54]],[[8311,8311],"mapped",[55]],[[8312,8312],"mapped",[56]],[[8313,8313],"mapped",[57]],[[8314,8314],"disallowed_STD3_mapped",[43]],[[8315,8315],"mapped",[8722]],[[8316,8316],"disallowed_STD3_mapped",[61]],[[8317,8317],"disallowed_STD3_mapped",[40]],[[8318,8318],"disallowed_STD3_mapped",[41]],[[8319,8319],"mapped",[110]],[[8320,8320],"mapped",[48]],[[8321,8321],"mapped",[49]],[[8322,8322],"mapped",[50]],[[8323,8323],"mapped",[51]],[[8324,8324],"mapped",[52]],[[8325,8325],"mapped",[53]],[[8326,8326],"mapped",[54]],[[8327,8327],"mapped",[55]],[[8328,8328],"mapped",[56]],[[8329,8329],"mapped",[57]],[[8330,8330],"disallowed_STD3_mapped",[43]],[[8331,8331],"mapped",[8722]],[[8332,8332],"disallowed_STD3_mapped",[61]],[[8333,8333],"disallowed_STD3_mapped",[40]],[[8334,8334],"disallowed_STD3_mapped",[41]],[[8335,8335],"disallowed"],[[8336,8336],"mapped",[97]],[[8337,8337],"mapped",[101]],[[8338,8338],"mapped",[111]],[[8339,8339],"mapped",[120]],[[8340,8340],"mapped",[601]],[[8341,8341],"mapped",[104]],[[8342,8342],"mapped",[107]],[[8343,8343],"mapped",[108]],[[8344,8344],"mapped",[109]],[[8345,8345],"mapped",[110]],[[8346,8346],"mapped",[112]],[[8347,8347],"mapped",[115]],[[8348,8348],"mapped",[116]],[[8349,8351],"disallowed"],[[8352,8359],"valid",[],"NV8"],[[8360,8360],"mapped",[114,115]],[[8361,8362],"valid",[],"NV8"],[[8363,8363],"valid",[],"NV8"],[[8364,8364],"valid",[],"NV8"],[[8365,8367],"valid",[],"NV8"],[[8368,8369],"valid",[],"NV8"],[[8370,8373],"valid",[],"NV8"],[[8374,8376],"valid",[],"NV8"],[[8377,8377],"valid",[],"NV8"],[[8378,8378],"valid",[],"NV8"],[[8379,8381],"valid",[],"NV8"],[[8382,8382],"valid",[],"NV8"],[[8383,8399],"disallowed"],[[8400,8417],"valid",[],"NV8"],[[8418,8419],"valid",[],"NV8"],[[8420,8426],"valid",[],"NV8"],[[8427,8427],"valid",[],"NV8"],[[8428,8431],"valid",[],"NV8"],[[8432,8432],"valid",[],"NV8"],[[8433,8447],"disallowed"],[[8448,8448],"disallowed_STD3_mapped",[97,47,99]],[[8449,8449],"disallowed_STD3_mapped",[97,47,115]],[[8450,8450],"mapped",[99]],[[8451,8451],"mapped",[176,99]],[[8452,8452],"valid",[],"NV8"],[[8453,8453],"disallowed_STD3_mapped",[99,47,111]],[[8454,8454],"disallowed_STD3_mapped",[99,47,117]],[[8455,8455],"mapped",[603]],[[8456,8456],"valid",[],"NV8"],[[8457,8457],"mapped",[176,102]],[[8458,8458],"mapped",[103]],[[8459,8462],"mapped",[104]],[[8463,8463],"mapped",[295]],[[8464,8465],"mapped",[105]],[[8466,8467],"mapped",[108]],[[8468,8468],"valid",[],"NV8"],[[8469,8469],"mapped",[110]],[[8470,8470],"mapped",[110,111]],[[8471,8472],"valid",[],"NV8"],[[8473,8473],"mapped",[112]],[[8474,8474],"mapped",[113]],[[8475,8477],"mapped",[114]],[[8478,8479],"valid",[],"NV8"],[[8480,8480],"mapped",[115,109]],[[8481,8481],"mapped",[116,101,108]],[[8482,8482],"mapped",[116,109]],[[8483,8483],"valid",[],"NV8"],[[8484,8484],"mapped",[122]],[[8485,8485],"valid",[],"NV8"],[[8486,8486],"mapped",[969]],[[8487,8487],"valid",[],"NV8"],[[8488,8488],"mapped",[122]],[[8489,8489],"valid",[],"NV8"],[[8490,8490],"mapped",[107]],[[8491,8491],"mapped",[229]],[[8492,8492],"mapped",[98]],[[8493,8493],"mapped",[99]],[[8494,8494],"valid",[],"NV8"],[[8495,8496],"mapped",[101]],[[8497,8497],"mapped",[102]],[[8498,8498],"disallowed"],[[8499,8499],"mapped",[109]],[[8500,8500],"mapped",[111]],[[8501,8501],"mapped",[1488]],[[8502,8502],"mapped",[1489]],[[8503,8503],"mapped",[1490]],[[8504,8504],"mapped",[1491]],[[8505,8505],"mapped",[105]],[[8506,8506],"valid",[],"NV8"],[[8507,8507],"mapped",[102,97,120]],[[8508,8508],"mapped",[960]],[[8509,8510],"mapped",[947]],[[8511,8511],"mapped",[960]],[[8512,8512],"mapped",[8721]],[[8513,8516],"valid",[],"NV8"],[[8517,8518],"mapped",[100]],[[8519,8519],"mapped",[101]],[[8520,8520],"mapped",[105]],[[8521,8521],"mapped",[106]],[[8522,8523],"valid",[],"NV8"],[[8524,8524],"valid",[],"NV8"],[[8525,8525],"valid",[],"NV8"],[[8526,8526],"valid"],[[8527,8527],"valid",[],"NV8"],[[8528,8528],"mapped",[49,8260,55]],[[8529,8529],"mapped",[49,8260,57]],[[8530,8530],"mapped",[49,8260,49,48]],[[8531,8531],"mapped",[49,8260,51]],[[8532,8532],"mapped",[50,8260,51]],[[8533,8533],"mapped",[49,8260,53]],[[8534,8534],"mapped",[50,8260,53]],[[8535,8535],"mapped",[51,8260,53]],[[8536,8536],"mapped",[52,8260,53]],[[8537,8537],"mapped",[49,8260,54]],[[8538,8538],"mapped",[53,8260,54]],[[8539,8539],"mapped",[49,8260,56]],[[8540,8540],"mapped",[51,8260,56]],[[8541,8541],"mapped",[53,8260,56]],[[8542,8542],"mapped",[55,8260,56]],[[8543,8543],"mapped",[49,8260]],[[8544,8544],"mapped",[105]],[[8545,8545],"mapped",[105,105]],[[8546,8546],"mapped",[105,105,105]],[[8547,8547],"mapped",[105,118]],[[8548,8548],"mapped",[118]],[[8549,8549],"mapped",[118,105]],[[8550,8550],"mapped",[118,105,105]],[[8551,8551],"mapped",[118,105,105,105]],[[8552,8552],"mapped",[105,120]],[[8553,8553],"mapped",[120]],[[8554,8554],"mapped",[120,105]],[[8555,8555],"mapped",[120,105,105]],[[8556,8556],"mapped",[108]],[[8557,8557],"mapped",[99]],[[8558,8558],"mapped",[100]],[[8559,8559],"mapped",[109]],[[8560,8560],"mapped",[105]],[[8561,8561],"mapped",[105,105]],[[8562,8562],"mapped",[105,105,105]],[[8563,8563],"mapped",[105,118]],[[8564,8564],"mapped",[118]],[[8565,8565],"mapped",[118,105]],[[8566,8566],"mapped",[118,105,105]],[[8567,8567],"mapped",[118,105,105,105]],[[8568,8568],"mapped",[105,120]],[[8569,8569],"mapped",[120]],[[8570,8570],"mapped",[120,105]],[[8571,8571],"mapped",[120,105,105]],[[8572,8572],"mapped",[108]],[[8573,8573],"mapped",[99]],[[8574,8574],"mapped",[100]],[[8575,8575],"mapped",[109]],[[8576,8578],"valid",[],"NV8"],[[8579,8579],"disallowed"],[[8580,8580],"valid"],[[8581,8584],"valid",[],"NV8"],[[8585,8585],"mapped",[48,8260,51]],[[8586,8587],"valid",[],"NV8"],[[8588,8591],"disallowed"],[[8592,8682],"valid",[],"NV8"],[[8683,8691],"valid",[],"NV8"],[[8692,8703],"valid",[],"NV8"],[[8704,8747],"valid",[],"NV8"],[[8748,8748],"mapped",[8747,8747]],[[8749,8749],"mapped",[8747,8747,8747]],[[8750,8750],"valid",[],"NV8"],[[8751,8751],"mapped",[8750,8750]],[[8752,8752],"mapped",[8750,8750,8750]],[[8753,8799],"valid",[],"NV8"],[[8800,8800],"disallowed_STD3_valid"],[[8801,8813],"valid",[],"NV8"],[[8814,8815],"disallowed_STD3_valid"],[[8816,8945],"valid",[],"NV8"],[[8946,8959],"valid",[],"NV8"],[[8960,8960],"valid",[],"NV8"],[[8961,8961],"valid",[],"NV8"],[[8962,9000],"valid",[],"NV8"],[[9001,9001],"mapped",[12296]],[[9002,9002],"mapped",[12297]],[[9003,9082],"valid",[],"NV8"],[[9083,9083],"valid",[],"NV8"],[[9084,9084],"valid",[],"NV8"],[[9085,9114],"valid",[],"NV8"],[[9115,9166],"valid",[],"NV8"],[[9167,9168],"valid",[],"NV8"],[[9169,9179],"valid",[],"NV8"],[[9180,9191],"valid",[],"NV8"],[[9192,9192],"valid",[],"NV8"],[[9193,9203],"valid",[],"NV8"],[[9204,9210],"valid",[],"NV8"],[[9211,9215],"disallowed"],[[9216,9252],"valid",[],"NV8"],[[9253,9254],"valid",[],"NV8"],[[9255,9279],"disallowed"],[[9280,9290],"valid",[],"NV8"],[[9291,9311],"disallowed"],[[9312,9312],"mapped",[49]],[[9313,9313],"mapped",[50]],[[9314,9314],"mapped",[51]],[[9315,9315],"mapped",[52]],[[9316,9316],"mapped",[53]],[[9317,9317],"mapped",[54]],[[9318,9318],"mapped",[55]],[[9319,9319],"mapped",[56]],[[9320,9320],"mapped",[57]],[[9321,9321],"mapped",[49,48]],[[9322,9322],"mapped",[49,49]],[[9323,9323],"mapped",[49,50]],[[9324,9324],"mapped",[49,51]],[[9325,9325],"mapped",[49,52]],[[9326,9326],"mapped",[49,53]],[[9327,9327],"mapped",[49,54]],[[9328,9328],"mapped",[49,55]],[[9329,9329],"mapped",[49,56]],[[9330,9330],"mapped",[49,57]],[[9331,9331],"mapped",[50,48]],[[9332,9332],"disallowed_STD3_mapped",[40,49,41]],[[9333,9333],"disallowed_STD3_mapped",[40,50,41]],[[9334,9334],"disallowed_STD3_mapped",[40,51,41]],[[9335,9335],"disallowed_STD3_mapped",[40,52,41]],[[9336,9336],"disallowed_STD3_mapped",[40,53,41]],[[9337,9337],"disallowed_STD3_mapped",[40,54,41]],[[9338,9338],"disallowed_STD3_mapped",[40,55,41]],[[9339,9339],"disallowed_STD3_mapped",[40,56,41]],[[9340,9340],"disallowed_STD3_mapped",[40,57,41]],[[9341,9341],"disallowed_STD3_mapped",[40,49,48,41]],[[9342,9342],"disallowed_STD3_mapped",[40,49,49,41]],[[9343,9343],"disallowed_STD3_mapped",[40,49,50,41]],[[9344,9344],"disallowed_STD3_mapped",[40,49,51,41]],[[9345,9345],"disallowed_STD3_mapped",[40,49,52,41]],[[9346,9346],"disallowed_STD3_mapped",[40,49,53,41]],[[9347,9347],"disallowed_STD3_mapped",[40,49,54,41]],[[9348,9348],"disallowed_STD3_mapped",[40,49,55,41]],[[9349,9349],"disallowed_STD3_mapped",[40,49,56,41]],[[9350,9350],"disallowed_STD3_mapped",[40,49,57,41]],[[9351,9351],"disallowed_STD3_mapped",[40,50,48,41]],[[9352,9371],"disallowed"],[[9372,9372],"disallowed_STD3_mapped",[40,97,41]],[[9373,9373],"disallowed_STD3_mapped",[40,98,41]],[[9374,9374],"disallowed_STD3_mapped",[40,99,41]],[[9375,9375],"disallowed_STD3_mapped",[40,100,41]],[[9376,9376],"disallowed_STD3_mapped",[40,101,41]],[[9377,9377],"disallowed_STD3_mapped",[40,102,41]],[[9378,9378],"disallowed_STD3_mapped",[40,103,41]],[[9379,9379],"disallowed_STD3_mapped",[40,104,41]],[[9380,9380],"disallowed_STD3_mapped",[40,105,41]],[[9381,9381],"disallowed_STD3_mapped",[40,106,41]],[[9382,9382],"disallowed_STD3_mapped",[40,107,41]],[[9383,9383],"disallowed_STD3_mapped",[40,108,41]],[[9384,9384],"disallowed_STD3_mapped",[40,109,41]],[[9385,9385],"disallowed_STD3_mapped",[40,110,41]],[[9386,9386],"disallowed_STD3_mapped",[40,111,41]],[[9387,9387],"disallowed_STD3_mapped",[40,112,41]],[[9388,9388],"disallowed_STD3_mapped",[40,113,41]],[[9389,9389],"disallowed_STD3_mapped",[40,114,41]],[[9390,9390],"disallowed_STD3_mapped",[40,115,41]],[[9391,9391],"disallowed_STD3_mapped",[40,116,41]],[[9392,9392],"disallowed_STD3_mapped",[40,117,41]],[[9393,9393],"disallowed_STD3_mapped",[40,118,41]],[[9394,9394],"disallowed_STD3_mapped",[40,119,41]],[[9395,9395],"disallowed_STD3_mapped",[40,120,41]],[[9396,9396],"disallowed_STD3_mapped",[40,121,41]],[[9397,9397],"disallowed_STD3_mapped",[40,122,41]],[[9398,9398],"mapped",[97]],[[9399,9399],"mapped",[98]],[[9400,9400],"mapped",[99]],[[9401,9401],"mapped",[100]],[[9402,9402],"mapped",[101]],[[9403,9403],"mapped",[102]],[[9404,9404],"mapped",[103]],[[9405,9405],"mapped",[104]],[[9406,9406],"mapped",[105]],[[9407,9407],"mapped",[106]],[[9408,9408],"mapped",[107]],[[9409,9409],"mapped",[108]],[[9410,9410],"mapped",[109]],[[9411,9411],"mapped",[110]],[[9412,9412],"mapped",[111]],[[9413,9413],"mapped",[112]],[[9414,9414],"mapped",[113]],[[9415,9415],"mapped",[114]],[[9416,9416],"mapped",[115]],[[9417,9417],"mapped",[116]],[[9418,9418],"mapped",[117]],[[9419,9419],"mapped",[118]],[[9420,9420],"mapped",[119]],[[9421,9421],"mapped",[120]],[[9422,9422],"mapped",[121]],[[9423,9423],"mapped",[122]],[[9424,9424],"mapped",[97]],[[9425,9425],"mapped",[98]],[[9426,9426],"mapped",[99]],[[9427,9427],"mapped",[100]],[[9428,9428],"mapped",[101]],[[9429,9429],"mapped",[102]],[[9430,9430],"mapped",[103]],[[9431,9431],"mapped",[104]],[[9432,9432],"mapped",[105]],[[9433,9433],"mapped",[106]],[[9434,9434],"mapped",[107]],[[9435,9435],"mapped",[108]],[[9436,9436],"mapped",[109]],[[9437,9437],"mapped",[110]],[[9438,9438],"mapped",[111]],[[9439,9439],"mapped",[112]],[[9440,9440],"mapped",[113]],[[9441,9441],"mapped",[114]],[[9442,9442],"mapped",[115]],[[9443,9443],"mapped",[116]],[[9444,9444],"mapped",[117]],[[9445,9445],"mapped",[118]],[[9446,9446],"mapped",[119]],[[9447,9447],"mapped",[120]],[[9448,9448],"mapped",[121]],[[9449,9449],"mapped",[122]],[[9450,9450],"mapped",[48]],[[9451,9470],"valid",[],"NV8"],[[9471,9471],"valid",[],"NV8"],[[9472,9621],"valid",[],"NV8"],[[9622,9631],"valid",[],"NV8"],[[9632,9711],"valid",[],"NV8"],[[9712,9719],"valid",[],"NV8"],[[9720,9727],"valid",[],"NV8"],[[9728,9747],"valid",[],"NV8"],[[9748,9749],"valid",[],"NV8"],[[9750,9751],"valid",[],"NV8"],[[9752,9752],"valid",[],"NV8"],[[9753,9753],"valid",[],"NV8"],[[9754,9839],"valid",[],"NV8"],[[9840,9841],"valid",[],"NV8"],[[9842,9853],"valid",[],"NV8"],[[9854,9855],"valid",[],"NV8"],[[9856,9865],"valid",[],"NV8"],[[9866,9873],"valid",[],"NV8"],[[9874,9884],"valid",[],"NV8"],[[9885,9885],"valid",[],"NV8"],[[9886,9887],"valid",[],"NV8"],[[9888,9889],"valid",[],"NV8"],[[9890,9905],"valid",[],"NV8"],[[9906,9906],"valid",[],"NV8"],[[9907,9916],"valid",[],"NV8"],[[9917,9919],"valid",[],"NV8"],[[9920,9923],"valid",[],"NV8"],[[9924,9933],"valid",[],"NV8"],[[9934,9934],"valid",[],"NV8"],[[9935,9953],"valid",[],"NV8"],[[9954,9954],"valid",[],"NV8"],[[9955,9955],"valid",[],"NV8"],[[9956,9959],"valid",[],"NV8"],[[9960,9983],"valid",[],"NV8"],[[9984,9984],"valid",[],"NV8"],[[9985,9988],"valid",[],"NV8"],[[9989,9989],"valid",[],"NV8"],[[9990,9993],"valid",[],"NV8"],[[9994,9995],"valid",[],"NV8"],[[9996,10023],"valid",[],"NV8"],[[10024,10024],"valid",[],"NV8"],[[10025,10059],"valid",[],"NV8"],[[10060,10060],"valid",[],"NV8"],[[10061,10061],"valid",[],"NV8"],[[10062,10062],"valid",[],"NV8"],[[10063,10066],"valid",[],"NV8"],[[10067,10069],"valid",[],"NV8"],[[10070,10070],"valid",[],"NV8"],[[10071,10071],"valid",[],"NV8"],[[10072,10078],"valid",[],"NV8"],[[10079,10080],"valid",[],"NV8"],[[10081,10087],"valid",[],"NV8"],[[10088,10101],"valid",[],"NV8"],[[10102,10132],"valid",[],"NV8"],[[10133,10135],"valid",[],"NV8"],[[10136,10159],"valid",[],"NV8"],[[10160,10160],"valid",[],"NV8"],[[10161,10174],"valid",[],"NV8"],[[10175,10175],"valid",[],"NV8"],[[10176,10182],"valid",[],"NV8"],[[10183,10186],"valid",[],"NV8"],[[10187,10187],"valid",[],"NV8"],[[10188,10188],"valid",[],"NV8"],[[10189,10189],"valid",[],"NV8"],[[10190,10191],"valid",[],"NV8"],[[10192,10219],"valid",[],"NV8"],[[10220,10223],"valid",[],"NV8"],[[10224,10239],"valid",[],"NV8"],[[10240,10495],"valid",[],"NV8"],[[10496,10763],"valid",[],"NV8"],[[10764,10764],"mapped",[8747,8747,8747,8747]],[[10765,10867],"valid",[],"NV8"],[[10868,10868],"disallowed_STD3_mapped",[58,58,61]],[[10869,10869],"disallowed_STD3_mapped",[61,61]],[[10870,10870],"disallowed_STD3_mapped",[61,61,61]],[[10871,10971],"valid",[],"NV8"],[[10972,10972],"mapped",[10973,824]],[[10973,11007],"valid",[],"NV8"],[[11008,11021],"valid",[],"NV8"],[[11022,11027],"valid",[],"NV8"],[[11028,11034],"valid",[],"NV8"],[[11035,11039],"valid",[],"NV8"],[[11040,11043],"valid",[],"NV8"],[[11044,11084],"valid",[],"NV8"],[[11085,11087],"valid",[],"NV8"],[[11088,11092],"valid",[],"NV8"],[[11093,11097],"valid",[],"NV8"],[[11098,11123],"valid",[],"NV8"],[[11124,11125],"disallowed"],[[11126,11157],"valid",[],"NV8"],[[11158,11159],"disallowed"],[[11160,11193],"valid",[],"NV8"],[[11194,11196],"disallowed"],[[11197,11208],"valid",[],"NV8"],[[11209,11209],"disallowed"],[[11210,11217],"valid",[],"NV8"],[[11218,11243],"disallowed"],[[11244,11247],"valid",[],"NV8"],[[11248,11263],"disallowed"],[[11264,11264],"mapped",[11312]],[[11265,11265],"mapped",[11313]],[[11266,11266],"mapped",[11314]],[[11267,11267],"mapped",[11315]],[[11268,11268],"mapped",[11316]],[[11269,11269],"mapped",[11317]],[[11270,11270],"mapped",[11318]],[[11271,11271],"mapped",[11319]],[[11272,11272],"mapped",[11320]],[[11273,11273],"mapped",[11321]],[[11274,11274],"mapped",[11322]],[[11275,11275],"mapped",[11323]],[[11276,11276],"mapped",[11324]],[[11277,11277],"mapped",[11325]],[[11278,11278],"mapped",[11326]],[[11279,11279],"mapped",[11327]],[[11280,11280],"mapped",[11328]],[[11281,11281],"mapped",[11329]],[[11282,11282],"mapped",[11330]],[[11283,11283],"mapped",[11331]],[[11284,11284],"mapped",[11332]],[[11285,11285],"mapped",[11333]],[[11286,11286],"mapped",[11334]],[[11287,11287],"mapped",[11335]],[[11288,11288],"mapped",[11336]],[[11289,11289],"mapped",[11337]],[[11290,11290],"mapped",[11338]],[[11291,11291],"mapped",[11339]],[[11292,11292],"mapped",[11340]],[[11293,11293],"mapped",[11341]],[[11294,11294],"mapped",[11342]],[[11295,11295],"mapped",[11343]],[[11296,11296],"mapped",[11344]],[[11297,11297],"mapped",[11345]],[[11298,11298],"mapped",[11346]],[[11299,11299],"mapped",[11347]],[[11300,11300],"mapped",[11348]],[[11301,11301],"mapped",[11349]],[[11302,11302],"mapped",[11350]],[[11303,11303],"mapped",[11351]],[[11304,11304],"mapped",[11352]],[[11305,11305],"mapped",[11353]],[[11306,11306],"mapped",[11354]],[[11307,11307],"mapped",[11355]],[[11308,11308],"mapped",[11356]],[[11309,11309],"mapped",[11357]],[[11310,11310],"mapped",[11358]],[[11311,11311],"disallowed"],[[11312,11358],"valid"],[[11359,11359],"disallowed"],[[11360,11360],"mapped",[11361]],[[11361,11361],"valid"],[[11362,11362],"mapped",[619]],[[11363,11363],"mapped",[7549]],[[11364,11364],"mapped",[637]],[[11365,11366],"valid"],[[11367,11367],"mapped",[11368]],[[11368,11368],"valid"],[[11369,11369],"mapped",[11370]],[[11370,11370],"valid"],[[11371,11371],"mapped",[11372]],[[11372,11372],"valid"],[[11373,11373],"mapped",[593]],[[11374,11374],"mapped",[625]],[[11375,11375],"mapped",[592]],[[11376,11376],"mapped",[594]],[[11377,11377],"valid"],[[11378,11378],"mapped",[11379]],[[11379,11379],"valid"],[[11380,11380],"valid"],[[11381,11381],"mapped",[11382]],[[11382,11383],"valid"],[[11384,11387],"valid"],[[11388,11388],"mapped",[106]],[[11389,11389],"mapped",[118]],[[11390,11390],"mapped",[575]],[[11391,11391],"mapped",[576]],[[11392,11392],"mapped",[11393]],[[11393,11393],"valid"],[[11394,11394],"mapped",[11395]],[[11395,11395],"valid"],[[11396,11396],"mapped",[11397]],[[11397,11397],"valid"],[[11398,11398],"mapped",[11399]],[[11399,11399],"valid"],[[11400,11400],"mapped",[11401]],[[11401,11401],"valid"],[[11402,11402],"mapped",[11403]],[[11403,11403],"valid"],[[11404,11404],"mapped",[11405]],[[11405,11405],"valid"],[[11406,11406],"mapped",[11407]],[[11407,11407],"valid"],[[11408,11408],"mapped",[11409]],[[11409,11409],"valid"],[[11410,11410],"mapped",[11411]],[[11411,11411],"valid"],[[11412,11412],"mapped",[11413]],[[11413,11413],"valid"],[[11414,11414],"mapped",[11415]],[[11415,11415],"valid"],[[11416,11416],"mapped",[11417]],[[11417,11417],"valid"],[[11418,11418],"mapped",[11419]],[[11419,11419],"valid"],[[11420,11420],"mapped",[11421]],[[11421,11421],"valid"],[[11422,11422],"mapped",[11423]],[[11423,11423],"valid"],[[11424,11424],"mapped",[11425]],[[11425,11425],"valid"],[[11426,11426],"mapped",[11427]],[[11427,11427],"valid"],[[11428,11428],"mapped",[11429]],[[11429,11429],"valid"],[[11430,11430],"mapped",[11431]],[[11431,11431],"valid"],[[11432,11432],"mapped",[11433]],[[11433,11433],"valid"],[[11434,11434],"mapped",[11435]],[[11435,11435],"valid"],[[11436,11436],"mapped",[11437]],[[11437,11437],"valid"],[[11438,11438],"mapped",[11439]],[[11439,11439],"valid"],[[11440,11440],"mapped",[11441]],[[11441,11441],"valid"],[[11442,11442],"mapped",[11443]],[[11443,11443],"valid"],[[11444,11444],"mapped",[11445]],[[11445,11445],"valid"],[[11446,11446],"mapped",[11447]],[[11447,11447],"valid"],[[11448,11448],"mapped",[11449]],[[11449,11449],"valid"],[[11450,11450],"mapped",[11451]],[[11451,11451],"valid"],[[11452,11452],"mapped",[11453]],[[11453,11453],"valid"],[[11454,11454],"mapped",[11455]],[[11455,11455],"valid"],[[11456,11456],"mapped",[11457]],[[11457,11457],"valid"],[[11458,11458],"mapped",[11459]],[[11459,11459],"valid"],[[11460,11460],"mapped",[11461]],[[11461,11461],"valid"],[[11462,11462],"mapped",[11463]],[[11463,11463],"valid"],[[11464,11464],"mapped",[11465]],[[11465,11465],"valid"],[[11466,11466],"mapped",[11467]],[[11467,11467],"valid"],[[11468,11468],"mapped",[11469]],[[11469,11469],"valid"],[[11470,11470],"mapped",[11471]],[[11471,11471],"valid"],[[11472,11472],"mapped",[11473]],[[11473,11473],"valid"],[[11474,11474],"mapped",[11475]],[[11475,11475],"valid"],[[11476,11476],"mapped",[11477]],[[11477,11477],"valid"],[[11478,11478],"mapped",[11479]],[[11479,11479],"valid"],[[11480,11480],"mapped",[11481]],[[11481,11481],"valid"],[[11482,11482],"mapped",[11483]],[[11483,11483],"valid"],[[11484,11484],"mapped",[11485]],[[11485,11485],"valid"],[[11486,11486],"mapped",[11487]],[[11487,11487],"valid"],[[11488,11488],"mapped",[11489]],[[11489,11489],"valid"],[[11490,11490],"mapped",[11491]],[[11491,11492],"valid"],[[11493,11498],"valid",[],"NV8"],[[11499,11499],"mapped",[11500]],[[11500,11500],"valid"],[[11501,11501],"mapped",[11502]],[[11502,11505],"valid"],[[11506,11506],"mapped",[11507]],[[11507,11507],"valid"],[[11508,11512],"disallowed"],[[11513,11519],"valid",[],"NV8"],[[11520,11557],"valid"],[[11558,11558],"disallowed"],[[11559,11559],"valid"],[[11560,11564],"disallowed"],[[11565,11565],"valid"],[[11566,11567],"disallowed"],[[11568,11621],"valid"],[[11622,11623],"valid"],[[11624,11630],"disallowed"],[[11631,11631],"mapped",[11617]],[[11632,11632],"valid",[],"NV8"],[[11633,11646],"disallowed"],[[11647,11647],"valid"],[[11648,11670],"valid"],[[11671,11679],"disallowed"],[[11680,11686],"valid"],[[11687,11687],"disallowed"],[[11688,11694],"valid"],[[11695,11695],"disallowed"],[[11696,11702],"valid"],[[11703,11703],"disallowed"],[[11704,11710],"valid"],[[11711,11711],"disallowed"],[[11712,11718],"valid"],[[11719,11719],"disallowed"],[[11720,11726],"valid"],[[11727,11727],"disallowed"],[[11728,11734],"valid"],[[11735,11735],"disallowed"],[[11736,11742],"valid"],[[11743,11743],"disallowed"],[[11744,11775],"valid"],[[11776,11799],"valid",[],"NV8"],[[11800,11803],"valid",[],"NV8"],[[11804,11805],"valid",[],"NV8"],[[11806,11822],"valid",[],"NV8"],[[11823,11823],"valid"],[[11824,11824],"valid",[],"NV8"],[[11825,11825],"valid",[],"NV8"],[[11826,11835],"valid",[],"NV8"],[[11836,11842],"valid",[],"NV8"],[[11843,11903],"disallowed"],[[11904,11929],"valid",[],"NV8"],[[11930,11930],"disallowed"],[[11931,11934],"valid",[],"NV8"],[[11935,11935],"mapped",[27597]],[[11936,12018],"valid",[],"NV8"],[[12019,12019],"mapped",[40863]],[[12020,12031],"disallowed"],[[12032,12032],"mapped",[19968]],[[12033,12033],"mapped",[20008]],[[12034,12034],"mapped",[20022]],[[12035,12035],"mapped",[20031]],[[12036,12036],"mapped",[20057]],[[12037,12037],"mapped",[20101]],[[12038,12038],"mapped",[20108]],[[12039,12039],"mapped",[20128]],[[12040,12040],"mapped",[20154]],[[12041,12041],"mapped",[20799]],[[12042,12042],"mapped",[20837]],[[12043,12043],"mapped",[20843]],[[12044,12044],"mapped",[20866]],[[12045,12045],"mapped",[20886]],[[12046,12046],"mapped",[20907]],[[12047,12047],"mapped",[20960]],[[12048,12048],"mapped",[20981]],[[12049,12049],"mapped",[20992]],[[12050,12050],"mapped",[21147]],[[12051,12051],"mapped",[21241]],[[12052,12052],"mapped",[21269]],[[12053,12053],"mapped",[21274]],[[12054,12054],"mapped",[21304]],[[12055,12055],"mapped",[21313]],[[12056,12056],"mapped",[21340]],[[12057,12057],"mapped",[21353]],[[12058,12058],"mapped",[21378]],[[12059,12059],"mapped",[21430]],[[12060,12060],"mapped",[21448]],[[12061,12061],"mapped",[21475]],[[12062,12062],"mapped",[22231]],[[12063,12063],"mapped",[22303]],[[12064,12064],"mapped",[22763]],[[12065,12065],"mapped",[22786]],[[12066,12066],"mapped",[22794]],[[12067,12067],"mapped",[22805]],[[12068,12068],"mapped",[22823]],[[12069,12069],"mapped",[22899]],[[12070,12070],"mapped",[23376]],[[12071,12071],"mapped",[23424]],[[12072,12072],"mapped",[23544]],[[12073,12073],"mapped",[23567]],[[12074,12074],"mapped",[23586]],[[12075,12075],"mapped",[23608]],[[12076,12076],"mapped",[23662]],[[12077,12077],"mapped",[23665]],[[12078,12078],"mapped",[24027]],[[12079,12079],"mapped",[24037]],[[12080,12080],"mapped",[24049]],[[12081,12081],"mapped",[24062]],[[12082,12082],"mapped",[24178]],[[12083,12083],"mapped",[24186]],[[12084,12084],"mapped",[24191]],[[12085,12085],"mapped",[24308]],[[12086,12086],"mapped",[24318]],[[12087,12087],"mapped",[24331]],[[12088,12088],"mapped",[24339]],[[12089,12089],"mapped",[24400]],[[12090,12090],"mapped",[24417]],[[12091,12091],"mapped",[24435]],[[12092,12092],"mapped",[24515]],[[12093,12093],"mapped",[25096]],[[12094,12094],"mapped",[25142]],[[12095,12095],"mapped",[25163]],[[12096,12096],"mapped",[25903]],[[12097,12097],"mapped",[25908]],[[12098,12098],"mapped",[25991]],[[12099,12099],"mapped",[26007]],[[12100,12100],"mapped",[26020]],[[12101,12101],"mapped",[26041]],[[12102,12102],"mapped",[26080]],[[12103,12103],"mapped",[26085]],[[12104,12104],"mapped",[26352]],[[12105,12105],"mapped",[26376]],[[12106,12106],"mapped",[26408]],[[12107,12107],"mapped",[27424]],[[12108,12108],"mapped",[27490]],[[12109,12109],"mapped",[27513]],[[12110,12110],"mapped",[27571]],[[12111,12111],"mapped",[27595]],[[12112,12112],"mapped",[27604]],[[12113,12113],"mapped",[27611]],[[12114,12114],"mapped",[27663]],[[12115,12115],"mapped",[27668]],[[12116,12116],"mapped",[27700]],[[12117,12117],"mapped",[28779]],[[12118,12118],"mapped",[29226]],[[12119,12119],"mapped",[29238]],[[12120,12120],"mapped",[29243]],[[12121,12121],"mapped",[29247]],[[12122,12122],"mapped",[29255]],[[12123,12123],"mapped",[29273]],[[12124,12124],"mapped",[29275]],[[12125,12125],"mapped",[29356]],[[12126,12126],"mapped",[29572]],[[12127,12127],"mapped",[29577]],[[12128,12128],"mapped",[29916]],[[12129,12129],"mapped",[29926]],[[12130,12130],"mapped",[29976]],[[12131,12131],"mapped",[29983]],[[12132,12132],"mapped",[29992]],[[12133,12133],"mapped",[30000]],[[12134,12134],"mapped",[30091]],[[12135,12135],"mapped",[30098]],[[12136,12136],"mapped",[30326]],[[12137,12137],"mapped",[30333]],[[12138,12138],"mapped",[30382]],[[12139,12139],"mapped",[30399]],[[12140,12140],"mapped",[30446]],[[12141,12141],"mapped",[30683]],[[12142,12142],"mapped",[30690]],[[12143,12143],"mapped",[30707]],[[12144,12144],"mapped",[31034]],[[12145,12145],"mapped",[31160]],[[12146,12146],"mapped",[31166]],[[12147,12147],"mapped",[31348]],[[12148,12148],"mapped",[31435]],[[12149,12149],"mapped",[31481]],[[12150,12150],"mapped",[31859]],[[12151,12151],"mapped",[31992]],[[12152,12152],"mapped",[32566]],[[12153,12153],"mapped",[32593]],[[12154,12154],"mapped",[32650]],[[12155,12155],"mapped",[32701]],[[12156,12156],"mapped",[32769]],[[12157,12157],"mapped",[32780]],[[12158,12158],"mapped",[32786]],[[12159,12159],"mapped",[32819]],[[12160,12160],"mapped",[32895]],[[12161,12161],"mapped",[32905]],[[12162,12162],"mapped",[33251]],[[12163,12163],"mapped",[33258]],[[12164,12164],"mapped",[33267]],[[12165,12165],"mapped",[33276]],[[12166,12166],"mapped",[33292]],[[12167,12167],"mapped",[33307]],[[12168,12168],"mapped",[33311]],[[12169,12169],"mapped",[33390]],[[12170,12170],"mapped",[33394]],[[12171,12171],"mapped",[33400]],[[12172,12172],"mapped",[34381]],[[12173,12173],"mapped",[34411]],[[12174,12174],"mapped",[34880]],[[12175,12175],"mapped",[34892]],[[12176,12176],"mapped",[34915]],[[12177,12177],"mapped",[35198]],[[12178,12178],"mapped",[35211]],[[12179,12179],"mapped",[35282]],[[12180,12180],"mapped",[35328]],[[12181,12181],"mapped",[35895]],[[12182,12182],"mapped",[35910]],[[12183,12183],"mapped",[35925]],[[12184,12184],"mapped",[35960]],[[12185,12185],"mapped",[35997]],[[12186,12186],"mapped",[36196]],[[12187,12187],"mapped",[36208]],[[12188,12188],"mapped",[36275]],[[12189,12189],"mapped",[36523]],[[12190,12190],"mapped",[36554]],[[12191,12191],"mapped",[36763]],[[12192,12192],"mapped",[36784]],[[12193,12193],"mapped",[36789]],[[12194,12194],"mapped",[37009]],[[12195,12195],"mapped",[37193]],[[12196,12196],"mapped",[37318]],[[12197,12197],"mapped",[37324]],[[12198,12198],"mapped",[37329]],[[12199,12199],"mapped",[38263]],[[12200,12200],"mapped",[38272]],[[12201,12201],"mapped",[38428]],[[12202,12202],"mapped",[38582]],[[12203,12203],"mapped",[38585]],[[12204,12204],"mapped",[38632]],[[12205,12205],"mapped",[38737]],[[12206,12206],"mapped",[38750]],[[12207,12207],"mapped",[38754]],[[12208,12208],"mapped",[38761]],[[12209,12209],"mapped",[38859]],[[12210,12210],"mapped",[38893]],[[12211,12211],"mapped",[38899]],[[12212,12212],"mapped",[38913]],[[12213,12213],"mapped",[39080]],[[12214,12214],"mapped",[39131]],[[12215,12215],"mapped",[39135]],[[12216,12216],"mapped",[39318]],[[12217,12217],"mapped",[39321]],[[12218,12218],"mapped",[39340]],[[12219,12219],"mapped",[39592]],[[12220,12220],"mapped",[39640]],[[12221,12221],"mapped",[39647]],[[12222,12222],"mapped",[39717]],[[12223,12223],"mapped",[39727]],[[12224,12224],"mapped",[39730]],[[12225,12225],"mapped",[39740]],[[12226,12226],"mapped",[39770]],[[12227,12227],"mapped",[40165]],[[12228,12228],"mapped",[40565]],[[12229,12229],"mapped",[40575]],[[12230,12230],"mapped",[40613]],[[12231,12231],"mapped",[40635]],[[12232,12232],"mapped",[40643]],[[12233,12233],"mapped",[40653]],[[12234,12234],"mapped",[40657]],[[12235,12235],"mapped",[40697]],[[12236,12236],"mapped",[40701]],[[12237,12237],"mapped",[40718]],[[12238,12238],"mapped",[40723]],[[12239,12239],"mapped",[40736]],[[12240,12240],"mapped",[40763]],[[12241,12241],"mapped",[40778]],[[12242,12242],"mapped",[40786]],[[12243,12243],"mapped",[40845]],[[12244,12244],"mapped",[40860]],[[12245,12245],"mapped",[40864]],[[12246,12271],"disallowed"],[[12272,12283],"disallowed"],[[12284,12287],"disallowed"],[[12288,12288],"disallowed_STD3_mapped",[32]],[[12289,12289],"valid",[],"NV8"],[[12290,12290],"mapped",[46]],[[12291,12292],"valid",[],"NV8"],[[12293,12295],"valid"],[[12296,12329],"valid",[],"NV8"],[[12330,12333],"valid"],[[12334,12341],"valid",[],"NV8"],[[12342,12342],"mapped",[12306]],[[12343,12343],"valid",[],"NV8"],[[12344,12344],"mapped",[21313]],[[12345,12345],"mapped",[21316]],[[12346,12346],"mapped",[21317]],[[12347,12347],"valid",[],"NV8"],[[12348,12348],"valid"],[[12349,12349],"valid",[],"NV8"],[[12350,12350],"valid",[],"NV8"],[[12351,12351],"valid",[],"NV8"],[[12352,12352],"disallowed"],[[12353,12436],"valid"],[[12437,12438],"valid"],[[12439,12440],"disallowed"],[[12441,12442],"valid"],[[12443,12443],"disallowed_STD3_mapped",[32,12441]],[[12444,12444],"disallowed_STD3_mapped",[32,12442]],[[12445,12446],"valid"],[[12447,12447],"mapped",[12424,12426]],[[12448,12448],"valid",[],"NV8"],[[12449,12542],"valid"],[[12543,12543],"mapped",[12467,12488]],[[12544,12548],"disallowed"],[[12549,12588],"valid"],[[12589,12589],"valid"],[[12590,12592],"disallowed"],[[12593,12593],"mapped",[4352]],[[12594,12594],"mapped",[4353]],[[12595,12595],"mapped",[4522]],[[12596,12596],"mapped",[4354]],[[12597,12597],"mapped",[4524]],[[12598,12598],"mapped",[4525]],[[12599,12599],"mapped",[4355]],[[12600,12600],"mapped",[4356]],[[12601,12601],"mapped",[4357]],[[12602,12602],"mapped",[4528]],[[12603,12603],"mapped",[4529]],[[12604,12604],"mapped",[4530]],[[12605,12605],"mapped",[4531]],[[12606,12606],"mapped",[4532]],[[12607,12607],"mapped",[4533]],[[12608,12608],"mapped",[4378]],[[12609,12609],"mapped",[4358]],[[12610,12610],"mapped",[4359]],[[12611,12611],"mapped",[4360]],[[12612,12612],"mapped",[4385]],[[12613,12613],"mapped",[4361]],[[12614,12614],"mapped",[4362]],[[12615,12615],"mapped",[4363]],[[12616,12616],"mapped",[4364]],[[12617,12617],"mapped",[4365]],[[12618,12618],"mapped",[4366]],[[12619,12619],"mapped",[4367]],[[12620,12620],"mapped",[4368]],[[12621,12621],"mapped",[4369]],[[12622,12622],"mapped",[4370]],[[12623,12623],"mapped",[4449]],[[12624,12624],"mapped",[4450]],[[12625,12625],"mapped",[4451]],[[12626,12626],"mapped",[4452]],[[12627,12627],"mapped",[4453]],[[12628,12628],"mapped",[4454]],[[12629,12629],"mapped",[4455]],[[12630,12630],"mapped",[4456]],[[12631,12631],"mapped",[4457]],[[12632,12632],"mapped",[4458]],[[12633,12633],"mapped",[4459]],[[12634,12634],"mapped",[4460]],[[12635,12635],"mapped",[4461]],[[12636,12636],"mapped",[4462]],[[12637,12637],"mapped",[4463]],[[12638,12638],"mapped",[4464]],[[12639,12639],"mapped",[4465]],[[12640,12640],"mapped",[4466]],[[12641,12641],"mapped",[4467]],[[12642,12642],"mapped",[4468]],[[12643,12643],"mapped",[4469]],[[12644,12644],"disallowed"],[[12645,12645],"mapped",[4372]],[[12646,12646],"mapped",[4373]],[[12647,12647],"mapped",[4551]],[[12648,12648],"mapped",[4552]],[[12649,12649],"mapped",[4556]],[[12650,12650],"mapped",[4558]],[[12651,12651],"mapped",[4563]],[[12652,12652],"mapped",[4567]],[[12653,12653],"mapped",[4569]],[[12654,12654],"mapped",[4380]],[[12655,12655],"mapped",[4573]],[[12656,12656],"mapped",[4575]],[[12657,12657],"mapped",[4381]],[[12658,12658],"mapped",[4382]],[[12659,12659],"mapped",[4384]],[[12660,12660],"mapped",[4386]],[[12661,12661],"mapped",[4387]],[[12662,12662],"mapped",[4391]],[[12663,12663],"mapped",[4393]],[[12664,12664],"mapped",[4395]],[[12665,12665],"mapped",[4396]],[[12666,12666],"mapped",[4397]],[[12667,12667],"mapped",[4398]],[[12668,12668],"mapped",[4399]],[[12669,12669],"mapped",[4402]],[[12670,12670],"mapped",[4406]],[[12671,12671],"mapped",[4416]],[[12672,12672],"mapped",[4423]],[[12673,12673],"mapped",[4428]],[[12674,12674],"mapped",[4593]],[[12675,12675],"mapped",[4594]],[[12676,12676],"mapped",[4439]],[[12677,12677],"mapped",[4440]],[[12678,12678],"mapped",[4441]],[[12679,12679],"mapped",[4484]],[[12680,12680],"mapped",[4485]],[[12681,12681],"mapped",[4488]],[[12682,12682],"mapped",[4497]],[[12683,12683],"mapped",[4498]],[[12684,12684],"mapped",[4500]],[[12685,12685],"mapped",[4510]],[[12686,12686],"mapped",[4513]],[[12687,12687],"disallowed"],[[12688,12689],"valid",[],"NV8"],[[12690,12690],"mapped",[19968]],[[12691,12691],"mapped",[20108]],[[12692,12692],"mapped",[19977]],[[12693,12693],"mapped",[22235]],[[12694,12694],"mapped",[19978]],[[12695,12695],"mapped",[20013]],[[12696,12696],"mapped",[19979]],[[12697,12697],"mapped",[30002]],[[12698,12698],"mapped",[20057]],[[12699,12699],"mapped",[19993]],[[12700,12700],"mapped",[19969]],[[12701,12701],"mapped",[22825]],[[12702,12702],"mapped",[22320]],[[12703,12703],"mapped",[20154]],[[12704,12727],"valid"],[[12728,12730],"valid"],[[12731,12735],"disallowed"],[[12736,12751],"valid",[],"NV8"],[[12752,12771],"valid",[],"NV8"],[[12772,12783],"disallowed"],[[12784,12799],"valid"],[[12800,12800],"disallowed_STD3_mapped",[40,4352,41]],[[12801,12801],"disallowed_STD3_mapped",[40,4354,41]],[[12802,12802],"disallowed_STD3_mapped",[40,4355,41]],[[12803,12803],"disallowed_STD3_mapped",[40,4357,41]],[[12804,12804],"disallowed_STD3_mapped",[40,4358,41]],[[12805,12805],"disallowed_STD3_mapped",[40,4359,41]],[[12806,12806],"disallowed_STD3_mapped",[40,4361,41]],[[12807,12807],"disallowed_STD3_mapped",[40,4363,41]],[[12808,12808],"disallowed_STD3_mapped",[40,4364,41]],[[12809,12809],"disallowed_STD3_mapped",[40,4366,41]],[[12810,12810],"disallowed_STD3_mapped",[40,4367,41]],[[12811,12811],"disallowed_STD3_mapped",[40,4368,41]],[[12812,12812],"disallowed_STD3_mapped",[40,4369,41]],[[12813,12813],"disallowed_STD3_mapped",[40,4370,41]],[[12814,12814],"disallowed_STD3_mapped",[40,44032,41]],[[12815,12815],"disallowed_STD3_mapped",[40,45208,41]],[[12816,12816],"disallowed_STD3_mapped",[40,45796,41]],[[12817,12817],"disallowed_STD3_mapped",[40,46972,41]],[[12818,12818],"disallowed_STD3_mapped",[40,47560,41]],[[12819,12819],"disallowed_STD3_mapped",[40,48148,41]],[[12820,12820],"disallowed_STD3_mapped",[40,49324,41]],[[12821,12821],"disallowed_STD3_mapped",[40,50500,41]],[[12822,12822],"disallowed_STD3_mapped",[40,51088,41]],[[12823,12823],"disallowed_STD3_mapped",[40,52264,41]],[[12824,12824],"disallowed_STD3_mapped",[40,52852,41]],[[12825,12825],"disallowed_STD3_mapped",[40,53440,41]],[[12826,12826],"disallowed_STD3_mapped",[40,54028,41]],[[12827,12827],"disallowed_STD3_mapped",[40,54616,41]],[[12828,12828],"disallowed_STD3_mapped",[40,51452,41]],[[12829,12829],"disallowed_STD3_mapped",[40,50724,51204,41]],[[12830,12830],"disallowed_STD3_mapped",[40,50724,54980,41]],[[12831,12831],"disallowed"],[[12832,12832],"disallowed_STD3_mapped",[40,19968,41]],[[12833,12833],"disallowed_STD3_mapped",[40,20108,41]],[[12834,12834],"disallowed_STD3_mapped",[40,19977,41]],[[12835,12835],"disallowed_STD3_mapped",[40,22235,41]],[[12836,12836],"disallowed_STD3_mapped",[40,20116,41]],[[12837,12837],"disallowed_STD3_mapped",[40,20845,41]],[[12838,12838],"disallowed_STD3_mapped",[40,19971,41]],[[12839,12839],"disallowed_STD3_mapped",[40,20843,41]],[[12840,12840],"disallowed_STD3_mapped",[40,20061,41]],[[12841,12841],"disallowed_STD3_mapped",[40,21313,41]],[[12842,12842],"disallowed_STD3_mapped",[40,26376,41]],[[12843,12843],"disallowed_STD3_mapped",[40,28779,41]],[[12844,12844],"disallowed_STD3_mapped",[40,27700,41]],[[12845,12845],"disallowed_STD3_mapped",[40,26408,41]],[[12846,12846],"disallowed_STD3_mapped",[40,37329,41]],[[12847,12847],"disallowed_STD3_mapped",[40,22303,41]],[[12848,12848],"disallowed_STD3_mapped",[40,26085,41]],[[12849,12849],"disallowed_STD3_mapped",[40,26666,41]],[[12850,12850],"disallowed_STD3_mapped",[40,26377,41]],[[12851,12851],"disallowed_STD3_mapped",[40,31038,41]],[[12852,12852],"disallowed_STD3_mapped",[40,21517,41]],[[12853,12853],"disallowed_STD3_mapped",[40,29305,41]],[[12854,12854],"disallowed_STD3_mapped",[40,36001,41]],[[12855,12855],"disallowed_STD3_mapped",[40,31069,41]],[[12856,12856],"disallowed_STD3_mapped",[40,21172,41]],[[12857,12857],"disallowed_STD3_mapped",[40,20195,41]],[[12858,12858],"disallowed_STD3_mapped",[40,21628,41]],[[12859,12859],"disallowed_STD3_mapped",[40,23398,41]],[[12860,12860],"disallowed_STD3_mapped",[40,30435,41]],[[12861,12861],"disallowed_STD3_mapped",[40,20225,41]],[[12862,12862],"disallowed_STD3_mapped",[40,36039,41]],[[12863,12863],"disallowed_STD3_mapped",[40,21332,41]],[[12864,12864],"disallowed_STD3_mapped",[40,31085,41]],[[12865,12865],"disallowed_STD3_mapped",[40,20241,41]],[[12866,12866],"disallowed_STD3_mapped",[40,33258,41]],[[12867,12867],"disallowed_STD3_mapped",[40,33267,41]],[[12868,12868],"mapped",[21839]],[[12869,12869],"mapped",[24188]],[[12870,12870],"mapped",[25991]],[[12871,12871],"mapped",[31631]],[[12872,12879],"valid",[],"NV8"],[[12880,12880],"mapped",[112,116,101]],[[12881,12881],"mapped",[50,49]],[[12882,12882],"mapped",[50,50]],[[12883,12883],"mapped",[50,51]],[[12884,12884],"mapped",[50,52]],[[12885,12885],"mapped",[50,53]],[[12886,12886],"mapped",[50,54]],[[12887,12887],"mapped",[50,55]],[[12888,12888],"mapped",[50,56]],[[12889,12889],"mapped",[50,57]],[[12890,12890],"mapped",[51,48]],[[12891,12891],"mapped",[51,49]],[[12892,12892],"mapped",[51,50]],[[12893,12893],"mapped",[51,51]],[[12894,12894],"mapped",[51,52]],[[12895,12895],"mapped",[51,53]],[[12896,12896],"mapped",[4352]],[[12897,12897],"mapped",[4354]],[[12898,12898],"mapped",[4355]],[[12899,12899],"mapped",[4357]],[[12900,12900],"mapped",[4358]],[[12901,12901],"mapped",[4359]],[[12902,12902],"mapped",[4361]],[[12903,12903],"mapped",[4363]],[[12904,12904],"mapped",[4364]],[[12905,12905],"mapped",[4366]],[[12906,12906],"mapped",[4367]],[[12907,12907],"mapped",[4368]],[[12908,12908],"mapped",[4369]],[[12909,12909],"mapped",[4370]],[[12910,12910],"mapped",[44032]],[[12911,12911],"mapped",[45208]],[[12912,12912],"mapped",[45796]],[[12913,12913],"mapped",[46972]],[[12914,12914],"mapped",[47560]],[[12915,12915],"mapped",[48148]],[[12916,12916],"mapped",[49324]],[[12917,12917],"mapped",[50500]],[[12918,12918],"mapped",[51088]],[[12919,12919],"mapped",[52264]],[[12920,12920],"mapped",[52852]],[[12921,12921],"mapped",[53440]],[[12922,12922],"mapped",[54028]],[[12923,12923],"mapped",[54616]],[[12924,12924],"mapped",[52280,44256]],[[12925,12925],"mapped",[51452,51032]],[[12926,12926],"mapped",[50864]],[[12927,12927],"valid",[],"NV8"],[[12928,12928],"mapped",[19968]],[[12929,12929],"mapped",[20108]],[[12930,12930],"mapped",[19977]],[[12931,12931],"mapped",[22235]],[[12932,12932],"mapped",[20116]],[[12933,12933],"mapped",[20845]],[[12934,12934],"mapped",[19971]],[[12935,12935],"mapped",[20843]],[[12936,12936],"mapped",[20061]],[[12937,12937],"mapped",[21313]],[[12938,12938],"mapped",[26376]],[[12939,12939],"mapped",[28779]],[[12940,12940],"mapped",[27700]],[[12941,12941],"mapped",[26408]],[[12942,12942],"mapped",[37329]],[[12943,12943],"mapped",[22303]],[[12944,12944],"mapped",[26085]],[[12945,12945],"mapped",[26666]],[[12946,12946],"mapped",[26377]],[[12947,12947],"mapped",[31038]],[[12948,12948],"mapped",[21517]],[[12949,12949],"mapped",[29305]],[[12950,12950],"mapped",[36001]],[[12951,12951],"mapped",[31069]],[[12952,12952],"mapped",[21172]],[[12953,12953],"mapped",[31192]],[[12954,12954],"mapped",[30007]],[[12955,12955],"mapped",[22899]],[[12956,12956],"mapped",[36969]],[[12957,12957],"mapped",[20778]],[[12958,12958],"mapped",[21360]],[[12959,12959],"mapped",[27880]],[[12960,12960],"mapped",[38917]],[[12961,12961],"mapped",[20241]],[[12962,12962],"mapped",[20889]],[[12963,12963],"mapped",[27491]],[[12964,12964],"mapped",[19978]],[[12965,12965],"mapped",[20013]],[[12966,12966],"mapped",[19979]],[[12967,12967],"mapped",[24038]],[[12968,12968],"mapped",[21491]],[[12969,12969],"mapped",[21307]],[[12970,12970],"mapped",[23447]],[[12971,12971],"mapped",[23398]],[[12972,12972],"mapped",[30435]],[[12973,12973],"mapped",[20225]],[[12974,12974],"mapped",[36039]],[[12975,12975],"mapped",[21332]],[[12976,12976],"mapped",[22812]],[[12977,12977],"mapped",[51,54]],[[12978,12978],"mapped",[51,55]],[[12979,12979],"mapped",[51,56]],[[12980,12980],"mapped",[51,57]],[[12981,12981],"mapped",[52,48]],[[12982,12982],"mapped",[52,49]],[[12983,12983],"mapped",[52,50]],[[12984,12984],"mapped",[52,51]],[[12985,12985],"mapped",[52,52]],[[12986,12986],"mapped",[52,53]],[[12987,12987],"mapped",[52,54]],[[12988,12988],"mapped",[52,55]],[[12989,12989],"mapped",[52,56]],[[12990,12990],"mapped",[52,57]],[[12991,12991],"mapped",[53,48]],[[12992,12992],"mapped",[49,26376]],[[12993,12993],"mapped",[50,26376]],[[12994,12994],"mapped",[51,26376]],[[12995,12995],"mapped",[52,26376]],[[12996,12996],"mapped",[53,26376]],[[12997,12997],"mapped",[54,26376]],[[12998,12998],"mapped",[55,26376]],[[12999,12999],"mapped",[56,26376]],[[13000,13000],"mapped",[57,26376]],[[13001,13001],"mapped",[49,48,26376]],[[13002,13002],"mapped",[49,49,26376]],[[13003,13003],"mapped",[49,50,26376]],[[13004,13004],"mapped",[104,103]],[[13005,13005],"mapped",[101,114,103]],[[13006,13006],"mapped",[101,118]],[[13007,13007],"mapped",[108,116,100]],[[13008,13008],"mapped",[12450]],[[13009,13009],"mapped",[12452]],[[13010,13010],"mapped",[12454]],[[13011,13011],"mapped",[12456]],[[13012,13012],"mapped",[12458]],[[13013,13013],"mapped",[12459]],[[13014,13014],"mapped",[12461]],[[13015,13015],"mapped",[12463]],[[13016,13016],"mapped",[12465]],[[13017,13017],"mapped",[12467]],[[13018,13018],"mapped",[12469]],[[13019,13019],"mapped",[12471]],[[13020,13020],"mapped",[12473]],[[13021,13021],"mapped",[12475]],[[13022,13022],"mapped",[12477]],[[13023,13023],"mapped",[12479]],[[13024,13024],"mapped",[12481]],[[13025,13025],"mapped",[12484]],[[13026,13026],"mapped",[12486]],[[13027,13027],"mapped",[12488]],[[13028,13028],"mapped",[12490]],[[13029,13029],"mapped",[12491]],[[13030,13030],"mapped",[12492]],[[13031,13031],"mapped",[12493]],[[13032,13032],"mapped",[12494]],[[13033,13033],"mapped",[12495]],[[13034,13034],"mapped",[12498]],[[13035,13035],"mapped",[12501]],[[13036,13036],"mapped",[12504]],[[13037,13037],"mapped",[12507]],[[13038,13038],"mapped",[12510]],[[13039,13039],"mapped",[12511]],[[13040,13040],"mapped",[12512]],[[13041,13041],"mapped",[12513]],[[13042,13042],"mapped",[12514]],[[13043,13043],"mapped",[12516]],[[13044,13044],"mapped",[12518]],[[13045,13045],"mapped",[12520]],[[13046,13046],"mapped",[12521]],[[13047,13047],"mapped",[12522]],[[13048,13048],"mapped",[12523]],[[13049,13049],"mapped",[12524]],[[13050,13050],"mapped",[12525]],[[13051,13051],"mapped",[12527]],[[13052,13052],"mapped",[12528]],[[13053,13053],"mapped",[12529]],[[13054,13054],"mapped",[12530]],[[13055,13055],"disallowed"],[[13056,13056],"mapped",[12450,12497,12540,12488]],[[13057,13057],"mapped",[12450,12523,12501,12449]],[[13058,13058],"mapped",[12450,12531,12506,12450]],[[13059,13059],"mapped",[12450,12540,12523]],[[13060,13060],"mapped",[12452,12491,12531,12464]],[[13061,13061],"mapped",[12452,12531,12481]],[[13062,13062],"mapped",[12454,12457,12531]],[[13063,13063],"mapped",[12456,12473,12463,12540,12489]],[[13064,13064],"mapped",[12456,12540,12459,12540]],[[13065,13065],"mapped",[12458,12531,12473]],[[13066,13066],"mapped",[12458,12540,12512]],[[13067,13067],"mapped",[12459,12452,12522]],[[13068,13068],"mapped",[12459,12521,12483,12488]],[[13069,13069],"mapped",[12459,12525,12522,12540]],[[13070,13070],"mapped",[12460,12525,12531]],[[13071,13071],"mapped",[12460,12531,12510]],[[13072,13072],"mapped",[12462,12460]],[[13073,13073],"mapped",[12462,12491,12540]],[[13074,13074],"mapped",[12461,12517,12522,12540]],[[13075,13075],"mapped",[12462,12523,12480,12540]],[[13076,13076],"mapped",[12461,12525]],[[13077,13077],"mapped",[12461,12525,12464,12521,12512]],[[13078,13078],"mapped",[12461,12525,12513,12540,12488,12523]],[[13079,13079],"mapped",[12461,12525,12527,12483,12488]],[[13080,13080],"mapped",[12464,12521,12512]],[[13081,13081],"mapped",[12464,12521,12512,12488,12531]],[[13082,13082],"mapped",[12463,12523,12476,12452,12525]],[[13083,13083],"mapped",[12463,12525,12540,12493]],[[13084,13084],"mapped",[12465,12540,12473]],[[13085,13085],"mapped",[12467,12523,12490]],[[13086,13086],"mapped",[12467,12540,12509]],[[13087,13087],"mapped",[12469,12452,12463,12523]],[[13088,13088],"mapped",[12469,12531,12481,12540,12512]],[[13089,13089],"mapped",[12471,12522,12531,12464]],[[13090,13090],"mapped",[12475,12531,12481]],[[13091,13091],"mapped",[12475,12531,12488]],[[13092,13092],"mapped",[12480,12540,12473]],[[13093,13093],"mapped",[12487,12471]],[[13094,13094],"mapped",[12489,12523]],[[13095,13095],"mapped",[12488,12531]],[[13096,13096],"mapped",[12490,12494]],[[13097,13097],"mapped",[12494,12483,12488]],[[13098,13098],"mapped",[12495,12452,12484]],[[13099,13099],"mapped",[12497,12540,12475,12531,12488]],[[13100,13100],"mapped",[12497,12540,12484]],[[13101,13101],"mapped",[12496,12540,12524,12523]],[[13102,13102],"mapped",[12500,12450,12473,12488,12523]],[[13103,13103],"mapped",[12500,12463,12523]],[[13104,13104],"mapped",[12500,12467]],[[13105,13105],"mapped",[12499,12523]],[[13106,13106],"mapped",[12501,12449,12521,12483,12489]],[[13107,13107],"mapped",[12501,12451,12540,12488]],[[13108,13108],"mapped",[12502,12483,12471,12455,12523]],[[13109,13109],"mapped",[12501,12521,12531]],[[13110,13110],"mapped",[12504,12463,12479,12540,12523]],[[13111,13111],"mapped",[12506,12477]],[[13112,13112],"mapped",[12506,12491,12498]],[[13113,13113],"mapped",[12504,12523,12484]],[[13114,13114],"mapped",[12506,12531,12473]],[[13115,13115],"mapped",[12506,12540,12472]],[[13116,13116],"mapped",[12505,12540,12479]],[[13117,13117],"mapped",[12509,12452,12531,12488]],[[13118,13118],"mapped",[12508,12523,12488]],[[13119,13119],"mapped",[12507,12531]],[[13120,13120],"mapped",[12509,12531,12489]],[[13121,13121],"mapped",[12507,12540,12523]],[[13122,13122],"mapped",[12507,12540,12531]],[[13123,13123],"mapped",[12510,12452,12463,12525]],[[13124,13124],"mapped",[12510,12452,12523]],[[13125,13125],"mapped",[12510,12483,12495]],[[13126,13126],"mapped",[12510,12523,12463]],[[13127,13127],"mapped",[12510,12531,12471,12519,12531]],[[13128,13128],"mapped",[12511,12463,12525,12531]],[[13129,13129],"mapped",[12511,12522]],[[13130,13130],"mapped",[12511,12522,12496,12540,12523]],[[13131,13131],"mapped",[12513,12460]],[[13132,13132],"mapped",[12513,12460,12488,12531]],[[13133,13133],"mapped",[12513,12540,12488,12523]],[[13134,13134],"mapped",[12516,12540,12489]],[[13135,13135],"mapped",[12516,12540,12523]],[[13136,13136],"mapped",[12518,12450,12531]],[[13137,13137],"mapped",[12522,12483,12488,12523]],[[13138,13138],"mapped",[12522,12521]],[[13139,13139],"mapped",[12523,12500,12540]],[[13140,13140],"mapped",[12523,12540,12502,12523]],[[13141,13141],"mapped",[12524,12512]],[[13142,13142],"mapped",[12524,12531,12488,12466,12531]],[[13143,13143],"mapped",[12527,12483,12488]],[[13144,13144],"mapped",[48,28857]],[[13145,13145],"mapped",[49,28857]],[[13146,13146],"mapped",[50,28857]],[[13147,13147],"mapped",[51,28857]],[[13148,13148],"mapped",[52,28857]],[[13149,13149],"mapped",[53,28857]],[[13150,13150],"mapped",[54,28857]],[[13151,13151],"mapped",[55,28857]],[[13152,13152],"mapped",[56,28857]],[[13153,13153],"mapped",[57,28857]],[[13154,13154],"mapped",[49,48,28857]],[[13155,13155],"mapped",[49,49,28857]],[[13156,13156],"mapped",[49,50,28857]],[[13157,13157],"mapped",[49,51,28857]],[[13158,13158],"mapped",[49,52,28857]],[[13159,13159],"mapped",[49,53,28857]],[[13160,13160],"mapped",[49,54,28857]],[[13161,13161],"mapped",[49,55,28857]],[[13162,13162],"mapped",[49,56,28857]],[[13163,13163],"mapped",[49,57,28857]],[[13164,13164],"mapped",[50,48,28857]],[[13165,13165],"mapped",[50,49,28857]],[[13166,13166],"mapped",[50,50,28857]],[[13167,13167],"mapped",[50,51,28857]],[[13168,13168],"mapped",[50,52,28857]],[[13169,13169],"mapped",[104,112,97]],[[13170,13170],"mapped",[100,97]],[[13171,13171],"mapped",[97,117]],[[13172,13172],"mapped",[98,97,114]],[[13173,13173],"mapped",[111,118]],[[13174,13174],"mapped",[112,99]],[[13175,13175],"mapped",[100,109]],[[13176,13176],"mapped",[100,109,50]],[[13177,13177],"mapped",[100,109,51]],[[13178,13178],"mapped",[105,117]],[[13179,13179],"mapped",[24179,25104]],[[13180,13180],"mapped",[26157,21644]],[[13181,13181],"mapped",[22823,27491]],[[13182,13182],"mapped",[26126,27835]],[[13183,13183],"mapped",[26666,24335,20250,31038]],[[13184,13184],"mapped",[112,97]],[[13185,13185],"mapped",[110,97]],[[13186,13186],"mapped",[956,97]],[[13187,13187],"mapped",[109,97]],[[13188,13188],"mapped",[107,97]],[[13189,13189],"mapped",[107,98]],[[13190,13190],"mapped",[109,98]],[[13191,13191],"mapped",[103,98]],[[13192,13192],"mapped",[99,97,108]],[[13193,13193],"mapped",[107,99,97,108]],[[13194,13194],"mapped",[112,102]],[[13195,13195],"mapped",[110,102]],[[13196,13196],"mapped",[956,102]],[[13197,13197],"mapped",[956,103]],[[13198,13198],"mapped",[109,103]],[[13199,13199],"mapped",[107,103]],[[13200,13200],"mapped",[104,122]],[[13201,13201],"mapped",[107,104,122]],[[13202,13202],"mapped",[109,104,122]],[[13203,13203],"mapped",[103,104,122]],[[13204,13204],"mapped",[116,104,122]],[[13205,13205],"mapped",[956,108]],[[13206,13206],"mapped",[109,108]],[[13207,13207],"mapped",[100,108]],[[13208,13208],"mapped",[107,108]],[[13209,13209],"mapped",[102,109]],[[13210,13210],"mapped",[110,109]],[[13211,13211],"mapped",[956,109]],[[13212,13212],"mapped",[109,109]],[[13213,13213],"mapped",[99,109]],[[13214,13214],"mapped",[107,109]],[[13215,13215],"mapped",[109,109,50]],[[13216,13216],"mapped",[99,109,50]],[[13217,13217],"mapped",[109,50]],[[13218,13218],"mapped",[107,109,50]],[[13219,13219],"mapped",[109,109,51]],[[13220,13220],"mapped",[99,109,51]],[[13221,13221],"mapped",[109,51]],[[13222,13222],"mapped",[107,109,51]],[[13223,13223],"mapped",[109,8725,115]],[[13224,13224],"mapped",[109,8725,115,50]],[[13225,13225],"mapped",[112,97]],[[13226,13226],"mapped",[107,112,97]],[[13227,13227],"mapped",[109,112,97]],[[13228,13228],"mapped",[103,112,97]],[[13229,13229],"mapped",[114,97,100]],[[13230,13230],"mapped",[114,97,100,8725,115]],[[13231,13231],"mapped",[114,97,100,8725,115,50]],[[13232,13232],"mapped",[112,115]],[[13233,13233],"mapped",[110,115]],[[13234,13234],"mapped",[956,115]],[[13235,13235],"mapped",[109,115]],[[13236,13236],"mapped",[112,118]],[[13237,13237],"mapped",[110,118]],[[13238,13238],"mapped",[956,118]],[[13239,13239],"mapped",[109,118]],[[13240,13240],"mapped",[107,118]],[[13241,13241],"mapped",[109,118]],[[13242,13242],"mapped",[112,119]],[[13243,13243],"mapped",[110,119]],[[13244,13244],"mapped",[956,119]],[[13245,13245],"mapped",[109,119]],[[13246,13246],"mapped",[107,119]],[[13247,13247],"mapped",[109,119]],[[13248,13248],"mapped",[107,969]],[[13249,13249],"mapped",[109,969]],[[13250,13250],"disallowed"],[[13251,13251],"mapped",[98,113]],[[13252,13252],"mapped",[99,99]],[[13253,13253],"mapped",[99,100]],[[13254,13254],"mapped",[99,8725,107,103]],[[13255,13255],"disallowed"],[[13256,13256],"mapped",[100,98]],[[13257,13257],"mapped",[103,121]],[[13258,13258],"mapped",[104,97]],[[13259,13259],"mapped",[104,112]],[[13260,13260],"mapped",[105,110]],[[13261,13261],"mapped",[107,107]],[[13262,13262],"mapped",[107,109]],[[13263,13263],"mapped",[107,116]],[[13264,13264],"mapped",[108,109]],[[13265,13265],"mapped",[108,110]],[[13266,13266],"mapped",[108,111,103]],[[13267,13267],"mapped",[108,120]],[[13268,13268],"mapped",[109,98]],[[13269,13269],"mapped",[109,105,108]],[[13270,13270],"mapped",[109,111,108]],[[13271,13271],"mapped",[112,104]],[[13272,13272],"disallowed"],[[13273,13273],"mapped",[112,112,109]],[[13274,13274],"mapped",[112,114]],[[13275,13275],"mapped",[115,114]],[[13276,13276],"mapped",[115,118]],[[13277,13277],"mapped",[119,98]],[[13278,13278],"mapped",[118,8725,109]],[[13279,13279],"mapped",[97,8725,109]],[[13280,13280],"mapped",[49,26085]],[[13281,13281],"mapped",[50,26085]],[[13282,13282],"mapped",[51,26085]],[[13283,13283],"mapped",[52,26085]],[[13284,13284],"mapped",[53,26085]],[[13285,13285],"mapped",[54,26085]],[[13286,13286],"mapped",[55,26085]],[[13287,13287],"mapped",[56,26085]],[[13288,13288],"mapped",[57,26085]],[[13289,13289],"mapped",[49,48,26085]],[[13290,13290],"mapped",[49,49,26085]],[[13291,13291],"mapped",[49,50,26085]],[[13292,13292],"mapped",[49,51,26085]],[[13293,13293],"mapped",[49,52,26085]],[[13294,13294],"mapped",[49,53,26085]],[[13295,13295],"mapped",[49,54,26085]],[[13296,13296],"mapped",[49,55,26085]],[[13297,13297],"mapped",[49,56,26085]],[[13298,13298],"mapped",[49,57,26085]],[[13299,13299],"mapped",[50,48,26085]],[[13300,13300],"mapped",[50,49,26085]],[[13301,13301],"mapped",[50,50,26085]],[[13302,13302],"mapped",[50,51,26085]],[[13303,13303],"mapped",[50,52,26085]],[[13304,13304],"mapped",[50,53,26085]],[[13305,13305],"mapped",[50,54,26085]],[[13306,13306],"mapped",[50,55,26085]],[[13307,13307],"mapped",[50,56,26085]],[[13308,13308],"mapped",[50,57,26085]],[[13309,13309],"mapped",[51,48,26085]],[[13310,13310],"mapped",[51,49,26085]],[[13311,13311],"mapped",[103,97,108]],[[13312,19893],"valid"],[[19894,19903],"disallowed"],[[19904,19967],"valid",[],"NV8"],[[19968,40869],"valid"],[[40870,40891],"valid"],[[40892,40899],"valid"],[[40900,40907],"valid"],[[40908,40908],"valid"],[[40909,40917],"valid"],[[40918,40959],"disallowed"],[[40960,42124],"valid"],[[42125,42127],"disallowed"],[[42128,42145],"valid",[],"NV8"],[[42146,42147],"valid",[],"NV8"],[[42148,42163],"valid",[],"NV8"],[[42164,42164],"valid",[],"NV8"],[[42165,42176],"valid",[],"NV8"],[[42177,42177],"valid",[],"NV8"],[[42178,42180],"valid",[],"NV8"],[[42181,42181],"valid",[],"NV8"],[[42182,42182],"valid",[],"NV8"],[[42183,42191],"disallowed"],[[42192,42237],"valid"],[[42238,42239],"valid",[],"NV8"],[[42240,42508],"valid"],[[42509,42511],"valid",[],"NV8"],[[42512,42539],"valid"],[[42540,42559],"disallowed"],[[42560,42560],"mapped",[42561]],[[42561,42561],"valid"],[[42562,42562],"mapped",[42563]],[[42563,42563],"valid"],[[42564,42564],"mapped",[42565]],[[42565,42565],"valid"],[[42566,42566],"mapped",[42567]],[[42567,42567],"valid"],[[42568,42568],"mapped",[42569]],[[42569,42569],"valid"],[[42570,42570],"mapped",[42571]],[[42571,42571],"valid"],[[42572,42572],"mapped",[42573]],[[42573,42573],"valid"],[[42574,42574],"mapped",[42575]],[[42575,42575],"valid"],[[42576,42576],"mapped",[42577]],[[42577,42577],"valid"],[[42578,42578],"mapped",[42579]],[[42579,42579],"valid"],[[42580,42580],"mapped",[42581]],[[42581,42581],"valid"],[[42582,42582],"mapped",[42583]],[[42583,42583],"valid"],[[42584,42584],"mapped",[42585]],[[42585,42585],"valid"],[[42586,42586],"mapped",[42587]],[[42587,42587],"valid"],[[42588,42588],"mapped",[42589]],[[42589,42589],"valid"],[[42590,42590],"mapped",[42591]],[[42591,42591],"valid"],[[42592,42592],"mapped",[42593]],[[42593,42593],"valid"],[[42594,42594],"mapped",[42595]],[[42595,42595],"valid"],[[42596,42596],"mapped",[42597]],[[42597,42597],"valid"],[[42598,42598],"mapped",[42599]],[[42599,42599],"valid"],[[42600,42600],"mapped",[42601]],[[42601,42601],"valid"],[[42602,42602],"mapped",[42603]],[[42603,42603],"valid"],[[42604,42604],"mapped",[42605]],[[42605,42607],"valid"],[[42608,42611],"valid",[],"NV8"],[[42612,42619],"valid"],[[42620,42621],"valid"],[[42622,42622],"valid",[],"NV8"],[[42623,42623],"valid"],[[42624,42624],"mapped",[42625]],[[42625,42625],"valid"],[[42626,42626],"mapped",[42627]],[[42627,42627],"valid"],[[42628,42628],"mapped",[42629]],[[42629,42629],"valid"],[[42630,42630],"mapped",[42631]],[[42631,42631],"valid"],[[42632,42632],"mapped",[42633]],[[42633,42633],"valid"],[[42634,42634],"mapped",[42635]],[[42635,42635],"valid"],[[42636,42636],"mapped",[42637]],[[42637,42637],"valid"],[[42638,42638],"mapped",[42639]],[[42639,42639],"valid"],[[42640,42640],"mapped",[42641]],[[42641,42641],"valid"],[[42642,42642],"mapped",[42643]],[[42643,42643],"valid"],[[42644,42644],"mapped",[42645]],[[42645,42645],"valid"],[[42646,42646],"mapped",[42647]],[[42647,42647],"valid"],[[42648,42648],"mapped",[42649]],[[42649,42649],"valid"],[[42650,42650],"mapped",[42651]],[[42651,42651],"valid"],[[42652,42652],"mapped",[1098]],[[42653,42653],"mapped",[1100]],[[42654,42654],"valid"],[[42655,42655],"valid"],[[42656,42725],"valid"],[[42726,42735],"valid",[],"NV8"],[[42736,42737],"valid"],[[42738,42743],"valid",[],"NV8"],[[42744,42751],"disallowed"],[[42752,42774],"valid",[],"NV8"],[[42775,42778],"valid"],[[42779,42783],"valid"],[[42784,42785],"valid",[],"NV8"],[[42786,42786],"mapped",[42787]],[[42787,42787],"valid"],[[42788,42788],"mapped",[42789]],[[42789,42789],"valid"],[[42790,42790],"mapped",[42791]],[[42791,42791],"valid"],[[42792,42792],"mapped",[42793]],[[42793,42793],"valid"],[[42794,42794],"mapped",[42795]],[[42795,42795],"valid"],[[42796,42796],"mapped",[42797]],[[42797,42797],"valid"],[[42798,42798],"mapped",[42799]],[[42799,42801],"valid"],[[42802,42802],"mapped",[42803]],[[42803,42803],"valid"],[[42804,42804],"mapped",[42805]],[[42805,42805],"valid"],[[42806,42806],"mapped",[42807]],[[42807,42807],"valid"],[[42808,42808],"mapped",[42809]],[[42809,42809],"valid"],[[42810,42810],"mapped",[42811]],[[42811,42811],"valid"],[[42812,42812],"mapped",[42813]],[[42813,42813],"valid"],[[42814,42814],"mapped",[42815]],[[42815,42815],"valid"],[[42816,42816],"mapped",[42817]],[[42817,42817],"valid"],[[42818,42818],"mapped",[42819]],[[42819,42819],"valid"],[[42820,42820],"mapped",[42821]],[[42821,42821],"valid"],[[42822,42822],"mapped",[42823]],[[42823,42823],"valid"],[[42824,42824],"mapped",[42825]],[[42825,42825],"valid"],[[42826,42826],"mapped",[42827]],[[42827,42827],"valid"],[[42828,42828],"mapped",[42829]],[[42829,42829],"valid"],[[42830,42830],"mapped",[42831]],[[42831,42831],"valid"],[[42832,42832],"mapped",[42833]],[[42833,42833],"valid"],[[42834,42834],"mapped",[42835]],[[42835,42835],"valid"],[[42836,42836],"mapped",[42837]],[[42837,42837],"valid"],[[42838,42838],"mapped",[42839]],[[42839,42839],"valid"],[[42840,42840],"mapped",[42841]],[[42841,42841],"valid"],[[42842,42842],"mapped",[42843]],[[42843,42843],"valid"],[[42844,42844],"mapped",[42845]],[[42845,42845],"valid"],[[42846,42846],"mapped",[42847]],[[42847,42847],"valid"],[[42848,42848],"mapped",[42849]],[[42849,42849],"valid"],[[42850,42850],"mapped",[42851]],[[42851,42851],"valid"],[[42852,42852],"mapped",[42853]],[[42853,42853],"valid"],[[42854,42854],"mapped",[42855]],[[42855,42855],"valid"],[[42856,42856],"mapped",[42857]],[[42857,42857],"valid"],[[42858,42858],"mapped",[42859]],[[42859,42859],"valid"],[[42860,42860],"mapped",[42861]],[[42861,42861],"valid"],[[42862,42862],"mapped",[42863]],[[42863,42863],"valid"],[[42864,42864],"mapped",[42863]],[[42865,42872],"valid"],[[42873,42873],"mapped",[42874]],[[42874,42874],"valid"],[[42875,42875],"mapped",[42876]],[[42876,42876],"valid"],[[42877,42877],"mapped",[7545]],[[42878,42878],"mapped",[42879]],[[42879,42879],"valid"],[[42880,42880],"mapped",[42881]],[[42881,42881],"valid"],[[42882,42882],"mapped",[42883]],[[42883,42883],"valid"],[[42884,42884],"mapped",[42885]],[[42885,42885],"valid"],[[42886,42886],"mapped",[42887]],[[42887,42888],"valid"],[[42889,42890],"valid",[],"NV8"],[[42891,42891],"mapped",[42892]],[[42892,42892],"valid"],[[42893,42893],"mapped",[613]],[[42894,42894],"valid"],[[42895,42895],"valid"],[[42896,42896],"mapped",[42897]],[[42897,42897],"valid"],[[42898,42898],"mapped",[42899]],[[42899,42899],"valid"],[[42900,42901],"valid"],[[42902,42902],"mapped",[42903]],[[42903,42903],"valid"],[[42904,42904],"mapped",[42905]],[[42905,42905],"valid"],[[42906,42906],"mapped",[42907]],[[42907,42907],"valid"],[[42908,42908],"mapped",[42909]],[[42909,42909],"valid"],[[42910,42910],"mapped",[42911]],[[42911,42911],"valid"],[[42912,42912],"mapped",[42913]],[[42913,42913],"valid"],[[42914,42914],"mapped",[42915]],[[42915,42915],"valid"],[[42916,42916],"mapped",[42917]],[[42917,42917],"valid"],[[42918,42918],"mapped",[42919]],[[42919,42919],"valid"],[[42920,42920],"mapped",[42921]],[[42921,42921],"valid"],[[42922,42922],"mapped",[614]],[[42923,42923],"mapped",[604]],[[42924,42924],"mapped",[609]],[[42925,42925],"mapped",[620]],[[42926,42927],"disallowed"],[[42928,42928],"mapped",[670]],[[42929,42929],"mapped",[647]],[[42930,42930],"mapped",[669]],[[42931,42931],"mapped",[43859]],[[42932,42932],"mapped",[42933]],[[42933,42933],"valid"],[[42934,42934],"mapped",[42935]],[[42935,42935],"valid"],[[42936,42998],"disallowed"],[[42999,42999],"valid"],[[43000,43000],"mapped",[295]],[[43001,43001],"mapped",[339]],[[43002,43002],"valid"],[[43003,43007],"valid"],[[43008,43047],"valid"],[[43048,43051],"valid",[],"NV8"],[[43052,43055],"disallowed"],[[43056,43065],"valid",[],"NV8"],[[43066,43071],"disallowed"],[[43072,43123],"valid"],[[43124,43127],"valid",[],"NV8"],[[43128,43135],"disallowed"],[[43136,43204],"valid"],[[43205,43213],"disallowed"],[[43214,43215],"valid",[],"NV8"],[[43216,43225],"valid"],[[43226,43231],"disallowed"],[[43232,43255],"valid"],[[43256,43258],"valid",[],"NV8"],[[43259,43259],"valid"],[[43260,43260],"valid",[],"NV8"],[[43261,43261],"valid"],[[43262,43263],"disallowed"],[[43264,43309],"valid"],[[43310,43311],"valid",[],"NV8"],[[43312,43347],"valid"],[[43348,43358],"disallowed"],[[43359,43359],"valid",[],"NV8"],[[43360,43388],"valid",[],"NV8"],[[43389,43391],"disallowed"],[[43392,43456],"valid"],[[43457,43469],"valid",[],"NV8"],[[43470,43470],"disallowed"],[[43471,43481],"valid"],[[43482,43485],"disallowed"],[[43486,43487],"valid",[],"NV8"],[[43488,43518],"valid"],[[43519,43519],"disallowed"],[[43520,43574],"valid"],[[43575,43583],"disallowed"],[[43584,43597],"valid"],[[43598,43599],"disallowed"],[[43600,43609],"valid"],[[43610,43611],"disallowed"],[[43612,43615],"valid",[],"NV8"],[[43616,43638],"valid"],[[43639,43641],"valid",[],"NV8"],[[43642,43643],"valid"],[[43644,43647],"valid"],[[43648,43714],"valid"],[[43715,43738],"disallowed"],[[43739,43741],"valid"],[[43742,43743],"valid",[],"NV8"],[[43744,43759],"valid"],[[43760,43761],"valid",[],"NV8"],[[43762,43766],"valid"],[[43767,43776],"disallowed"],[[43777,43782],"valid"],[[43783,43784],"disallowed"],[[43785,43790],"valid"],[[43791,43792],"disallowed"],[[43793,43798],"valid"],[[43799,43807],"disallowed"],[[43808,43814],"valid"],[[43815,43815],"disallowed"],[[43816,43822],"valid"],[[43823,43823],"disallowed"],[[43824,43866],"valid"],[[43867,43867],"valid",[],"NV8"],[[43868,43868],"mapped",[42791]],[[43869,43869],"mapped",[43831]],[[43870,43870],"mapped",[619]],[[43871,43871],"mapped",[43858]],[[43872,43875],"valid"],[[43876,43877],"valid"],[[43878,43887],"disallowed"],[[43888,43888],"mapped",[5024]],[[43889,43889],"mapped",[5025]],[[43890,43890],"mapped",[5026]],[[43891,43891],"mapped",[5027]],[[43892,43892],"mapped",[5028]],[[43893,43893],"mapped",[5029]],[[43894,43894],"mapped",[5030]],[[43895,43895],"mapped",[5031]],[[43896,43896],"mapped",[5032]],[[43897,43897],"mapped",[5033]],[[43898,43898],"mapped",[5034]],[[43899,43899],"mapped",[5035]],[[43900,43900],"mapped",[5036]],[[43901,43901],"mapped",[5037]],[[43902,43902],"mapped",[5038]],[[43903,43903],"mapped",[5039]],[[43904,43904],"mapped",[5040]],[[43905,43905],"mapped",[5041]],[[43906,43906],"mapped",[5042]],[[43907,43907],"mapped",[5043]],[[43908,43908],"mapped",[5044]],[[43909,43909],"mapped",[5045]],[[43910,43910],"mapped",[5046]],[[43911,43911],"mapped",[5047]],[[43912,43912],"mapped",[5048]],[[43913,43913],"mapped",[5049]],[[43914,43914],"mapped",[5050]],[[43915,43915],"mapped",[5051]],[[43916,43916],"mapped",[5052]],[[43917,43917],"mapped",[5053]],[[43918,43918],"mapped",[5054]],[[43919,43919],"mapped",[5055]],[[43920,43920],"mapped",[5056]],[[43921,43921],"mapped",[5057]],[[43922,43922],"mapped",[5058]],[[43923,43923],"mapped",[5059]],[[43924,43924],"mapped",[5060]],[[43925,43925],"mapped",[5061]],[[43926,43926],"mapped",[5062]],[[43927,43927],"mapped",[5063]],[[43928,43928],"mapped",[5064]],[[43929,43929],"mapped",[5065]],[[43930,43930],"mapped",[5066]],[[43931,43931],"mapped",[5067]],[[43932,43932],"mapped",[5068]],[[43933,43933],"mapped",[5069]],[[43934,43934],"mapped",[5070]],[[43935,43935],"mapped",[5071]],[[43936,43936],"mapped",[5072]],[[43937,43937],"mapped",[5073]],[[43938,43938],"mapped",[5074]],[[43939,43939],"mapped",[5075]],[[43940,43940],"mapped",[5076]],[[43941,43941],"mapped",[5077]],[[43942,43942],"mapped",[5078]],[[43943,43943],"mapped",[5079]],[[43944,43944],"mapped",[5080]],[[43945,43945],"mapped",[5081]],[[43946,43946],"mapped",[5082]],[[43947,43947],"mapped",[5083]],[[43948,43948],"mapped",[5084]],[[43949,43949],"mapped",[5085]],[[43950,43950],"mapped",[5086]],[[43951,43951],"mapped",[5087]],[[43952,43952],"mapped",[5088]],[[43953,43953],"mapped",[5089]],[[43954,43954],"mapped",[5090]],[[43955,43955],"mapped",[5091]],[[43956,43956],"mapped",[5092]],[[43957,43957],"mapped",[5093]],[[43958,43958],"mapped",[5094]],[[43959,43959],"mapped",[5095]],[[43960,43960],"mapped",[5096]],[[43961,43961],"mapped",[5097]],[[43962,43962],"mapped",[5098]],[[43963,43963],"mapped",[5099]],[[43964,43964],"mapped",[5100]],[[43965,43965],"mapped",[5101]],[[43966,43966],"mapped",[5102]],[[43967,43967],"mapped",[5103]],[[43968,44010],"valid"],[[44011,44011],"valid",[],"NV8"],[[44012,44013],"valid"],[[44014,44015],"disallowed"],[[44016,44025],"valid"],[[44026,44031],"disallowed"],[[44032,55203],"valid"],[[55204,55215],"disallowed"],[[55216,55238],"valid",[],"NV8"],[[55239,55242],"disallowed"],[[55243,55291],"valid",[],"NV8"],[[55292,55295],"disallowed"],[[55296,57343],"disallowed"],[[57344,63743],"disallowed"],[[63744,63744],"mapped",[35912]],[[63745,63745],"mapped",[26356]],[[63746,63746],"mapped",[36554]],[[63747,63747],"mapped",[36040]],[[63748,63748],"mapped",[28369]],[[63749,63749],"mapped",[20018]],[[63750,63750],"mapped",[21477]],[[63751,63752],"mapped",[40860]],[[63753,63753],"mapped",[22865]],[[63754,63754],"mapped",[37329]],[[63755,63755],"mapped",[21895]],[[63756,63756],"mapped",[22856]],[[63757,63757],"mapped",[25078]],[[63758,63758],"mapped",[30313]],[[63759,63759],"mapped",[32645]],[[63760,63760],"mapped",[34367]],[[63761,63761],"mapped",[34746]],[[63762,63762],"mapped",[35064]],[[63763,63763],"mapped",[37007]],[[63764,63764],"mapped",[27138]],[[63765,63765],"mapped",[27931]],[[63766,63766],"mapped",[28889]],[[63767,63767],"mapped",[29662]],[[63768,63768],"mapped",[33853]],[[63769,63769],"mapped",[37226]],[[63770,63770],"mapped",[39409]],[[63771,63771],"mapped",[20098]],[[63772,63772],"mapped",[21365]],[[63773,63773],"mapped",[27396]],[[63774,63774],"mapped",[29211]],[[63775,63775],"mapped",[34349]],[[63776,63776],"mapped",[40478]],[[63777,63777],"mapped",[23888]],[[63778,63778],"mapped",[28651]],[[63779,63779],"mapped",[34253]],[[63780,63780],"mapped",[35172]],[[63781,63781],"mapped",[25289]],[[63782,63782],"mapped",[33240]],[[63783,63783],"mapped",[34847]],[[63784,63784],"mapped",[24266]],[[63785,63785],"mapped",[26391]],[[63786,63786],"mapped",[28010]],[[63787,63787],"mapped",[29436]],[[63788,63788],"mapped",[37070]],[[63789,63789],"mapped",[20358]],[[63790,63790],"mapped",[20919]],[[63791,63791],"mapped",[21214]],[[63792,63792],"mapped",[25796]],[[63793,63793],"mapped",[27347]],[[63794,63794],"mapped",[29200]],[[63795,63795],"mapped",[30439]],[[63796,63796],"mapped",[32769]],[[63797,63797],"mapped",[34310]],[[63798,63798],"mapped",[34396]],[[63799,63799],"mapped",[36335]],[[63800,63800],"mapped",[38706]],[[63801,63801],"mapped",[39791]],[[63802,63802],"mapped",[40442]],[[63803,63803],"mapped",[30860]],[[63804,63804],"mapped",[31103]],[[63805,63805],"mapped",[32160]],[[63806,63806],"mapped",[33737]],[[63807,63807],"mapped",[37636]],[[63808,63808],"mapped",[40575]],[[63809,63809],"mapped",[35542]],[[63810,63810],"mapped",[22751]],[[63811,63811],"mapped",[24324]],[[63812,63812],"mapped",[31840]],[[63813,63813],"mapped",[32894]],[[63814,63814],"mapped",[29282]],[[63815,63815],"mapped",[30922]],[[63816,63816],"mapped",[36034]],[[63817,63817],"mapped",[38647]],[[63818,63818],"mapped",[22744]],[[63819,63819],"mapped",[23650]],[[63820,63820],"mapped",[27155]],[[63821,63821],"mapped",[28122]],[[63822,63822],"mapped",[28431]],[[63823,63823],"mapped",[32047]],[[63824,63824],"mapped",[32311]],[[63825,63825],"mapped",[38475]],[[63826,63826],"mapped",[21202]],[[63827,63827],"mapped",[32907]],[[63828,63828],"mapped",[20956]],[[63829,63829],"mapped",[20940]],[[63830,63830],"mapped",[31260]],[[63831,63831],"mapped",[32190]],[[63832,63832],"mapped",[33777]],[[63833,63833],"mapped",[38517]],[[63834,63834],"mapped",[35712]],[[63835,63835],"mapped",[25295]],[[63836,63836],"mapped",[27138]],[[63837,63837],"mapped",[35582]],[[63838,63838],"mapped",[20025]],[[63839,63839],"mapped",[23527]],[[63840,63840],"mapped",[24594]],[[63841,63841],"mapped",[29575]],[[63842,63842],"mapped",[30064]],[[63843,63843],"mapped",[21271]],[[63844,63844],"mapped",[30971]],[[63845,63845],"mapped",[20415]],[[63846,63846],"mapped",[24489]],[[63847,63847],"mapped",[19981]],[[63848,63848],"mapped",[27852]],[[63849,63849],"mapped",[25976]],[[63850,63850],"mapped",[32034]],[[63851,63851],"mapped",[21443]],[[63852,63852],"mapped",[22622]],[[63853,63853],"mapped",[30465]],[[63854,63854],"mapped",[33865]],[[63855,63855],"mapped",[35498]],[[63856,63856],"mapped",[27578]],[[63857,63857],"mapped",[36784]],[[63858,63858],"mapped",[27784]],[[63859,63859],"mapped",[25342]],[[63860,63860],"mapped",[33509]],[[63861,63861],"mapped",[25504]],[[63862,63862],"mapped",[30053]],[[63863,63863],"mapped",[20142]],[[63864,63864],"mapped",[20841]],[[63865,63865],"mapped",[20937]],[[63866,63866],"mapped",[26753]],[[63867,63867],"mapped",[31975]],[[63868,63868],"mapped",[33391]],[[63869,63869],"mapped",[35538]],[[63870,63870],"mapped",[37327]],[[63871,63871],"mapped",[21237]],[[63872,63872],"mapped",[21570]],[[63873,63873],"mapped",[22899]],[[63874,63874],"mapped",[24300]],[[63875,63875],"mapped",[26053]],[[63876,63876],"mapped",[28670]],[[63877,63877],"mapped",[31018]],[[63878,63878],"mapped",[38317]],[[63879,63879],"mapped",[39530]],[[63880,63880],"mapped",[40599]],[[63881,63881],"mapped",[40654]],[[63882,63882],"mapped",[21147]],[[63883,63883],"mapped",[26310]],[[63884,63884],"mapped",[27511]],[[63885,63885],"mapped",[36706]],[[63886,63886],"mapped",[24180]],[[63887,63887],"mapped",[24976]],[[63888,63888],"mapped",[25088]],[[63889,63889],"mapped",[25754]],[[63890,63890],"mapped",[28451]],[[63891,63891],"mapped",[29001]],[[63892,63892],"mapped",[29833]],[[63893,63893],"mapped",[31178]],[[63894,63894],"mapped",[32244]],[[63895,63895],"mapped",[32879]],[[63896,63896],"mapped",[36646]],[[63897,63897],"mapped",[34030]],[[63898,63898],"mapped",[36899]],[[63899,63899],"mapped",[37706]],[[63900,63900],"mapped",[21015]],[[63901,63901],"mapped",[21155]],[[63902,63902],"mapped",[21693]],[[63903,63903],"mapped",[28872]],[[63904,63904],"mapped",[35010]],[[63905,63905],"mapped",[35498]],[[63906,63906],"mapped",[24265]],[[63907,63907],"mapped",[24565]],[[63908,63908],"mapped",[25467]],[[63909,63909],"mapped",[27566]],[[63910,63910],"mapped",[31806]],[[63911,63911],"mapped",[29557]],[[63912,63912],"mapped",[20196]],[[63913,63913],"mapped",[22265]],[[63914,63914],"mapped",[23527]],[[63915,63915],"mapped",[23994]],[[63916,63916],"mapped",[24604]],[[63917,63917],"mapped",[29618]],[[63918,63918],"mapped",[29801]],[[63919,63919],"mapped",[32666]],[[63920,63920],"mapped",[32838]],[[63921,63921],"mapped",[37428]],[[63922,63922],"mapped",[38646]],[[63923,63923],"mapped",[38728]],[[63924,63924],"mapped",[38936]],[[63925,63925],"mapped",[20363]],[[63926,63926],"mapped",[31150]],[[63927,63927],"mapped",[37300]],[[63928,63928],"mapped",[38584]],[[63929,63929],"mapped",[24801]],[[63930,63930],"mapped",[20102]],[[63931,63931],"mapped",[20698]],[[63932,63932],"mapped",[23534]],[[63933,63933],"mapped",[23615]],[[63934,63934],"mapped",[26009]],[[63935,63935],"mapped",[27138]],[[63936,63936],"mapped",[29134]],[[63937,63937],"mapped",[30274]],[[63938,63938],"mapped",[34044]],[[63939,63939],"mapped",[36988]],[[63940,63940],"mapped",[40845]],[[63941,63941],"mapped",[26248]],[[63942,63942],"mapped",[38446]],[[63943,63943],"mapped",[21129]],[[63944,63944],"mapped",[26491]],[[63945,63945],"mapped",[26611]],[[63946,63946],"mapped",[27969]],[[63947,63947],"mapped",[28316]],[[63948,63948],"mapped",[29705]],[[63949,63949],"mapped",[30041]],[[63950,63950],"mapped",[30827]],[[63951,63951],"mapped",[32016]],[[63952,63952],"mapped",[39006]],[[63953,63953],"mapped",[20845]],[[63954,63954],"mapped",[25134]],[[63955,63955],"mapped",[38520]],[[63956,63956],"mapped",[20523]],[[63957,63957],"mapped",[23833]],[[63958,63958],"mapped",[28138]],[[63959,63959],"mapped",[36650]],[[63960,63960],"mapped",[24459]],[[63961,63961],"mapped",[24900]],[[63962,63962],"mapped",[26647]],[[63963,63963],"mapped",[29575]],[[63964,63964],"mapped",[38534]],[[63965,63965],"mapped",[21033]],[[63966,63966],"mapped",[21519]],[[63967,63967],"mapped",[23653]],[[63968,63968],"mapped",[26131]],[[63969,63969],"mapped",[26446]],[[63970,63970],"mapped",[26792]],[[63971,63971],"mapped",[27877]],[[63972,63972],"mapped",[29702]],[[63973,63973],"mapped",[30178]],[[63974,63974],"mapped",[32633]],[[63975,63975],"mapped",[35023]],[[63976,63976],"mapped",[35041]],[[63977,63977],"mapped",[37324]],[[63978,63978],"mapped",[38626]],[[63979,63979],"mapped",[21311]],[[63980,63980],"mapped",[28346]],[[63981,63981],"mapped",[21533]],[[63982,63982],"mapped",[29136]],[[63983,63983],"mapped",[29848]],[[63984,63984],"mapped",[34298]],[[63985,63985],"mapped",[38563]],[[63986,63986],"mapped",[40023]],[[63987,63987],"mapped",[40607]],[[63988,63988],"mapped",[26519]],[[63989,63989],"mapped",[28107]],[[63990,63990],"mapped",[33256]],[[63991,63991],"mapped",[31435]],[[63992,63992],"mapped",[31520]],[[63993,63993],"mapped",[31890]],[[63994,63994],"mapped",[29376]],[[63995,63995],"mapped",[28825]],[[63996,63996],"mapped",[35672]],[[63997,63997],"mapped",[20160]],[[63998,63998],"mapped",[33590]],[[63999,63999],"mapped",[21050]],[[64000,64000],"mapped",[20999]],[[64001,64001],"mapped",[24230]],[[64002,64002],"mapped",[25299]],[[64003,64003],"mapped",[31958]],[[64004,64004],"mapped",[23429]],[[64005,64005],"mapped",[27934]],[[64006,64006],"mapped",[26292]],[[64007,64007],"mapped",[36667]],[[64008,64008],"mapped",[34892]],[[64009,64009],"mapped",[38477]],[[64010,64010],"mapped",[35211]],[[64011,64011],"mapped",[24275]],[[64012,64012],"mapped",[20800]],[[64013,64013],"mapped",[21952]],[[64014,64015],"valid"],[[64016,64016],"mapped",[22618]],[[64017,64017],"valid"],[[64018,64018],"mapped",[26228]],[[64019,64020],"valid"],[[64021,64021],"mapped",[20958]],[[64022,64022],"mapped",[29482]],[[64023,64023],"mapped",[30410]],[[64024,64024],"mapped",[31036]],[[64025,64025],"mapped",[31070]],[[64026,64026],"mapped",[31077]],[[64027,64027],"mapped",[31119]],[[64028,64028],"mapped",[38742]],[[64029,64029],"mapped",[31934]],[[64030,64030],"mapped",[32701]],[[64031,64031],"valid"],[[64032,64032],"mapped",[34322]],[[64033,64033],"valid"],[[64034,64034],"mapped",[35576]],[[64035,64036],"valid"],[[64037,64037],"mapped",[36920]],[[64038,64038],"mapped",[37117]],[[64039,64041],"valid"],[[64042,64042],"mapped",[39151]],[[64043,64043],"mapped",[39164]],[[64044,64044],"mapped",[39208]],[[64045,64045],"mapped",[40372]],[[64046,64046],"mapped",[37086]],[[64047,64047],"mapped",[38583]],[[64048,64048],"mapped",[20398]],[[64049,64049],"mapped",[20711]],[[64050,64050],"mapped",[20813]],[[64051,64051],"mapped",[21193]],[[64052,64052],"mapped",[21220]],[[64053,64053],"mapped",[21329]],[[64054,64054],"mapped",[21917]],[[64055,64055],"mapped",[22022]],[[64056,64056],"mapped",[22120]],[[64057,64057],"mapped",[22592]],[[64058,64058],"mapped",[22696]],[[64059,64059],"mapped",[23652]],[[64060,64060],"mapped",[23662]],[[64061,64061],"mapped",[24724]],[[64062,64062],"mapped",[24936]],[[64063,64063],"mapped",[24974]],[[64064,64064],"mapped",[25074]],[[64065,64065],"mapped",[25935]],[[64066,64066],"mapped",[26082]],[[64067,64067],"mapped",[26257]],[[64068,64068],"mapped",[26757]],[[64069,64069],"mapped",[28023]],[[64070,64070],"mapped",[28186]],[[64071,64071],"mapped",[28450]],[[64072,64072],"mapped",[29038]],[[64073,64073],"mapped",[29227]],[[64074,64074],"mapped",[29730]],[[64075,64075],"mapped",[30865]],[[64076,64076],"mapped",[31038]],[[64077,64077],"mapped",[31049]],[[64078,64078],"mapped",[31048]],[[64079,64079],"mapped",[31056]],[[64080,64080],"mapped",[31062]],[[64081,64081],"mapped",[31069]],[[64082,64082],"mapped",[31117]],[[64083,64083],"mapped",[31118]],[[64084,64084],"mapped",[31296]],[[64085,64085],"mapped",[31361]],[[64086,64086],"mapped",[31680]],[[64087,64087],"mapped",[32244]],[[64088,64088],"mapped",[32265]],[[64089,64089],"mapped",[32321]],[[64090,64090],"mapped",[32626]],[[64091,64091],"mapped",[32773]],[[64092,64092],"mapped",[33261]],[[64093,64094],"mapped",[33401]],[[64095,64095],"mapped",[33879]],[[64096,64096],"mapped",[35088]],[[64097,64097],"mapped",[35222]],[[64098,64098],"mapped",[35585]],[[64099,64099],"mapped",[35641]],[[64100,64100],"mapped",[36051]],[[64101,64101],"mapped",[36104]],[[64102,64102],"mapped",[36790]],[[64103,64103],"mapped",[36920]],[[64104,64104],"mapped",[38627]],[[64105,64105],"mapped",[38911]],[[64106,64106],"mapped",[38971]],[[64107,64107],"mapped",[24693]],[[64108,64108],"mapped",[148206]],[[64109,64109],"mapped",[33304]],[[64110,64111],"disallowed"],[[64112,64112],"mapped",[20006]],[[64113,64113],"mapped",[20917]],[[64114,64114],"mapped",[20840]],[[64115,64115],"mapped",[20352]],[[64116,64116],"mapped",[20805]],[[64117,64117],"mapped",[20864]],[[64118,64118],"mapped",[21191]],[[64119,64119],"mapped",[21242]],[[64120,64120],"mapped",[21917]],[[64121,64121],"mapped",[21845]],[[64122,64122],"mapped",[21913]],[[64123,64123],"mapped",[21986]],[[64124,64124],"mapped",[22618]],[[64125,64125],"mapped",[22707]],[[64126,64126],"mapped",[22852]],[[64127,64127],"mapped",[22868]],[[64128,64128],"mapped",[23138]],[[64129,64129],"mapped",[23336]],[[64130,64130],"mapped",[24274]],[[64131,64131],"mapped",[24281]],[[64132,64132],"mapped",[24425]],[[64133,64133],"mapped",[24493]],[[64134,64134],"mapped",[24792]],[[64135,64135],"mapped",[24910]],[[64136,64136],"mapped",[24840]],[[64137,64137],"mapped",[24974]],[[64138,64138],"mapped",[24928]],[[64139,64139],"mapped",[25074]],[[64140,64140],"mapped",[25140]],[[64141,64141],"mapped",[25540]],[[64142,64142],"mapped",[25628]],[[64143,64143],"mapped",[25682]],[[64144,64144],"mapped",[25942]],[[64145,64145],"mapped",[26228]],[[64146,64146],"mapped",[26391]],[[64147,64147],"mapped",[26395]],[[64148,64148],"mapped",[26454]],[[64149,64149],"mapped",[27513]],[[64150,64150],"mapped",[27578]],[[64151,64151],"mapped",[27969]],[[64152,64152],"mapped",[28379]],[[64153,64153],"mapped",[28363]],[[64154,64154],"mapped",[28450]],[[64155,64155],"mapped",[28702]],[[64156,64156],"mapped",[29038]],[[64157,64157],"mapped",[30631]],[[64158,64158],"mapped",[29237]],[[64159,64159],"mapped",[29359]],[[64160,64160],"mapped",[29482]],[[64161,64161],"mapped",[29809]],[[64162,64162],"mapped",[29958]],[[64163,64163],"mapped",[30011]],[[64164,64164],"mapped",[30237]],[[64165,64165],"mapped",[30239]],[[64166,64166],"mapped",[30410]],[[64167,64167],"mapped",[30427]],[[64168,64168],"mapped",[30452]],[[64169,64169],"mapped",[30538]],[[64170,64170],"mapped",[30528]],[[64171,64171],"mapped",[30924]],[[64172,64172],"mapped",[31409]],[[64173,64173],"mapped",[31680]],[[64174,64174],"mapped",[31867]],[[64175,64175],"mapped",[32091]],[[64176,64176],"mapped",[32244]],[[64177,64177],"mapped",[32574]],[[64178,64178],"mapped",[32773]],[[64179,64179],"mapped",[33618]],[[64180,64180],"mapped",[33775]],[[64181,64181],"mapped",[34681]],[[64182,64182],"mapped",[35137]],[[64183,64183],"mapped",[35206]],[[64184,64184],"mapped",[35222]],[[64185,64185],"mapped",[35519]],[[64186,64186],"mapped",[35576]],[[64187,64187],"mapped",[35531]],[[64188,64188],"mapped",[35585]],[[64189,64189],"mapped",[35582]],[[64190,64190],"mapped",[35565]],[[64191,64191],"mapped",[35641]],[[64192,64192],"mapped",[35722]],[[64193,64193],"mapped",[36104]],[[64194,64194],"mapped",[36664]],[[64195,64195],"mapped",[36978]],[[64196,64196],"mapped",[37273]],[[64197,64197],"mapped",[37494]],[[64198,64198],"mapped",[38524]],[[64199,64199],"mapped",[38627]],[[64200,64200],"mapped",[38742]],[[64201,64201],"mapped",[38875]],[[64202,64202],"mapped",[38911]],[[64203,64203],"mapped",[38923]],[[64204,64204],"mapped",[38971]],[[64205,64205],"mapped",[39698]],[[64206,64206],"mapped",[40860]],[[64207,64207],"mapped",[141386]],[[64208,64208],"mapped",[141380]],[[64209,64209],"mapped",[144341]],[[64210,64210],"mapped",[15261]],[[64211,64211],"mapped",[16408]],[[64212,64212],"mapped",[16441]],[[64213,64213],"mapped",[152137]],[[64214,64214],"mapped",[154832]],[[64215,64215],"mapped",[163539]],[[64216,64216],"mapped",[40771]],[[64217,64217],"mapped",[40846]],[[64218,64255],"disallowed"],[[64256,64256],"mapped",[102,102]],[[64257,64257],"mapped",[102,105]],[[64258,64258],"mapped",[102,108]],[[64259,64259],"mapped",[102,102,105]],[[64260,64260],"mapped",[102,102,108]],[[64261,64262],"mapped",[115,116]],[[64263,64274],"disallowed"],[[64275,64275],"mapped",[1396,1398]],[[64276,64276],"mapped",[1396,1381]],[[64277,64277],"mapped",[1396,1387]],[[64278,64278],"mapped",[1406,1398]],[[64279,64279],"mapped",[1396,1389]],[[64280,64284],"disallowed"],[[64285,64285],"mapped",[1497,1460]],[[64286,64286],"valid"],[[64287,64287],"mapped",[1522,1463]],[[64288,64288],"mapped",[1506]],[[64289,64289],"mapped",[1488]],[[64290,64290],"mapped",[1491]],[[64291,64291],"mapped",[1492]],[[64292,64292],"mapped",[1499]],[[64293,64293],"mapped",[1500]],[[64294,64294],"mapped",[1501]],[[64295,64295],"mapped",[1512]],[[64296,64296],"mapped",[1514]],[[64297,64297],"disallowed_STD3_mapped",[43]],[[64298,64298],"mapped",[1513,1473]],[[64299,64299],"mapped",[1513,1474]],[[64300,64300],"mapped",[1513,1468,1473]],[[64301,64301],"mapped",[1513,1468,1474]],[[64302,64302],"mapped",[1488,1463]],[[64303,64303],"mapped",[1488,1464]],[[64304,64304],"mapped",[1488,1468]],[[64305,64305],"mapped",[1489,1468]],[[64306,64306],"mapped",[1490,1468]],[[64307,64307],"mapped",[1491,1468]],[[64308,64308],"mapped",[1492,1468]],[[64309,64309],"mapped",[1493,1468]],[[64310,64310],"mapped",[1494,1468]],[[64311,64311],"disallowed"],[[64312,64312],"mapped",[1496,1468]],[[64313,64313],"mapped",[1497,1468]],[[64314,64314],"mapped",[1498,1468]],[[64315,64315],"mapped",[1499,1468]],[[64316,64316],"mapped",[1500,1468]],[[64317,64317],"disallowed"],[[64318,64318],"mapped",[1502,1468]],[[64319,64319],"disallowed"],[[64320,64320],"mapped",[1504,1468]],[[64321,64321],"mapped",[1505,1468]],[[64322,64322],"disallowed"],[[64323,64323],"mapped",[1507,1468]],[[64324,64324],"mapped",[1508,1468]],[[64325,64325],"disallowed"],[[64326,64326],"mapped",[1510,1468]],[[64327,64327],"mapped",[1511,1468]],[[64328,64328],"mapped",[1512,1468]],[[64329,64329],"mapped",[1513,1468]],[[64330,64330],"mapped",[1514,1468]],[[64331,64331],"mapped",[1493,1465]],[[64332,64332],"mapped",[1489,1471]],[[64333,64333],"mapped",[1499,1471]],[[64334,64334],"mapped",[1508,1471]],[[64335,64335],"mapped",[1488,1500]],[[64336,64337],"mapped",[1649]],[[64338,64341],"mapped",[1659]],[[64342,64345],"mapped",[1662]],[[64346,64349],"mapped",[1664]],[[64350,64353],"mapped",[1658]],[[64354,64357],"mapped",[1663]],[[64358,64361],"mapped",[1657]],[[64362,64365],"mapped",[1700]],[[64366,64369],"mapped",[1702]],[[64370,64373],"mapped",[1668]],[[64374,64377],"mapped",[1667]],[[64378,64381],"mapped",[1670]],[[64382,64385],"mapped",[1671]],[[64386,64387],"mapped",[1677]],[[64388,64389],"mapped",[1676]],[[64390,64391],"mapped",[1678]],[[64392,64393],"mapped",[1672]],[[64394,64395],"mapped",[1688]],[[64396,64397],"mapped",[1681]],[[64398,64401],"mapped",[1705]],[[64402,64405],"mapped",[1711]],[[64406,64409],"mapped",[1715]],[[64410,64413],"mapped",[1713]],[[64414,64415],"mapped",[1722]],[[64416,64419],"mapped",[1723]],[[64420,64421],"mapped",[1728]],[[64422,64425],"mapped",[1729]],[[64426,64429],"mapped",[1726]],[[64430,64431],"mapped",[1746]],[[64432,64433],"mapped",[1747]],[[64434,64449],"valid",[],"NV8"],[[64450,64466],"disallowed"],[[64467,64470],"mapped",[1709]],[[64471,64472],"mapped",[1735]],[[64473,64474],"mapped",[1734]],[[64475,64476],"mapped",[1736]],[[64477,64477],"mapped",[1735,1652]],[[64478,64479],"mapped",[1739]],[[64480,64481],"mapped",[1733]],[[64482,64483],"mapped",[1737]],[[64484,64487],"mapped",[1744]],[[64488,64489],"mapped",[1609]],[[64490,64491],"mapped",[1574,1575]],[[64492,64493],"mapped",[1574,1749]],[[64494,64495],"mapped",[1574,1608]],[[64496,64497],"mapped",[1574,1735]],[[64498,64499],"mapped",[1574,1734]],[[64500,64501],"mapped",[1574,1736]],[[64502,64504],"mapped",[1574,1744]],[[64505,64507],"mapped",[1574,1609]],[[64508,64511],"mapped",[1740]],[[64512,64512],"mapped",[1574,1580]],[[64513,64513],"mapped",[1574,1581]],[[64514,64514],"mapped",[1574,1605]],[[64515,64515],"mapped",[1574,1609]],[[64516,64516],"mapped",[1574,1610]],[[64517,64517],"mapped",[1576,1580]],[[64518,64518],"mapped",[1576,1581]],[[64519,64519],"mapped",[1576,1582]],[[64520,64520],"mapped",[1576,1605]],[[64521,64521],"mapped",[1576,1609]],[[64522,64522],"mapped",[1576,1610]],[[64523,64523],"mapped",[1578,1580]],[[64524,64524],"mapped",[1578,1581]],[[64525,64525],"mapped",[1578,1582]],[[64526,64526],"mapped",[1578,1605]],[[64527,64527],"mapped",[1578,1609]],[[64528,64528],"mapped",[1578,1610]],[[64529,64529],"mapped",[1579,1580]],[[64530,64530],"mapped",[1579,1605]],[[64531,64531],"mapped",[1579,1609]],[[64532,64532],"mapped",[1579,1610]],[[64533,64533],"mapped",[1580,1581]],[[64534,64534],"mapped",[1580,1605]],[[64535,64535],"mapped",[1581,1580]],[[64536,64536],"mapped",[1581,1605]],[[64537,64537],"mapped",[1582,1580]],[[64538,64538],"mapped",[1582,1581]],[[64539,64539],"mapped",[1582,1605]],[[64540,64540],"mapped",[1587,1580]],[[64541,64541],"mapped",[1587,1581]],[[64542,64542],"mapped",[1587,1582]],[[64543,64543],"mapped",[1587,1605]],[[64544,64544],"mapped",[1589,1581]],[[64545,64545],"mapped",[1589,1605]],[[64546,64546],"mapped",[1590,1580]],[[64547,64547],"mapped",[1590,1581]],[[64548,64548],"mapped",[1590,1582]],[[64549,64549],"mapped",[1590,1605]],[[64550,64550],"mapped",[1591,1581]],[[64551,64551],"mapped",[1591,1605]],[[64552,64552],"mapped",[1592,1605]],[[64553,64553],"mapped",[1593,1580]],[[64554,64554],"mapped",[1593,1605]],[[64555,64555],"mapped",[1594,1580]],[[64556,64556],"mapped",[1594,1605]],[[64557,64557],"mapped",[1601,1580]],[[64558,64558],"mapped",[1601,1581]],[[64559,64559],"mapped",[1601,1582]],[[64560,64560],"mapped",[1601,1605]],[[64561,64561],"mapped",[1601,1609]],[[64562,64562],"mapped",[1601,1610]],[[64563,64563],"mapped",[1602,1581]],[[64564,64564],"mapped",[1602,1605]],[[64565,64565],"mapped",[1602,1609]],[[64566,64566],"mapped",[1602,1610]],[[64567,64567],"mapped",[1603,1575]],[[64568,64568],"mapped",[1603,1580]],[[64569,64569],"mapped",[1603,1581]],[[64570,64570],"mapped",[1603,1582]],[[64571,64571],"mapped",[1603,1604]],[[64572,64572],"mapped",[1603,1605]],[[64573,64573],"mapped",[1603,1609]],[[64574,64574],"mapped",[1603,1610]],[[64575,64575],"mapped",[1604,1580]],[[64576,64576],"mapped",[1604,1581]],[[64577,64577],"mapped",[1604,1582]],[[64578,64578],"mapped",[1604,1605]],[[64579,64579],"mapped",[1604,1609]],[[64580,64580],"mapped",[1604,1610]],[[64581,64581],"mapped",[1605,1580]],[[64582,64582],"mapped",[1605,1581]],[[64583,64583],"mapped",[1605,1582]],[[64584,64584],"mapped",[1605,1605]],[[64585,64585],"mapped",[1605,1609]],[[64586,64586],"mapped",[1605,1610]],[[64587,64587],"mapped",[1606,1580]],[[64588,64588],"mapped",[1606,1581]],[[64589,64589],"mapped",[1606,1582]],[[64590,64590],"mapped",[1606,1605]],[[64591,64591],"mapped",[1606,1609]],[[64592,64592],"mapped",[1606,1610]],[[64593,64593],"mapped",[1607,1580]],[[64594,64594],"mapped",[1607,1605]],[[64595,64595],"mapped",[1607,1609]],[[64596,64596],"mapped",[1607,1610]],[[64597,64597],"mapped",[1610,1580]],[[64598,64598],"mapped",[1610,1581]],[[64599,64599],"mapped",[1610,1582]],[[64600,64600],"mapped",[1610,1605]],[[64601,64601],"mapped",[1610,1609]],[[64602,64602],"mapped",[1610,1610]],[[64603,64603],"mapped",[1584,1648]],[[64604,64604],"mapped",[1585,1648]],[[64605,64605],"mapped",[1609,1648]],[[64606,64606],"disallowed_STD3_mapped",[32,1612,1617]],[[64607,64607],"disallowed_STD3_mapped",[32,1613,1617]],[[64608,64608],"disallowed_STD3_mapped",[32,1614,1617]],[[64609,64609],"disallowed_STD3_mapped",[32,1615,1617]],[[64610,64610],"disallowed_STD3_mapped",[32,1616,1617]],[[64611,64611],"disallowed_STD3_mapped",[32,1617,1648]],[[64612,64612],"mapped",[1574,1585]],[[64613,64613],"mapped",[1574,1586]],[[64614,64614],"mapped",[1574,1605]],[[64615,64615],"mapped",[1574,1606]],[[64616,64616],"mapped",[1574,1609]],[[64617,64617],"mapped",[1574,1610]],[[64618,64618],"mapped",[1576,1585]],[[64619,64619],"mapped",[1576,1586]],[[64620,64620],"mapped",[1576,1605]],[[64621,64621],"mapped",[1576,1606]],[[64622,64622],"mapped",[1576,1609]],[[64623,64623],"mapped",[1576,1610]],[[64624,64624],"mapped",[1578,1585]],[[64625,64625],"mapped",[1578,1586]],[[64626,64626],"mapped",[1578,1605]],[[64627,64627],"mapped",[1578,1606]],[[64628,64628],"mapped",[1578,1609]],[[64629,64629],"mapped",[1578,1610]],[[64630,64630],"mapped",[1579,1585]],[[64631,64631],"mapped",[1579,1586]],[[64632,64632],"mapped",[1579,1605]],[[64633,64633],"mapped",[1579,1606]],[[64634,64634],"mapped",[1579,1609]],[[64635,64635],"mapped",[1579,1610]],[[64636,64636],"mapped",[1601,1609]],[[64637,64637],"mapped",[1601,1610]],[[64638,64638],"mapped",[1602,1609]],[[64639,64639],"mapped",[1602,1610]],[[64640,64640],"mapped",[1603,1575]],[[64641,64641],"mapped",[1603,1604]],[[64642,64642],"mapped",[1603,1605]],[[64643,64643],"mapped",[1603,1609]],[[64644,64644],"mapped",[1603,1610]],[[64645,64645],"mapped",[1604,1605]],[[64646,64646],"mapped",[1604,1609]],[[64647,64647],"mapped",[1604,1610]],[[64648,64648],"mapped",[1605,1575]],[[64649,64649],"mapped",[1605,1605]],[[64650,64650],"mapped",[1606,1585]],[[64651,64651],"mapped",[1606,1586]],[[64652,64652],"mapped",[1606,1605]],[[64653,64653],"mapped",[1606,1606]],[[64654,64654],"mapped",[1606,1609]],[[64655,64655],"mapped",[1606,1610]],[[64656,64656],"mapped",[1609,1648]],[[64657,64657],"mapped",[1610,1585]],[[64658,64658],"mapped",[1610,1586]],[[64659,64659],"mapped",[1610,1605]],[[64660,64660],"mapped",[1610,1606]],[[64661,64661],"mapped",[1610,1609]],[[64662,64662],"mapped",[1610,1610]],[[64663,64663],"mapped",[1574,1580]],[[64664,64664],"mapped",[1574,1581]],[[64665,64665],"mapped",[1574,1582]],[[64666,64666],"mapped",[1574,1605]],[[64667,64667],"mapped",[1574,1607]],[[64668,64668],"mapped",[1576,1580]],[[64669,64669],"mapped",[1576,1581]],[[64670,64670],"mapped",[1576,1582]],[[64671,64671],"mapped",[1576,1605]],[[64672,64672],"mapped",[1576,1607]],[[64673,64673],"mapped",[1578,1580]],[[64674,64674],"mapped",[1578,1581]],[[64675,64675],"mapped",[1578,1582]],[[64676,64676],"mapped",[1578,1605]],[[64677,64677],"mapped",[1578,1607]],[[64678,64678],"mapped",[1579,1605]],[[64679,64679],"mapped",[1580,1581]],[[64680,64680],"mapped",[1580,1605]],[[64681,64681],"mapped",[1581,1580]],[[64682,64682],"mapped",[1581,1605]],[[64683,64683],"mapped",[1582,1580]],[[64684,64684],"mapped",[1582,1605]],[[64685,64685],"mapped",[1587,1580]],[[64686,64686],"mapped",[1587,1581]],[[64687,64687],"mapped",[1587,1582]],[[64688,64688],"mapped",[1587,1605]],[[64689,64689],"mapped",[1589,1581]],[[64690,64690],"mapped",[1589,1582]],[[64691,64691],"mapped",[1589,1605]],[[64692,64692],"mapped",[1590,1580]],[[64693,64693],"mapped",[1590,1581]],[[64694,64694],"mapped",[1590,1582]],[[64695,64695],"mapped",[1590,1605]],[[64696,64696],"mapped",[1591,1581]],[[64697,64697],"mapped",[1592,1605]],[[64698,64698],"mapped",[1593,1580]],[[64699,64699],"mapped",[1593,1605]],[[64700,64700],"mapped",[1594,1580]],[[64701,64701],"mapped",[1594,1605]],[[64702,64702],"mapped",[1601,1580]],[[64703,64703],"mapped",[1601,1581]],[[64704,64704],"mapped",[1601,1582]],[[64705,64705],"mapped",[1601,1605]],[[64706,64706],"mapped",[1602,1581]],[[64707,64707],"mapped",[1602,1605]],[[64708,64708],"mapped",[1603,1580]],[[64709,64709],"mapped",[1603,1581]],[[64710,64710],"mapped",[1603,1582]],[[64711,64711],"mapped",[1603,1604]],[[64712,64712],"mapped",[1603,1605]],[[64713,64713],"mapped",[1604,1580]],[[64714,64714],"mapped",[1604,1581]],[[64715,64715],"mapped",[1604,1582]],[[64716,64716],"mapped",[1604,1605]],[[64717,64717],"mapped",[1604,1607]],[[64718,64718],"mapped",[1605,1580]],[[64719,64719],"mapped",[1605,1581]],[[64720,64720],"mapped",[1605,1582]],[[64721,64721],"mapped",[1605,1605]],[[64722,64722],"mapped",[1606,1580]],[[64723,64723],"mapped",[1606,1581]],[[64724,64724],"mapped",[1606,1582]],[[64725,64725],"mapped",[1606,1605]],[[64726,64726],"mapped",[1606,1607]],[[64727,64727],"mapped",[1607,1580]],[[64728,64728],"mapped",[1607,1605]],[[64729,64729],"mapped",[1607,1648]],[[64730,64730],"mapped",[1610,1580]],[[64731,64731],"mapped",[1610,1581]],[[64732,64732],"mapped",[1610,1582]],[[64733,64733],"mapped",[1610,1605]],[[64734,64734],"mapped",[1610,1607]],[[64735,64735],"mapped",[1574,1605]],[[64736,64736],"mapped",[1574,1607]],[[64737,64737],"mapped",[1576,1605]],[[64738,64738],"mapped",[1576,1607]],[[64739,64739],"mapped",[1578,1605]],[[64740,64740],"mapped",[1578,1607]],[[64741,64741],"mapped",[1579,1605]],[[64742,64742],"mapped",[1579,1607]],[[64743,64743],"mapped",[1587,1605]],[[64744,64744],"mapped",[1587,1607]],[[64745,64745],"mapped",[1588,1605]],[[64746,64746],"mapped",[1588,1607]],[[64747,64747],"mapped",[1603,1604]],[[64748,64748],"mapped",[1603,1605]],[[64749,64749],"mapped",[1604,1605]],[[64750,64750],"mapped",[1606,1605]],[[64751,64751],"mapped",[1606,1607]],[[64752,64752],"mapped",[1610,1605]],[[64753,64753],"mapped",[1610,1607]],[[64754,64754],"mapped",[1600,1614,1617]],[[64755,64755],"mapped",[1600,1615,1617]],[[64756,64756],"mapped",[1600,1616,1617]],[[64757,64757],"mapped",[1591,1609]],[[64758,64758],"mapped",[1591,1610]],[[64759,64759],"mapped",[1593,1609]],[[64760,64760],"mapped",[1593,1610]],[[64761,64761],"mapped",[1594,1609]],[[64762,64762],"mapped",[1594,1610]],[[64763,64763],"mapped",[1587,1609]],[[64764,64764],"mapped",[1587,1610]],[[64765,64765],"mapped",[1588,1609]],[[64766,64766],"mapped",[1588,1610]],[[64767,64767],"mapped",[1581,1609]],[[64768,64768],"mapped",[1581,1610]],[[64769,64769],"mapped",[1580,1609]],[[64770,64770],"mapped",[1580,1610]],[[64771,64771],"mapped",[1582,1609]],[[64772,64772],"mapped",[1582,1610]],[[64773,64773],"mapped",[1589,1609]],[[64774,64774],"mapped",[1589,1610]],[[64775,64775],"mapped",[1590,1609]],[[64776,64776],"mapped",[1590,1610]],[[64777,64777],"mapped",[1588,1580]],[[64778,64778],"mapped",[1588,1581]],[[64779,64779],"mapped",[1588,1582]],[[64780,64780],"mapped",[1588,1605]],[[64781,64781],"mapped",[1588,1585]],[[64782,64782],"mapped",[1587,1585]],[[64783,64783],"mapped",[1589,1585]],[[64784,64784],"mapped",[1590,1585]],[[64785,64785],"mapped",[1591,1609]],[[64786,64786],"mapped",[1591,1610]],[[64787,64787],"mapped",[1593,1609]],[[64788,64788],"mapped",[1593,1610]],[[64789,64789],"mapped",[1594,1609]],[[64790,64790],"mapped",[1594,1610]],[[64791,64791],"mapped",[1587,1609]],[[64792,64792],"mapped",[1587,1610]],[[64793,64793],"mapped",[1588,1609]],[[64794,64794],"mapped",[1588,1610]],[[64795,64795],"mapped",[1581,1609]],[[64796,64796],"mapped",[1581,1610]],[[64797,64797],"mapped",[1580,1609]],[[64798,64798],"mapped",[1580,1610]],[[64799,64799],"mapped",[1582,1609]],[[64800,64800],"mapped",[1582,1610]],[[64801,64801],"mapped",[1589,1609]],[[64802,64802],"mapped",[1589,1610]],[[64803,64803],"mapped",[1590,1609]],[[64804,64804],"mapped",[1590,1610]],[[64805,64805],"mapped",[1588,1580]],[[64806,64806],"mapped",[1588,1581]],[[64807,64807],"mapped",[1588,1582]],[[64808,64808],"mapped",[1588,1605]],[[64809,64809],"mapped",[1588,1585]],[[64810,64810],"mapped",[1587,1585]],[[64811,64811],"mapped",[1589,1585]],[[64812,64812],"mapped",[1590,1585]],[[64813,64813],"mapped",[1588,1580]],[[64814,64814],"mapped",[1588,1581]],[[64815,64815],"mapped",[1588,1582]],[[64816,64816],"mapped",[1588,1605]],[[64817,64817],"mapped",[1587,1607]],[[64818,64818],"mapped",[1588,1607]],[[64819,64819],"mapped",[1591,1605]],[[64820,64820],"mapped",[1587,1580]],[[64821,64821],"mapped",[1587,1581]],[[64822,64822],"mapped",[1587,1582]],[[64823,64823],"mapped",[1588,1580]],[[64824,64824],"mapped",[1588,1581]],[[64825,64825],"mapped",[1588,1582]],[[64826,64826],"mapped",[1591,1605]],[[64827,64827],"mapped",[1592,1605]],[[64828,64829],"mapped",[1575,1611]],[[64830,64831],"valid",[],"NV8"],[[64832,64847],"disallowed"],[[64848,64848],"mapped",[1578,1580,1605]],[[64849,64850],"mapped",[1578,1581,1580]],[[64851,64851],"mapped",[1578,1581,1605]],[[64852,64852],"mapped",[1578,1582,1605]],[[64853,64853],"mapped",[1578,1605,1580]],[[64854,64854],"mapped",[1578,1605,1581]],[[64855,64855],"mapped",[1578,1605,1582]],[[64856,64857],"mapped",[1580,1605,1581]],[[64858,64858],"mapped",[1581,1605,1610]],[[64859,64859],"mapped",[1581,1605,1609]],[[64860,64860],"mapped",[1587,1581,1580]],[[64861,64861],"mapped",[1587,1580,1581]],[[64862,64862],"mapped",[1587,1580,1609]],[[64863,64864],"mapped",[1587,1605,1581]],[[64865,64865],"mapped",[1587,1605,1580]],[[64866,64867],"mapped",[1587,1605,1605]],[[64868,64869],"mapped",[1589,1581,1581]],[[64870,64870],"mapped",[1589,1605,1605]],[[64871,64872],"mapped",[1588,1581,1605]],[[64873,64873],"mapped",[1588,1580,1610]],[[64874,64875],"mapped",[1588,1605,1582]],[[64876,64877],"mapped",[1588,1605,1605]],[[64878,64878],"mapped",[1590,1581,1609]],[[64879,64880],"mapped",[1590,1582,1605]],[[64881,64882],"mapped",[1591,1605,1581]],[[64883,64883],"mapped",[1591,1605,1605]],[[64884,64884],"mapped",[1591,1605,1610]],[[64885,64885],"mapped",[1593,1580,1605]],[[64886,64887],"mapped",[1593,1605,1605]],[[64888,64888],"mapped",[1593,1605,1609]],[[64889,64889],"mapped",[1594,1605,1605]],[[64890,64890],"mapped",[1594,1605,1610]],[[64891,64891],"mapped",[1594,1605,1609]],[[64892,64893],"mapped",[1601,1582,1605]],[[64894,64894],"mapped",[1602,1605,1581]],[[64895,64895],"mapped",[1602,1605,1605]],[[64896,64896],"mapped",[1604,1581,1605]],[[64897,64897],"mapped",[1604,1581,1610]],[[64898,64898],"mapped",[1604,1581,1609]],[[64899,64900],"mapped",[1604,1580,1580]],[[64901,64902],"mapped",[1604,1582,1605]],[[64903,64904],"mapped",[1604,1605,1581]],[[64905,64905],"mapped",[1605,1581,1580]],[[64906,64906],"mapped",[1605,1581,1605]],[[64907,64907],"mapped",[1605,1581,1610]],[[64908,64908],"mapped",[1605,1580,1581]],[[64909,64909],"mapped",[1605,1580,1605]],[[64910,64910],"mapped",[1605,1582,1580]],[[64911,64911],"mapped",[1605,1582,1605]],[[64912,64913],"disallowed"],[[64914,64914],"mapped",[1605,1580,1582]],[[64915,64915],"mapped",[1607,1605,1580]],[[64916,64916],"mapped",[1607,1605,1605]],[[64917,64917],"mapped",[1606,1581,1605]],[[64918,64918],"mapped",[1606,1581,1609]],[[64919,64920],"mapped",[1606,1580,1605]],[[64921,64921],"mapped",[1606,1580,1609]],[[64922,64922],"mapped",[1606,1605,1610]],[[64923,64923],"mapped",[1606,1605,1609]],[[64924,64925],"mapped",[1610,1605,1605]],[[64926,64926],"mapped",[1576,1582,1610]],[[64927,64927],"mapped",[1578,1580,1610]],[[64928,64928],"mapped",[1578,1580,1609]],[[64929,64929],"mapped",[1578,1582,1610]],[[64930,64930],"mapped",[1578,1582,1609]],[[64931,64931],"mapped",[1578,1605,1610]],[[64932,64932],"mapped",[1578,1605,1609]],[[64933,64933],"mapped",[1580,1605,1610]],[[64934,64934],"mapped",[1580,1581,1609]],[[64935,64935],"mapped",[1580,1605,1609]],[[64936,64936],"mapped",[1587,1582,1609]],[[64937,64937],"mapped",[1589,1581,1610]],[[64938,64938],"mapped",[1588,1581,1610]],[[64939,64939],"mapped",[1590,1581,1610]],[[64940,64940],"mapped",[1604,1580,1610]],[[64941,64941],"mapped",[1604,1605,1610]],[[64942,64942],"mapped",[1610,1581,1610]],[[64943,64943],"mapped",[1610,1580,1610]],[[64944,64944],"mapped",[1610,1605,1610]],[[64945,64945],"mapped",[1605,1605,1610]],[[64946,64946],"mapped",[1602,1605,1610]],[[64947,64947],"mapped",[1606,1581,1610]],[[64948,64948],"mapped",[1602,1605,1581]],[[64949,64949],"mapped",[1604,1581,1605]],[[64950,64950],"mapped",[1593,1605,1610]],[[64951,64951],"mapped",[1603,1605,1610]],[[64952,64952],"mapped",[1606,1580,1581]],[[64953,64953],"mapped",[1605,1582,1610]],[[64954,64954],"mapped",[1604,1580,1605]],[[64955,64955],"mapped",[1603,1605,1605]],[[64956,64956],"mapped",[1604,1580,1605]],[[64957,64957],"mapped",[1606,1580,1581]],[[64958,64958],"mapped",[1580,1581,1610]],[[64959,64959],"mapped",[1581,1580,1610]],[[64960,64960],"mapped",[1605,1580,1610]],[[64961,64961],"mapped",[1601,1605,1610]],[[64962,64962],"mapped",[1576,1581,1610]],[[64963,64963],"mapped",[1603,1605,1605]],[[64964,64964],"mapped",[1593,1580,1605]],[[64965,64965],"mapped",[1589,1605,1605]],[[64966,64966],"mapped",[1587,1582,1610]],[[64967,64967],"mapped",[1606,1580,1610]],[[64968,64975],"disallowed"],[[64976,65007],"disallowed"],[[65008,65008],"mapped",[1589,1604,1746]],[[65009,65009],"mapped",[1602,1604,1746]],[[65010,65010],"mapped",[1575,1604,1604,1607]],[[65011,65011],"mapped",[1575,1603,1576,1585]],[[65012,65012],"mapped",[1605,1581,1605,1583]],[[65013,65013],"mapped",[1589,1604,1593,1605]],[[65014,65014],"mapped",[1585,1587,1608,1604]],[[65015,65015],"mapped",[1593,1604,1610,1607]],[[65016,65016],"mapped",[1608,1587,1604,1605]],[[65017,65017],"mapped",[1589,1604,1609]],[[65018,65018],"disallowed_STD3_mapped",[1589,1604,1609,32,1575,1604,1604,1607,32,1593,1604,1610,1607,32,1608,1587,1604,1605]],[[65019,65019],"disallowed_STD3_mapped",[1580,1604,32,1580,1604,1575,1604,1607]],[[65020,65020],"mapped",[1585,1740,1575,1604]],[[65021,65021],"valid",[],"NV8"],[[65022,65023],"disallowed"],[[65024,65039],"ignored"],[[65040,65040],"disallowed_STD3_mapped",[44]],[[65041,65041],"mapped",[12289]],[[65042,65042],"disallowed"],[[65043,65043],"disallowed_STD3_mapped",[58]],[[65044,65044],"disallowed_STD3_mapped",[59]],[[65045,65045],"disallowed_STD3_mapped",[33]],[[65046,65046],"disallowed_STD3_mapped",[63]],[[65047,65047],"mapped",[12310]],[[65048,65048],"mapped",[12311]],[[65049,65049],"disallowed"],[[65050,65055],"disallowed"],[[65056,65059],"valid"],[[65060,65062],"valid"],[[65063,65069],"valid"],[[65070,65071],"valid"],[[65072,65072],"disallowed"],[[65073,65073],"mapped",[8212]],[[65074,65074],"mapped",[8211]],[[65075,65076],"disallowed_STD3_mapped",[95]],[[65077,65077],"disallowed_STD3_mapped",[40]],[[65078,65078],"disallowed_STD3_mapped",[41]],[[65079,65079],"disallowed_STD3_mapped",[123]],[[65080,65080],"disallowed_STD3_mapped",[125]],[[65081,65081],"mapped",[12308]],[[65082,65082],"mapped",[12309]],[[65083,65083],"mapped",[12304]],[[65084,65084],"mapped",[12305]],[[65085,65085],"mapped",[12298]],[[65086,65086],"mapped",[12299]],[[65087,65087],"mapped",[12296]],[[65088,65088],"mapped",[12297]],[[65089,65089],"mapped",[12300]],[[65090,65090],"mapped",[12301]],[[65091,65091],"mapped",[12302]],[[65092,65092],"mapped",[12303]],[[65093,65094],"valid",[],"NV8"],[[65095,65095],"disallowed_STD3_mapped",[91]],[[65096,65096],"disallowed_STD3_mapped",[93]],[[65097,65100],"disallowed_STD3_mapped",[32,773]],[[65101,65103],"disallowed_STD3_mapped",[95]],[[65104,65104],"disallowed_STD3_mapped",[44]],[[65105,65105],"mapped",[12289]],[[65106,65106],"disallowed"],[[65107,65107],"disallowed"],[[65108,65108],"disallowed_STD3_mapped",[59]],[[65109,65109],"disallowed_STD3_mapped",[58]],[[65110,65110],"disallowed_STD3_mapped",[63]],[[65111,65111],"disallowed_STD3_mapped",[33]],[[65112,65112],"mapped",[8212]],[[65113,65113],"disallowed_STD3_mapped",[40]],[[65114,65114],"disallowed_STD3_mapped",[41]],[[65115,65115],"disallowed_STD3_mapped",[123]],[[65116,65116],"disallowed_STD3_mapped",[125]],[[65117,65117],"mapped",[12308]],[[65118,65118],"mapped",[12309]],[[65119,65119],"disallowed_STD3_mapped",[35]],[[65120,65120],"disallowed_STD3_mapped",[38]],[[65121,65121],"disallowed_STD3_mapped",[42]],[[65122,65122],"disallowed_STD3_mapped",[43]],[[65123,65123],"mapped",[45]],[[65124,65124],"disallowed_STD3_mapped",[60]],[[65125,65125],"disallowed_STD3_mapped",[62]],[[65126,65126],"disallowed_STD3_mapped",[61]],[[65127,65127],"disallowed"],[[65128,65128],"disallowed_STD3_mapped",[92]],[[65129,65129],"disallowed_STD3_mapped",[36]],[[65130,65130],"disallowed_STD3_mapped",[37]],[[65131,65131],"disallowed_STD3_mapped",[64]],[[65132,65135],"disallowed"],[[65136,65136],"disallowed_STD3_mapped",[32,1611]],[[65137,65137],"mapped",[1600,1611]],[[65138,65138],"disallowed_STD3_mapped",[32,1612]],[[65139,65139],"valid"],[[65140,65140],"disallowed_STD3_mapped",[32,1613]],[[65141,65141],"disallowed"],[[65142,65142],"disallowed_STD3_mapped",[32,1614]],[[65143,65143],"mapped",[1600,1614]],[[65144,65144],"disallowed_STD3_mapped",[32,1615]],[[65145,65145],"mapped",[1600,1615]],[[65146,65146],"disallowed_STD3_mapped",[32,1616]],[[65147,65147],"mapped",[1600,1616]],[[65148,65148],"disallowed_STD3_mapped",[32,1617]],[[65149,65149],"mapped",[1600,1617]],[[65150,65150],"disallowed_STD3_mapped",[32,1618]],[[65151,65151],"mapped",[1600,1618]],[[65152,65152],"mapped",[1569]],[[65153,65154],"mapped",[1570]],[[65155,65156],"mapped",[1571]],[[65157,65158],"mapped",[1572]],[[65159,65160],"mapped",[1573]],[[65161,65164],"mapped",[1574]],[[65165,65166],"mapped",[1575]],[[65167,65170],"mapped",[1576]],[[65171,65172],"mapped",[1577]],[[65173,65176],"mapped",[1578]],[[65177,65180],"mapped",[1579]],[[65181,65184],"mapped",[1580]],[[65185,65188],"mapped",[1581]],[[65189,65192],"mapped",[1582]],[[65193,65194],"mapped",[1583]],[[65195,65196],"mapped",[1584]],[[65197,65198],"mapped",[1585]],[[65199,65200],"mapped",[1586]],[[65201,65204],"mapped",[1587]],[[65205,65208],"mapped",[1588]],[[65209,65212],"mapped",[1589]],[[65213,65216],"mapped",[1590]],[[65217,65220],"mapped",[1591]],[[65221,65224],"mapped",[1592]],[[65225,65228],"mapped",[1593]],[[65229,65232],"mapped",[1594]],[[65233,65236],"mapped",[1601]],[[65237,65240],"mapped",[1602]],[[65241,65244],"mapped",[1603]],[[65245,65248],"mapped",[1604]],[[65249,65252],"mapped",[1605]],[[65253,65256],"mapped",[1606]],[[65257,65260],"mapped",[1607]],[[65261,65262],"mapped",[1608]],[[65263,65264],"mapped",[1609]],[[65265,65268],"mapped",[1610]],[[65269,65270],"mapped",[1604,1570]],[[65271,65272],"mapped",[1604,1571]],[[65273,65274],"mapped",[1604,1573]],[[65275,65276],"mapped",[1604,1575]],[[65277,65278],"disallowed"],[[65279,65279],"ignored"],[[65280,65280],"disallowed"],[[65281,65281],"disallowed_STD3_mapped",[33]],[[65282,65282],"disallowed_STD3_mapped",[34]],[[65283,65283],"disallowed_STD3_mapped",[35]],[[65284,65284],"disallowed_STD3_mapped",[36]],[[65285,65285],"disallowed_STD3_mapped",[37]],[[65286,65286],"disallowed_STD3_mapped",[38]],[[65287,65287],"disallowed_STD3_mapped",[39]],[[65288,65288],"disallowed_STD3_mapped",[40]],[[65289,65289],"disallowed_STD3_mapped",[41]],[[65290,65290],"disallowed_STD3_mapped",[42]],[[65291,65291],"disallowed_STD3_mapped",[43]],[[65292,65292],"disallowed_STD3_mapped",[44]],[[65293,65293],"mapped",[45]],[[65294,65294],"mapped",[46]],[[65295,65295],"disallowed_STD3_mapped",[47]],[[65296,65296],"mapped",[48]],[[65297,65297],"mapped",[49]],[[65298,65298],"mapped",[50]],[[65299,65299],"mapped",[51]],[[65300,65300],"mapped",[52]],[[65301,65301],"mapped",[53]],[[65302,65302],"mapped",[54]],[[65303,65303],"mapped",[55]],[[65304,65304],"mapped",[56]],[[65305,65305],"mapped",[57]],[[65306,65306],"disallowed_STD3_mapped",[58]],[[65307,65307],"disallowed_STD3_mapped",[59]],[[65308,65308],"disallowed_STD3_mapped",[60]],[[65309,65309],"disallowed_STD3_mapped",[61]],[[65310,65310],"disallowed_STD3_mapped",[62]],[[65311,65311],"disallowed_STD3_mapped",[63]],[[65312,65312],"disallowed_STD3_mapped",[64]],[[65313,65313],"mapped",[97]],[[65314,65314],"mapped",[98]],[[65315,65315],"mapped",[99]],[[65316,65316],"mapped",[100]],[[65317,65317],"mapped",[101]],[[65318,65318],"mapped",[102]],[[65319,65319],"mapped",[103]],[[65320,65320],"mapped",[104]],[[65321,65321],"mapped",[105]],[[65322,65322],"mapped",[106]],[[65323,65323],"mapped",[107]],[[65324,65324],"mapped",[108]],[[65325,65325],"mapped",[109]],[[65326,65326],"mapped",[110]],[[65327,65327],"mapped",[111]],[[65328,65328],"mapped",[112]],[[65329,65329],"mapped",[113]],[[65330,65330],"mapped",[114]],[[65331,65331],"mapped",[115]],[[65332,65332],"mapped",[116]],[[65333,65333],"mapped",[117]],[[65334,65334],"mapped",[118]],[[65335,65335],"mapped",[119]],[[65336,65336],"mapped",[120]],[[65337,65337],"mapped",[121]],[[65338,65338],"mapped",[122]],[[65339,65339],"disallowed_STD3_mapped",[91]],[[65340,65340],"disallowed_STD3_mapped",[92]],[[65341,65341],"disallowed_STD3_mapped",[93]],[[65342,65342],"disallowed_STD3_mapped",[94]],[[65343,65343],"disallowed_STD3_mapped",[95]],[[65344,65344],"disallowed_STD3_mapped",[96]],[[65345,65345],"mapped",[97]],[[65346,65346],"mapped",[98]],[[65347,65347],"mapped",[99]],[[65348,65348],"mapped",[100]],[[65349,65349],"mapped",[101]],[[65350,65350],"mapped",[102]],[[65351,65351],"mapped",[103]],[[65352,65352],"mapped",[104]],[[65353,65353],"mapped",[105]],[[65354,65354],"mapped",[106]],[[65355,65355],"mapped",[107]],[[65356,65356],"mapped",[108]],[[65357,65357],"mapped",[109]],[[65358,65358],"mapped",[110]],[[65359,65359],"mapped",[111]],[[65360,65360],"mapped",[112]],[[65361,65361],"mapped",[113]],[[65362,65362],"mapped",[114]],[[65363,65363],"mapped",[115]],[[65364,65364],"mapped",[116]],[[65365,65365],"mapped",[117]],[[65366,65366],"mapped",[118]],[[65367,65367],"mapped",[119]],[[65368,65368],"mapped",[120]],[[65369,65369],"mapped",[121]],[[65370,65370],"mapped",[122]],[[65371,65371],"disallowed_STD3_mapped",[123]],[[65372,65372],"disallowed_STD3_mapped",[124]],[[65373,65373],"disallowed_STD3_mapped",[125]],[[65374,65374],"disallowed_STD3_mapped",[126]],[[65375,65375],"mapped",[10629]],[[65376,65376],"mapped",[10630]],[[65377,65377],"mapped",[46]],[[65378,65378],"mapped",[12300]],[[65379,65379],"mapped",[12301]],[[65380,65380],"mapped",[12289]],[[65381,65381],"mapped",[12539]],[[65382,65382],"mapped",[12530]],[[65383,65383],"mapped",[12449]],[[65384,65384],"mapped",[12451]],[[65385,65385],"mapped",[12453]],[[65386,65386],"mapped",[12455]],[[65387,65387],"mapped",[12457]],[[65388,65388],"mapped",[12515]],[[65389,65389],"mapped",[12517]],[[65390,65390],"mapped",[12519]],[[65391,65391],"mapped",[12483]],[[65392,65392],"mapped",[12540]],[[65393,65393],"mapped",[12450]],[[65394,65394],"mapped",[12452]],[[65395,65395],"mapped",[12454]],[[65396,65396],"mapped",[12456]],[[65397,65397],"mapped",[12458]],[[65398,65398],"mapped",[12459]],[[65399,65399],"mapped",[12461]],[[65400,65400],"mapped",[12463]],[[65401,65401],"mapped",[12465]],[[65402,65402],"mapped",[12467]],[[65403,65403],"mapped",[12469]],[[65404,65404],"mapped",[12471]],[[65405,65405],"mapped",[12473]],[[65406,65406],"mapped",[12475]],[[65407,65407],"mapped",[12477]],[[65408,65408],"mapped",[12479]],[[65409,65409],"mapped",[12481]],[[65410,65410],"mapped",[12484]],[[65411,65411],"mapped",[12486]],[[65412,65412],"mapped",[12488]],[[65413,65413],"mapped",[12490]],[[65414,65414],"mapped",[12491]],[[65415,65415],"mapped",[12492]],[[65416,65416],"mapped",[12493]],[[65417,65417],"mapped",[12494]],[[65418,65418],"mapped",[12495]],[[65419,65419],"mapped",[12498]],[[65420,65420],"mapped",[12501]],[[65421,65421],"mapped",[12504]],[[65422,65422],"mapped",[12507]],[[65423,65423],"mapped",[12510]],[[65424,65424],"mapped",[12511]],[[65425,65425],"mapped",[12512]],[[65426,65426],"mapped",[12513]],[[65427,65427],"mapped",[12514]],[[65428,65428],"mapped",[12516]],[[65429,65429],"mapped",[12518]],[[65430,65430],"mapped",[12520]],[[65431,65431],"mapped",[12521]],[[65432,65432],"mapped",[12522]],[[65433,65433],"mapped",[12523]],[[65434,65434],"mapped",[12524]],[[65435,65435],"mapped",[12525]],[[65436,65436],"mapped",[12527]],[[65437,65437],"mapped",[12531]],[[65438,65438],"mapped",[12441]],[[65439,65439],"mapped",[12442]],[[65440,65440],"disallowed"],[[65441,65441],"mapped",[4352]],[[65442,65442],"mapped",[4353]],[[65443,65443],"mapped",[4522]],[[65444,65444],"mapped",[4354]],[[65445,65445],"mapped",[4524]],[[65446,65446],"mapped",[4525]],[[65447,65447],"mapped",[4355]],[[65448,65448],"mapped",[4356]],[[65449,65449],"mapped",[4357]],[[65450,65450],"mapped",[4528]],[[65451,65451],"mapped",[4529]],[[65452,65452],"mapped",[4530]],[[65453,65453],"mapped",[4531]],[[65454,65454],"mapped",[4532]],[[65455,65455],"mapped",[4533]],[[65456,65456],"mapped",[4378]],[[65457,65457],"mapped",[4358]],[[65458,65458],"mapped",[4359]],[[65459,65459],"mapped",[4360]],[[65460,65460],"mapped",[4385]],[[65461,65461],"mapped",[4361]],[[65462,65462],"mapped",[4362]],[[65463,65463],"mapped",[4363]],[[65464,65464],"mapped",[4364]],[[65465,65465],"mapped",[4365]],[[65466,65466],"mapped",[4366]],[[65467,65467],"mapped",[4367]],[[65468,65468],"mapped",[4368]],[[65469,65469],"mapped",[4369]],[[65470,65470],"mapped",[4370]],[[65471,65473],"disallowed"],[[65474,65474],"mapped",[4449]],[[65475,65475],"mapped",[4450]],[[65476,65476],"mapped",[4451]],[[65477,65477],"mapped",[4452]],[[65478,65478],"mapped",[4453]],[[65479,65479],"mapped",[4454]],[[65480,65481],"disallowed"],[[65482,65482],"mapped",[4455]],[[65483,65483],"mapped",[4456]],[[65484,65484],"mapped",[4457]],[[65485,65485],"mapped",[4458]],[[65486,65486],"mapped",[4459]],[[65487,65487],"mapped",[4460]],[[65488,65489],"disallowed"],[[65490,65490],"mapped",[4461]],[[65491,65491],"mapped",[4462]],[[65492,65492],"mapped",[4463]],[[65493,65493],"mapped",[4464]],[[65494,65494],"mapped",[4465]],[[65495,65495],"mapped",[4466]],[[65496,65497],"disallowed"],[[65498,65498],"mapped",[4467]],[[65499,65499],"mapped",[4468]],[[65500,65500],"mapped",[4469]],[[65501,65503],"disallowed"],[[65504,65504],"mapped",[162]],[[65505,65505],"mapped",[163]],[[65506,65506],"mapped",[172]],[[65507,65507],"disallowed_STD3_mapped",[32,772]],[[65508,65508],"mapped",[166]],[[65509,65509],"mapped",[165]],[[65510,65510],"mapped",[8361]],[[65511,65511],"disallowed"],[[65512,65512],"mapped",[9474]],[[65513,65513],"mapped",[8592]],[[65514,65514],"mapped",[8593]],[[65515,65515],"mapped",[8594]],[[65516,65516],"mapped",[8595]],[[65517,65517],"mapped",[9632]],[[65518,65518],"mapped",[9675]],[[65519,65528],"disallowed"],[[65529,65531],"disallowed"],[[65532,65532],"disallowed"],[[65533,65533],"disallowed"],[[65534,65535],"disallowed"],[[65536,65547],"valid"],[[65548,65548],"disallowed"],[[65549,65574],"valid"],[[65575,65575],"disallowed"],[[65576,65594],"valid"],[[65595,65595],"disallowed"],[[65596,65597],"valid"],[[65598,65598],"disallowed"],[[65599,65613],"valid"],[[65614,65615],"disallowed"],[[65616,65629],"valid"],[[65630,65663],"disallowed"],[[65664,65786],"valid"],[[65787,65791],"disallowed"],[[65792,65794],"valid",[],"NV8"],[[65795,65798],"disallowed"],[[65799,65843],"valid",[],"NV8"],[[65844,65846],"disallowed"],[[65847,65855],"valid",[],"NV8"],[[65856,65930],"valid",[],"NV8"],[[65931,65932],"valid",[],"NV8"],[[65933,65935],"disallowed"],[[65936,65947],"valid",[],"NV8"],[[65948,65951],"disallowed"],[[65952,65952],"valid",[],"NV8"],[[65953,65999],"disallowed"],[[66000,66044],"valid",[],"NV8"],[[66045,66045],"valid"],[[66046,66175],"disallowed"],[[66176,66204],"valid"],[[66205,66207],"disallowed"],[[66208,66256],"valid"],[[66257,66271],"disallowed"],[[66272,66272],"valid"],[[66273,66299],"valid",[],"NV8"],[[66300,66303],"disallowed"],[[66304,66334],"valid"],[[66335,66335],"valid"],[[66336,66339],"valid",[],"NV8"],[[66340,66351],"disallowed"],[[66352,66368],"valid"],[[66369,66369],"valid",[],"NV8"],[[66370,66377],"valid"],[[66378,66378],"valid",[],"NV8"],[[66379,66383],"disallowed"],[[66384,66426],"valid"],[[66427,66431],"disallowed"],[[66432,66461],"valid"],[[66462,66462],"disallowed"],[[66463,66463],"valid",[],"NV8"],[[66464,66499],"valid"],[[66500,66503],"disallowed"],[[66504,66511],"valid"],[[66512,66517],"valid",[],"NV8"],[[66518,66559],"disallowed"],[[66560,66560],"mapped",[66600]],[[66561,66561],"mapped",[66601]],[[66562,66562],"mapped",[66602]],[[66563,66563],"mapped",[66603]],[[66564,66564],"mapped",[66604]],[[66565,66565],"mapped",[66605]],[[66566,66566],"mapped",[66606]],[[66567,66567],"mapped",[66607]],[[66568,66568],"mapped",[66608]],[[66569,66569],"mapped",[66609]],[[66570,66570],"mapped",[66610]],[[66571,66571],"mapped",[66611]],[[66572,66572],"mapped",[66612]],[[66573,66573],"mapped",[66613]],[[66574,66574],"mapped",[66614]],[[66575,66575],"mapped",[66615]],[[66576,66576],"mapped",[66616]],[[66577,66577],"mapped",[66617]],[[66578,66578],"mapped",[66618]],[[66579,66579],"mapped",[66619]],[[66580,66580],"mapped",[66620]],[[66581,66581],"mapped",[66621]],[[66582,66582],"mapped",[66622]],[[66583,66583],"mapped",[66623]],[[66584,66584],"mapped",[66624]],[[66585,66585],"mapped",[66625]],[[66586,66586],"mapped",[66626]],[[66587,66587],"mapped",[66627]],[[66588,66588],"mapped",[66628]],[[66589,66589],"mapped",[66629]],[[66590,66590],"mapped",[66630]],[[66591,66591],"mapped",[66631]],[[66592,66592],"mapped",[66632]],[[66593,66593],"mapped",[66633]],[[66594,66594],"mapped",[66634]],[[66595,66595],"mapped",[66635]],[[66596,66596],"mapped",[66636]],[[66597,66597],"mapped",[66637]],[[66598,66598],"mapped",[66638]],[[66599,66599],"mapped",[66639]],[[66600,66637],"valid"],[[66638,66717],"valid"],[[66718,66719],"disallowed"],[[66720,66729],"valid"],[[66730,66815],"disallowed"],[[66816,66855],"valid"],[[66856,66863],"disallowed"],[[66864,66915],"valid"],[[66916,66926],"disallowed"],[[66927,66927],"valid",[],"NV8"],[[66928,67071],"disallowed"],[[67072,67382],"valid"],[[67383,67391],"disallowed"],[[67392,67413],"valid"],[[67414,67423],"disallowed"],[[67424,67431],"valid"],[[67432,67583],"disallowed"],[[67584,67589],"valid"],[[67590,67591],"disallowed"],[[67592,67592],"valid"],[[67593,67593],"disallowed"],[[67594,67637],"valid"],[[67638,67638],"disallowed"],[[67639,67640],"valid"],[[67641,67643],"disallowed"],[[67644,67644],"valid"],[[67645,67646],"disallowed"],[[67647,67647],"valid"],[[67648,67669],"valid"],[[67670,67670],"disallowed"],[[67671,67679],"valid",[],"NV8"],[[67680,67702],"valid"],[[67703,67711],"valid",[],"NV8"],[[67712,67742],"valid"],[[67743,67750],"disallowed"],[[67751,67759],"valid",[],"NV8"],[[67760,67807],"disallowed"],[[67808,67826],"valid"],[[67827,67827],"disallowed"],[[67828,67829],"valid"],[[67830,67834],"disallowed"],[[67835,67839],"valid",[],"NV8"],[[67840,67861],"valid"],[[67862,67865],"valid",[],"NV8"],[[67866,67867],"valid",[],"NV8"],[[67868,67870],"disallowed"],[[67871,67871],"valid",[],"NV8"],[[67872,67897],"valid"],[[67898,67902],"disallowed"],[[67903,67903],"valid",[],"NV8"],[[67904,67967],"disallowed"],[[67968,68023],"valid"],[[68024,68027],"disallowed"],[[68028,68029],"valid",[],"NV8"],[[68030,68031],"valid"],[[68032,68047],"valid",[],"NV8"],[[68048,68049],"disallowed"],[[68050,68095],"valid",[],"NV8"],[[68096,68099],"valid"],[[68100,68100],"disallowed"],[[68101,68102],"valid"],[[68103,68107],"disallowed"],[[68108,68115],"valid"],[[68116,68116],"disallowed"],[[68117,68119],"valid"],[[68120,68120],"disallowed"],[[68121,68147],"valid"],[[68148,68151],"disallowed"],[[68152,68154],"valid"],[[68155,68158],"disallowed"],[[68159,68159],"valid"],[[68160,68167],"valid",[],"NV8"],[[68168,68175],"disallowed"],[[68176,68184],"valid",[],"NV8"],[[68185,68191],"disallowed"],[[68192,68220],"valid"],[[68221,68223],"valid",[],"NV8"],[[68224,68252],"valid"],[[68253,68255],"valid",[],"NV8"],[[68256,68287],"disallowed"],[[68288,68295],"valid"],[[68296,68296],"valid",[],"NV8"],[[68297,68326],"valid"],[[68327,68330],"disallowed"],[[68331,68342],"valid",[],"NV8"],[[68343,68351],"disallowed"],[[68352,68405],"valid"],[[68406,68408],"disallowed"],[[68409,68415],"valid",[],"NV8"],[[68416,68437],"valid"],[[68438,68439],"disallowed"],[[68440,68447],"valid",[],"NV8"],[[68448,68466],"valid"],[[68467,68471],"disallowed"],[[68472,68479],"valid",[],"NV8"],[[68480,68497],"valid"],[[68498,68504],"disallowed"],[[68505,68508],"valid",[],"NV8"],[[68509,68520],"disallowed"],[[68521,68527],"valid",[],"NV8"],[[68528,68607],"disallowed"],[[68608,68680],"valid"],[[68681,68735],"disallowed"],[[68736,68736],"mapped",[68800]],[[68737,68737],"mapped",[68801]],[[68738,68738],"mapped",[68802]],[[68739,68739],"mapped",[68803]],[[68740,68740],"mapped",[68804]],[[68741,68741],"mapped",[68805]],[[68742,68742],"mapped",[68806]],[[68743,68743],"mapped",[68807]],[[68744,68744],"mapped",[68808]],[[68745,68745],"mapped",[68809]],[[68746,68746],"mapped",[68810]],[[68747,68747],"mapped",[68811]],[[68748,68748],"mapped",[68812]],[[68749,68749],"mapped",[68813]],[[68750,68750],"mapped",[68814]],[[68751,68751],"mapped",[68815]],[[68752,68752],"mapped",[68816]],[[68753,68753],"mapped",[68817]],[[68754,68754],"mapped",[68818]],[[68755,68755],"mapped",[68819]],[[68756,68756],"mapped",[68820]],[[68757,68757],"mapped",[68821]],[[68758,68758],"mapped",[68822]],[[68759,68759],"mapped",[68823]],[[68760,68760],"mapped",[68824]],[[68761,68761],"mapped",[68825]],[[68762,68762],"mapped",[68826]],[[68763,68763],"mapped",[68827]],[[68764,68764],"mapped",[68828]],[[68765,68765],"mapped",[68829]],[[68766,68766],"mapped",[68830]],[[68767,68767],"mapped",[68831]],[[68768,68768],"mapped",[68832]],[[68769,68769],"mapped",[68833]],[[68770,68770],"mapped",[68834]],[[68771,68771],"mapped",[68835]],[[68772,68772],"mapped",[68836]],[[68773,68773],"mapped",[68837]],[[68774,68774],"mapped",[68838]],[[68775,68775],"mapped",[68839]],[[68776,68776],"mapped",[68840]],[[68777,68777],"mapped",[68841]],[[68778,68778],"mapped",[68842]],[[68779,68779],"mapped",[68843]],[[68780,68780],"mapped",[68844]],[[68781,68781],"mapped",[68845]],[[68782,68782],"mapped",[68846]],[[68783,68783],"mapped",[68847]],[[68784,68784],"mapped",[68848]],[[68785,68785],"mapped",[68849]],[[68786,68786],"mapped",[68850]],[[68787,68799],"disallowed"],[[68800,68850],"valid"],[[68851,68857],"disallowed"],[[68858,68863],"valid",[],"NV8"],[[68864,69215],"disallowed"],[[69216,69246],"valid",[],"NV8"],[[69247,69631],"disallowed"],[[69632,69702],"valid"],[[69703,69709],"valid",[],"NV8"],[[69710,69713],"disallowed"],[[69714,69733],"valid",[],"NV8"],[[69734,69743],"valid"],[[69744,69758],"disallowed"],[[69759,69759],"valid"],[[69760,69818],"valid"],[[69819,69820],"valid",[],"NV8"],[[69821,69821],"disallowed"],[[69822,69825],"valid",[],"NV8"],[[69826,69839],"disallowed"],[[69840,69864],"valid"],[[69865,69871],"disallowed"],[[69872,69881],"valid"],[[69882,69887],"disallowed"],[[69888,69940],"valid"],[[69941,69941],"disallowed"],[[69942,69951],"valid"],[[69952,69955],"valid",[],"NV8"],[[69956,69967],"disallowed"],[[69968,70003],"valid"],[[70004,70005],"valid",[],"NV8"],[[70006,70006],"valid"],[[70007,70015],"disallowed"],[[70016,70084],"valid"],[[70085,70088],"valid",[],"NV8"],[[70089,70089],"valid",[],"NV8"],[[70090,70092],"valid"],[[70093,70093],"valid",[],"NV8"],[[70094,70095],"disallowed"],[[70096,70105],"valid"],[[70106,70106],"valid"],[[70107,70107],"valid",[],"NV8"],[[70108,70108],"valid"],[[70109,70111],"valid",[],"NV8"],[[70112,70112],"disallowed"],[[70113,70132],"valid",[],"NV8"],[[70133,70143],"disallowed"],[[70144,70161],"valid"],[[70162,70162],"disallowed"],[[70163,70199],"valid"],[[70200,70205],"valid",[],"NV8"],[[70206,70271],"disallowed"],[[70272,70278],"valid"],[[70279,70279],"disallowed"],[[70280,70280],"valid"],[[70281,70281],"disallowed"],[[70282,70285],"valid"],[[70286,70286],"disallowed"],[[70287,70301],"valid"],[[70302,70302],"disallowed"],[[70303,70312],"valid"],[[70313,70313],"valid",[],"NV8"],[[70314,70319],"disallowed"],[[70320,70378],"valid"],[[70379,70383],"disallowed"],[[70384,70393],"valid"],[[70394,70399],"disallowed"],[[70400,70400],"valid"],[[70401,70403],"valid"],[[70404,70404],"disallowed"],[[70405,70412],"valid"],[[70413,70414],"disallowed"],[[70415,70416],"valid"],[[70417,70418],"disallowed"],[[70419,70440],"valid"],[[70441,70441],"disallowed"],[[70442,70448],"valid"],[[70449,70449],"disallowed"],[[70450,70451],"valid"],[[70452,70452],"disallowed"],[[70453,70457],"valid"],[[70458,70459],"disallowed"],[[70460,70468],"valid"],[[70469,70470],"disallowed"],[[70471,70472],"valid"],[[70473,70474],"disallowed"],[[70475,70477],"valid"],[[70478,70479],"disallowed"],[[70480,70480],"valid"],[[70481,70486],"disallowed"],[[70487,70487],"valid"],[[70488,70492],"disallowed"],[[70493,70499],"valid"],[[70500,70501],"disallowed"],[[70502,70508],"valid"],[[70509,70511],"disallowed"],[[70512,70516],"valid"],[[70517,70783],"disallowed"],[[70784,70853],"valid"],[[70854,70854],"valid",[],"NV8"],[[70855,70855],"valid"],[[70856,70863],"disallowed"],[[70864,70873],"valid"],[[70874,71039],"disallowed"],[[71040,71093],"valid"],[[71094,71095],"disallowed"],[[71096,71104],"valid"],[[71105,71113],"valid",[],"NV8"],[[71114,71127],"valid",[],"NV8"],[[71128,71133],"valid"],[[71134,71167],"disallowed"],[[71168,71232],"valid"],[[71233,71235],"valid",[],"NV8"],[[71236,71236],"valid"],[[71237,71247],"disallowed"],[[71248,71257],"valid"],[[71258,71295],"disallowed"],[[71296,71351],"valid"],[[71352,71359],"disallowed"],[[71360,71369],"valid"],[[71370,71423],"disallowed"],[[71424,71449],"valid"],[[71450,71452],"disallowed"],[[71453,71467],"valid"],[[71468,71471],"disallowed"],[[71472,71481],"valid"],[[71482,71487],"valid",[],"NV8"],[[71488,71839],"disallowed"],[[71840,71840],"mapped",[71872]],[[71841,71841],"mapped",[71873]],[[71842,71842],"mapped",[71874]],[[71843,71843],"mapped",[71875]],[[71844,71844],"mapped",[71876]],[[71845,71845],"mapped",[71877]],[[71846,71846],"mapped",[71878]],[[71847,71847],"mapped",[71879]],[[71848,71848],"mapped",[71880]],[[71849,71849],"mapped",[71881]],[[71850,71850],"mapped",[71882]],[[71851,71851],"mapped",[71883]],[[71852,71852],"mapped",[71884]],[[71853,71853],"mapped",[71885]],[[71854,71854],"mapped",[71886]],[[71855,71855],"mapped",[71887]],[[71856,71856],"mapped",[71888]],[[71857,71857],"mapped",[71889]],[[71858,71858],"mapped",[71890]],[[71859,71859],"mapped",[71891]],[[71860,71860],"mapped",[71892]],[[71861,71861],"mapped",[71893]],[[71862,71862],"mapped",[71894]],[[71863,71863],"mapped",[71895]],[[71864,71864],"mapped",[71896]],[[71865,71865],"mapped",[71897]],[[71866,71866],"mapped",[71898]],[[71867,71867],"mapped",[71899]],[[71868,71868],"mapped",[71900]],[[71869,71869],"mapped",[71901]],[[71870,71870],"mapped",[71902]],[[71871,71871],"mapped",[71903]],[[71872,71913],"valid"],[[71914,71922],"valid",[],"NV8"],[[71923,71934],"disallowed"],[[71935,71935],"valid"],[[71936,72383],"disallowed"],[[72384,72440],"valid"],[[72441,73727],"disallowed"],[[73728,74606],"valid"],[[74607,74648],"valid"],[[74649,74649],"valid"],[[74650,74751],"disallowed"],[[74752,74850],"valid",[],"NV8"],[[74851,74862],"valid",[],"NV8"],[[74863,74863],"disallowed"],[[74864,74867],"valid",[],"NV8"],[[74868,74868],"valid",[],"NV8"],[[74869,74879],"disallowed"],[[74880,75075],"valid"],[[75076,77823],"disallowed"],[[77824,78894],"valid"],[[78895,82943],"disallowed"],[[82944,83526],"valid"],[[83527,92159],"disallowed"],[[92160,92728],"valid"],[[92729,92735],"disallowed"],[[92736,92766],"valid"],[[92767,92767],"disallowed"],[[92768,92777],"valid"],[[92778,92781],"disallowed"],[[92782,92783],"valid",[],"NV8"],[[92784,92879],"disallowed"],[[92880,92909],"valid"],[[92910,92911],"disallowed"],[[92912,92916],"valid"],[[92917,92917],"valid",[],"NV8"],[[92918,92927],"disallowed"],[[92928,92982],"valid"],[[92983,92991],"valid",[],"NV8"],[[92992,92995],"valid"],[[92996,92997],"valid",[],"NV8"],[[92998,93007],"disallowed"],[[93008,93017],"valid"],[[93018,93018],"disallowed"],[[93019,93025],"valid",[],"NV8"],[[93026,93026],"disallowed"],[[93027,93047],"valid"],[[93048,93052],"disallowed"],[[93053,93071],"valid"],[[93072,93951],"disallowed"],[[93952,94020],"valid"],[[94021,94031],"disallowed"],[[94032,94078],"valid"],[[94079,94094],"disallowed"],[[94095,94111],"valid"],[[94112,110591],"disallowed"],[[110592,110593],"valid"],[[110594,113663],"disallowed"],[[113664,113770],"valid"],[[113771,113775],"disallowed"],[[113776,113788],"valid"],[[113789,113791],"disallowed"],[[113792,113800],"valid"],[[113801,113807],"disallowed"],[[113808,113817],"valid"],[[113818,113819],"disallowed"],[[113820,113820],"valid",[],"NV8"],[[113821,113822],"valid"],[[113823,113823],"valid",[],"NV8"],[[113824,113827],"ignored"],[[113828,118783],"disallowed"],[[118784,119029],"valid",[],"NV8"],[[119030,119039],"disallowed"],[[119040,119078],"valid",[],"NV8"],[[119079,119080],"disallowed"],[[119081,119081],"valid",[],"NV8"],[[119082,119133],"valid",[],"NV8"],[[119134,119134],"mapped",[119127,119141]],[[119135,119135],"mapped",[119128,119141]],[[119136,119136],"mapped",[119128,119141,119150]],[[119137,119137],"mapped",[119128,119141,119151]],[[119138,119138],"mapped",[119128,119141,119152]],[[119139,119139],"mapped",[119128,119141,119153]],[[119140,119140],"mapped",[119128,119141,119154]],[[119141,119154],"valid",[],"NV8"],[[119155,119162],"disallowed"],[[119163,119226],"valid",[],"NV8"],[[119227,119227],"mapped",[119225,119141]],[[119228,119228],"mapped",[119226,119141]],[[119229,119229],"mapped",[119225,119141,119150]],[[119230,119230],"mapped",[119226,119141,119150]],[[119231,119231],"mapped",[119225,119141,119151]],[[119232,119232],"mapped",[119226,119141,119151]],[[119233,119261],"valid",[],"NV8"],[[119262,119272],"valid",[],"NV8"],[[119273,119295],"disallowed"],[[119296,119365],"valid",[],"NV8"],[[119366,119551],"disallowed"],[[119552,119638],"valid",[],"NV8"],[[119639,119647],"disallowed"],[[119648,119665],"valid",[],"NV8"],[[119666,119807],"disallowed"],[[119808,119808],"mapped",[97]],[[119809,119809],"mapped",[98]],[[119810,119810],"mapped",[99]],[[119811,119811],"mapped",[100]],[[119812,119812],"mapped",[101]],[[119813,119813],"mapped",[102]],[[119814,119814],"mapped",[103]],[[119815,119815],"mapped",[104]],[[119816,119816],"mapped",[105]],[[119817,119817],"mapped",[106]],[[119818,119818],"mapped",[107]],[[119819,119819],"mapped",[108]],[[119820,119820],"mapped",[109]],[[119821,119821],"mapped",[110]],[[119822,119822],"mapped",[111]],[[119823,119823],"mapped",[112]],[[119824,119824],"mapped",[113]],[[119825,119825],"mapped",[114]],[[119826,119826],"mapped",[115]],[[119827,119827],"mapped",[116]],[[119828,119828],"mapped",[117]],[[119829,119829],"mapped",[118]],[[119830,119830],"mapped",[119]],[[119831,119831],"mapped",[120]],[[119832,119832],"mapped",[121]],[[119833,119833],"mapped",[122]],[[119834,119834],"mapped",[97]],[[119835,119835],"mapped",[98]],[[119836,119836],"mapped",[99]],[[119837,119837],"mapped",[100]],[[119838,119838],"mapped",[101]],[[119839,119839],"mapped",[102]],[[119840,119840],"mapped",[103]],[[119841,119841],"mapped",[104]],[[119842,119842],"mapped",[105]],[[119843,119843],"mapped",[106]],[[119844,119844],"mapped",[107]],[[119845,119845],"mapped",[108]],[[119846,119846],"mapped",[109]],[[119847,119847],"mapped",[110]],[[119848,119848],"mapped",[111]],[[119849,119849],"mapped",[112]],[[119850,119850],"mapped",[113]],[[119851,119851],"mapped",[114]],[[119852,119852],"mapped",[115]],[[119853,119853],"mapped",[116]],[[119854,119854],"mapped",[117]],[[119855,119855],"mapped",[118]],[[119856,119856],"mapped",[119]],[[119857,119857],"mapped",[120]],[[119858,119858],"mapped",[121]],[[119859,119859],"mapped",[122]],[[119860,119860],"mapped",[97]],[[119861,119861],"mapped",[98]],[[119862,119862],"mapped",[99]],[[119863,119863],"mapped",[100]],[[119864,119864],"mapped",[101]],[[119865,119865],"mapped",[102]],[[119866,119866],"mapped",[103]],[[119867,119867],"mapped",[104]],[[119868,119868],"mapped",[105]],[[119869,119869],"mapped",[106]],[[119870,119870],"mapped",[107]],[[119871,119871],"mapped",[108]],[[119872,119872],"mapped",[109]],[[119873,119873],"mapped",[110]],[[119874,119874],"mapped",[111]],[[119875,119875],"mapped",[112]],[[119876,119876],"mapped",[113]],[[119877,119877],"mapped",[114]],[[119878,119878],"mapped",[115]],[[119879,119879],"mapped",[116]],[[119880,119880],"mapped",[117]],[[119881,119881],"mapped",[118]],[[119882,119882],"mapped",[119]],[[119883,119883],"mapped",[120]],[[119884,119884],"mapped",[121]],[[119885,119885],"mapped",[122]],[[119886,119886],"mapped",[97]],[[119887,119887],"mapped",[98]],[[119888,119888],"mapped",[99]],[[119889,119889],"mapped",[100]],[[119890,119890],"mapped",[101]],[[119891,119891],"mapped",[102]],[[119892,119892],"mapped",[103]],[[119893,119893],"disallowed"],[[119894,119894],"mapped",[105]],[[119895,119895],"mapped",[106]],[[119896,119896],"mapped",[107]],[[119897,119897],"mapped",[108]],[[119898,119898],"mapped",[109]],[[119899,119899],"mapped",[110]],[[119900,119900],"mapped",[111]],[[119901,119901],"mapped",[112]],[[119902,119902],"mapped",[113]],[[119903,119903],"mapped",[114]],[[119904,119904],"mapped",[115]],[[119905,119905],"mapped",[116]],[[119906,119906],"mapped",[117]],[[119907,119907],"mapped",[118]],[[119908,119908],"mapped",[119]],[[119909,119909],"mapped",[120]],[[119910,119910],"mapped",[121]],[[119911,119911],"mapped",[122]],[[119912,119912],"mapped",[97]],[[119913,119913],"mapped",[98]],[[119914,119914],"mapped",[99]],[[119915,119915],"mapped",[100]],[[119916,119916],"mapped",[101]],[[119917,119917],"mapped",[102]],[[119918,119918],"mapped",[103]],[[119919,119919],"mapped",[104]],[[119920,119920],"mapped",[105]],[[119921,119921],"mapped",[106]],[[119922,119922],"mapped",[107]],[[119923,119923],"mapped",[108]],[[119924,119924],"mapped",[109]],[[119925,119925],"mapped",[110]],[[119926,119926],"mapped",[111]],[[119927,119927],"mapped",[112]],[[119928,119928],"mapped",[113]],[[119929,119929],"mapped",[114]],[[119930,119930],"mapped",[115]],[[119931,119931],"mapped",[116]],[[119932,119932],"mapped",[117]],[[119933,119933],"mapped",[118]],[[119934,119934],"mapped",[119]],[[119935,119935],"mapped",[120]],[[119936,119936],"mapped",[121]],[[119937,119937],"mapped",[122]],[[119938,119938],"mapped",[97]],[[119939,119939],"mapped",[98]],[[119940,119940],"mapped",[99]],[[119941,119941],"mapped",[100]],[[119942,119942],"mapped",[101]],[[119943,119943],"mapped",[102]],[[119944,119944],"mapped",[103]],[[119945,119945],"mapped",[104]],[[119946,119946],"mapped",[105]],[[119947,119947],"mapped",[106]],[[119948,119948],"mapped",[107]],[[119949,119949],"mapped",[108]],[[119950,119950],"mapped",[109]],[[119951,119951],"mapped",[110]],[[119952,119952],"mapped",[111]],[[119953,119953],"mapped",[112]],[[119954,119954],"mapped",[113]],[[119955,119955],"mapped",[114]],[[119956,119956],"mapped",[115]],[[119957,119957],"mapped",[116]],[[119958,119958],"mapped",[117]],[[119959,119959],"mapped",[118]],[[119960,119960],"mapped",[119]],[[119961,119961],"mapped",[120]],[[119962,119962],"mapped",[121]],[[119963,119963],"mapped",[122]],[[119964,119964],"mapped",[97]],[[119965,119965],"disallowed"],[[119966,119966],"mapped",[99]],[[119967,119967],"mapped",[100]],[[119968,119969],"disallowed"],[[119970,119970],"mapped",[103]],[[119971,119972],"disallowed"],[[119973,119973],"mapped",[106]],[[119974,119974],"mapped",[107]],[[119975,119976],"disallowed"],[[119977,119977],"mapped",[110]],[[119978,119978],"mapped",[111]],[[119979,119979],"mapped",[112]],[[119980,119980],"mapped",[113]],[[119981,119981],"disallowed"],[[119982,119982],"mapped",[115]],[[119983,119983],"mapped",[116]],[[119984,119984],"mapped",[117]],[[119985,119985],"mapped",[118]],[[119986,119986],"mapped",[119]],[[119987,119987],"mapped",[120]],[[119988,119988],"mapped",[121]],[[119989,119989],"mapped",[122]],[[119990,119990],"mapped",[97]],[[119991,119991],"mapped",[98]],[[119992,119992],"mapped",[99]],[[119993,119993],"mapped",[100]],[[119994,119994],"disallowed"],[[119995,119995],"mapped",[102]],[[119996,119996],"disallowed"],[[119997,119997],"mapped",[104]],[[119998,119998],"mapped",[105]],[[119999,119999],"mapped",[106]],[[120000,120000],"mapped",[107]],[[120001,120001],"mapped",[108]],[[120002,120002],"mapped",[109]],[[120003,120003],"mapped",[110]],[[120004,120004],"disallowed"],[[120005,120005],"mapped",[112]],[[120006,120006],"mapped",[113]],[[120007,120007],"mapped",[114]],[[120008,120008],"mapped",[115]],[[120009,120009],"mapped",[116]],[[120010,120010],"mapped",[117]],[[120011,120011],"mapped",[118]],[[120012,120012],"mapped",[119]],[[120013,120013],"mapped",[120]],[[120014,120014],"mapped",[121]],[[120015,120015],"mapped",[122]],[[120016,120016],"mapped",[97]],[[120017,120017],"mapped",[98]],[[120018,120018],"mapped",[99]],[[120019,120019],"mapped",[100]],[[120020,120020],"mapped",[101]],[[120021,120021],"mapped",[102]],[[120022,120022],"mapped",[103]],[[120023,120023],"mapped",[104]],[[120024,120024],"mapped",[105]],[[120025,120025],"mapped",[106]],[[120026,120026],"mapped",[107]],[[120027,120027],"mapped",[108]],[[120028,120028],"mapped",[109]],[[120029,120029],"mapped",[110]],[[120030,120030],"mapped",[111]],[[120031,120031],"mapped",[112]],[[120032,120032],"mapped",[113]],[[120033,120033],"mapped",[114]],[[120034,120034],"mapped",[115]],[[120035,120035],"mapped",[116]],[[120036,120036],"mapped",[117]],[[120037,120037],"mapped",[118]],[[120038,120038],"mapped",[119]],[[120039,120039],"mapped",[120]],[[120040,120040],"mapped",[121]],[[120041,120041],"mapped",[122]],[[120042,120042],"mapped",[97]],[[120043,120043],"mapped",[98]],[[120044,120044],"mapped",[99]],[[120045,120045],"mapped",[100]],[[120046,120046],"mapped",[101]],[[120047,120047],"mapped",[102]],[[120048,120048],"mapped",[103]],[[120049,120049],"mapped",[104]],[[120050,120050],"mapped",[105]],[[120051,120051],"mapped",[106]],[[120052,120052],"mapped",[107]],[[120053,120053],"mapped",[108]],[[120054,120054],"mapped",[109]],[[120055,120055],"mapped",[110]],[[120056,120056],"mapped",[111]],[[120057,120057],"mapped",[112]],[[120058,120058],"mapped",[113]],[[120059,120059],"mapped",[114]],[[120060,120060],"mapped",[115]],[[120061,120061],"mapped",[116]],[[120062,120062],"mapped",[117]],[[120063,120063],"mapped",[118]],[[120064,120064],"mapped",[119]],[[120065,120065],"mapped",[120]],[[120066,120066],"mapped",[121]],[[120067,120067],"mapped",[122]],[[120068,120068],"mapped",[97]],[[120069,120069],"mapped",[98]],[[120070,120070],"disallowed"],[[120071,120071],"mapped",[100]],[[120072,120072],"mapped",[101]],[[120073,120073],"mapped",[102]],[[120074,120074],"mapped",[103]],[[120075,120076],"disallowed"],[[120077,120077],"mapped",[106]],[[120078,120078],"mapped",[107]],[[120079,120079],"mapped",[108]],[[120080,120080],"mapped",[109]],[[120081,120081],"mapped",[110]],[[120082,120082],"mapped",[111]],[[120083,120083],"mapped",[112]],[[120084,120084],"mapped",[113]],[[120085,120085],"disallowed"],[[120086,120086],"mapped",[115]],[[120087,120087],"mapped",[116]],[[120088,120088],"mapped",[117]],[[120089,120089],"mapped",[118]],[[120090,120090],"mapped",[119]],[[120091,120091],"mapped",[120]],[[120092,120092],"mapped",[121]],[[120093,120093],"disallowed"],[[120094,120094],"mapped",[97]],[[120095,120095],"mapped",[98]],[[120096,120096],"mapped",[99]],[[120097,120097],"mapped",[100]],[[120098,120098],"mapped",[101]],[[120099,120099],"mapped",[102]],[[120100,120100],"mapped",[103]],[[120101,120101],"mapped",[104]],[[120102,120102],"mapped",[105]],[[120103,120103],"mapped",[106]],[[120104,120104],"mapped",[107]],[[120105,120105],"mapped",[108]],[[120106,120106],"mapped",[109]],[[120107,120107],"mapped",[110]],[[120108,120108],"mapped",[111]],[[120109,120109],"mapped",[112]],[[120110,120110],"mapped",[113]],[[120111,120111],"mapped",[114]],[[120112,120112],"mapped",[115]],[[120113,120113],"mapped",[116]],[[120114,120114],"mapped",[117]],[[120115,120115],"mapped",[118]],[[120116,120116],"mapped",[119]],[[120117,120117],"mapped",[120]],[[120118,120118],"mapped",[121]],[[120119,120119],"mapped",[122]],[[120120,120120],"mapped",[97]],[[120121,120121],"mapped",[98]],[[120122,120122],"disallowed"],[[120123,120123],"mapped",[100]],[[120124,120124],"mapped",[101]],[[120125,120125],"mapped",[102]],[[120126,120126],"mapped",[103]],[[120127,120127],"disallowed"],[[120128,120128],"mapped",[105]],[[120129,120129],"mapped",[106]],[[120130,120130],"mapped",[107]],[[120131,120131],"mapped",[108]],[[120132,120132],"mapped",[109]],[[120133,120133],"disallowed"],[[120134,120134],"mapped",[111]],[[120135,120137],"disallowed"],[[120138,120138],"mapped",[115]],[[120139,120139],"mapped",[116]],[[120140,120140],"mapped",[117]],[[120141,120141],"mapped",[118]],[[120142,120142],"mapped",[119]],[[120143,120143],"mapped",[120]],[[120144,120144],"mapped",[121]],[[120145,120145],"disallowed"],[[120146,120146],"mapped",[97]],[[120147,120147],"mapped",[98]],[[120148,120148],"mapped",[99]],[[120149,120149],"mapped",[100]],[[120150,120150],"mapped",[101]],[[120151,120151],"mapped",[102]],[[120152,120152],"mapped",[103]],[[120153,120153],"mapped",[104]],[[120154,120154],"mapped",[105]],[[120155,120155],"mapped",[106]],[[120156,120156],"mapped",[107]],[[120157,120157],"mapped",[108]],[[120158,120158],"mapped",[109]],[[120159,120159],"mapped",[110]],[[120160,120160],"mapped",[111]],[[120161,120161],"mapped",[112]],[[120162,120162],"mapped",[113]],[[120163,120163],"mapped",[114]],[[120164,120164],"mapped",[115]],[[120165,120165],"mapped",[116]],[[120166,120166],"mapped",[117]],[[120167,120167],"mapped",[118]],[[120168,120168],"mapped",[119]],[[120169,120169],"mapped",[120]],[[120170,120170],"mapped",[121]],[[120171,120171],"mapped",[122]],[[120172,120172],"mapped",[97]],[[120173,120173],"mapped",[98]],[[120174,120174],"mapped",[99]],[[120175,120175],"mapped",[100]],[[120176,120176],"mapped",[101]],[[120177,120177],"mapped",[102]],[[120178,120178],"mapped",[103]],[[120179,120179],"mapped",[104]],[[120180,120180],"mapped",[105]],[[120181,120181],"mapped",[106]],[[120182,120182],"mapped",[107]],[[120183,120183],"mapped",[108]],[[120184,120184],"mapped",[109]],[[120185,120185],"mapped",[110]],[[120186,120186],"mapped",[111]],[[120187,120187],"mapped",[112]],[[120188,120188],"mapped",[113]],[[120189,120189],"mapped",[114]],[[120190,120190],"mapped",[115]],[[120191,120191],"mapped",[116]],[[120192,120192],"mapped",[117]],[[120193,120193],"mapped",[118]],[[120194,120194],"mapped",[119]],[[120195,120195],"mapped",[120]],[[120196,120196],"mapped",[121]],[[120197,120197],"mapped",[122]],[[120198,120198],"mapped",[97]],[[120199,120199],"mapped",[98]],[[120200,120200],"mapped",[99]],[[120201,120201],"mapped",[100]],[[120202,120202],"mapped",[101]],[[120203,120203],"mapped",[102]],[[120204,120204],"mapped",[103]],[[120205,120205],"mapped",[104]],[[120206,120206],"mapped",[105]],[[120207,120207],"mapped",[106]],[[120208,120208],"mapped",[107]],[[120209,120209],"mapped",[108]],[[120210,120210],"mapped",[109]],[[120211,120211],"mapped",[110]],[[120212,120212],"mapped",[111]],[[120213,120213],"mapped",[112]],[[120214,120214],"mapped",[113]],[[120215,120215],"mapped",[114]],[[120216,120216],"mapped",[115]],[[120217,120217],"mapped",[116]],[[120218,120218],"mapped",[117]],[[120219,120219],"mapped",[118]],[[120220,120220],"mapped",[119]],[[120221,120221],"mapped",[120]],[[120222,120222],"mapped",[121]],[[120223,120223],"mapped",[122]],[[120224,120224],"mapped",[97]],[[120225,120225],"mapped",[98]],[[120226,120226],"mapped",[99]],[[120227,120227],"mapped",[100]],[[120228,120228],"mapped",[101]],[[120229,120229],"mapped",[102]],[[120230,120230],"mapped",[103]],[[120231,120231],"mapped",[104]],[[120232,120232],"mapped",[105]],[[120233,120233],"mapped",[106]],[[120234,120234],"mapped",[107]],[[120235,120235],"mapped",[108]],[[120236,120236],"mapped",[109]],[[120237,120237],"mapped",[110]],[[120238,120238],"mapped",[111]],[[120239,120239],"mapped",[112]],[[120240,120240],"mapped",[113]],[[120241,120241],"mapped",[114]],[[120242,120242],"mapped",[115]],[[120243,120243],"mapped",[116]],[[120244,120244],"mapped",[117]],[[120245,120245],"mapped",[118]],[[120246,120246],"mapped",[119]],[[120247,120247],"mapped",[120]],[[120248,120248],"mapped",[121]],[[120249,120249],"mapped",[122]],[[120250,120250],"mapped",[97]],[[120251,120251],"mapped",[98]],[[120252,120252],"mapped",[99]],[[120253,120253],"mapped",[100]],[[120254,120254],"mapped",[101]],[[120255,120255],"mapped",[102]],[[120256,120256],"mapped",[103]],[[120257,120257],"mapped",[104]],[[120258,120258],"mapped",[105]],[[120259,120259],"mapped",[106]],[[120260,120260],"mapped",[107]],[[120261,120261],"mapped",[108]],[[120262,120262],"mapped",[109]],[[120263,120263],"mapped",[110]],[[120264,120264],"mapped",[111]],[[120265,120265],"mapped",[112]],[[120266,120266],"mapped",[113]],[[120267,120267],"mapped",[114]],[[120268,120268],"mapped",[115]],[[120269,120269],"mapped",[116]],[[120270,120270],"mapped",[117]],[[120271,120271],"mapped",[118]],[[120272,120272],"mapped",[119]],[[120273,120273],"mapped",[120]],[[120274,120274],"mapped",[121]],[[120275,120275],"mapped",[122]],[[120276,120276],"mapped",[97]],[[120277,120277],"mapped",[98]],[[120278,120278],"mapped",[99]],[[120279,120279],"mapped",[100]],[[120280,120280],"mapped",[101]],[[120281,120281],"mapped",[102]],[[120282,120282],"mapped",[103]],[[120283,120283],"mapped",[104]],[[120284,120284],"mapped",[105]],[[120285,120285],"mapped",[106]],[[120286,120286],"mapped",[107]],[[120287,120287],"mapped",[108]],[[120288,120288],"mapped",[109]],[[120289,120289],"mapped",[110]],[[120290,120290],"mapped",[111]],[[120291,120291],"mapped",[112]],[[120292,120292],"mapped",[113]],[[120293,120293],"mapped",[114]],[[120294,120294],"mapped",[115]],[[120295,120295],"mapped",[116]],[[120296,120296],"mapped",[117]],[[120297,120297],"mapped",[118]],[[120298,120298],"mapped",[119]],[[120299,120299],"mapped",[120]],[[120300,120300],"mapped",[121]],[[120301,120301],"mapped",[122]],[[120302,120302],"mapped",[97]],[[120303,120303],"mapped",[98]],[[120304,120304],"mapped",[99]],[[120305,120305],"mapped",[100]],[[120306,120306],"mapped",[101]],[[120307,120307],"mapped",[102]],[[120308,120308],"mapped",[103]],[[120309,120309],"mapped",[104]],[[120310,120310],"mapped",[105]],[[120311,120311],"mapped",[106]],[[120312,120312],"mapped",[107]],[[120313,120313],"mapped",[108]],[[120314,120314],"mapped",[109]],[[120315,120315],"mapped",[110]],[[120316,120316],"mapped",[111]],[[120317,120317],"mapped",[112]],[[120318,120318],"mapped",[113]],[[120319,120319],"mapped",[114]],[[120320,120320],"mapped",[115]],[[120321,120321],"mapped",[116]],[[120322,120322],"mapped",[117]],[[120323,120323],"mapped",[118]],[[120324,120324],"mapped",[119]],[[120325,120325],"mapped",[120]],[[120326,120326],"mapped",[121]],[[120327,120327],"mapped",[122]],[[120328,120328],"mapped",[97]],[[120329,120329],"mapped",[98]],[[120330,120330],"mapped",[99]],[[120331,120331],"mapped",[100]],[[120332,120332],"mapped",[101]],[[120333,120333],"mapped",[102]],[[120334,120334],"mapped",[103]],[[120335,120335],"mapped",[104]],[[120336,120336],"mapped",[105]],[[120337,120337],"mapped",[106]],[[120338,120338],"mapped",[107]],[[120339,120339],"mapped",[108]],[[120340,120340],"mapped",[109]],[[120341,120341],"mapped",[110]],[[120342,120342],"mapped",[111]],[[120343,120343],"mapped",[112]],[[120344,120344],"mapped",[113]],[[120345,120345],"mapped",[114]],[[120346,120346],"mapped",[115]],[[120347,120347],"mapped",[116]],[[120348,120348],"mapped",[117]],[[120349,120349],"mapped",[118]],[[120350,120350],"mapped",[119]],[[120351,120351],"mapped",[120]],[[120352,120352],"mapped",[121]],[[120353,120353],"mapped",[122]],[[120354,120354],"mapped",[97]],[[120355,120355],"mapped",[98]],[[120356,120356],"mapped",[99]],[[120357,120357],"mapped",[100]],[[120358,120358],"mapped",[101]],[[120359,120359],"mapped",[102]],[[120360,120360],"mapped",[103]],[[120361,120361],"mapped",[104]],[[120362,120362],"mapped",[105]],[[120363,120363],"mapped",[106]],[[120364,120364],"mapped",[107]],[[120365,120365],"mapped",[108]],[[120366,120366],"mapped",[109]],[[120367,120367],"mapped",[110]],[[120368,120368],"mapped",[111]],[[120369,120369],"mapped",[112]],[[120370,120370],"mapped",[113]],[[120371,120371],"mapped",[114]],[[120372,120372],"mapped",[115]],[[120373,120373],"mapped",[116]],[[120374,120374],"mapped",[117]],[[120375,120375],"mapped",[118]],[[120376,120376],"mapped",[119]],[[120377,120377],"mapped",[120]],[[120378,120378],"mapped",[121]],[[120379,120379],"mapped",[122]],[[120380,120380],"mapped",[97]],[[120381,120381],"mapped",[98]],[[120382,120382],"mapped",[99]],[[120383,120383],"mapped",[100]],[[120384,120384],"mapped",[101]],[[120385,120385],"mapped",[102]],[[120386,120386],"mapped",[103]],[[120387,120387],"mapped",[104]],[[120388,120388],"mapped",[105]],[[120389,120389],"mapped",[106]],[[120390,120390],"mapped",[107]],[[120391,120391],"mapped",[108]],[[120392,120392],"mapped",[109]],[[120393,120393],"mapped",[110]],[[120394,120394],"mapped",[111]],[[120395,120395],"mapped",[112]],[[120396,120396],"mapped",[113]],[[120397,120397],"mapped",[114]],[[120398,120398],"mapped",[115]],[[120399,120399],"mapped",[116]],[[120400,120400],"mapped",[117]],[[120401,120401],"mapped",[118]],[[120402,120402],"mapped",[119]],[[120403,120403],"mapped",[120]],[[120404,120404],"mapped",[121]],[[120405,120405],"mapped",[122]],[[120406,120406],"mapped",[97]],[[120407,120407],"mapped",[98]],[[120408,120408],"mapped",[99]],[[120409,120409],"mapped",[100]],[[120410,120410],"mapped",[101]],[[120411,120411],"mapped",[102]],[[120412,120412],"mapped",[103]],[[120413,120413],"mapped",[104]],[[120414,120414],"mapped",[105]],[[120415,120415],"mapped",[106]],[[120416,120416],"mapped",[107]],[[120417,120417],"mapped",[108]],[[120418,120418],"mapped",[109]],[[120419,120419],"mapped",[110]],[[120420,120420],"mapped",[111]],[[120421,120421],"mapped",[112]],[[120422,120422],"mapped",[113]],[[120423,120423],"mapped",[114]],[[120424,120424],"mapped",[115]],[[120425,120425],"mapped",[116]],[[120426,120426],"mapped",[117]],[[120427,120427],"mapped",[118]],[[120428,120428],"mapped",[119]],[[120429,120429],"mapped",[120]],[[120430,120430],"mapped",[121]],[[120431,120431],"mapped",[122]],[[120432,120432],"mapped",[97]],[[120433,120433],"mapped",[98]],[[120434,120434],"mapped",[99]],[[120435,120435],"mapped",[100]],[[120436,120436],"mapped",[101]],[[120437,120437],"mapped",[102]],[[120438,120438],"mapped",[103]],[[120439,120439],"mapped",[104]],[[120440,120440],"mapped",[105]],[[120441,120441],"mapped",[106]],[[120442,120442],"mapped",[107]],[[120443,120443],"mapped",[108]],[[120444,120444],"mapped",[109]],[[120445,120445],"mapped",[110]],[[120446,120446],"mapped",[111]],[[120447,120447],"mapped",[112]],[[120448,120448],"mapped",[113]],[[120449,120449],"mapped",[114]],[[120450,120450],"mapped",[115]],[[120451,120451],"mapped",[116]],[[120452,120452],"mapped",[117]],[[120453,120453],"mapped",[118]],[[120454,120454],"mapped",[119]],[[120455,120455],"mapped",[120]],[[120456,120456],"mapped",[121]],[[120457,120457],"mapped",[122]],[[120458,120458],"mapped",[97]],[[120459,120459],"mapped",[98]],[[120460,120460],"mapped",[99]],[[120461,120461],"mapped",[100]],[[120462,120462],"mapped",[101]],[[120463,120463],"mapped",[102]],[[120464,120464],"mapped",[103]],[[120465,120465],"mapped",[104]],[[120466,120466],"mapped",[105]],[[120467,120467],"mapped",[106]],[[120468,120468],"mapped",[107]],[[120469,120469],"mapped",[108]],[[120470,120470],"mapped",[109]],[[120471,120471],"mapped",[110]],[[120472,120472],"mapped",[111]],[[120473,120473],"mapped",[112]],[[120474,120474],"mapped",[113]],[[120475,120475],"mapped",[114]],[[120476,120476],"mapped",[115]],[[120477,120477],"mapped",[116]],[[120478,120478],"mapped",[117]],[[120479,120479],"mapped",[118]],[[120480,120480],"mapped",[119]],[[120481,120481],"mapped",[120]],[[120482,120482],"mapped",[121]],[[120483,120483],"mapped",[122]],[[120484,120484],"mapped",[305]],[[120485,120485],"mapped",[567]],[[120486,120487],"disallowed"],[[120488,120488],"mapped",[945]],[[120489,120489],"mapped",[946]],[[120490,120490],"mapped",[947]],[[120491,120491],"mapped",[948]],[[120492,120492],"mapped",[949]],[[120493,120493],"mapped",[950]],[[120494,120494],"mapped",[951]],[[120495,120495],"mapped",[952]],[[120496,120496],"mapped",[953]],[[120497,120497],"mapped",[954]],[[120498,120498],"mapped",[955]],[[120499,120499],"mapped",[956]],[[120500,120500],"mapped",[957]],[[120501,120501],"mapped",[958]],[[120502,120502],"mapped",[959]],[[120503,120503],"mapped",[960]],[[120504,120504],"mapped",[961]],[[120505,120505],"mapped",[952]],[[120506,120506],"mapped",[963]],[[120507,120507],"mapped",[964]],[[120508,120508],"mapped",[965]],[[120509,120509],"mapped",[966]],[[120510,120510],"mapped",[967]],[[120511,120511],"mapped",[968]],[[120512,120512],"mapped",[969]],[[120513,120513],"mapped",[8711]],[[120514,120514],"mapped",[945]],[[120515,120515],"mapped",[946]],[[120516,120516],"mapped",[947]],[[120517,120517],"mapped",[948]],[[120518,120518],"mapped",[949]],[[120519,120519],"mapped",[950]],[[120520,120520],"mapped",[951]],[[120521,120521],"mapped",[952]],[[120522,120522],"mapped",[953]],[[120523,120523],"mapped",[954]],[[120524,120524],"mapped",[955]],[[120525,120525],"mapped",[956]],[[120526,120526],"mapped",[957]],[[120527,120527],"mapped",[958]],[[120528,120528],"mapped",[959]],[[120529,120529],"mapped",[960]],[[120530,120530],"mapped",[961]],[[120531,120532],"mapped",[963]],[[120533,120533],"mapped",[964]],[[120534,120534],"mapped",[965]],[[120535,120535],"mapped",[966]],[[120536,120536],"mapped",[967]],[[120537,120537],"mapped",[968]],[[120538,120538],"mapped",[969]],[[120539,120539],"mapped",[8706]],[[120540,120540],"mapped",[949]],[[120541,120541],"mapped",[952]],[[120542,120542],"mapped",[954]],[[120543,120543],"mapped",[966]],[[120544,120544],"mapped",[961]],[[120545,120545],"mapped",[960]],[[120546,120546],"mapped",[945]],[[120547,120547],"mapped",[946]],[[120548,120548],"mapped",[947]],[[120549,120549],"mapped",[948]],[[120550,120550],"mapped",[949]],[[120551,120551],"mapped",[950]],[[120552,120552],"mapped",[951]],[[120553,120553],"mapped",[952]],[[120554,120554],"mapped",[953]],[[120555,120555],"mapped",[954]],[[120556,120556],"mapped",[955]],[[120557,120557],"mapped",[956]],[[120558,120558],"mapped",[957]],[[120559,120559],"mapped",[958]],[[120560,120560],"mapped",[959]],[[120561,120561],"mapped",[960]],[[120562,120562],"mapped",[961]],[[120563,120563],"mapped",[952]],[[120564,120564],"mapped",[963]],[[120565,120565],"mapped",[964]],[[120566,120566],"mapped",[965]],[[120567,120567],"mapped",[966]],[[120568,120568],"mapped",[967]],[[120569,120569],"mapped",[968]],[[120570,120570],"mapped",[969]],[[120571,120571],"mapped",[8711]],[[120572,120572],"mapped",[945]],[[120573,120573],"mapped",[946]],[[120574,120574],"mapped",[947]],[[120575,120575],"mapped",[948]],[[120576,120576],"mapped",[949]],[[120577,120577],"mapped",[950]],[[120578,120578],"mapped",[951]],[[120579,120579],"mapped",[952]],[[120580,120580],"mapped",[953]],[[120581,120581],"mapped",[954]],[[120582,120582],"mapped",[955]],[[120583,120583],"mapped",[956]],[[120584,120584],"mapped",[957]],[[120585,120585],"mapped",[958]],[[120586,120586],"mapped",[959]],[[120587,120587],"mapped",[960]],[[120588,120588],"mapped",[961]],[[120589,120590],"mapped",[963]],[[120591,120591],"mapped",[964]],[[120592,120592],"mapped",[965]],[[120593,120593],"mapped",[966]],[[120594,120594],"mapped",[967]],[[120595,120595],"mapped",[968]],[[120596,120596],"mapped",[969]],[[120597,120597],"mapped",[8706]],[[120598,120598],"mapped",[949]],[[120599,120599],"mapped",[952]],[[120600,120600],"mapped",[954]],[[120601,120601],"mapped",[966]],[[120602,120602],"mapped",[961]],[[120603,120603],"mapped",[960]],[[120604,120604],"mapped",[945]],[[120605,120605],"mapped",[946]],[[120606,120606],"mapped",[947]],[[120607,120607],"mapped",[948]],[[120608,120608],"mapped",[949]],[[120609,120609],"mapped",[950]],[[120610,120610],"mapped",[951]],[[120611,120611],"mapped",[952]],[[120612,120612],"mapped",[953]],[[120613,120613],"mapped",[954]],[[120614,120614],"mapped",[955]],[[120615,120615],"mapped",[956]],[[120616,120616],"mapped",[957]],[[120617,120617],"mapped",[958]],[[120618,120618],"mapped",[959]],[[120619,120619],"mapped",[960]],[[120620,120620],"mapped",[961]],[[120621,120621],"mapped",[952]],[[120622,120622],"mapped",[963]],[[120623,120623],"mapped",[964]],[[120624,120624],"mapped",[965]],[[120625,120625],"mapped",[966]],[[120626,120626],"mapped",[967]],[[120627,120627],"mapped",[968]],[[120628,120628],"mapped",[969]],[[120629,120629],"mapped",[8711]],[[120630,120630],"mapped",[945]],[[120631,120631],"mapped",[946]],[[120632,120632],"mapped",[947]],[[120633,120633],"mapped",[948]],[[120634,120634],"mapped",[949]],[[120635,120635],"mapped",[950]],[[120636,120636],"mapped",[951]],[[120637,120637],"mapped",[952]],[[120638,120638],"mapped",[953]],[[120639,120639],"mapped",[954]],[[120640,120640],"mapped",[955]],[[120641,120641],"mapped",[956]],[[120642,120642],"mapped",[957]],[[120643,120643],"mapped",[958]],[[120644,120644],"mapped",[959]],[[120645,120645],"mapped",[960]],[[120646,120646],"mapped",[961]],[[120647,120648],"mapped",[963]],[[120649,120649],"mapped",[964]],[[120650,120650],"mapped",[965]],[[120651,120651],"mapped",[966]],[[120652,120652],"mapped",[967]],[[120653,120653],"mapped",[968]],[[120654,120654],"mapped",[969]],[[120655,120655],"mapped",[8706]],[[120656,120656],"mapped",[949]],[[120657,120657],"mapped",[952]],[[120658,120658],"mapped",[954]],[[120659,120659],"mapped",[966]],[[120660,120660],"mapped",[961]],[[120661,120661],"mapped",[960]],[[120662,120662],"mapped",[945]],[[120663,120663],"mapped",[946]],[[120664,120664],"mapped",[947]],[[120665,120665],"mapped",[948]],[[120666,120666],"mapped",[949]],[[120667,120667],"mapped",[950]],[[120668,120668],"mapped",[951]],[[120669,120669],"mapped",[952]],[[120670,120670],"mapped",[953]],[[120671,120671],"mapped",[954]],[[120672,120672],"mapped",[955]],[[120673,120673],"mapped",[956]],[[120674,120674],"mapped",[957]],[[120675,120675],"mapped",[958]],[[120676,120676],"mapped",[959]],[[120677,120677],"mapped",[960]],[[120678,120678],"mapped",[961]],[[120679,120679],"mapped",[952]],[[120680,120680],"mapped",[963]],[[120681,120681],"mapped",[964]],[[120682,120682],"mapped",[965]],[[120683,120683],"mapped",[966]],[[120684,120684],"mapped",[967]],[[120685,120685],"mapped",[968]],[[120686,120686],"mapped",[969]],[[120687,120687],"mapped",[8711]],[[120688,120688],"mapped",[945]],[[120689,120689],"mapped",[946]],[[120690,120690],"mapped",[947]],[[120691,120691],"mapped",[948]],[[120692,120692],"mapped",[949]],[[120693,120693],"mapped",[950]],[[120694,120694],"mapped",[951]],[[120695,120695],"mapped",[952]],[[120696,120696],"mapped",[953]],[[120697,120697],"mapped",[954]],[[120698,120698],"mapped",[955]],[[120699,120699],"mapped",[956]],[[120700,120700],"mapped",[957]],[[120701,120701],"mapped",[958]],[[120702,120702],"mapped",[959]],[[120703,120703],"mapped",[960]],[[120704,120704],"mapped",[961]],[[120705,120706],"mapped",[963]],[[120707,120707],"mapped",[964]],[[120708,120708],"mapped",[965]],[[120709,120709],"mapped",[966]],[[120710,120710],"mapped",[967]],[[120711,120711],"mapped",[968]],[[120712,120712],"mapped",[969]],[[120713,120713],"mapped",[8706]],[[120714,120714],"mapped",[949]],[[120715,120715],"mapped",[952]],[[120716,120716],"mapped",[954]],[[120717,120717],"mapped",[966]],[[120718,120718],"mapped",[961]],[[120719,120719],"mapped",[960]],[[120720,120720],"mapped",[945]],[[120721,120721],"mapped",[946]],[[120722,120722],"mapped",[947]],[[120723,120723],"mapped",[948]],[[120724,120724],"mapped",[949]],[[120725,120725],"mapped",[950]],[[120726,120726],"mapped",[951]],[[120727,120727],"mapped",[952]],[[120728,120728],"mapped",[953]],[[120729,120729],"mapped",[954]],[[120730,120730],"mapped",[955]],[[120731,120731],"mapped",[956]],[[120732,120732],"mapped",[957]],[[120733,120733],"mapped",[958]],[[120734,120734],"mapped",[959]],[[120735,120735],"mapped",[960]],[[120736,120736],"mapped",[961]],[[120737,120737],"mapped",[952]],[[120738,120738],"mapped",[963]],[[120739,120739],"mapped",[964]],[[120740,120740],"mapped",[965]],[[120741,120741],"mapped",[966]],[[120742,120742],"mapped",[967]],[[120743,120743],"mapped",[968]],[[120744,120744],"mapped",[969]],[[120745,120745],"mapped",[8711]],[[120746,120746],"mapped",[945]],[[120747,120747],"mapped",[946]],[[120748,120748],"mapped",[947]],[[120749,120749],"mapped",[948]],[[120750,120750],"mapped",[949]],[[120751,120751],"mapped",[950]],[[120752,120752],"mapped",[951]],[[120753,120753],"mapped",[952]],[[120754,120754],"mapped",[953]],[[120755,120755],"mapped",[954]],[[120756,120756],"mapped",[955]],[[120757,120757],"mapped",[956]],[[120758,120758],"mapped",[957]],[[120759,120759],"mapped",[958]],[[120760,120760],"mapped",[959]],[[120761,120761],"mapped",[960]],[[120762,120762],"mapped",[961]],[[120763,120764],"mapped",[963]],[[120765,120765],"mapped",[964]],[[120766,120766],"mapped",[965]],[[120767,120767],"mapped",[966]],[[120768,120768],"mapped",[967]],[[120769,120769],"mapped",[968]],[[120770,120770],"mapped",[969]],[[120771,120771],"mapped",[8706]],[[120772,120772],"mapped",[949]],[[120773,120773],"mapped",[952]],[[120774,120774],"mapped",[954]],[[120775,120775],"mapped",[966]],[[120776,120776],"mapped",[961]],[[120777,120777],"mapped",[960]],[[120778,120779],"mapped",[989]],[[120780,120781],"disallowed"],[[120782,120782],"mapped",[48]],[[120783,120783],"mapped",[49]],[[120784,120784],"mapped",[50]],[[120785,120785],"mapped",[51]],[[120786,120786],"mapped",[52]],[[120787,120787],"mapped",[53]],[[120788,120788],"mapped",[54]],[[120789,120789],"mapped",[55]],[[120790,120790],"mapped",[56]],[[120791,120791],"mapped",[57]],[[120792,120792],"mapped",[48]],[[120793,120793],"mapped",[49]],[[120794,120794],"mapped",[50]],[[120795,120795],"mapped",[51]],[[120796,120796],"mapped",[52]],[[120797,120797],"mapped",[53]],[[120798,120798],"mapped",[54]],[[120799,120799],"mapped",[55]],[[120800,120800],"mapped",[56]],[[120801,120801],"mapped",[57]],[[120802,120802],"mapped",[48]],[[120803,120803],"mapped",[49]],[[120804,120804],"mapped",[50]],[[120805,120805],"mapped",[51]],[[120806,120806],"mapped",[52]],[[120807,120807],"mapped",[53]],[[120808,120808],"mapped",[54]],[[120809,120809],"mapped",[55]],[[120810,120810],"mapped",[56]],[[120811,120811],"mapped",[57]],[[120812,120812],"mapped",[48]],[[120813,120813],"mapped",[49]],[[120814,120814],"mapped",[50]],[[120815,120815],"mapped",[51]],[[120816,120816],"mapped",[52]],[[120817,120817],"mapped",[53]],[[120818,120818],"mapped",[54]],[[120819,120819],"mapped",[55]],[[120820,120820],"mapped",[56]],[[120821,120821],"mapped",[57]],[[120822,120822],"mapped",[48]],[[120823,120823],"mapped",[49]],[[120824,120824],"mapped",[50]],[[120825,120825],"mapped",[51]],[[120826,120826],"mapped",[52]],[[120827,120827],"mapped",[53]],[[120828,120828],"mapped",[54]],[[120829,120829],"mapped",[55]],[[120830,120830],"mapped",[56]],[[120831,120831],"mapped",[57]],[[120832,121343],"valid",[],"NV8"],[[121344,121398],"valid"],[[121399,121402],"valid",[],"NV8"],[[121403,121452],"valid"],[[121453,121460],"valid",[],"NV8"],[[121461,121461],"valid"],[[121462,121475],"valid",[],"NV8"],[[121476,121476],"valid"],[[121477,121483],"valid",[],"NV8"],[[121484,121498],"disallowed"],[[121499,121503],"valid"],[[121504,121504],"disallowed"],[[121505,121519],"valid"],[[121520,124927],"disallowed"],[[124928,125124],"valid"],[[125125,125126],"disallowed"],[[125127,125135],"valid",[],"NV8"],[[125136,125142],"valid"],[[125143,126463],"disallowed"],[[126464,126464],"mapped",[1575]],[[126465,126465],"mapped",[1576]],[[126466,126466],"mapped",[1580]],[[126467,126467],"mapped",[1583]],[[126468,126468],"disallowed"],[[126469,126469],"mapped",[1608]],[[126470,126470],"mapped",[1586]],[[126471,126471],"mapped",[1581]],[[126472,126472],"mapped",[1591]],[[126473,126473],"mapped",[1610]],[[126474,126474],"mapped",[1603]],[[126475,126475],"mapped",[1604]],[[126476,126476],"mapped",[1605]],[[126477,126477],"mapped",[1606]],[[126478,126478],"mapped",[1587]],[[126479,126479],"mapped",[1593]],[[126480,126480],"mapped",[1601]],[[126481,126481],"mapped",[1589]],[[126482,126482],"mapped",[1602]],[[126483,126483],"mapped",[1585]],[[126484,126484],"mapped",[1588]],[[126485,126485],"mapped",[1578]],[[126486,126486],"mapped",[1579]],[[126487,126487],"mapped",[1582]],[[126488,126488],"mapped",[1584]],[[126489,126489],"mapped",[1590]],[[126490,126490],"mapped",[1592]],[[126491,126491],"mapped",[1594]],[[126492,126492],"mapped",[1646]],[[126493,126493],"mapped",[1722]],[[126494,126494],"mapped",[1697]],[[126495,126495],"mapped",[1647]],[[126496,126496],"disallowed"],[[126497,126497],"mapped",[1576]],[[126498,126498],"mapped",[1580]],[[126499,126499],"disallowed"],[[126500,126500],"mapped",[1607]],[[126501,126502],"disallowed"],[[126503,126503],"mapped",[1581]],[[126504,126504],"disallowed"],[[126505,126505],"mapped",[1610]],[[126506,126506],"mapped",[1603]],[[126507,126507],"mapped",[1604]],[[126508,126508],"mapped",[1605]],[[126509,126509],"mapped",[1606]],[[126510,126510],"mapped",[1587]],[[126511,126511],"mapped",[1593]],[[126512,126512],"mapped",[1601]],[[126513,126513],"mapped",[1589]],[[126514,126514],"mapped",[1602]],[[126515,126515],"disallowed"],[[126516,126516],"mapped",[1588]],[[126517,126517],"mapped",[1578]],[[126518,126518],"mapped",[1579]],[[126519,126519],"mapped",[1582]],[[126520,126520],"disallowed"],[[126521,126521],"mapped",[1590]],[[126522,126522],"disallowed"],[[126523,126523],"mapped",[1594]],[[126524,126529],"disallowed"],[[126530,126530],"mapped",[1580]],[[126531,126534],"disallowed"],[[126535,126535],"mapped",[1581]],[[126536,126536],"disallowed"],[[126537,126537],"mapped",[1610]],[[126538,126538],"disallowed"],[[126539,126539],"mapped",[1604]],[[126540,126540],"disallowed"],[[126541,126541],"mapped",[1606]],[[126542,126542],"mapped",[1587]],[[126543,126543],"mapped",[1593]],[[126544,126544],"disallowed"],[[126545,126545],"mapped",[1589]],[[126546,126546],"mapped",[1602]],[[126547,126547],"disallowed"],[[126548,126548],"mapped",[1588]],[[126549,126550],"disallowed"],[[126551,126551],"mapped",[1582]],[[126552,126552],"disallowed"],[[126553,126553],"mapped",[1590]],[[126554,126554],"disallowed"],[[126555,126555],"mapped",[1594]],[[126556,126556],"disallowed"],[[126557,126557],"mapped",[1722]],[[126558,126558],"disallowed"],[[126559,126559],"mapped",[1647]],[[126560,126560],"disallowed"],[[126561,126561],"mapped",[1576]],[[126562,126562],"mapped",[1580]],[[126563,126563],"disallowed"],[[126564,126564],"mapped",[1607]],[[126565,126566],"disallowed"],[[126567,126567],"mapped",[1581]],[[126568,126568],"mapped",[1591]],[[126569,126569],"mapped",[1610]],[[126570,126570],"mapped",[1603]],[[126571,126571],"disallowed"],[[126572,126572],"mapped",[1605]],[[126573,126573],"mapped",[1606]],[[126574,126574],"mapped",[1587]],[[126575,126575],"mapped",[1593]],[[126576,126576],"mapped",[1601]],[[126577,126577],"mapped",[1589]],[[126578,126578],"mapped",[1602]],[[126579,126579],"disallowed"],[[126580,126580],"mapped",[1588]],[[126581,126581],"mapped",[1578]],[[126582,126582],"mapped",[1579]],[[126583,126583],"mapped",[1582]],[[126584,126584],"disallowed"],[[126585,126585],"mapped",[1590]],[[126586,126586],"mapped",[1592]],[[126587,126587],"mapped",[1594]],[[126588,126588],"mapped",[1646]],[[126589,126589],"disallowed"],[[126590,126590],"mapped",[1697]],[[126591,126591],"disallowed"],[[126592,126592],"mapped",[1575]],[[126593,126593],"mapped",[1576]],[[126594,126594],"mapped",[1580]],[[126595,126595],"mapped",[1583]],[[126596,126596],"mapped",[1607]],[[126597,126597],"mapped",[1608]],[[126598,126598],"mapped",[1586]],[[126599,126599],"mapped",[1581]],[[126600,126600],"mapped",[1591]],[[126601,126601],"mapped",[1610]],[[126602,126602],"disallowed"],[[126603,126603],"mapped",[1604]],[[126604,126604],"mapped",[1605]],[[126605,126605],"mapped",[1606]],[[126606,126606],"mapped",[1587]],[[126607,126607],"mapped",[1593]],[[126608,126608],"mapped",[1601]],[[126609,126609],"mapped",[1589]],[[126610,126610],"mapped",[1602]],[[126611,126611],"mapped",[1585]],[[126612,126612],"mapped",[1588]],[[126613,126613],"mapped",[1578]],[[126614,126614],"mapped",[1579]],[[126615,126615],"mapped",[1582]],[[126616,126616],"mapped",[1584]],[[126617,126617],"mapped",[1590]],[[126618,126618],"mapped",[1592]],[[126619,126619],"mapped",[1594]],[[126620,126624],"disallowed"],[[126625,126625],"mapped",[1576]],[[126626,126626],"mapped",[1580]],[[126627,126627],"mapped",[1583]],[[126628,126628],"disallowed"],[[126629,126629],"mapped",[1608]],[[126630,126630],"mapped",[1586]],[[126631,126631],"mapped",[1581]],[[126632,126632],"mapped",[1591]],[[126633,126633],"mapped",[1610]],[[126634,126634],"disallowed"],[[126635,126635],"mapped",[1604]],[[126636,126636],"mapped",[1605]],[[126637,126637],"mapped",[1606]],[[126638,126638],"mapped",[1587]],[[126639,126639],"mapped",[1593]],[[126640,126640],"mapped",[1601]],[[126641,126641],"mapped",[1589]],[[126642,126642],"mapped",[1602]],[[126643,126643],"mapped",[1585]],[[126644,126644],"mapped",[1588]],[[126645,126645],"mapped",[1578]],[[126646,126646],"mapped",[1579]],[[126647,126647],"mapped",[1582]],[[126648,126648],"mapped",[1584]],[[126649,126649],"mapped",[1590]],[[126650,126650],"mapped",[1592]],[[126651,126651],"mapped",[1594]],[[126652,126703],"disallowed"],[[126704,126705],"valid",[],"NV8"],[[126706,126975],"disallowed"],[[126976,127019],"valid",[],"NV8"],[[127020,127023],"disallowed"],[[127024,127123],"valid",[],"NV8"],[[127124,127135],"disallowed"],[[127136,127150],"valid",[],"NV8"],[[127151,127152],"disallowed"],[[127153,127166],"valid",[],"NV8"],[[127167,127167],"valid",[],"NV8"],[[127168,127168],"disallowed"],[[127169,127183],"valid",[],"NV8"],[[127184,127184],"disallowed"],[[127185,127199],"valid",[],"NV8"],[[127200,127221],"valid",[],"NV8"],[[127222,127231],"disallowed"],[[127232,127232],"disallowed"],[[127233,127233],"disallowed_STD3_mapped",[48,44]],[[127234,127234],"disallowed_STD3_mapped",[49,44]],[[127235,127235],"disallowed_STD3_mapped",[50,44]],[[127236,127236],"disallowed_STD3_mapped",[51,44]],[[127237,127237],"disallowed_STD3_mapped",[52,44]],[[127238,127238],"disallowed_STD3_mapped",[53,44]],[[127239,127239],"disallowed_STD3_mapped",[54,44]],[[127240,127240],"disallowed_STD3_mapped",[55,44]],[[127241,127241],"disallowed_STD3_mapped",[56,44]],[[127242,127242],"disallowed_STD3_mapped",[57,44]],[[127243,127244],"valid",[],"NV8"],[[127245,127247],"disallowed"],[[127248,127248],"disallowed_STD3_mapped",[40,97,41]],[[127249,127249],"disallowed_STD3_mapped",[40,98,41]],[[127250,127250],"disallowed_STD3_mapped",[40,99,41]],[[127251,127251],"disallowed_STD3_mapped",[40,100,41]],[[127252,127252],"disallowed_STD3_mapped",[40,101,41]],[[127253,127253],"disallowed_STD3_mapped",[40,102,41]],[[127254,127254],"disallowed_STD3_mapped",[40,103,41]],[[127255,127255],"disallowed_STD3_mapped",[40,104,41]],[[127256,127256],"disallowed_STD3_mapped",[40,105,41]],[[127257,127257],"disallowed_STD3_mapped",[40,106,41]],[[127258,127258],"disallowed_STD3_mapped",[40,107,41]],[[127259,127259],"disallowed_STD3_mapped",[40,108,41]],[[127260,127260],"disallowed_STD3_mapped",[40,109,41]],[[127261,127261],"disallowed_STD3_mapped",[40,110,41]],[[127262,127262],"disallowed_STD3_mapped",[40,111,41]],[[127263,127263],"disallowed_STD3_mapped",[40,112,41]],[[127264,127264],"disallowed_STD3_mapped",[40,113,41]],[[127265,127265],"disallowed_STD3_mapped",[40,114,41]],[[127266,127266],"disallowed_STD3_mapped",[40,115,41]],[[127267,127267],"disallowed_STD3_mapped",[40,116,41]],[[127268,127268],"disallowed_STD3_mapped",[40,117,41]],[[127269,127269],"disallowed_STD3_mapped",[40,118,41]],[[127270,127270],"disallowed_STD3_mapped",[40,119,41]],[[127271,127271],"disallowed_STD3_mapped",[40,120,41]],[[127272,127272],"disallowed_STD3_mapped",[40,121,41]],[[127273,127273],"disallowed_STD3_mapped",[40,122,41]],[[127274,127274],"mapped",[12308,115,12309]],[[127275,127275],"mapped",[99]],[[127276,127276],"mapped",[114]],[[127277,127277],"mapped",[99,100]],[[127278,127278],"mapped",[119,122]],[[127279,127279],"disallowed"],[[127280,127280],"mapped",[97]],[[127281,127281],"mapped",[98]],[[127282,127282],"mapped",[99]],[[127283,127283],"mapped",[100]],[[127284,127284],"mapped",[101]],[[127285,127285],"mapped",[102]],[[127286,127286],"mapped",[103]],[[127287,127287],"mapped",[104]],[[127288,127288],"mapped",[105]],[[127289,127289],"mapped",[106]],[[127290,127290],"mapped",[107]],[[127291,127291],"mapped",[108]],[[127292,127292],"mapped",[109]],[[127293,127293],"mapped",[110]],[[127294,127294],"mapped",[111]],[[127295,127295],"mapped",[112]],[[127296,127296],"mapped",[113]],[[127297,127297],"mapped",[114]],[[127298,127298],"mapped",[115]],[[127299,127299],"mapped",[116]],[[127300,127300],"mapped",[117]],[[127301,127301],"mapped",[118]],[[127302,127302],"mapped",[119]],[[127303,127303],"mapped",[120]],[[127304,127304],"mapped",[121]],[[127305,127305],"mapped",[122]],[[127306,127306],"mapped",[104,118]],[[127307,127307],"mapped",[109,118]],[[127308,127308],"mapped",[115,100]],[[127309,127309],"mapped",[115,115]],[[127310,127310],"mapped",[112,112,118]],[[127311,127311],"mapped",[119,99]],[[127312,127318],"valid",[],"NV8"],[[127319,127319],"valid",[],"NV8"],[[127320,127326],"valid",[],"NV8"],[[127327,127327],"valid",[],"NV8"],[[127328,127337],"valid",[],"NV8"],[[127338,127338],"mapped",[109,99]],[[127339,127339],"mapped",[109,100]],[[127340,127343],"disallowed"],[[127344,127352],"valid",[],"NV8"],[[127353,127353],"valid",[],"NV8"],[[127354,127354],"valid",[],"NV8"],[[127355,127356],"valid",[],"NV8"],[[127357,127358],"valid",[],"NV8"],[[127359,127359],"valid",[],"NV8"],[[127360,127369],"valid",[],"NV8"],[[127370,127373],"valid",[],"NV8"],[[127374,127375],"valid",[],"NV8"],[[127376,127376],"mapped",[100,106]],[[127377,127386],"valid",[],"NV8"],[[127387,127461],"disallowed"],[[127462,127487],"valid",[],"NV8"],[[127488,127488],"mapped",[12411,12363]],[[127489,127489],"mapped",[12467,12467]],[[127490,127490],"mapped",[12469]],[[127491,127503],"disallowed"],[[127504,127504],"mapped",[25163]],[[127505,127505],"mapped",[23383]],[[127506,127506],"mapped",[21452]],[[127507,127507],"mapped",[12487]],[[127508,127508],"mapped",[20108]],[[127509,127509],"mapped",[22810]],[[127510,127510],"mapped",[35299]],[[127511,127511],"mapped",[22825]],[[127512,127512],"mapped",[20132]],[[127513,127513],"mapped",[26144]],[[127514,127514],"mapped",[28961]],[[127515,127515],"mapped",[26009]],[[127516,127516],"mapped",[21069]],[[127517,127517],"mapped",[24460]],[[127518,127518],"mapped",[20877]],[[127519,127519],"mapped",[26032]],[[127520,127520],"mapped",[21021]],[[127521,127521],"mapped",[32066]],[[127522,127522],"mapped",[29983]],[[127523,127523],"mapped",[36009]],[[127524,127524],"mapped",[22768]],[[127525,127525],"mapped",[21561]],[[127526,127526],"mapped",[28436]],[[127527,127527],"mapped",[25237]],[[127528,127528],"mapped",[25429]],[[127529,127529],"mapped",[19968]],[[127530,127530],"mapped",[19977]],[[127531,127531],"mapped",[36938]],[[127532,127532],"mapped",[24038]],[[127533,127533],"mapped",[20013]],[[127534,127534],"mapped",[21491]],[[127535,127535],"mapped",[25351]],[[127536,127536],"mapped",[36208]],[[127537,127537],"mapped",[25171]],[[127538,127538],"mapped",[31105]],[[127539,127539],"mapped",[31354]],[[127540,127540],"mapped",[21512]],[[127541,127541],"mapped",[28288]],[[127542,127542],"mapped",[26377]],[[127543,127543],"mapped",[26376]],[[127544,127544],"mapped",[30003]],[[127545,127545],"mapped",[21106]],[[127546,127546],"mapped",[21942]],[[127547,127551],"disallowed"],[[127552,127552],"mapped",[12308,26412,12309]],[[127553,127553],"mapped",[12308,19977,12309]],[[127554,127554],"mapped",[12308,20108,12309]],[[127555,127555],"mapped",[12308,23433,12309]],[[127556,127556],"mapped",[12308,28857,12309]],[[127557,127557],"mapped",[12308,25171,12309]],[[127558,127558],"mapped",[12308,30423,12309]],[[127559,127559],"mapped",[12308,21213,12309]],[[127560,127560],"mapped",[12308,25943,12309]],[[127561,127567],"disallowed"],[[127568,127568],"mapped",[24471]],[[127569,127569],"mapped",[21487]],[[127570,127743],"disallowed"],[[127744,127776],"valid",[],"NV8"],[[127777,127788],"valid",[],"NV8"],[[127789,127791],"valid",[],"NV8"],[[127792,127797],"valid",[],"NV8"],[[127798,127798],"valid",[],"NV8"],[[127799,127868],"valid",[],"NV8"],[[127869,127869],"valid",[],"NV8"],[[127870,127871],"valid",[],"NV8"],[[127872,127891],"valid",[],"NV8"],[[127892,127903],"valid",[],"NV8"],[[127904,127940],"valid",[],"NV8"],[[127941,127941],"valid",[],"NV8"],[[127942,127946],"valid",[],"NV8"],[[127947,127950],"valid",[],"NV8"],[[127951,127955],"valid",[],"NV8"],[[127956,127967],"valid",[],"NV8"],[[127968,127984],"valid",[],"NV8"],[[127985,127991],"valid",[],"NV8"],[[127992,127999],"valid",[],"NV8"],[[128000,128062],"valid",[],"NV8"],[[128063,128063],"valid",[],"NV8"],[[128064,128064],"valid",[],"NV8"],[[128065,128065],"valid",[],"NV8"],[[128066,128247],"valid",[],"NV8"],[[128248,128248],"valid",[],"NV8"],[[128249,128252],"valid",[],"NV8"],[[128253,128254],"valid",[],"NV8"],[[128255,128255],"valid",[],"NV8"],[[128256,128317],"valid",[],"NV8"],[[128318,128319],"valid",[],"NV8"],[[128320,128323],"valid",[],"NV8"],[[128324,128330],"valid",[],"NV8"],[[128331,128335],"valid",[],"NV8"],[[128336,128359],"valid",[],"NV8"],[[128360,128377],"valid",[],"NV8"],[[128378,128378],"disallowed"],[[128379,128419],"valid",[],"NV8"],[[128420,128420],"disallowed"],[[128421,128506],"valid",[],"NV8"],[[128507,128511],"valid",[],"NV8"],[[128512,128512],"valid",[],"NV8"],[[128513,128528],"valid",[],"NV8"],[[128529,128529],"valid",[],"NV8"],[[128530,128532],"valid",[],"NV8"],[[128533,128533],"valid",[],"NV8"],[[128534,128534],"valid",[],"NV8"],[[128535,128535],"valid",[],"NV8"],[[128536,128536],"valid",[],"NV8"],[[128537,128537],"valid",[],"NV8"],[[128538,128538],"valid",[],"NV8"],[[128539,128539],"valid",[],"NV8"],[[128540,128542],"valid",[],"NV8"],[[128543,128543],"valid",[],"NV8"],[[128544,128549],"valid",[],"NV8"],[[128550,128551],"valid",[],"NV8"],[[128552,128555],"valid",[],"NV8"],[[128556,128556],"valid",[],"NV8"],[[128557,128557],"valid",[],"NV8"],[[128558,128559],"valid",[],"NV8"],[[128560,128563],"valid",[],"NV8"],[[128564,128564],"valid",[],"NV8"],[[128565,128576],"valid",[],"NV8"],[[128577,128578],"valid",[],"NV8"],[[128579,128580],"valid",[],"NV8"],[[128581,128591],"valid",[],"NV8"],[[128592,128639],"valid",[],"NV8"],[[128640,128709],"valid",[],"NV8"],[[128710,128719],"valid",[],"NV8"],[[128720,128720],"valid",[],"NV8"],[[128721,128735],"disallowed"],[[128736,128748],"valid",[],"NV8"],[[128749,128751],"disallowed"],[[128752,128755],"valid",[],"NV8"],[[128756,128767],"disallowed"],[[128768,128883],"valid",[],"NV8"],[[128884,128895],"disallowed"],[[128896,128980],"valid",[],"NV8"],[[128981,129023],"disallowed"],[[129024,129035],"valid",[],"NV8"],[[129036,129039],"disallowed"],[[129040,129095],"valid",[],"NV8"],[[129096,129103],"disallowed"],[[129104,129113],"valid",[],"NV8"],[[129114,129119],"disallowed"],[[129120,129159],"valid",[],"NV8"],[[129160,129167],"disallowed"],[[129168,129197],"valid",[],"NV8"],[[129198,129295],"disallowed"],[[129296,129304],"valid",[],"NV8"],[[129305,129407],"disallowed"],[[129408,129412],"valid",[],"NV8"],[[129413,129471],"disallowed"],[[129472,129472],"valid",[],"NV8"],[[129473,131069],"disallowed"],[[131070,131071],"disallowed"],[[131072,173782],"valid"],[[173783,173823],"disallowed"],[[173824,177972],"valid"],[[177973,177983],"disallowed"],[[177984,178205],"valid"],[[178206,178207],"disallowed"],[[178208,183969],"valid"],[[183970,194559],"disallowed"],[[194560,194560],"mapped",[20029]],[[194561,194561],"mapped",[20024]],[[194562,194562],"mapped",[20033]],[[194563,194563],"mapped",[131362]],[[194564,194564],"mapped",[20320]],[[194565,194565],"mapped",[20398]],[[194566,194566],"mapped",[20411]],[[194567,194567],"mapped",[20482]],[[194568,194568],"mapped",[20602]],[[194569,194569],"mapped",[20633]],[[194570,194570],"mapped",[20711]],[[194571,194571],"mapped",[20687]],[[194572,194572],"mapped",[13470]],[[194573,194573],"mapped",[132666]],[[194574,194574],"mapped",[20813]],[[194575,194575],"mapped",[20820]],[[194576,194576],"mapped",[20836]],[[194577,194577],"mapped",[20855]],[[194578,194578],"mapped",[132380]],[[194579,194579],"mapped",[13497]],[[194580,194580],"mapped",[20839]],[[194581,194581],"mapped",[20877]],[[194582,194582],"mapped",[132427]],[[194583,194583],"mapped",[20887]],[[194584,194584],"mapped",[20900]],[[194585,194585],"mapped",[20172]],[[194586,194586],"mapped",[20908]],[[194587,194587],"mapped",[20917]],[[194588,194588],"mapped",[168415]],[[194589,194589],"mapped",[20981]],[[194590,194590],"mapped",[20995]],[[194591,194591],"mapped",[13535]],[[194592,194592],"mapped",[21051]],[[194593,194593],"mapped",[21062]],[[194594,194594],"mapped",[21106]],[[194595,194595],"mapped",[21111]],[[194596,194596],"mapped",[13589]],[[194597,194597],"mapped",[21191]],[[194598,194598],"mapped",[21193]],[[194599,194599],"mapped",[21220]],[[194600,194600],"mapped",[21242]],[[194601,194601],"mapped",[21253]],[[194602,194602],"mapped",[21254]],[[194603,194603],"mapped",[21271]],[[194604,194604],"mapped",[21321]],[[194605,194605],"mapped",[21329]],[[194606,194606],"mapped",[21338]],[[194607,194607],"mapped",[21363]],[[194608,194608],"mapped",[21373]],[[194609,194611],"mapped",[21375]],[[194612,194612],"mapped",[133676]],[[194613,194613],"mapped",[28784]],[[194614,194614],"mapped",[21450]],[[194615,194615],"mapped",[21471]],[[194616,194616],"mapped",[133987]],[[194617,194617],"mapped",[21483]],[[194618,194618],"mapped",[21489]],[[194619,194619],"mapped",[21510]],[[194620,194620],"mapped",[21662]],[[194621,194621],"mapped",[21560]],[[194622,194622],"mapped",[21576]],[[194623,194623],"mapped",[21608]],[[194624,194624],"mapped",[21666]],[[194625,194625],"mapped",[21750]],[[194626,194626],"mapped",[21776]],[[194627,194627],"mapped",[21843]],[[194628,194628],"mapped",[21859]],[[194629,194630],"mapped",[21892]],[[194631,194631],"mapped",[21913]],[[194632,194632],"mapped",[21931]],[[194633,194633],"mapped",[21939]],[[194634,194634],"mapped",[21954]],[[194635,194635],"mapped",[22294]],[[194636,194636],"mapped",[22022]],[[194637,194637],"mapped",[22295]],[[194638,194638],"mapped",[22097]],[[194639,194639],"mapped",[22132]],[[194640,194640],"mapped",[20999]],[[194641,194641],"mapped",[22766]],[[194642,194642],"mapped",[22478]],[[194643,194643],"mapped",[22516]],[[194644,194644],"mapped",[22541]],[[194645,194645],"mapped",[22411]],[[194646,194646],"mapped",[22578]],[[194647,194647],"mapped",[22577]],[[194648,194648],"mapped",[22700]],[[194649,194649],"mapped",[136420]],[[194650,194650],"mapped",[22770]],[[194651,194651],"mapped",[22775]],[[194652,194652],"mapped",[22790]],[[194653,194653],"mapped",[22810]],[[194654,194654],"mapped",[22818]],[[194655,194655],"mapped",[22882]],[[194656,194656],"mapped",[136872]],[[194657,194657],"mapped",[136938]],[[194658,194658],"mapped",[23020]],[[194659,194659],"mapped",[23067]],[[194660,194660],"mapped",[23079]],[[194661,194661],"mapped",[23000]],[[194662,194662],"mapped",[23142]],[[194663,194663],"mapped",[14062]],[[194664,194664],"disallowed"],[[194665,194665],"mapped",[23304]],[[194666,194667],"mapped",[23358]],[[194668,194668],"mapped",[137672]],[[194669,194669],"mapped",[23491]],[[194670,194670],"mapped",[23512]],[[194671,194671],"mapped",[23527]],[[194672,194672],"mapped",[23539]],[[194673,194673],"mapped",[138008]],[[194674,194674],"mapped",[23551]],[[194675,194675],"mapped",[23558]],[[194676,194676],"disallowed"],[[194677,194677],"mapped",[23586]],[[194678,194678],"mapped",[14209]],[[194679,194679],"mapped",[23648]],[[194680,194680],"mapped",[23662]],[[194681,194681],"mapped",[23744]],[[194682,194682],"mapped",[23693]],[[194683,194683],"mapped",[138724]],[[194684,194684],"mapped",[23875]],[[194685,194685],"mapped",[138726]],[[194686,194686],"mapped",[23918]],[[194687,194687],"mapped",[23915]],[[194688,194688],"mapped",[23932]],[[194689,194689],"mapped",[24033]],[[194690,194690],"mapped",[24034]],[[194691,194691],"mapped",[14383]],[[194692,194692],"mapped",[24061]],[[194693,194693],"mapped",[24104]],[[194694,194694],"mapped",[24125]],[[194695,194695],"mapped",[24169]],[[194696,194696],"mapped",[14434]],[[194697,194697],"mapped",[139651]],[[194698,194698],"mapped",[14460]],[[194699,194699],"mapped",[24240]],[[194700,194700],"mapped",[24243]],[[194701,194701],"mapped",[24246]],[[194702,194702],"mapped",[24266]],[[194703,194703],"mapped",[172946]],[[194704,194704],"mapped",[24318]],[[194705,194706],"mapped",[140081]],[[194707,194707],"mapped",[33281]],[[194708,194709],"mapped",[24354]],[[194710,194710],"mapped",[14535]],[[194711,194711],"mapped",[144056]],[[194712,194712],"mapped",[156122]],[[194713,194713],"mapped",[24418]],[[194714,194714],"mapped",[24427]],[[194715,194715],"mapped",[14563]],[[194716,194716],"mapped",[24474]],[[194717,194717],"mapped",[24525]],[[194718,194718],"mapped",[24535]],[[194719,194719],"mapped",[24569]],[[194720,194720],"mapped",[24705]],[[194721,194721],"mapped",[14650]],[[194722,194722],"mapped",[14620]],[[194723,194723],"mapped",[24724]],[[194724,194724],"mapped",[141012]],[[194725,194725],"mapped",[24775]],[[194726,194726],"mapped",[24904]],[[194727,194727],"mapped",[24908]],[[194728,194728],"mapped",[24910]],[[194729,194729],"mapped",[24908]],[[194730,194730],"mapped",[24954]],[[194731,194731],"mapped",[24974]],[[194732,194732],"mapped",[25010]],[[194733,194733],"mapped",[24996]],[[194734,194734],"mapped",[25007]],[[194735,194735],"mapped",[25054]],[[194736,194736],"mapped",[25074]],[[194737,194737],"mapped",[25078]],[[194738,194738],"mapped",[25104]],[[194739,194739],"mapped",[25115]],[[194740,194740],"mapped",[25181]],[[194741,194741],"mapped",[25265]],[[194742,194742],"mapped",[25300]],[[194743,194743],"mapped",[25424]],[[194744,194744],"mapped",[142092]],[[194745,194745],"mapped",[25405]],[[194746,194746],"mapped",[25340]],[[194747,194747],"mapped",[25448]],[[194748,194748],"mapped",[25475]],[[194749,194749],"mapped",[25572]],[[194750,194750],"mapped",[142321]],[[194751,194751],"mapped",[25634]],[[194752,194752],"mapped",[25541]],[[194753,194753],"mapped",[25513]],[[194754,194754],"mapped",[14894]],[[194755,194755],"mapped",[25705]],[[194756,194756],"mapped",[25726]],[[194757,194757],"mapped",[25757]],[[194758,194758],"mapped",[25719]],[[194759,194759],"mapped",[14956]],[[194760,194760],"mapped",[25935]],[[194761,194761],"mapped",[25964]],[[194762,194762],"mapped",[143370]],[[194763,194763],"mapped",[26083]],[[194764,194764],"mapped",[26360]],[[194765,194765],"mapped",[26185]],[[194766,194766],"mapped",[15129]],[[194767,194767],"mapped",[26257]],[[194768,194768],"mapped",[15112]],[[194769,194769],"mapped",[15076]],[[194770,194770],"mapped",[20882]],[[194771,194771],"mapped",[20885]],[[194772,194772],"mapped",[26368]],[[194773,194773],"mapped",[26268]],[[194774,194774],"mapped",[32941]],[[194775,194775],"mapped",[17369]],[[194776,194776],"mapped",[26391]],[[194777,194777],"mapped",[26395]],[[194778,194778],"mapped",[26401]],[[194779,194779],"mapped",[26462]],[[194780,194780],"mapped",[26451]],[[194781,194781],"mapped",[144323]],[[194782,194782],"mapped",[15177]],[[194783,194783],"mapped",[26618]],[[194784,194784],"mapped",[26501]],[[194785,194785],"mapped",[26706]],[[194786,194786],"mapped",[26757]],[[194787,194787],"mapped",[144493]],[[194788,194788],"mapped",[26766]],[[194789,194789],"mapped",[26655]],[[194790,194790],"mapped",[26900]],[[194791,194791],"mapped",[15261]],[[194792,194792],"mapped",[26946]],[[194793,194793],"mapped",[27043]],[[194794,194794],"mapped",[27114]],[[194795,194795],"mapped",[27304]],[[194796,194796],"mapped",[145059]],[[194797,194797],"mapped",[27355]],[[194798,194798],"mapped",[15384]],[[194799,194799],"mapped",[27425]],[[194800,194800],"mapped",[145575]],[[194801,194801],"mapped",[27476]],[[194802,194802],"mapped",[15438]],[[194803,194803],"mapped",[27506]],[[194804,194804],"mapped",[27551]],[[194805,194805],"mapped",[27578]],[[194806,194806],"mapped",[27579]],[[194807,194807],"mapped",[146061]],[[194808,194808],"mapped",[138507]],[[194809,194809],"mapped",[146170]],[[194810,194810],"mapped",[27726]],[[194811,194811],"mapped",[146620]],[[194812,194812],"mapped",[27839]],[[194813,194813],"mapped",[27853]],[[194814,194814],"mapped",[27751]],[[194815,194815],"mapped",[27926]],[[194816,194816],"mapped",[27966]],[[194817,194817],"mapped",[28023]],[[194818,194818],"mapped",[27969]],[[194819,194819],"mapped",[28009]],[[194820,194820],"mapped",[28024]],[[194821,194821],"mapped",[28037]],[[194822,194822],"mapped",[146718]],[[194823,194823],"mapped",[27956]],[[194824,194824],"mapped",[28207]],[[194825,194825],"mapped",[28270]],[[194826,194826],"mapped",[15667]],[[194827,194827],"mapped",[28363]],[[194828,194828],"mapped",[28359]],[[194829,194829],"mapped",[147153]],[[194830,194830],"mapped",[28153]],[[194831,194831],"mapped",[28526]],[[194832,194832],"mapped",[147294]],[[194833,194833],"mapped",[147342]],[[194834,194834],"mapped",[28614]],[[194835,194835],"mapped",[28729]],[[194836,194836],"mapped",[28702]],[[194837,194837],"mapped",[28699]],[[194838,194838],"mapped",[15766]],[[194839,194839],"mapped",[28746]],[[194840,194840],"mapped",[28797]],[[194841,194841],"mapped",[28791]],[[194842,194842],"mapped",[28845]],[[194843,194843],"mapped",[132389]],[[194844,194844],"mapped",[28997]],[[194845,194845],"mapped",[148067]],[[194846,194846],"mapped",[29084]],[[194847,194847],"disallowed"],[[194848,194848],"mapped",[29224]],[[194849,194849],"mapped",[29237]],[[194850,194850],"mapped",[29264]],[[194851,194851],"mapped",[149000]],[[194852,194852],"mapped",[29312]],[[194853,194853],"mapped",[29333]],[[194854,194854],"mapped",[149301]],[[194855,194855],"mapped",[149524]],[[194856,194856],"mapped",[29562]],[[194857,194857],"mapped",[29579]],[[194858,194858],"mapped",[16044]],[[194859,194859],"mapped",[29605]],[[194860,194861],"mapped",[16056]],[[194862,194862],"mapped",[29767]],[[194863,194863],"mapped",[29788]],[[194864,194864],"mapped",[29809]],[[194865,194865],"mapped",[29829]],[[194866,194866],"mapped",[29898]],[[194867,194867],"mapped",[16155]],[[194868,194868],"mapped",[29988]],[[194869,194869],"mapped",[150582]],[[194870,194870],"mapped",[30014]],[[194871,194871],"mapped",[150674]],[[194872,194872],"mapped",[30064]],[[194873,194873],"mapped",[139679]],[[194874,194874],"mapped",[30224]],[[194875,194875],"mapped",[151457]],[[194876,194876],"mapped",[151480]],[[194877,194877],"mapped",[151620]],[[194878,194878],"mapped",[16380]],[[194879,194879],"mapped",[16392]],[[194880,194880],"mapped",[30452]],[[194881,194881],"mapped",[151795]],[[194882,194882],"mapped",[151794]],[[194883,194883],"mapped",[151833]],[[194884,194884],"mapped",[151859]],[[194885,194885],"mapped",[30494]],[[194886,194887],"mapped",[30495]],[[194888,194888],"mapped",[30538]],[[194889,194889],"mapped",[16441]],[[194890,194890],"mapped",[30603]],[[194891,194891],"mapped",[16454]],[[194892,194892],"mapped",[16534]],[[194893,194893],"mapped",[152605]],[[194894,194894],"mapped",[30798]],[[194895,194895],"mapped",[30860]],[[194896,194896],"mapped",[30924]],[[194897,194897],"mapped",[16611]],[[194898,194898],"mapped",[153126]],[[194899,194899],"mapped",[31062]],[[194900,194900],"mapped",[153242]],[[194901,194901],"mapped",[153285]],[[194902,194902],"mapped",[31119]],[[194903,194903],"mapped",[31211]],[[194904,194904],"mapped",[16687]],[[194905,194905],"mapped",[31296]],[[194906,194906],"mapped",[31306]],[[194907,194907],"mapped",[31311]],[[194908,194908],"mapped",[153980]],[[194909,194910],"mapped",[154279]],[[194911,194911],"disallowed"],[[194912,194912],"mapped",[16898]],[[194913,194913],"mapped",[154539]],[[194914,194914],"mapped",[31686]],[[194915,194915],"mapped",[31689]],[[194916,194916],"mapped",[16935]],[[194917,194917],"mapped",[154752]],[[194918,194918],"mapped",[31954]],[[194919,194919],"mapped",[17056]],[[194920,194920],"mapped",[31976]],[[194921,194921],"mapped",[31971]],[[194922,194922],"mapped",[32000]],[[194923,194923],"mapped",[155526]],[[194924,194924],"mapped",[32099]],[[194925,194925],"mapped",[17153]],[[194926,194926],"mapped",[32199]],[[194927,194927],"mapped",[32258]],[[194928,194928],"mapped",[32325]],[[194929,194929],"mapped",[17204]],[[194930,194930],"mapped",[156200]],[[194931,194931],"mapped",[156231]],[[194932,194932],"mapped",[17241]],[[194933,194933],"mapped",[156377]],[[194934,194934],"mapped",[32634]],[[194935,194935],"mapped",[156478]],[[194936,194936],"mapped",[32661]],[[194937,194937],"mapped",[32762]],[[194938,194938],"mapped",[32773]],[[194939,194939],"mapped",[156890]],[[194940,194940],"mapped",[156963]],[[194941,194941],"mapped",[32864]],[[194942,194942],"mapped",[157096]],[[194943,194943],"mapped",[32880]],[[194944,194944],"mapped",[144223]],[[194945,194945],"mapped",[17365]],[[194946,194946],"mapped",[32946]],[[194947,194947],"mapped",[33027]],[[194948,194948],"mapped",[17419]],[[194949,194949],"mapped",[33086]],[[194950,194950],"mapped",[23221]],[[194951,194951],"mapped",[157607]],[[194952,194952],"mapped",[157621]],[[194953,194953],"mapped",[144275]],[[194954,194954],"mapped",[144284]],[[194955,194955],"mapped",[33281]],[[194956,194956],"mapped",[33284]],[[194957,194957],"mapped",[36766]],[[194958,194958],"mapped",[17515]],[[194959,194959],"mapped",[33425]],[[194960,194960],"mapped",[33419]],[[194961,194961],"mapped",[33437]],[[194962,194962],"mapped",[21171]],[[194963,194963],"mapped",[33457]],[[194964,194964],"mapped",[33459]],[[194965,194965],"mapped",[33469]],[[194966,194966],"mapped",[33510]],[[194967,194967],"mapped",[158524]],[[194968,194968],"mapped",[33509]],[[194969,194969],"mapped",[33565]],[[194970,194970],"mapped",[33635]],[[194971,194971],"mapped",[33709]],[[194972,194972],"mapped",[33571]],[[194973,194973],"mapped",[33725]],[[194974,194974],"mapped",[33767]],[[194975,194975],"mapped",[33879]],[[194976,194976],"mapped",[33619]],[[194977,194977],"mapped",[33738]],[[194978,194978],"mapped",[33740]],[[194979,194979],"mapped",[33756]],[[194980,194980],"mapped",[158774]],[[194981,194981],"mapped",[159083]],[[194982,194982],"mapped",[158933]],[[194983,194983],"mapped",[17707]],[[194984,194984],"mapped",[34033]],[[194985,194985],"mapped",[34035]],[[194986,194986],"mapped",[34070]],[[194987,194987],"mapped",[160714]],[[194988,194988],"mapped",[34148]],[[194989,194989],"mapped",[159532]],[[194990,194990],"mapped",[17757]],[[194991,194991],"mapped",[17761]],[[194992,194992],"mapped",[159665]],[[194993,194993],"mapped",[159954]],[[194994,194994],"mapped",[17771]],[[194995,194995],"mapped",[34384]],[[194996,194996],"mapped",[34396]],[[194997,194997],"mapped",[34407]],[[194998,194998],"mapped",[34409]],[[194999,194999],"mapped",[34473]],[[195000,195000],"mapped",[34440]],[[195001,195001],"mapped",[34574]],[[195002,195002],"mapped",[34530]],[[195003,195003],"mapped",[34681]],[[195004,195004],"mapped",[34600]],[[195005,195005],"mapped",[34667]],[[195006,195006],"mapped",[34694]],[[195007,195007],"disallowed"],[[195008,195008],"mapped",[34785]],[[195009,195009],"mapped",[34817]],[[195010,195010],"mapped",[17913]],[[195011,195011],"mapped",[34912]],[[195012,195012],"mapped",[34915]],[[195013,195013],"mapped",[161383]],[[195014,195014],"mapped",[35031]],[[195015,195015],"mapped",[35038]],[[195016,195016],"mapped",[17973]],[[195017,195017],"mapped",[35066]],[[195018,195018],"mapped",[13499]],[[195019,195019],"mapped",[161966]],[[195020,195020],"mapped",[162150]],[[195021,195021],"mapped",[18110]],[[195022,195022],"mapped",[18119]],[[195023,195023],"mapped",[35488]],[[195024,195024],"mapped",[35565]],[[195025,195025],"mapped",[35722]],[[195026,195026],"mapped",[35925]],[[195027,195027],"mapped",[162984]],[[195028,195028],"mapped",[36011]],[[195029,195029],"mapped",[36033]],[[195030,195030],"mapped",[36123]],[[195031,195031],"mapped",[36215]],[[195032,195032],"mapped",[163631]],[[195033,195033],"mapped",[133124]],[[195034,195034],"mapped",[36299]],[[195035,195035],"mapped",[36284]],[[195036,195036],"mapped",[36336]],[[195037,195037],"mapped",[133342]],[[195038,195038],"mapped",[36564]],[[195039,195039],"mapped",[36664]],[[195040,195040],"mapped",[165330]],[[195041,195041],"mapped",[165357]],[[195042,195042],"mapped",[37012]],[[195043,195043],"mapped",[37105]],[[195044,195044],"mapped",[37137]],[[195045,195045],"mapped",[165678]],[[195046,195046],"mapped",[37147]],[[195047,195047],"mapped",[37432]],[[195048,195048],"mapped",[37591]],[[195049,195049],"mapped",[37592]],[[195050,195050],"mapped",[37500]],[[195051,195051],"mapped",[37881]],[[195052,195052],"mapped",[37909]],[[195053,195053],"mapped",[166906]],[[195054,195054],"mapped",[38283]],[[195055,195055],"mapped",[18837]],[[195056,195056],"mapped",[38327]],[[195057,195057],"mapped",[167287]],[[195058,195058],"mapped",[18918]],[[195059,195059],"mapped",[38595]],[[195060,195060],"mapped",[23986]],[[195061,195061],"mapped",[38691]],[[195062,195062],"mapped",[168261]],[[195063,195063],"mapped",[168474]],[[195064,195064],"mapped",[19054]],[[195065,195065],"mapped",[19062]],[[195066,195066],"mapped",[38880]],[[195067,195067],"mapped",[168970]],[[195068,195068],"mapped",[19122]],[[195069,195069],"mapped",[169110]],[[195070,195071],"mapped",[38923]],[[195072,195072],"mapped",[38953]],[[195073,195073],"mapped",[169398]],[[195074,195074],"mapped",[39138]],[[195075,195075],"mapped",[19251]],[[195076,195076],"mapped",[39209]],[[195077,195077],"mapped",[39335]],[[195078,195078],"mapped",[39362]],[[195079,195079],"mapped",[39422]],[[195080,195080],"mapped",[19406]],[[195081,195081],"mapped",[170800]],[[195082,195082],"mapped",[39698]],[[195083,195083],"mapped",[40000]],[[195084,195084],"mapped",[40189]],[[195085,195085],"mapped",[19662]],[[195086,195086],"mapped",[19693]],[[195087,195087],"mapped",[40295]],[[195088,195088],"mapped",[172238]],[[195089,195089],"mapped",[19704]],[[195090,195090],"mapped",[172293]],[[195091,195091],"mapped",[172558]],[[195092,195092],"mapped",[172689]],[[195093,195093],"mapped",[40635]],[[195094,195094],"mapped",[19798]],[[195095,195095],"mapped",[40697]],[[195096,195096],"mapped",[40702]],[[195097,195097],"mapped",[40709]],[[195098,195098],"mapped",[40719]],[[195099,195099],"mapped",[40726]],[[195100,195100],"mapped",[40763]],[[195101,195101],"mapped",[173568]],[[195102,196605],"disallowed"],[[196606,196607],"disallowed"],[[196608,262141],"disallowed"],[[262142,262143],"disallowed"],[[262144,327677],"disallowed"],[[327678,327679],"disallowed"],[[327680,393213],"disallowed"],[[393214,393215],"disallowed"],[[393216,458749],"disallowed"],[[458750,458751],"disallowed"],[[458752,524285],"disallowed"],[[524286,524287],"disallowed"],[[524288,589821],"disallowed"],[[589822,589823],"disallowed"],[[589824,655357],"disallowed"],[[655358,655359],"disallowed"],[[655360,720893],"disallowed"],[[720894,720895],"disallowed"],[[720896,786429],"disallowed"],[[786430,786431],"disallowed"],[[786432,851965],"disallowed"],[[851966,851967],"disallowed"],[[851968,917501],"disallowed"],[[917502,917503],"disallowed"],[[917504,917504],"disallowed"],[[917505,917505],"disallowed"],[[917506,917535],"disallowed"],[[917536,917631],"disallowed"],[[917632,917759],"disallowed"],[[917760,917999],"ignored"],[[918000,983037],"disallowed"],[[983038,983039],"disallowed"],[[983040,1048573],"disallowed"],[[1048574,1048575],"disallowed"],[[1048576,1114109],"disallowed"],[[1114110,1114111],"disallowed"]]')}};var __webpack_module_cache__={};function __nccwpck_require__(e){var p=__webpack_module_cache__[e];if(p!==undefined){return p.exports}var a=__webpack_module_cache__[e]={exports:{}};var t=true;try{__webpack_modules__[e].call(a.exports,a,a.exports,__nccwpck_require__);t=false}finally{if(t)delete __webpack_module_cache__[e]}return a.exports}if(typeof __nccwpck_require__!=="undefined")__nccwpck_require__.ab=new URL(".",import.meta.url).pathname.slice(import.meta.url.match(/^file:\/\/\/\w:/)?1:0,-1)+"/";var __webpack_exports__={};(()=>{var e=__nccwpck_require__(5438);var p=__nccwpck_require__(2186);var a=__nccwpck_require__(1383);const t="please verify canary";const d="template: bug";const r="please add a complete reproduction";const s=!!process.env.DEBUG;const json=e=>JSON.stringify(e,null,2);async function run(){try{const{payload:a,repo:i}=e.context;const{issue:o,pull_request:n}=a;if(n||!o?.body)return;const l=a.label;const{body:m,number:u}=o;const c=o.labels;if(s){p.info(`Validating issue ${u}:\n Labels:\n New: ${json(l)}\n All: ${json(c)}\n Body: ${m}`)}const h=l.name===d;const v=c.some((e=>e.name===t));if(!h&&!v){return p.info("Issue is ignored, because it is not a bug report or is not manually labeled")}if(!process.env.GITHUB_TOKEN){throw new Error("GITHUB_TOKEN is not set")}const g=e.getOctokit(process.env.GITHUB_TOKEN).rest;function notifyOnIssue(e,a){const t={...i,issue_number:u};if(s){p.info("Skipping comment/label because we are in DEBUG mode");p.info(json({label:e,comment:a}));return}return Promise.all([g.issues.addLabels({...t,labels:[e]}),g.issues.createComment({...t,body:a})])}const w=m.includes("- [X] I verified that the issue exists in Next.js canary release");if(!w||v){await notifyOnIssue(t,"Please verify your issue reproduces with `next@canary`. The canary version of Next.js ships daily and includes all features and fixes that have not been released to the stable version yet. Think of canary as a public beta. Some issues may already be fixed in the canary version, so please verify that your issue reproduces by running `npm install next@canary`. If the issue does not reproduce with the canary version, then it has already been fixed and this issue can be closed.");return p.info(`Commented on issue, because it was ${v?"manually labeled":"not verified against canary"}`)}const E=m.match(/### Link to reproduction\n\n(?.*)\n/)?.groups?.url.trim();if(!E||!(await fetch(E)).ok){await notifyOnIssue(r,"The link to the reproduction appears to be incorrect/unreachable. Please add a link to the reproduction of the issue. This is a required field. If your project is private, you can invite @balazsorban44 to the repository so the Next.js team can investigate further.");return p.info(`Commented on issue, because the reproduction url (${E}) was not reachable`)}const T=["Operating System:","Binaries:","Relevant packages:"].every((e=>m.includes(e)));if(!T){return p.info("Could not detect `next info` output, skipping as version detection might be unreliable")}const _=m.match(/Relevant packages:\n next: (?\d+\.\d+\.\d+)/)?.groups?.version;p.info(`Reported Next.js version: ${_}`);if(!_)return;const b=await getLastVersion();p.info(`Last Next.js version, based on npm releases: ${b}`);if(!b.includes("canary")||_===b)return;await notifyOnIssue(t,`The reported Next.js version did not match the latest \`next@canary\` version (${b}). The canary version of Next.js ships daily and includes all features and fixes that have not been released to the stable version yet. Think of canary as a public beta. Some issues may already be fixed in the canary version, so please verify that your issue reproduces by running \`npm install next@canary\`. If the issue does not reproduce with the canary version, then it has already been fixed and this issue can be closed.`);return p.info(`Commented on issue, because it was not verified against canary`)}catch(e){p.setFailed(e.message)}}run();async function getLastVersion(){try{const e=await fetch("https://registry.npmjs.org/next",{headers:{accept:"application/vnd.npm.install-v1+json; q=1.0, application/json; q=0.8, */*"}});const p=(await(e.body?.getReader().read()))?.value;const t=(new TextDecoder).decode(p?.slice(0,100));const d=/"latest":"(?.*)","canary":"(?.*)","/;const{latest:r,canary:s}=t.match(d)?.groups??{};return(0,a.gte)(r,s)?s:r}catch(e){p.error(e);return""}}})(); \ No newline at end of file diff --git a/.github/actions/issue-validator/licenses.txt b/.github/actions/issue-validator/licenses.txt new file mode 100644 index 0000000000000..6e91d57f4162e --- /dev/null +++ b/.github/actions/issue-validator/licenses.txt @@ -0,0 +1,703 @@ +@actions/core +MIT +The MIT License (MIT) + +Copyright 2019 GitHub + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +@actions/github +MIT +The MIT License (MIT) + +Copyright 2019 GitHub + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +@actions/http-client +MIT +Actions Http Client for Node.js + +Copyright (c) GitHub, Inc. + +All rights reserved. + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT +LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +@octokit/auth-token +MIT +The MIT License + +Copyright (c) 2019 Octokit contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +@octokit/core +MIT +The MIT License + +Copyright (c) 2019 Octokit contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +@octokit/endpoint +MIT +The MIT License + +Copyright (c) 2018 Octokit contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +@octokit/graphql +MIT +The MIT License + +Copyright (c) 2018 Octokit contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +@octokit/plugin-paginate-rest +MIT +MIT License Copyright (c) 2019 Octokit contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +@octokit/plugin-rest-endpoint-methods +MIT +MIT License Copyright (c) 2019 Octokit contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +@octokit/request +MIT +The MIT License + +Copyright (c) 2018 Octokit contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +@octokit/request-error +MIT +The MIT License + +Copyright (c) 2019 Octokit contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +@vercel/ncc +MIT +Copyright 2018 ZEIT, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +before-after-hook +Apache-2.0 + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2018 Gregor Martynus and other contributors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +deprecation +ISC +The ISC License + +Copyright (c) Gregor Martynus and contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + +is-plain-object +MIT +The MIT License (MIT) + +Copyright (c) 2014-2017, Jon Schlinkert. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +lru-cache +ISC +The ISC License + +Copyright (c) Isaac Z. Schlueter and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + +nextjs-project +The MIT License (MIT) + +Copyright (c) 2022 Vercel, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +node-fetch +MIT +The MIT License (MIT) + +Copyright (c) 2016 David Frank + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + + +once +ISC +The ISC License + +Copyright (c) Isaac Z. Schlueter and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + +semver +ISC +The ISC License + +Copyright (c) Isaac Z. Schlueter and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + +tr46 +MIT + +tunnel +MIT +The MIT License (MIT) + +Copyright (c) 2012 Koichi Kobayashi + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +universal-user-agent +ISC +# [ISC License](https://spdx.org/licenses/ISC) + +Copyright (c) 2018, Gregor Martynus (https://github.com/gr2m) + +Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + +webidl-conversions +BSD-2-Clause +# The BSD 2-Clause License + +Copyright (c) 2014, Domenic Denicola +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +whatwg-url +MIT +The MIT License (MIT) + +Copyright (c) 2015–2016 Sebastian Mayr + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +wrappy +ISC +The ISC License + +Copyright (c) Isaac Z. Schlueter and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + +yallist +ISC +The ISC License + +Copyright (c) Isaac Z. Schlueter and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/.github/actions/issue-validator/package-lock.json b/.github/actions/issue-validator/package-lock.json new file mode 100644 index 0000000000000..e5f2333a30b9a --- /dev/null +++ b/.github/actions/issue-validator/package-lock.json @@ -0,0 +1,495 @@ +{ + "name": "issue-validator", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "dependencies": { + "@actions/core": "1.9.0", + "@actions/github": "5.0.3", + "semver": "7.3.7" + }, + "devDependencies": { + "@types/semver": "7.3.10", + "@vercel/ncc": "0.34.0" + } + }, + "node_modules/@actions/core": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.9.0.tgz", + "integrity": "sha512-5pbM693Ih59ZdUhgk+fts+bUWTnIdHV3kwOSr+QIoFHMLg7Gzhwm0cifDY/AG68ekEJAkHnQVpcy4f6GjmzBCA==", + "dependencies": { + "@actions/http-client": "^2.0.1" + } + }, + "node_modules/@actions/github": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@actions/github/-/github-5.0.3.tgz", + "integrity": "sha512-myjA/pdLQfhUGLtRZC/J4L1RXOG4o6aYdiEq+zr5wVVKljzbFld+xv10k1FX6IkIJtNxbAq44BdwSNpQ015P0A==", + "dependencies": { + "@actions/http-client": "^2.0.1", + "@octokit/core": "^3.6.0", + "@octokit/plugin-paginate-rest": "^2.17.0", + "@octokit/plugin-rest-endpoint-methods": "^5.13.0" + } + }, + "node_modules/@actions/http-client": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.0.1.tgz", + "integrity": "sha512-PIXiMVtz6VvyaRsGY268qvj57hXQEpsYogYOu2nrQhlf+XCGmZstmuZBbAybUl1nQGnvS1k1eEsQ69ZoD7xlSw==", + "dependencies": { + "tunnel": "^0.0.6" + } + }, + "node_modules/@octokit/auth-token": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz", + "integrity": "sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g==", + "dependencies": { + "@octokit/types": "^6.0.3" + } + }, + "node_modules/@octokit/core": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.6.0.tgz", + "integrity": "sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q==", + "dependencies": { + "@octokit/auth-token": "^2.4.4", + "@octokit/graphql": "^4.5.8", + "@octokit/request": "^5.6.3", + "@octokit/request-error": "^2.0.5", + "@octokit/types": "^6.0.3", + "before-after-hook": "^2.2.0", + "universal-user-agent": "^6.0.0" + } + }, + "node_modules/@octokit/endpoint": { + "version": "6.0.12", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz", + "integrity": "sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA==", + "dependencies": { + "@octokit/types": "^6.0.3", + "is-plain-object": "^5.0.0", + "universal-user-agent": "^6.0.0" + } + }, + "node_modules/@octokit/graphql": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz", + "integrity": "sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg==", + "dependencies": { + "@octokit/request": "^5.6.0", + "@octokit/types": "^6.0.3", + "universal-user-agent": "^6.0.0" + } + }, + "node_modules/@octokit/openapi-types": { + "version": "12.10.1", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.10.1.tgz", + "integrity": "sha512-P+SukKanjFY0ZhsK6wSVnQmxTP2eVPPE8OPSNuxaMYtgVzwJZgfGdwlYjf4RlRU4vLEw4ts2fsE2icG4nZ5ddQ==" + }, + "node_modules/@octokit/plugin-paginate-rest": { + "version": "2.21.3", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.21.3.tgz", + "integrity": "sha512-aCZTEf0y2h3OLbrgKkrfFdjRL6eSOo8komneVQJnYecAxIej7Bafor2xhuDJOIFau4pk0i/P28/XgtbyPF0ZHw==", + "dependencies": { + "@octokit/types": "^6.40.0" + }, + "peerDependencies": { + "@octokit/core": ">=2" + } + }, + "node_modules/@octokit/plugin-rest-endpoint-methods": { + "version": "5.16.2", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.16.2.tgz", + "integrity": "sha512-8QFz29Fg5jDuTPXVtey05BLm7OB+M8fnvE64RNegzX7U+5NUXcOcnpTIK0YfSHBg8gYd0oxIq3IZTe9SfPZiRw==", + "dependencies": { + "@octokit/types": "^6.39.0", + "deprecation": "^2.3.1" + }, + "peerDependencies": { + "@octokit/core": ">=3" + } + }, + "node_modules/@octokit/request": { + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.3.tgz", + "integrity": "sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A==", + "dependencies": { + "@octokit/endpoint": "^6.0.1", + "@octokit/request-error": "^2.1.0", + "@octokit/types": "^6.16.1", + "is-plain-object": "^5.0.0", + "node-fetch": "^2.6.7", + "universal-user-agent": "^6.0.0" + } + }, + "node_modules/@octokit/request-error": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz", + "integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==", + "dependencies": { + "@octokit/types": "^6.0.3", + "deprecation": "^2.0.0", + "once": "^1.4.0" + } + }, + "node_modules/@octokit/types": { + "version": "6.40.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.40.0.tgz", + "integrity": "sha512-MFZOU5r8SwgJWDMhrLUSvyJPtVsqA6VnbVI3TNbsmw+Jnvrktzvq2fYES/6RiJA/5Ykdwq4mJmtlYUfW7CGjmw==", + "dependencies": { + "@octokit/openapi-types": "^12.10.0" + } + }, + "node_modules/@types/semver": { + "version": "7.3.10", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.10.tgz", + "integrity": "sha512-zsv3fsC7S84NN6nPK06u79oWgrPVd0NvOyqgghV1haPaFcVxIrP4DLomRwGAXk0ui4HZA7mOcSFL98sMVW9viw==", + "dev": true + }, + "node_modules/@vercel/ncc": { + "version": "0.34.0", + "resolved": "https://registry.npmjs.org/@vercel/ncc/-/ncc-0.34.0.tgz", + "integrity": "sha512-G9h5ZLBJ/V57Ou9vz5hI8pda/YQX5HQszCs3AmIus3XzsmRn/0Ptic5otD3xVST8QLKk7AMk7AqpsyQGN7MZ9A==", + "dev": true, + "bin": { + "ncc": "dist/ncc/cli.js" + } + }, + "node_modules/before-after-hook": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.2.tgz", + "integrity": "sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ==" + }, + "node_modules/deprecation": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", + "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==" + }, + "node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "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==", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", + "engines": { + "node": ">=0.6.11 <=0.7.0 || >=0.7.3" + } + }, + "node_modules/universal-user-agent": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", + "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==" + }, + "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==" + }, + "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==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "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==" + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } + }, + "dependencies": { + "@actions/core": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.9.0.tgz", + "integrity": "sha512-5pbM693Ih59ZdUhgk+fts+bUWTnIdHV3kwOSr+QIoFHMLg7Gzhwm0cifDY/AG68ekEJAkHnQVpcy4f6GjmzBCA==", + "requires": { + "@actions/http-client": "^2.0.1" + } + }, + "@actions/github": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@actions/github/-/github-5.0.3.tgz", + "integrity": "sha512-myjA/pdLQfhUGLtRZC/J4L1RXOG4o6aYdiEq+zr5wVVKljzbFld+xv10k1FX6IkIJtNxbAq44BdwSNpQ015P0A==", + "requires": { + "@actions/http-client": "^2.0.1", + "@octokit/core": "^3.6.0", + "@octokit/plugin-paginate-rest": "^2.17.0", + "@octokit/plugin-rest-endpoint-methods": "^5.13.0" + } + }, + "@actions/http-client": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.0.1.tgz", + "integrity": "sha512-PIXiMVtz6VvyaRsGY268qvj57hXQEpsYogYOu2nrQhlf+XCGmZstmuZBbAybUl1nQGnvS1k1eEsQ69ZoD7xlSw==", + "requires": { + "tunnel": "^0.0.6" + } + }, + "@octokit/auth-token": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz", + "integrity": "sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g==", + "requires": { + "@octokit/types": "^6.0.3" + } + }, + "@octokit/core": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.6.0.tgz", + "integrity": "sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q==", + "requires": { + "@octokit/auth-token": "^2.4.4", + "@octokit/graphql": "^4.5.8", + "@octokit/request": "^5.6.3", + "@octokit/request-error": "^2.0.5", + "@octokit/types": "^6.0.3", + "before-after-hook": "^2.2.0", + "universal-user-agent": "^6.0.0" + } + }, + "@octokit/endpoint": { + "version": "6.0.12", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz", + "integrity": "sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA==", + "requires": { + "@octokit/types": "^6.0.3", + "is-plain-object": "^5.0.0", + "universal-user-agent": "^6.0.0" + } + }, + "@octokit/graphql": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz", + "integrity": "sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg==", + "requires": { + "@octokit/request": "^5.6.0", + "@octokit/types": "^6.0.3", + "universal-user-agent": "^6.0.0" + } + }, + "@octokit/openapi-types": { + "version": "12.10.1", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.10.1.tgz", + "integrity": "sha512-P+SukKanjFY0ZhsK6wSVnQmxTP2eVPPE8OPSNuxaMYtgVzwJZgfGdwlYjf4RlRU4vLEw4ts2fsE2icG4nZ5ddQ==" + }, + "@octokit/plugin-paginate-rest": { + "version": "2.21.3", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.21.3.tgz", + "integrity": "sha512-aCZTEf0y2h3OLbrgKkrfFdjRL6eSOo8komneVQJnYecAxIej7Bafor2xhuDJOIFau4pk0i/P28/XgtbyPF0ZHw==", + "requires": { + "@octokit/types": "^6.40.0" + } + }, + "@octokit/plugin-rest-endpoint-methods": { + "version": "5.16.2", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.16.2.tgz", + "integrity": "sha512-8QFz29Fg5jDuTPXVtey05BLm7OB+M8fnvE64RNegzX7U+5NUXcOcnpTIK0YfSHBg8gYd0oxIq3IZTe9SfPZiRw==", + "requires": { + "@octokit/types": "^6.39.0", + "deprecation": "^2.3.1" + } + }, + "@octokit/request": { + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.3.tgz", + "integrity": "sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A==", + "requires": { + "@octokit/endpoint": "^6.0.1", + "@octokit/request-error": "^2.1.0", + "@octokit/types": "^6.16.1", + "is-plain-object": "^5.0.0", + "node-fetch": "^2.6.7", + "universal-user-agent": "^6.0.0" + } + }, + "@octokit/request-error": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz", + "integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==", + "requires": { + "@octokit/types": "^6.0.3", + "deprecation": "^2.0.0", + "once": "^1.4.0" + } + }, + "@octokit/types": { + "version": "6.40.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.40.0.tgz", + "integrity": "sha512-MFZOU5r8SwgJWDMhrLUSvyJPtVsqA6VnbVI3TNbsmw+Jnvrktzvq2fYES/6RiJA/5Ykdwq4mJmtlYUfW7CGjmw==", + "requires": { + "@octokit/openapi-types": "^12.10.0" + } + }, + "@types/semver": { + "version": "7.3.10", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.10.tgz", + "integrity": "sha512-zsv3fsC7S84NN6nPK06u79oWgrPVd0NvOyqgghV1haPaFcVxIrP4DLomRwGAXk0ui4HZA7mOcSFL98sMVW9viw==", + "dev": true + }, + "@vercel/ncc": { + "version": "0.34.0", + "resolved": "https://registry.npmjs.org/@vercel/ncc/-/ncc-0.34.0.tgz", + "integrity": "sha512-G9h5ZLBJ/V57Ou9vz5hI8pda/YQX5HQszCs3AmIus3XzsmRn/0Ptic5otD3xVST8QLKk7AMk7AqpsyQGN7MZ9A==", + "dev": true + }, + "before-after-hook": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.2.tgz", + "integrity": "sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ==" + }, + "deprecation": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", + "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==" + }, + "is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==" + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, + "node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "requires": { + "whatwg-url": "^5.0.0" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "requires": { + "wrappy": "1" + } + }, + "semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "requires": { + "lru-cache": "^6.0.0" + } + }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==" + }, + "universal-user-agent": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", + "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==" + }, + "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==" + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } + } +} diff --git a/.github/actions/issue-validator/package.json b/.github/actions/issue-validator/package.json new file mode 100644 index 0000000000000..24d5d9cfd6afd --- /dev/null +++ b/.github/actions/issue-validator/package.json @@ -0,0 +1,16 @@ +{ + "private": true, + "exports": "./index.mjs", + "scripts": { + "build": "ncc -m -o . build src/index.mjs --license licenses.txt" + }, + "devDependencies": { + "@types/semver": "7.3.10", + "@vercel/ncc": "0.34.0" + }, + "dependencies": { + "@actions/core": "1.9.0", + "@actions/github": "5.0.3", + "semver": "7.3.7" + } +} diff --git a/.github/actions/issue-validator/src/index.mjs b/.github/actions/issue-validator/src/index.mjs new file mode 100644 index 0000000000000..c51d3f01511c2 --- /dev/null +++ b/.github/actions/issue-validator/src/index.mjs @@ -0,0 +1,171 @@ +// @ts-check +import * as github from '@actions/github' +import * as core from '@actions/core' +import { gte as semverGte } from 'semver' + +const verifyCanaryLabel = 'please verify canary' +const bugReportLabel = 'template: bug' +const addReproductionLabel = 'please add a complete reproduction' +const debug = !!process.env.DEBUG +const json = (o) => JSON.stringify(o, null, 2) + +/** + * @typedef {{ + * id :number + * node_id :string + * url :string + * name :string + * description :string + * color :string + * default :boolean + * }} Label + */ + +async function run() { + try { + const { payload, repo } = github.context + const { issue, pull_request } = payload + + if (pull_request || !issue?.body) return + + /** @type {Label} */ + const newLabel = payload.label + const { body, number: issueNumber } = issue + /** @type {Label[]} */ + const labels = issue.labels + + if (debug) { + core.info( + `Validating issue ${issueNumber}: + Labels: + New: ${json(newLabel)} + All: ${json(labels)} + Body: ${body}` + ) + } + + const isBugReport = newLabel.name === bugReportLabel + + const isManuallyLabeled = labels.some( + (label) => label.name === verifyCanaryLabel + ) + + if (!isBugReport && !isManuallyLabeled) { + return core.info( + 'Issue is ignored, because it is not a bug report or is not manually labeled' + ) + } + + if (!process.env.GITHUB_TOKEN) { + throw new Error('GITHUB_TOKEN is not set') + } + + const client = github.getOctokit(process.env.GITHUB_TOKEN).rest + + /** + * @param {string} label + * @param {string} comment + */ + function notifyOnIssue(label, comment) { + const issueCommon = { ...repo, issue_number: issueNumber } + + if (debug) { + core.info('Skipping comment/label because we are in DEBUG mode') + core.info(json({ label, comment })) + return + } + + return Promise.all([ + client.issues.addLabels({ ...issueCommon, labels: [label] }), + client.issues.createComment({ ...issueCommon, body: comment }), + ]) + } + + const isVerifyCanaryChecked = body.includes( + '- [X] I verified that the issue exists in Next.js canary release' + ) + + if (!isVerifyCanaryChecked || isManuallyLabeled) { + await notifyOnIssue( + verifyCanaryLabel, + 'Please verify your issue reproduces with `next@canary`. The canary version of Next.js ships daily and includes all features and fixes that have not been released to the stable version yet. Think of canary as a public beta. Some issues may already be fixed in the canary version, so please verify that your issue reproduces by running `npm install next@canary`. If the issue does not reproduce with the canary version, then it has already been fixed and this issue can be closed.' + ) + return core.info( + `Commented on issue, because it was ${ + isManuallyLabeled ? 'manually labeled' : 'not verified against canary' + }` + ) + } + + const reproductionUrl = body + .match(/### Link to reproduction\n\n(?.*)\n/) + ?.groups?.url.trim() + + if (!reproductionUrl || !(await fetch(reproductionUrl)).ok) { + await notifyOnIssue( + addReproductionLabel, + 'The link to the reproduction appears to be incorrect/unreachable. Please add a link to the reproduction of the issue. This is a required field. If your project is private, you can invite @balazsorban44 to the repository so the Next.js team can investigate further.' + ) + return core.info( + `Commented on issue, because the reproduction url (${reproductionUrl}) was not reachable` + ) + } + + const containsNextInfoOutput = [ + 'Operating System:', + 'Binaries:', + 'Relevant packages:', + ].every((i) => body.includes(i)) + + if (!containsNextInfoOutput) { + return core.info( + 'Could not detect `next info` output, skipping as version detection might be unreliable' + ) + } + + const reported = body.match( + /Relevant packages:\n next: (?\d+\.\d+\.\d+)/ + )?.groups?.version + + core.info(`Reported Next.js version: ${reported}`) + + // REVIEW: Should we add a label here? + if (!reported) return + + const last = await getLastVersion() + core.info(`Last Next.js version, based on npm releases: ${last}`) + + if (!last.includes('canary') || reported === last) return + + await notifyOnIssue( + verifyCanaryLabel, + `The reported Next.js version did not match the latest \`next@canary\` version (${last}). The canary version of Next.js ships daily and includes all features and fixes that have not been released to the stable version yet. Think of canary as a public beta. Some issues may already be fixed in the canary version, so please verify that your issue reproduces by running \`npm install next@canary\`. If the issue does not reproduce with the canary version, then it has already been fixed and this issue can be closed.` + ) + return core.info( + `Commented on issue, because it was not verified against canary` + ) + } catch (error) { + core.setFailed(error.message) + } +} + +run() + +async function getLastVersion() { + try { + const res = await fetch('https://registry.npmjs.org/next', { + headers: { + accept: + 'application/vnd.npm.install-v1+json; q=1.0, application/json; q=0.8, */*', + }, + }) + const value = (await res.body?.getReader().read())?.value + const string = new TextDecoder().decode(value?.slice(0, 100)) + const re = /"latest":"(?.*)","canary":"(?.*)","/ + const { latest, canary } = string.match(re)?.groups ?? {} + return semverGte(latest, canary) ? canary : latest + } catch (error) { + core.error(error) + return '' + } +} diff --git a/.github/actions/next-stats-action/.gitignore b/.github/actions/next-stats-action/.gitignore new file mode 100644 index 0000000000000..d1de1508938ce --- /dev/null +++ b/.github/actions/next-stats-action/.gitignore @@ -0,0 +1,3 @@ +**/node_modules +out.md +.work \ No newline at end of file diff --git a/.github/actions/next-stats-action/Dockerfile b/.github/actions/next-stats-action/Dockerfile new file mode 100644 index 0000000000000..9cdb8b26e0695 --- /dev/null +++ b/.github/actions/next-stats-action/Dockerfile @@ -0,0 +1,20 @@ +FROM node:16-bullseye + +LABEL com.github.actions.name="Next.js PR Stats" +LABEL com.github.actions.description="Compares stats of a PR with the main branch" +LABEL repository="https://github.com/vercel/next-stats-action" + +COPY . /next-stats + +# Install node_modules +RUN npm i -g pnpm@7.3.0 +RUN cd /next-stats && pnpm install --production + +RUN git config --global user.email 'stats@localhost' +RUN git config --global user.name 'next stats' + +RUN apt update +RUN apt install apache2-utils -y + +COPY entrypoint.sh /entrypoint.sh +ENTRYPOINT ["/entrypoint.sh"] diff --git a/.github/actions/next-stats-action/README.md b/.github/actions/next-stats-action/README.md new file mode 100644 index 0000000000000..566a6296a68cc --- /dev/null +++ b/.github/actions/next-stats-action/README.md @@ -0,0 +1,93 @@ +# Next.js Stats GitHub Action + +> Downloads and runs project with provided configs gathering stats to compare branches + +See it in action at Next.js https://github.com/vercel/next.js + +## Getting Started + +1. Add a `.stats-app` folder to your project with a [`stats-config.js`](#stats-config) and any files to run against for example a test app that is to be built +2. Add the action to your [workflow](https://help.github.com/en/articles/configuring-a-workflow) +3. Enjoy the stats + +## Stats Config + +```TypeScript +const StatsConfig = { + // the Heading to show at the top of stats comments + commentHeading: 'Stats from current PR' | undefined, + commentReleaseHeading: 'Stats from current release' | undefined, + // the command to build your project if not done on post install + initialBuildCommand: undefined | string, + skipInitialInstall: undefined | boolean, + // the command to build the app (app source should be in `.stats-app`) + appBuildCommand: string, + appStartCommand: string | undefined, + // the main branch to compare against (what PRs will be merging into) + mainBranch: 'canary', + // the main repository path (relative to https://github.com/) + mainRepo: 'vercel/next.js', + // whether to attempt auto merging the main branch into PR before running stats + autoMergeMain: boolean | undefined, + // an array of configs for each run + configs: [ + { // first run's config + // title of the run + title: 'fastMode stats', + // whether to diff the outputted files (default: onOutputChange) + diff: 'onOutputChange' | false | undefined, + // config files to add before running diff (if `undefined` uses `configFiles`) + diffConfigFiles: [] | undefined, + // renames to apply to make file names deterministic + renames: [ + { + srcGlob: 'main-*.js', + dest: 'main.js' + } + ], + // config files to add before running (removed before successive runs) + configFiles: [ + { + path: './next.config.js', + content: 'module.exports = { fastMode: true }' + } + ], + // an array of file groups to diff/track + filesToTrack: [ + { + name: 'Pages', + globs: [ + 'build/pages/**/*.js' + ] + } + ], + // an array of URLs to fetch while `appStartCommand` is running + // will be output to fetched-pages/${pathname}.html + pagesToFetch: [ + 'https://localhost:$PORT/page-1' + ] + }, + { // second run's config + title: 'slowMode stats', + diff: false, + configFiles: [ + { + path: './next.config.js', + content: 'module.exports = { slowMode: true }' + } + ], + filesToTrack: [ + { + name: 'Main Bundles', + globs: [ + 'build/runtime/webpack-*.js', + 'build/runtime/main-*.js', + ] + } + ] + }, + ] +} + +module.exports = StatsConfig +``` diff --git a/.github/actions/next-stats-action/entrypoint.sh b/.github/actions/next-stats-action/entrypoint.sh new file mode 100755 index 0000000000000..5f5c38de22be1 --- /dev/null +++ b/.github/actions/next-stats-action/entrypoint.sh @@ -0,0 +1,6 @@ +#!/bin/bash +set -eu # stop on error + +export HOME=/root + +node /next-stats/src/index.js diff --git a/.github/actions/next-stats-action/package.json b/.github/actions/next-stats-action/package.json new file mode 100644 index 0000000000000..4d2e88dae3e3e --- /dev/null +++ b/.github/actions/next-stats-action/package.json @@ -0,0 +1,17 @@ +{ + "private": true, + "main": "src/index.js", + "dependencies": { + "async-sema": "^3.1.0", + "fs-extra": "^8.1.0", + "get-port": "^5.0.0", + "glob": "^7.1.4", + "gzip-size": "^5.1.1", + "minimatch": "^3.0.4", + "node-fetch": "^2.6.0", + "prettier": "^1.18.2", + "pretty-bytes": "^5.3.0", + "pretty-ms": "^5.0.0", + "semver": "7.3.4" + } +} diff --git a/.github/actions/next-stats-action/src/add-comment.js b/.github/actions/next-stats-action/src/add-comment.js new file mode 100644 index 0000000000000..a873067034049 --- /dev/null +++ b/.github/actions/next-stats-action/src/add-comment.js @@ -0,0 +1,274 @@ +const path = require('path') +const fs = require('fs').promises +const fetch = require('node-fetch') +const prettyMs = require('pretty-ms') +const logger = require('./util/logger') +const prettyBytes = require('pretty-bytes') +const { benchTitle } = require('./constants') + +const gzipIgnoreRegex = new RegExp(`(General|^Serverless|${benchTitle})`) + +const prettify = (val, type = 'bytes') => { + if (typeof val !== 'number') return 'N/A' + return type === 'bytes' ? prettyBytes(val) : prettyMs(val) +} + +const round = (num, places) => { + const placesFactor = Math.pow(10, places) + return Math.round(num * placesFactor) / placesFactor +} + +const shortenLabel = (itemKey) => + itemKey.length > 24 + ? `${itemKey.slice(0, 12)}..${itemKey.slice(-12)}` + : itemKey + +const twoMB = 2 * 1024 * 1024 + +module.exports = async function addComment( + results = [], + actionInfo, + statsConfig +) { + let comment = `# ${ + actionInfo.isRelease + ? statsConfig.commentReleaseHeading || 'Stats from current release' + : statsConfig.commentHeading || 'Stats from current PR' + }\n\n` + + const tableHead = `| | ${statsConfig.mainRepo} ${statsConfig.mainBranch} ${ + actionInfo.lastStableTag || '' + } | ${actionInfo.prRepo} ${actionInfo.prRef} | Change |\n| - | - | - | - |\n` + + for (let i = 0; i < results.length; i++) { + const result = results[i] + const isLastResult = i === results.length - 1 + let resultHasIncrease = false + let resultHasDecrease = false + let resultContent = '' + + Object.keys(result.mainRepoStats).forEach((groupKey) => { + const isBenchmark = groupKey === benchTitle + const mainRepoGroup = result.mainRepoStats[groupKey] + const diffRepoGroup = result.diffRepoStats[groupKey] + const itemKeys = new Set([ + ...Object.keys(mainRepoGroup), + ...Object.keys(diffRepoGroup), + ]) + let groupTable = tableHead + let mainRepoTotal = 0 + let diffRepoTotal = 0 + let totalChange = 0 + + itemKeys.forEach((itemKey) => { + const prettyType = itemKey.match(/(length|duration)/i) ? 'ms' : 'bytes' + const isGzipItem = itemKey.endsWith('gzip') + const mainItemVal = mainRepoGroup[itemKey] + const diffItemVal = diffRepoGroup[itemKey] + const useRawValue = isBenchmark && prettyType !== 'ms' + const mainItemStr = useRawValue + ? mainItemVal + : prettify(mainItemVal, prettyType) + + const diffItemStr = useRawValue + ? diffItemVal + : prettify(diffItemVal, prettyType) + + let change = '✓' + + // Don't show gzip values for serverless as they aren't + // deterministic currently + if (groupKey.startsWith('Serverless') && isGzipItem) return + // otherwise only show gzip values + else if (!isGzipItem && !groupKey.match(gzipIgnoreRegex)) return + + if ( + !itemKey.startsWith('buildDuration') || + (isBenchmark && itemKey.match(/req\/sec/)) + ) { + if (typeof mainItemVal === 'number') mainRepoTotal += mainItemVal + if (typeof diffItemVal === 'number') diffRepoTotal += diffItemVal + } + + // calculate the change + if (mainItemVal !== diffItemVal) { + if ( + typeof mainItemVal === 'number' && + typeof diffItemVal === 'number' + ) { + change = round(diffItemVal - mainItemVal, 2) + + // check if there is still a change after rounding + if (change !== 0) { + const absChange = Math.abs(change) + const warnIfNegative = isBenchmark && itemKey.match(/req\/sec/) + const warn = warnIfNegative + ? change < 0 + ? '⚠️ ' + : '' + : change > 0 + ? '⚠️ ' + : '' + change = `${warn}${change < 0 ? '-' : '+'}${ + useRawValue ? absChange : prettify(absChange, prettyType) + }` + } + } else { + change = 'N/A' + } + } + + groupTable += `| ${ + isBenchmark ? itemKey : shortenLabel(itemKey) + } | ${mainItemStr} | ${diffItemStr} | ${change} |\n` + }) + let groupTotalChange = '' + + totalChange = diffRepoTotal - mainRepoTotal + + if (totalChange !== 0) { + if (totalChange < 0) { + resultHasDecrease = true + groupTotalChange = ` Overall decrease ${isBenchmark ? '⚠️' : '✓'}` + } else { + if ( + (groupKey !== 'General' && totalChange > 5) || + totalChange > twoMB + ) { + resultHasIncrease = true + } + groupTotalChange = ` Overall increase ${isBenchmark ? '✓' : '⚠️'}` + } + } + + if (groupKey !== 'General' && groupKey !== benchTitle) { + let totalChangeSign = '' + + if (totalChange === 0) { + totalChange = '✓' + } else { + totalChangeSign = totalChange < 0 ? '-' : '⚠️ +' + } + totalChange = `${totalChangeSign}${ + typeof totalChange === 'number' + ? prettify(Math.abs(totalChange)) + : totalChange + }` + groupTable += `| Overall change | ${prettyBytes( + round(mainRepoTotal, 2) + )} | ${prettyBytes(round(diffRepoTotal, 2))} | ${totalChange} |\n` + } + + if (itemKeys.size > 0) { + resultContent += `
\n` + resultContent += `${groupKey}${groupTotalChange}\n\n` + resultContent += groupTable + resultContent += `\n
\n\n` + } + }) + + // add diffs + if (result.diffs) { + const diffHeading = '#### Diffs\n' + let diffContent = diffHeading + + Object.keys(result.diffs).forEach((itemKey) => { + const curDiff = result.diffs[itemKey] + diffContent += `
\n` + diffContent += `Diff for ${shortenLabel( + itemKey + )}\n\n` + + if (curDiff.length > 36 * 1000) { + diffContent += 'Diff too large to display' + } else { + diffContent += `\`\`\`diff\n${curDiff}\n\`\`\`` + } + diffContent += `\n
\n` + }) + + if (diffContent !== diffHeading) { + resultContent += diffContent + } + } + let increaseDecreaseNote = '' + + if (resultHasIncrease) { + increaseDecreaseNote = ' (Increase detected ⚠️)' + } else if (resultHasDecrease) { + increaseDecreaseNote = ' (Decrease detected ✓)' + } + + comment += `
\n` + comment += `${result.title}${increaseDecreaseNote}\n\n
\n\n` + comment += resultContent + comment += '
\n' + + if (!isLastResult) { + comment += `
\n` + } + } + if (process.env.LOCAL_STATS) { + const statsPath = path.resolve('pr-stats.md') + await fs.writeFile(statsPath, comment) + console.log(`Output PR stats to ${statsPath}`) + } else { + logger('\n--stats start--\n', comment, '\n--stats end--\n') + } + + if ( + actionInfo.customCommentEndpoint || + (actionInfo.githubToken && actionInfo.commentEndpoint) + ) { + logger(`Posting results to ${actionInfo.commentEndpoint}`) + + const body = { + body: comment, + ...(!actionInfo.githubToken + ? { + isRelease: actionInfo.isRelease, + commitId: actionInfo.commitId, + issueId: actionInfo.issueId, + } + : {}), + } + + if (actionInfo.customCommentEndpoint) { + logger(`Using body ${JSON.stringify({ ...body, body: 'OMITTED' })}`) + } + + try { + const res = await fetch(actionInfo.commentEndpoint, { + method: 'POST', + headers: { + ...(actionInfo.githubToken + ? { + Authorization: `bearer ${actionInfo.githubToken}`, + } + : { + 'content-type': 'application/json', + }), + }, + body: JSON.stringify(body), + }) + + if (!res.ok) { + logger.error(`Failed to post results ${res.status}`) + try { + logger.error(await res.text()) + } catch (_) { + /* no-op */ + } + } else { + logger('Successfully posted results') + } + } catch (err) { + logger.error(`Error occurred posting results`, err) + } + } else { + logger( + `Not posting results`, + actionInfo.githubToken ? 'No comment endpoint' : 'no GitHub token' + ) + } +} diff --git a/.github/actions/next-stats-action/src/constants.js b/.github/actions/next-stats-action/src/constants.js new file mode 100644 index 0000000000000..625624a18986d --- /dev/null +++ b/.github/actions/next-stats-action/src/constants.js @@ -0,0 +1,33 @@ +const path = require('path') +const os = require('os') + +const benchTitle = 'Page Load Tests' +const workDir = path.join(os.tmpdir(), 'next-stats') +const mainRepoName = 'main-repo' +const diffRepoName = 'diff-repo' +const mainRepoDir = path.join(workDir, mainRepoName) +const diffRepoDir = path.join(workDir, diffRepoName) +const statsAppDir = path.join(workDir, 'stats-app') +const diffingDir = path.join(workDir, 'diff') +const yarnEnvValues = { + YARN_CACHE_FOLDER: path.join(workDir, 'yarn-cache'), +} +const allowedConfigLocations = [ + './', + '.stats-app', + 'test/.stats-app', + '.github/.stats-app', +] + +module.exports = { + benchTitle, + workDir, + diffingDir, + mainRepoName, + diffRepoName, + mainRepoDir, + diffRepoDir, + statsAppDir, + yarnEnvValues, + allowedConfigLocations, +} diff --git a/.github/actions/next-stats-action/src/index.js b/.github/actions/next-stats-action/src/index.js new file mode 100644 index 0000000000000..be9284e0c2737 --- /dev/null +++ b/.github/actions/next-stats-action/src/index.js @@ -0,0 +1,162 @@ +const path = require('path') +const fs = require('fs-extra') +const exec = require('./util/exec') +const logger = require('./util/logger') +const runConfigs = require('./run') +const addComment = require('./add-comment') +const actionInfo = require('./prepare/action-info')() +const { mainRepoDir, diffRepoDir } = require('./constants') +const loadStatsConfig = require('./prepare/load-stats-config') +const { + cloneRepo, + checkoutRef, + mergeBranch, + getCommitId, + linkPackages, + getLastStable, +} = require('./prepare/repo-setup')(actionInfo) + +const allowedActions = new Set(['synchronize', 'opened']) + +if (!allowedActions.has(actionInfo.actionName) && !actionInfo.isRelease) { + logger( + `Not running for ${actionInfo.actionName} event action on repo: ${actionInfo.prRepo} and ref ${actionInfo.prRef}` + ) + process.exit(0) +} + +;(async () => { + try { + if (await fs.pathExists(path.join(__dirname, '../SKIP_NEXT_STATS.txt'))) { + console.log( + 'SKIP_NEXT_STATS.txt file present, exiting stats generation..' + ) + process.exit(0) + } + + const { stdout: gitName } = await exec( + 'git config user.name && git config user.email' + ) + console.log('git author result:', gitName) + + // clone PR/newer repository/ref first to get settings + if (!actionInfo.skipClone) { + await cloneRepo(actionInfo.prRepo, diffRepoDir) + await checkoutRef(actionInfo.prRef, diffRepoDir) + } + + if (actionInfo.isRelease) { + process.env.STATS_IS_RELEASE = 'true' + } + + // load stats config from allowed locations + const { statsConfig, relativeStatsAppDir } = loadStatsConfig() + + if (actionInfo.isLocal && actionInfo.prRef === statsConfig.mainBranch) { + throw new Error( + `'GITHUB_REF' can not be the same as mainBranch in 'stats-config.js'.\n` + + `This will result in comparing against the same branch` + ) + } + + if (actionInfo.isLocal) { + // make sure to use local repo location instead of the + // one provided in statsConfig + statsConfig.mainRepo = actionInfo.prRepo + } + + // clone main repository/ref + if (!actionInfo.skipClone) { + await cloneRepo(statsConfig.mainRepo, mainRepoDir) + await checkoutRef(statsConfig.mainBranch, mainRepoDir) + } + /* eslint-disable-next-line */ + actionInfo.commitId = await getCommitId(diffRepoDir) + let mainNextSwcVersion + + if (!actionInfo.skipClone) { + if (actionInfo.isRelease) { + logger('Release detected, resetting mainRepo to last stable tag') + const lastStableTag = await getLastStable(mainRepoDir, actionInfo.prRef) + mainNextSwcVersion = { + '@next/swc-linux-x64-gnu': lastStableTag, + } + if (!lastStableTag) throw new Error('failed to get last stable tag') + console.log('using latestStable', lastStableTag) + await checkoutRef(lastStableTag, mainRepoDir) + + /* eslint-disable-next-line */ + actionInfo.lastStableTag = lastStableTag + /* eslint-disable-next-line */ + actionInfo.commitId = await getCommitId(diffRepoDir) + + if (!actionInfo.customCommentEndpoint) { + /* eslint-disable-next-line */ + actionInfo.commentEndpoint = `https://api.github.com/repos/${statsConfig.mainRepo}/commits/${actionInfo.commitId}/comments` + } + } else if (statsConfig.autoMergeMain) { + logger('Attempting auto merge of main branch') + await mergeBranch(statsConfig.mainBranch, mainRepoDir, diffRepoDir) + } + } + + let mainRepoPkgPaths + let diffRepoPkgPaths + + // run install/initialBuildCommand + const repoDirs = [mainRepoDir, diffRepoDir] + + for (const dir of repoDirs) { + logger(`Running initial build for ${dir}`) + if (!actionInfo.skipClone) { + const usePnpm = await fs.pathExists(path.join(dir, 'pnpm-lock.yaml')) + let buildCommand = `cd ${dir}${ + !statsConfig.skipInitialInstall + ? usePnpm + ? ' && pnpm install && pnpm run build' + : ' && yarn install --network-timeout 1000000' + : '' + }` + + if (statsConfig.initialBuildCommand) { + buildCommand += ` && ${statsConfig.initialBuildCommand}` + } + // allow 5 minutes node_modules install + building all packages + // in case of noisy environment slowing down initial repo build + await exec(buildCommand, false, { timeout: 5 * 60 * 1000 }) + } + + await fs + .copy( + path.join(__dirname, '../native'), + path.join(dir, 'packages/next-swc/native') + ) + .catch(console.error) + + logger(`Linking packages in ${dir}`) + const isMainRepo = dir === mainRepoDir + const pkgPaths = await linkPackages( + dir, + isMainRepo ? mainNextSwcVersion : undefined + ) + + if (isMainRepo) mainRepoPkgPaths = pkgPaths + else diffRepoPkgPaths = pkgPaths + } + + // run the configs and post the comment + const results = await runConfigs(statsConfig.configs, { + statsConfig, + mainRepoPkgPaths, + diffRepoPkgPaths, + relativeStatsAppDir, + }) + await addComment(results, actionInfo, statsConfig) + logger('finished') + process.exit(0) + } catch (err) { + console.error('Error occurred generating stats:') + console.error(err) + process.exit(1) + } +})() diff --git a/.github/actions/next-stats-action/src/prepare/action-info.js b/.github/actions/next-stats-action/src/prepare/action-info.js new file mode 100644 index 0000000000000..fff1ffc955cb3 --- /dev/null +++ b/.github/actions/next-stats-action/src/prepare/action-info.js @@ -0,0 +1,99 @@ +const path = require('path') +const logger = require('../util/logger') +const { execSync } = require('child_process') +const releaseTypes = new Set(['release', 'published']) + +module.exports = function actionInfo() { + let { + ISSUE_ID, + SKIP_CLONE, + GITHUB_REF, + LOCAL_STATS, + GIT_ROOT_DIR, + GITHUB_ACTION, + COMMENT_ENDPOINT, + GITHUB_REPOSITORY, + GITHUB_EVENT_PATH, + PR_STATS_COMMENT_TOKEN, + } = process.env + + delete process.env.GITHUB_TOKEN + delete process.env.PR_STATS_COMMENT_TOKEN + + // only use custom endpoint if we don't have a token + const commentEndpoint = !PR_STATS_COMMENT_TOKEN && COMMENT_ENDPOINT + + if (LOCAL_STATS === 'true') { + const cwd = process.cwd() + const parentDir = path.join(cwd, '../..') + + if (!GITHUB_REF) { + // get the current branch name + GITHUB_REF = execSync(`cd "${cwd}" && git rev-parse --abbrev-ref HEAD`) + .toString() + .trim() + } + if (!GIT_ROOT_DIR) { + GIT_ROOT_DIR = path.join(parentDir, '/') + } + if (!GITHUB_REPOSITORY) { + GITHUB_REPOSITORY = path.relative(parentDir, cwd) + } + if (!GITHUB_ACTION) { + GITHUB_ACTION = 'opened' + } + } + + const info = { + commentEndpoint, + skipClone: SKIP_CLONE, + actionName: GITHUB_ACTION, + githubToken: PR_STATS_COMMENT_TOKEN, + customCommentEndpoint: !!commentEndpoint, + gitRoot: GIT_ROOT_DIR || 'https://github.com/', + prRepo: GITHUB_REPOSITORY, + prRef: GITHUB_REF, + isLocal: LOCAL_STATS, + commitId: null, + issueId: ISSUE_ID, + isRelease: + GITHUB_REPOSITORY === 'vercel/next.js' && + (GITHUB_REF || '').includes('canary'), + } + + // get comment + if (GITHUB_EVENT_PATH) { + const event = require(GITHUB_EVENT_PATH) + info.actionName = event.action || info.actionName + + if (releaseTypes.has(info.actionName)) { + info.isRelease = true + } else { + // Since GITHUB_REPOSITORY and REF might not match the fork + // use event data to get repository and ref info + const prData = event['pull_request'] + + if (prData) { + info.prRepo = prData.head.repo.full_name + info.prRef = prData.head.ref + info.issueId = prData.number + + if (!info.commentEndpoint) { + info.commentEndpoint = prData._links.comments || '' + } + // comment endpoint might be under `href` + if (typeof info.commentEndpoint === 'object') { + info.commentEndpoint = info.commentEndpoint.href + } + } + } + } + + logger('Got actionInfo:') + logger.json({ + ...info, + githubToken: PR_STATS_COMMENT_TOKEN ? 'found' : 'missing', + }) + + return info +} diff --git a/.github/actions/next-stats-action/src/prepare/load-stats-config.js b/.github/actions/next-stats-action/src/prepare/load-stats-config.js new file mode 100644 index 0000000000000..55cecb54faad3 --- /dev/null +++ b/.github/actions/next-stats-action/src/prepare/load-stats-config.js @@ -0,0 +1,44 @@ +const path = require('path') +const logger = require('../util/logger') +const { diffRepoDir, allowedConfigLocations } = require('../constants') + +// load stats-config +function loadStatsConfig() { + let statsConfig + let relativeStatsAppDir + + for (const configPath of allowedConfigLocations) { + try { + relativeStatsAppDir = configPath + statsConfig = require(path.join( + diffRepoDir, + configPath, + 'stats-config.js' + )) + break + } catch (err) { + if (err.code !== 'MODULE_NOT_FOUND') { + console.error('Failed to load stats-config at', configPath, err) + } + /* */ + } + } + + if (!statsConfig) { + throw new Error( + `Failed to locate \`.stats-app\`, allowed locations are: ${allowedConfigLocations.join( + ', ' + )}` + ) + } + + logger( + 'Got statsConfig at', + path.join(relativeStatsAppDir, 'stats-config.js'), + statsConfig, + '\n' + ) + return { statsConfig, relativeStatsAppDir } +} + +module.exports = loadStatsConfig diff --git a/.github/actions/next-stats-action/src/prepare/repo-setup.js b/.github/actions/next-stats-action/src/prepare/repo-setup.js new file mode 100644 index 0000000000000..1ff98843f197f --- /dev/null +++ b/.github/actions/next-stats-action/src/prepare/repo-setup.js @@ -0,0 +1,139 @@ +const path = require('path') +const fs = require('fs-extra') +const exec = require('../util/exec') +const { remove } = require('fs-extra') +const logger = require('../util/logger') +const semver = require('semver') + +module.exports = (actionInfo) => { + return { + async cloneRepo(repoPath = '', dest = '') { + await remove(dest) + await exec(`git clone ${actionInfo.gitRoot}${repoPath} ${dest}`) + }, + async checkoutRef(ref = '', repoDir = '') { + await exec(`cd ${repoDir} && git fetch && git checkout ${ref}`) + }, + async getLastStable(repoDir = '', ref) { + const { stdout } = await exec(`cd ${repoDir} && git tag -l`) + const tags = stdout.trim().split('\n') + let lastStableTag + + for (let i = tags.length - 1; i >= 0; i--) { + const curTag = tags[i] + // stable doesn't include `-canary` or `-beta` + if (!curTag.includes('-') && !ref.includes(curTag)) { + if (!lastStableTag || semver.gt(curTag, lastStableTag)) { + lastStableTag = curTag + } + } + } + return lastStableTag + }, + async getCommitId(repoDir = '') { + const { stdout } = await exec(`cd ${repoDir} && git rev-parse HEAD`) + return stdout.trim() + }, + async resetToRef(ref = '', repoDir = '') { + await exec(`cd ${repoDir} && git reset --hard ${ref}`) + }, + async mergeBranch(ref = '', origRepoDir = '', destRepoDir = '') { + await exec(`cd ${destRepoDir} && git remote add upstream ${origRepoDir}`) + await exec(`cd ${destRepoDir} && git fetch upstream`) + + try { + await exec(`cd ${destRepoDir} && git merge upstream/${ref}`) + logger('Auto merge of main branch successful') + } catch (err) { + logger.error('Failed to auto merge main branch:', err) + + if (err.stdout && err.stdout.includes('CONFLICT')) { + await exec(`cd ${destRepoDir} && git merge --abort`) + logger('aborted auto merge') + } + } + }, + async linkPackages(repoDir = '', nextSwcPkg) { + const pkgPaths = new Map() + const pkgDatas = new Map() + let pkgs + + try { + pkgs = await fs.readdir(path.join(repoDir, 'packages')) + } catch (err) { + if (err.code === 'ENOENT') { + console.log('no packages to link') + return pkgPaths + } + throw err + } + + for (const pkg of pkgs) { + const pkgPath = path.join(repoDir, 'packages', pkg) + const packedPkgPath = path.join(pkgPath, `${pkg}-packed.tgz`) + + const pkgDataPath = path.join(pkgPath, 'package.json') + if (!fs.existsSync(pkgDataPath)) { + console.log(`Skipping ${pkgDataPath}`) + continue + } + const pkgData = require(pkgDataPath) + const { name } = pkgData + pkgDatas.set(name, { + pkgDataPath, + pkg, + pkgPath, + pkgData, + packedPkgPath, + }) + pkgPaths.set(name, packedPkgPath) + } + + for (const pkg of pkgDatas.keys()) { + const { pkgDataPath, pkgData } = pkgDatas.get(pkg) + + for (const pkg of pkgDatas.keys()) { + const { packedPkgPath } = pkgDatas.get(pkg) + if (!pkgData.dependencies || !pkgData.dependencies[pkg]) continue + pkgData.dependencies[pkg] = packedPkgPath + } + // make sure native binaries are included in local linking + if (pkg === '@next/swc') { + if (!pkgData.files) { + pkgData.files = [] + } + pkgData.files.push('native') + console.log( + 'using swc binaries: ', + await exec(`ls ${path.join(path.dirname(pkgDataPath), 'native')}`) + ) + } + if (pkg === 'next') { + if (nextSwcPkg) { + Object.assign(pkgData.dependencies, nextSwcPkg) + } else { + if (pkgDatas.get('@next/swc')) { + pkgData.dependencies['@next/swc'] = + pkgDatas.get('@next/swc').packedPkgPath + } else { + pkgData.files.push('native') + } + } + } + await fs.writeFile( + pkgDataPath, + JSON.stringify(pkgData, null, 2), + 'utf8' + ) + } + + // wait to pack packages until after dependency paths have been updated + // to the correct versions + for (const pkgName of pkgDatas.keys()) { + const { pkg, pkgPath } = pkgDatas.get(pkgName) + await exec(`cd ${pkgPath} && yarn pack -f ${pkg}-packed.tgz`, true) + } + return pkgPaths + }, + } +} diff --git a/.github/actions/next-stats-action/src/run/benchmark-url.js b/.github/actions/next-stats-action/src/run/benchmark-url.js new file mode 100644 index 0000000000000..e92956a8c8dcf --- /dev/null +++ b/.github/actions/next-stats-action/src/run/benchmark-url.js @@ -0,0 +1,32 @@ +const exec = require('../util/exec') + +const parseField = (stdout = '', field = '') => { + return stdout.split(field).pop().trim().split(/\s/).shift().trim() +} + +// benchmark a url +async function benchmarkUrl( + url = '', + options = { + reqTimeout: 60, + concurrency: 50, + numRequests: 2500, + } +) { + const { numRequests, concurrency, reqTimeout } = options + + const { stdout } = await exec( + `ab -n ${numRequests} -c ${concurrency} -s ${reqTimeout} "${url}"` + ) + const totalTime = parseFloat(parseField(stdout, 'Time taken for tests:'), 10) + const failedRequests = parseInt(parseField(stdout, 'Failed requests:'), 10) + const avgReqPerSec = parseFloat(parseField(stdout, 'Requests per second:')) + + return { + totalTime, + avgReqPerSec, + failedRequests, + } +} + +module.exports = benchmarkUrl diff --git a/.github/actions/next-stats-action/src/run/collect-diffs.js b/.github/actions/next-stats-action/src/run/collect-diffs.js new file mode 100644 index 0000000000000..3440d8066be46 --- /dev/null +++ b/.github/actions/next-stats-action/src/run/collect-diffs.js @@ -0,0 +1,116 @@ +const path = require('path') +const fs = require('fs-extra') +const exec = require('../util/exec') +const glob = require('../util/glob') +const logger = require('../util/logger') +const { statsAppDir, diffingDir } = require('../constants') + +module.exports = async function collectDiffs( + filesToTrack = [], + initial = false +) { + if (initial) { + logger('Setting up directory for diffing') + // set-up diffing directory + await fs.remove(diffingDir) + await fs.mkdirp(diffingDir) + await exec(`cd ${diffingDir} && git init`) + } else { + // remove any previous files in case they won't be overwritten + const toRemove = await glob('!(.git)', { cwd: diffingDir, dot: true }) + + await Promise.all( + toRemove.map((file) => fs.remove(path.join(diffingDir, file))) + ) + } + const diffs = {} + + await Promise.all( + filesToTrack.map(async (fileGroup) => { + const { globs } = fileGroup + const curFiles = [] + + await Promise.all( + globs.map(async (pattern) => { + curFiles.push(...(await glob(pattern, { cwd: statsAppDir }))) + }) + ) + + for (let file of curFiles) { + const absPath = path.join(statsAppDir, file) + + const diffDest = path.join(diffingDir, file) + await fs.copy(absPath, diffDest) + } + + if (curFiles.length > 0) { + const prettierPath = path.join( + __dirname, + '../../node_modules/.bin/prettier' + ) + await exec( + `cd "${process.env.LOCAL_STATS ? process.cwd() : diffingDir}" && ` + + `${prettierPath} --write ${curFiles + .map((f) => path.join(diffingDir, f)) + .join(' ')}` + ) + } + }) + ) + + await exec(`cd ${diffingDir} && git add .`, true) + + if (initial) { + await exec(`cd ${diffingDir} && git commit -m 'initial commit'`) + } else { + let { stdout: renamedFiles } = await exec( + `cd ${diffingDir} && git diff --name-status HEAD` + ) + renamedFiles = renamedFiles + .trim() + .split('\n') + .filter((line) => line.startsWith('R')) + + diffs._renames = [] + + for (const line of renamedFiles) { + const [, prev, cur] = line.split('\t') + await fs.move(path.join(diffingDir, cur), path.join(diffingDir, prev)) + diffs._renames.push({ + prev, + cur, + }) + } + + await exec(`cd ${diffingDir} && git add .`) + + let { stdout: changedFiles } = await exec( + `cd ${diffingDir} && git diff --name-only HEAD` + ) + changedFiles = changedFiles.trim().split('\n') + + for (const file of changedFiles) { + const fileKey = path.basename(file) + const hasFile = await fs.exists(path.join(diffingDir, file)) + + if (!hasFile) { + diffs[fileKey] = 'deleted' + continue + } + + try { + let { stdout } = await exec( + `cd ${diffingDir} && git diff --minimal HEAD ${file}` + ) + stdout = (stdout.split(file).pop() || '').trim() + if (stdout.length > 0) { + diffs[fileKey] = stdout + } + } catch (err) { + console.error(`Failed to diff ${file}: ${err.message}`) + diffs[fileKey] = `failed to diff` + } + } + } + return diffs +} diff --git a/.github/actions/next-stats-action/src/run/collect-stats.js b/.github/actions/next-stats-action/src/run/collect-stats.js new file mode 100644 index 0000000000000..de50a50f88422 --- /dev/null +++ b/.github/actions/next-stats-action/src/run/collect-stats.js @@ -0,0 +1,171 @@ +const path = require('path') +const fs = require('fs-extra') +const getPort = require('get-port') +const fetch = require('node-fetch') +const glob = require('../util/glob') +const gzipSize = require('gzip-size') +const logger = require('../util/logger') +const { spawn } = require('../util/exec') +const { parse: urlParse } = require('url') +const benchmarkUrl = require('./benchmark-url') +const { statsAppDir, diffingDir, benchTitle } = require('../constants') + +module.exports = async function collectStats( + runConfig = {}, + statsConfig = {}, + fromDiff = false +) { + const stats = { + [benchTitle]: {}, + } + const orderedStats = { + [benchTitle]: {}, + } + const curDir = fromDiff ? diffingDir : statsAppDir + + const hasPagesToFetch = + Array.isArray(runConfig.pagesToFetch) && runConfig.pagesToFetch.length > 0 + + const hasPagesToBench = + Array.isArray(runConfig.pagesToBench) && runConfig.pagesToBench.length > 0 + + if ( + !fromDiff && + statsConfig.appStartCommand && + (hasPagesToFetch || hasPagesToBench) + ) { + const port = await getPort() + const startTime = Date.now() + const child = spawn(statsConfig.appStartCommand, { + cwd: curDir, + env: { + PORT: port, + }, + stdio: 'pipe', + }) + let exitCode = null + let logStderr = true + + let serverReadyResolve + let serverReadyResolved = false + const serverReadyPromise = new Promise((resolve) => { + serverReadyResolve = resolve + }) + + child.stdout.on('data', (data) => { + if (data.toString().includes('started server') && !serverReadyResolved) { + serverReadyResolved = true + serverReadyResolve() + } + process.stdout.write(data) + }) + child.stderr.on('data', (data) => logStderr && process.stderr.write(data)) + + child.on('exit', (code) => { + if (!serverReadyResolved) { + serverReadyResolve() + serverReadyResolved = true + } + exitCode = code + }) + + await serverReadyPromise + if (!orderedStats['General']) { + orderedStats['General'] = {} + } + orderedStats['General']['nextStartReadyDuration (ms)'] = + Date.now() - startTime + + if (exitCode !== null) { + throw new Error( + `Failed to run \`${statsConfig.appStartCommand}\` process exited with code ${exitCode}` + ) + } + + if (hasPagesToFetch) { + const fetchedPagesDir = path.join(curDir, 'fetched-pages') + await fs.mkdirp(fetchedPagesDir) + + for (let url of runConfig.pagesToFetch) { + url = url.replace('$PORT', port) + const { pathname } = urlParse(url) + try { + const res = await fetch(url) + if (!res.ok) { + throw new Error(`Failed to fetch ${url} got status: ${res.status}`) + } + const responseText = (await res.text()).trim() + + let fileName = pathname === '/' ? '/index' : pathname + if (fileName.endsWith('/')) fileName = fileName.slice(0, -1) + logger( + `Writing file to ${path.join(fetchedPagesDir, `${fileName}.html`)}` + ) + + await fs.writeFile( + path.join(fetchedPagesDir, `${fileName}.html`), + responseText, + 'utf8' + ) + } catch (err) { + logger.error(err) + } + } + } + + if (hasPagesToBench) { + // disable stderr so we don't clobber logs while benchmarking + // any pages that create logs + logStderr = false + + for (let url of runConfig.pagesToBench) { + url = url.replace('$PORT', port) + logger(`Benchmarking ${url}`) + + const results = await benchmarkUrl(url, runConfig.benchOptions) + logger(`Finished benchmarking ${url}`) + + const { pathname: key } = urlParse(url) + stats[benchTitle][`${key} failed reqs`] = results.failedRequests + stats[benchTitle][`${key} total time (seconds)`] = results.totalTime + + stats[benchTitle][`${key} avg req/sec`] = results.avgReqPerSec + } + } + child.kill() + } + + for (const fileGroup of runConfig.filesToTrack) { + const { name, globs } = fileGroup + const groupStats = {} + const curFiles = new Set() + + for (const pattern of globs) { + const results = await glob(pattern, { cwd: curDir, nodir: true }) + results.forEach((result) => curFiles.add(result)) + } + + for (const file of curFiles) { + const fileKey = path.basename(file) + const absPath = path.join(curDir, file) + try { + const fileInfo = await fs.stat(absPath) + groupStats[fileKey] = fileInfo.size + groupStats[`${fileKey} gzip`] = await gzipSize.file(absPath) + } catch (err) { + logger.error('Failed to get file stats', err) + } + } + stats[name] = groupStats + } + + for (const fileGroup of runConfig.filesToTrack) { + const { name } = fileGroup + orderedStats[name] = stats[name] + } + + if (stats[benchTitle]) { + orderedStats[benchTitle] = stats[benchTitle] + } + return orderedStats +} diff --git a/.github/actions/next-stats-action/src/run/get-dir-size.js b/.github/actions/next-stats-action/src/run/get-dir-size.js new file mode 100644 index 0000000000000..aa16e519382ef --- /dev/null +++ b/.github/actions/next-stats-action/src/run/get-dir-size.js @@ -0,0 +1,25 @@ +const path = require('path') +const fs = require('fs-extra') + +// getDirSize recursively gets size of all files in a directory +async function getDirSize(dir, ctx = { size: 0 }) { + let subDirs = await fs.readdir(dir) + subDirs = subDirs.map((d) => path.join(dir, d)) + + await Promise.all( + subDirs.map(async (curDir) => { + // we use dev builds so the size isn't helpful to track + // here as it's not reflective of full releases + if (curDir.includes('@next/swc')) return + + const fileStat = await fs.stat(curDir) + if (fileStat.isDirectory()) { + return getDirSize(curDir, ctx) + } + ctx.size += fileStat.size + }) + ) + return ctx.size +} + +module.exports = getDirSize diff --git a/.github/actions/next-stats-action/src/run/index.js b/.github/actions/next-stats-action/src/run/index.js new file mode 100644 index 0000000000000..2f0e053c35c78 --- /dev/null +++ b/.github/actions/next-stats-action/src/run/index.js @@ -0,0 +1,216 @@ +const path = require('path') +const fs = require('fs-extra') +const getPort = require('get-port') +const glob = require('../util/glob') +const exec = require('../util/exec') +const logger = require('../util/logger') +const getDirSize = require('./get-dir-size') +const collectStats = require('./collect-stats') +const collectDiffs = require('./collect-diffs') +const { statsAppDir, diffRepoDir, yarnEnvValues } = require('../constants') + +async function runConfigs( + configs = [], + { statsConfig, relativeStatsAppDir, mainRepoPkgPaths, diffRepoPkgPaths }, + diffing = false +) { + const results = [] + + for (const config of configs) { + logger(`Running config: ${config.title}${diffing ? ' (diff)' : ''}`) + + let mainRepoStats + let diffRepoStats + let diffs + + for (const pkgPaths of [mainRepoPkgPaths, diffRepoPkgPaths]) { + let curStats = { + General: { + buildDuration: null, + buildDurationCached: null, + nodeModulesSize: null, + }, + } + + // if stats-config is in root of project we're analyzing + // the whole project so copy from each repo + const curStatsAppPath = path.join(diffRepoDir, relativeStatsAppDir) + + // clean statsAppDir + await fs.remove(statsAppDir) + await fs.copy(curStatsAppPath, statsAppDir) + + logger(`Copying ${curStatsAppPath} ${statsAppDir}`) + + // apply config files + for (const configFile of config.configFiles || []) { + const filePath = path.join(statsAppDir, configFile.path) + await fs.writeFile(filePath, configFile.content, 'utf8') + } + + // links local builds of the packages and installs dependencies + await linkPkgs(statsAppDir, pkgPaths) + + if (!diffing) { + curStats.General.nodeModulesSize = await getDirSize( + path.join(statsAppDir, 'node_modules') + ) + } + + const buildStart = Date.now() + console.log( + await exec( + `cd ${statsAppDir} && ${statsConfig.appBuildCommand}`, + false, + { + env: yarnEnvValues, + } + ) + ) + curStats.General.buildDuration = Date.now() - buildStart + + // apply renames to get deterministic output names + for (const rename of config.renames) { + const results = await glob(rename.srcGlob, { cwd: statsAppDir }) + for (const result of results) { + let dest = rename.removeHash + ? result.replace(/(\.|-)[0-9a-f]{16}(\.|-)/g, '$1HASH$2') + : rename.dest + if (result === dest) continue + await fs.move( + path.join(statsAppDir, result), + path.join(statsAppDir, dest) + ) + } + } + + const collectedStats = await collectStats(config, statsConfig) + + for (const key of Object.keys(collectedStats)) { + curStats[key] = Object.assign({}, curStats[key], collectedStats[key]) + } + + const applyRenames = (renames, stats) => { + if (renames) { + for (const rename of renames) { + let { cur, prev } = rename + cur = path.basename(cur) + prev = path.basename(prev) + + Object.keys(stats).forEach((group) => { + if (stats[group][cur]) { + stats[group][prev] = stats[group][cur] + stats[group][prev + ' gzip'] = stats[group][cur + ' gzip'] + delete stats[group][cur] + delete stats[group][cur + ' gzip'] + } + }) + } + } + } + + if (mainRepoStats) { + diffRepoStats = curStats + + if (!diffing && config.diff !== false) { + for (const groupKey of Object.keys(curStats)) { + if (groupKey === 'General') continue + let changeDetected = config.diff === 'always' + + const curDiffs = await collectDiffs(config.filesToTrack) + changeDetected = changeDetected || Object.keys(curDiffs).length > 0 + + applyRenames(curDiffs._renames, diffRepoStats) + delete curDiffs._renames + + if (changeDetected) { + logger('Detected change, running diff') + diffs = await runConfigs( + [ + { + ...config, + configFiles: config.diffConfigFiles, + }, + ], + { + statsConfig, + mainRepoPkgPaths, + diffRepoPkgPaths, + relativeStatsAppDir, + }, + true + ) + delete diffs._renames + break + } + } + } + + if (diffing) { + // copy new files and get diff results + return collectDiffs(config.filesToTrack) + } + } else { + // set up diffing folder and copy initial files + await collectDiffs(config.filesToTrack, true) + + /* eslint-disable-next-line */ + mainRepoStats = curStats + } + + const secondBuildStart = Date.now() + console.log( + await exec( + `cd ${statsAppDir} && ${statsConfig.appBuildCommand}`, + false, + { + env: yarnEnvValues, + } + ) + ) + curStats.General.buildDurationCached = Date.now() - secondBuildStart + } + + logger(`Finished running: ${config.title}`) + + results.push({ + title: config.title, + mainRepoStats, + diffRepoStats, + diffs, + }) + } + + return results +} + +async function linkPkgs(pkgDir = '', pkgPaths) { + await fs.remove(path.join(pkgDir, 'node_modules')) + + const pkgJsonPath = path.join(pkgDir, 'package.json') + const pkgData = require(pkgJsonPath) + + if (!pkgData.dependencies && !pkgData.devDependencies) return + + for (const pkg of pkgPaths.keys()) { + const pkgPath = pkgPaths.get(pkg) + + if (pkgData.dependencies && pkgData.dependencies[pkg]) { + pkgData.dependencies[pkg] = pkgPath + } else if (pkgData.devDependencies && pkgData.devDependencies[pkg]) { + pkgData.devDependencies[pkg] = pkgPath + } + } + await fs.writeFile(pkgJsonPath, JSON.stringify(pkgData, null, 2), 'utf8') + + await fs.remove(yarnEnvValues.YARN_CACHE_FOLDER) + await exec( + `cd ${pkgDir} && pnpm install --strict-peer-dependencies=false`, + false, + { + env: yarnEnvValues, + } + ) +} + +module.exports = runConfigs diff --git a/.github/actions/next-stats-action/src/util/exec.js b/.github/actions/next-stats-action/src/util/exec.js new file mode 100644 index 0000000000000..689bd5b2952aa --- /dev/null +++ b/.github/actions/next-stats-action/src/util/exec.js @@ -0,0 +1,38 @@ +const logger = require('./logger') +const { promisify } = require('util') +const { exec: execOrig, spawn: spawnOrig } = require('child_process') + +const execP = promisify(execOrig) +const env = { + ...process.env, + GITHUB_TOKEN: '', + PR_STATS_COMMENT_TOKEN: '', +} + +function exec(command, noLog = false, opts = {}) { + if (!noLog) logger(`exec: ${command}`) + return execP(command, { + timeout: 180 * 1000, + ...opts, + env: { ...env, ...opts.env }, + }) +} + +exec.spawn = function spawn(command = '', opts = {}) { + logger(`spawn: ${command}`) + const child = spawnOrig('/bin/bash', ['-c', command], { + ...opts, + env: { + ...env, + ...opts.env, + }, + stdio: opts.stdio || 'inherit', + }) + + child.on('exit', (code, signal) => { + logger(`spawn exit (${code}, ${signal}): ${command}`) + }) + return child +} + +module.exports = exec diff --git a/.github/actions/next-stats-action/src/util/glob.js b/.github/actions/next-stats-action/src/util/glob.js new file mode 100644 index 0000000000000..297e429897bc0 --- /dev/null +++ b/.github/actions/next-stats-action/src/util/glob.js @@ -0,0 +1,3 @@ +const globOrig = require('glob') +const { promisify } = require('util') +module.exports = promisify(globOrig) diff --git a/.github/actions/next-stats-action/src/util/logger.js b/.github/actions/next-stats-action/src/util/logger.js new file mode 100644 index 0000000000000..695d1ec68d428 --- /dev/null +++ b/.github/actions/next-stats-action/src/util/logger.js @@ -0,0 +1,17 @@ +function logger(...args) { + console.log(...args) +} + +logger.json = (obj) => { + logger('\n', JSON.stringify(obj, null, 2), '\n') +} + +logger.error = (...args) => { + console.error(...args) +} + +logger.warn = (...args) => { + console.warn(...args) +} + +module.exports = logger diff --git a/.github/labeler.json b/.github/labeler.json new file mode 100644 index 0000000000000..ead33c713c82b --- /dev/null +++ b/.github/labeler.json @@ -0,0 +1,42 @@ +{ + "labels": { + "area: examples": ["examples/**"], + "area: documentation": ["docs/**", "errors/**"], + "area: create-next-app": ["packages/create-next-app/**"], + "type: next": [ + "packages/next/**", + "packages/react-dev-overlay/**", + "packages/react-refresh-utils/**", + "packages/next-codemod/**", + "packages/eslint-plugin-next/**", + "packages/eslint-config-next/**", + "packages/next-env/**", + "packages/next-swc/**" + ], + "created-by: Chrome Aurora": [ + { "type": "user", "pattern": "spanicker" }, + { "type": "user", "pattern": "housseindjirdeh" }, + { "type": "user", "pattern": "devknoll" }, + { "type": "user", "pattern": "janicklas-ralph" }, + { "type": "user", "pattern": "atcastle" }, + { "type": "user", "pattern": "kyliau" }, + { "type": "user", "pattern": "kara" } + ], + "created-by: Next.js team": [ + { "type": "user", "pattern": "ijjk" }, + { "type": "user", "pattern": "padmaia" }, + { "type": "user", "pattern": "huozhi" }, + { "type": "user", "pattern": "shuding" }, + { "type": "user", "pattern": "sokra" }, + { "type": "user", "pattern": "styfle" }, + { "type": "user", "pattern": "leerob" }, + { "type": "user", "pattern": "kdy1" }, + { "type": "user", "pattern": "timneutkens" } + ], + "created-by: Next.js docs team": [ + { "type": "user", "pattern": "MaedahBatool" }, + { "type": "user", "pattern": "molebox" }, + { "type": "user", "pattern": "ismaelrumzan" } + ] + } +} diff --git a/.github/lock.yml b/.github/lock.yml deleted file mode 100644 index a836a1e1e94aa..0000000000000 --- a/.github/lock.yml +++ /dev/null @@ -1,6 +0,0 @@ -# Configuration for lock-threads - https://github.com/dessant/lock-threads - -# Number of days of inactivity before a closed issue or pull request is locked -daysUntilLock: 365 -# Comment to post before locking. Set to `false` to disable -lockComment: false diff --git a/.github/main.workflow b/.github/main.workflow deleted file mode 100644 index 77d003f798ac4..0000000000000 --- a/.github/main.workflow +++ /dev/null @@ -1,14 +0,0 @@ -workflow "Generate pull request stats" { - on = "pull_request" - resolves = ["PR Stats"] -} - -workflow "Generate release stats" { - on = "release" - resolves = ["PR Stats"] -} - -action "PR Stats" { - uses = "zeit/next-stats-action@master" - secrets = ["GITHUB_TOKEN"] -} diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000000000..960be6d17b242 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,25 @@ + + +## Bug + +- [ ] Related issues linked using `fixes #number` +- [ ] Integration tests added +- [ ] Errors have a helpful link attached, see `contributing.md` + +## Feature + +- [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. +- [ ] Related issues linked using `fixes #number` +- [ ] Integration tests added +- [ ] Documentation added +- [ ] Telemetry added. In case of a feature if it's used or not. +- [ ] Errors have a helpful link attached, see `contributing.md` + +## Documentation / Examples + +- [ ] Make sure the linting passes by running `pnpm build && pnpm lint` +- [ ] The "examples guidelines" are followed from [our contributing doc](https://github.com/vercel/next.js/blob/canary/contributing/examples/adding-examples.md) diff --git a/.github/workflows/build_test_deploy.yml b/.github/workflows/build_test_deploy.yml new file mode 100644 index 0000000000000..e9318a40d60d2 --- /dev/null +++ b/.github/workflows/build_test_deploy.yml @@ -0,0 +1,1607 @@ +on: + push: + branches: [canary] + pull_request: + types: [opened, synchronize] + +name: Build, test, and deploy + +env: + NAPI_CLI_VERSION: 2.12.0 + TURBO_VERSION: 1.3.2-canary.1 + RUST_TOOLCHAIN: nightly-2022-11-04 + PNPM_VERSION: 7.3.0 + +jobs: + check-examples: + name: Check examples + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Install moreutils + run: sudo apt install moreutils + - name: Check examples + run: ./scripts/check-examples.sh + + build: + runs-on: ubuntu-latest + env: + NEXT_TELEMETRY_DISABLED: 1 + # we build a dev binary for use in CI so skip downloading + # canary next-swc binaries in the monorepo + NEXT_SKIP_NATIVE_POSTINSTALL: 1 + outputs: + docsChange: ${{ steps.docs-change.outputs.DOCS_CHANGE }} + isRelease: ${{ steps.check-release.outputs.IS_RELEASE }} + weekNum: ${{ steps.get-week.outputs.WEEK }} + steps: + - name: Setup node + uses: actions/setup-node@v3 + if: ${{ steps.docs-change.outputs.docsChange == 'nope' }} + with: + node-version: 16 + check-latest: true + + - uses: actions/checkout@v3 + with: + fetch-depth: 25 + + # https://github.com/actions/virtual-environments/issues/1187 + - name: tune linux network + run: sudo ethtool -K eth0 tx off rx off + + - name: Check non-docs only change + run: echo "::set-output name=DOCS_CHANGE::$(node scripts/run-for-change.js --not --type docs --exec echo 'nope')" + id: docs-change + + - run: echo ${{steps.docs-change.outputs.DOCS_CHANGE}} + + - run: npm i -g pnpm@${PNPM_VERSION} + + - id: get-store-path + run: echo ::set-output name=STORE_PATH::$(pnpm store path) + + - uses: actions/cache@v3 + id: cache-pnpm-store + with: + path: ${{ steps.get-store-path.outputs.STORE_PATH }} + key: pnpm-store-${{ hashFiles('pnpm-lock.yaml') }} + restore-keys: | + pnpm-store- + pnpm-store-${{ hashFiles('pnpm-lock.yaml') }} + + - run: pnpm install + - run: pnpm run build + - run: node run-tests.js --timings --write-timings -g 1/1 + - run: node ./scripts/fetch-tags.mjs ${{ github.sha }} + + - id: check-release + run: | + if [[ $(git describe --exact-match 2> /dev/null || :) = v* ]]; + then + echo "::set-output name=IS_RELEASE::true" + else + echo "::set-output name=IS_RELEASE::false" + fi + # We use week in the turbo cache key to keep the cache from infinitely growing + - id: get-week + run: echo ::set-output name=WEEK::$(date +%U) + + - uses: actions/cache@v3 + id: cache-build + with: + path: ./* + key: ${{ github.sha }}-${{ github.run_number }} + + lint: + runs-on: ubuntu-latest + needs: build + steps: + - name: Setup node + uses: actions/setup-node@v3 + with: + node-version: 16 + check-latest: true + + - run: npm i -g pnpm@${PNPM_VERSION} + + - uses: actions/cache@v3 + id: restore-build + with: + path: ./* + key: ${{ github.sha }}-${{ github.run_number }} + - run: ./scripts/check-manifests.js + - run: pnpm lint + + rust-check: + runs-on: ubuntu-latest + needs: build + steps: + - uses: actions/cache@v3 + id: restore-build + if: ${{needs.build.outputs.docsChange == 'nope'}} + with: + path: ./* + key: ${{ github.sha }}-${{ github.run_number }} + + - run: echo "::set-output name=SWC_CHANGE::$(node scripts/run-for-change.js --type next-swc --exec echo 'yup')" + id: swc-change + + - run: echo ${{ steps.swc-change.outputs.SWC_CHANGE }} + + - name: Install + uses: actions-rs/toolchain@v1 + if: ${{ steps.swc-change.outputs.SWC_CHANGE == 'yup' }} + with: + profile: minimal + toolchain: ${{ env.RUST_TOOLCHAIN }} + components: rustfmt, clippy + + - name: Cache cargo registry + uses: actions/cache@v3 + if: ${{ steps.swc-change.outputs.SWC_CHANGE == 'yup' }} + with: + path: ~/.cargo/registry + key: stable-ubuntu-clippy-cargo-registry + + - name: Cache cargo index + uses: actions/cache@v3 + if: ${{ steps.swc-change.outputs.SWC_CHANGE == 'yup' }} + with: + path: ~/.cargo/git + key: stable-ubuntu-clippy-cargo-index + + - name: Check + if: ${{ steps.swc-change.outputs.SWC_CHANGE == 'yup' }} + run: | + cargo fmt -- --check + cargo clippy --all -- -D warnings + working-directory: packages/next-swc + + checkPrecompiled: + name: Check Pre-compiled + runs-on: ubuntu-latest + needs: build + env: + NEXT_TELEMETRY_DISABLED: 1 + steps: + - name: Setup node + uses: actions/setup-node@v3 + if: ${{needs.build.outputs.docsChange == 'nope'}} + with: + node-version: 16 + check-latest: true + + # https://github.com/actions/virtual-environments/issues/1187 + - name: tune linux network + if: ${{needs.build.outputs.docsChange == 'nope'}} + run: sudo ethtool -K eth0 tx off rx off + + - uses: actions/checkout@v3 + if: ${{needs.build.outputs.docsChange == 'nope'}} + + - run: mv .git .git-bak + if: ${{needs.build.outputs.docsChange == 'nope'}} + + - uses: actions/cache@v3 + if: ${{needs.build.outputs.docsChange == 'nope'}} + id: restore-build + with: + path: ./* + key: ${{ github.sha }}-${{ github.run_number }} + + - run: npm i -g pnpm@${PNPM_VERSION} + + - run: rm -rf .git && mv .git-bak .git + if: ${{needs.build.outputs.docsChange == 'nope'}} + + - run: ./scripts/check-pre-compiled.sh + if: ${{needs.build.outputs.docsChange == 'nope'}} + + - uses: EndBug/add-and-commit@v7 + if: ${{ failure() }} + with: + add: 'packages/next/compiled packages/next/bundles --force' + message: '⚙ Update compiled files' + + testUnit: + name: Test Unit + runs-on: ubuntu-latest + needs: [build, build-native-test] + timeout-minutes: 10 + env: + NEXT_TELEMETRY_DISABLED: 1 + NEXT_TEST_JOB: 1 + steps: + - name: Setup node + uses: actions/setup-node@v3 + if: ${{needs.build.outputs.docsChange == 'nope'}} + with: + node-version: 16 + check-latest: true + + - uses: actions/cache@v3 + if: ${{needs.build.outputs.docsChange == 'nope'}} + id: restore-build + with: + path: ./* + key: ${{ github.sha }}-${{ github.run_number }} + + - uses: actions/download-artifact@v3 + if: ${{needs.build.outputs.docsChange == 'nope'}} + with: + name: next-swc-test-binary + path: packages/next-swc/native + + - run: npm i -g pnpm@${PNPM_VERSION} + if: ${{needs.build.outputs.docsChange == 'nope'}} + + - run: node run-tests.js --type unit + if: ${{needs.build.outputs.docsChange == 'nope'}} + + testDev: + name: Test Development + runs-on: ubuntu-latest + needs: [build, build-native-test] + timeout-minutes: 35 + env: + NEXT_TELEMETRY_DISABLED: 1 + NEXT_TEST_JOB: 1 + strategy: + fail-fast: false + matrix: + node: [16, 18] + group: [1, 2] + steps: + - name: Setup node + uses: actions/setup-node@v3 + if: ${{needs.build.outputs.docsChange == 'nope'}} + with: + node-version: ${{ matrix.node }} + check-latest: true + + - run: echo ${{needs.build.outputs.docsChange}} + + # https://github.com/actions/virtual-environments/issues/1187 + - name: tune linux network + run: sudo ethtool -K eth0 tx off rx off + + - uses: actions/cache@v3 + if: ${{needs.build.outputs.docsChange == 'nope'}} + id: restore-build + with: + path: ./* + key: ${{ github.sha }}-${{ github.run_number }} + + - uses: actions/download-artifact@v3 + if: ${{needs.build.outputs.docsChange == 'nope'}} + with: + name: next-swc-test-binary + path: packages/next-swc/native + + - run: npm i -g pnpm@${PNPM_VERSION} + if: ${{needs.build.outputs.docsChange == 'nope'}} + + - run: npm i -g playwright-chromium@1.22.2 && npx playwright install-deps + timeout-minutes: 10 + if: ${{needs.build.outputs.docsChange == 'nope'}} + + - run: npx @replayio/playwright install chromium + if: ${{needs.build.outputs.docsChange == 'nope'}} + + - run: node run-tests.js --type development --timings -g ${{ matrix.group }}/2 + name: Run test/development + if: ${{needs.build.outputs.docsChange == 'nope'}} + env: + RECORD_REPLAY_METADATA_TEST_RUN_TITLE: testDev / Group ${{ matrix.group }} + RECORD_ALL_CONTENT: 1 + RECORD_REPLAY: 1 + RECORD_REPLAY_TEST_METRICS: 1 + RECORD_REPLAY_WEBHOOK_URL: ${{ secrets.RECORD_REPLAY_WEBHOOK_URL }} + + - uses: replayio/action-upload@v0.4.5 + if: always() + with: + api-key: rwk_iKsQnEoQwKd31WAJxgN9ARPFuAlyXlVrDH4uhYpRnti + public: true + filter: ${{ 'function($v) { $v.metadata.test.result = "failed" }' }} + + - name: Upload test trace + if: always() + uses: actions/upload-artifact@v3 + with: + name: test-trace + if-no-files-found: ignore + retention-days: 2 + path: | + test/traces + + testDevE2E: + name: Test Development (E2E) + runs-on: ubuntu-latest + needs: [build, build-native-test] + timeout-minutes: 35 + env: + NEXT_TELEMETRY_DISABLED: 1 + NEXT_TEST_JOB: 1 + TEST_TIMINGS_TOKEN: ${{ secrets.TEST_TIMINGS_TOKEN }} + strategy: + fail-fast: false + matrix: + node: [16, 18] + group: [1, 2, 3] + steps: + - name: Setup node + uses: actions/setup-node@v3 + if: ${{needs.build.outputs.docsChange == 'nope'}} + with: + node-version: ${{ matrix.node }} + check-latest: true + + - run: echo ${{needs.build.outputs.docsChange}} + + # https://github.com/actions/virtual-environments/issues/1187 + - name: tune linux network + run: sudo ethtool -K eth0 tx off rx off + + - uses: actions/cache@v3 + if: ${{needs.build.outputs.docsChange == 'nope'}} + id: restore-build + with: + path: ./* + key: ${{ github.sha }}-${{ github.run_number }} + + - uses: actions/download-artifact@v3 + if: ${{needs.build.outputs.docsChange == 'nope'}} + with: + name: next-swc-test-binary + path: packages/next-swc/native + + - run: npm i -g pnpm@${PNPM_VERSION} + if: ${{needs.build.outputs.docsChange == 'nope'}} + + - run: npm i -g playwright-chromium@1.22.2 && npx playwright install-deps + timeout-minutes: 10 + if: ${{needs.build.outputs.docsChange == 'nope'}} + + - run: npx @replayio/playwright install chromium + if: ${{needs.build.outputs.docsChange == 'nope'}} + + - run: node run-tests.js --type e2e --timings -g ${{ matrix.group }}/3 + name: Run test/e2e (dev) + if: ${{needs.build.outputs.docsChange == 'nope'}} + env: + RECORD_REPLAY_METADATA_TEST_RUN_TITLE: testDevE2E / Group ${{ matrix.group }} / Node ${{ matrix.node }} + RECORD_ALL_CONTENT: 1 + RECORD_REPLAY: 1 + NEXT_TEST_MODE: dev + RECORD_REPLAY_TEST_METRICS: 1 + RECORD_REPLAY_WEBHOOK_URL: ${{ secrets.RECORD_REPLAY_WEBHOOK_URL }} + + - uses: replayio/action-upload@v0.4.5 + if: always() + with: + api-key: rwk_iKsQnEoQwKd31WAJxgN9ARPFuAlyXlVrDH4uhYpRnti + public: true + filter: ${{ 'function($v) { $v.metadata.test.result = "failed" }' }} + + - name: Upload test trace + if: always() + uses: actions/upload-artifact@v3 + with: + name: test-trace + if-no-files-found: ignore + retention-days: 2 + path: | + test/traces + + testProd: + name: Test Production + runs-on: ubuntu-latest + needs: [build, build-native-test] + timeout-minutes: 15 + env: + NEXT_TELEMETRY_DISABLED: 1 + NEXT_TEST_JOB: 1 + strategy: + fail-fast: false + matrix: + node: [16, 18] + group: [1, 2] + steps: + - name: Setup node + uses: actions/setup-node@v3 + if: ${{needs.build.outputs.docsChange == 'nope'}} + with: + node-version: ${{ matrix.node }} + check-latest: true + + - run: echo ${{needs.build.outputs.docsChange}} + + # https://github.com/actions/virtual-environments/issues/1187 + - name: tune linux network + run: sudo ethtool -K eth0 tx off rx off + + - uses: actions/cache@v3 + if: ${{needs.build.outputs.docsChange == 'nope'}} + id: restore-build + with: + path: ./* + key: ${{ github.sha }}-${{ github.run_number }} + + - uses: actions/download-artifact@v3 + if: ${{needs.build.outputs.docsChange == 'nope'}} + with: + name: next-swc-test-binary + path: packages/next-swc/native + + - run: npm i -g pnpm@${PNPM_VERSION} + if: ${{needs.build.outputs.docsChange == 'nope'}} + + - run: npm i -g playwright-chromium@1.22.2 && npx playwright install-deps + timeout-minutes: 10 + if: ${{needs.build.outputs.docsChange == 'nope'}} + + - run: npx @replayio/playwright install chromium + if: ${{needs.build.outputs.docsChange == 'nope'}} + + - run: node run-tests.js --type production --timings -g ${{ matrix.group }}/2 + name: Run test/production + if: ${{needs.build.outputs.docsChange == 'nope'}} + env: + RECORD_REPLAY_METADATA_TEST_RUN_TITLE: testProd / Group ${{ matrix.group }} / Node ${{ matrix.node }} + RECORD_ALL_CONTENT: 1 + RECORD_REPLAY: 1 + RECORD_REPLAY_TEST_METRICS: 1 + RECORD_REPLAY_WEBHOOK_URL: ${{ secrets.RECORD_REPLAY_WEBHOOK_URL }} + + - uses: replayio/action-upload@v0.4.5 + if: always() + with: + api-key: rwk_iKsQnEoQwKd31WAJxgN9ARPFuAlyXlVrDH4uhYpRnti + public: true + filter: ${{ 'function($v) { $v.metadata.test.result = "failed" }' }} + + testProdE2E: + name: Test Production (E2E) + runs-on: ubuntu-latest + needs: [build, build-native-test] + timeout-minutes: 35 + env: + NEXT_TELEMETRY_DISABLED: 1 + NEXT_TEST_JOB: 1 + TEST_TIMINGS_TOKEN: ${{ secrets.TEST_TIMINGS_TOKEN }} + strategy: + fail-fast: false + matrix: + node: [16, 18] + group: [1, 2, 3] + steps: + - name: Setup node + uses: actions/setup-node@v3 + if: ${{needs.build.outputs.docsChange == 'nope'}} + with: + node-version: ${{ matrix.node }} + check-latest: true + + - run: echo ${{needs.build.outputs.docsChange}} + + # https://github.com/actions/virtual-environments/issues/1187 + - name: tune linux network + run: sudo ethtool -K eth0 tx off rx off + + - uses: actions/cache@v3 + if: ${{needs.build.outputs.docsChange == 'nope'}} + id: restore-build + with: + path: ./* + key: ${{ github.sha }}-${{ github.run_number }} + + - uses: actions/download-artifact@v3 + if: ${{needs.build.outputs.docsChange == 'nope'}} + with: + name: next-swc-test-binary + path: packages/next-swc/native + + - run: npm i -g pnpm@${PNPM_VERSION} + if: ${{needs.build.outputs.docsChange == 'nope'}} + + - run: npm i -g playwright-chromium@1.22.2 && npx playwright install-deps + timeout-minutes: 10 + if: ${{needs.build.outputs.docsChange == 'nope'}} + + - run: npx @replayio/playwright install chromium + if: ${{needs.build.outputs.docsChange == 'nope'}} + + - run: node run-tests.js --type e2e --timings -g ${{ matrix.group }}/3 + name: Run test/e2e (production) + if: ${{needs.build.outputs.docsChange == 'nope'}} + env: + RECORD_REPLAY_METADATA_TEST_RUN_TITLE: testProdE2E / Group ${{ matrix.group }} / Node ${{ matrix.node }} + RECORD_ALL_CONTENT: 1 + RECORD_REPLAY: 1 + NEXT_TEST_MODE: start + RECORD_REPLAY_TEST_METRICS: 1 + RECORD_REPLAY_WEBHOOK_URL: ${{ secrets.RECORD_REPLAY_WEBHOOK_URL }} + + - uses: replayio/action-upload@v0.4.5 + if: always() + with: + api-key: rwk_iKsQnEoQwKd31WAJxgN9ARPFuAlyXlVrDH4uhYpRnti + public: true + filter: ${{ 'function($v) { $v.metadata.test.result = "failed" }' }} + + testIntegration: + name: Test Integration + runs-on: ubuntu-latest + needs: [build, build-native-test] + timeout-minutes: 35 + env: + NEXT_TELEMETRY_DISABLED: 1 + NEXT_TEST_JOB: 1 + TEST_TIMINGS_TOKEN: ${{ secrets.TEST_TIMINGS_TOKEN }} + strategy: + fail-fast: false + matrix: + group: + [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + ] + steps: + - name: Setup node + uses: actions/setup-node@v3 + if: ${{needs.build.outputs.docsChange == 'nope'}} + with: + node-version: 16 + check-latest: true + + - run: echo ${{needs.build.outputs.docsChange}} + + # https://github.com/actions/virtual-environments/issues/1187 + - name: tune linux network + run: sudo ethtool -K eth0 tx off rx off + + - uses: actions/cache@v3 + if: ${{needs.build.outputs.docsChange == 'nope'}} + id: restore-build + with: + path: ./* + key: ${{ github.sha }}-${{ github.run_number }} + + - uses: actions/download-artifact@v3 + if: ${{needs.build.outputs.docsChange == 'nope'}} + with: + name: next-swc-test-binary + path: packages/next-swc/native + + - run: npm i -g pnpm@${PNPM_VERSION} + if: ${{needs.build.outputs.docsChange == 'nope'}} + + - run: npm i -g playwright-chromium@1.22.2 && npx playwright install-deps + timeout-minutes: 10 + if: ${{needs.build.outputs.docsChange == 'nope'}} + + - run: npx @replayio/playwright install chromium + if: ${{needs.build.outputs.docsChange == 'nope'}} + + - run: xvfb-run node run-tests.js --timings -g ${{ matrix.group }}/20 + if: ${{needs.build.outputs.docsChange == 'nope'}} + env: + RECORD_REPLAY_METADATA_TEST_RUN_TITLE: testIntegration / Group ${{ matrix.group }} + RECORD_ALL_CONTENT: 1 + RECORD_REPLAY: 1 + RECORD_REPLAY_TEST_METRICS: 1 + RECORD_REPLAY_WEBHOOK_URL: ${{ secrets.RECORD_REPLAY_WEBHOOK_URL }} + + - uses: replayio/action-upload@v0.4.5 + if: always() + with: + api-key: rwk_iKsQnEoQwKd31WAJxgN9ARPFuAlyXlVrDH4uhYpRnti + public: true + filter: ${{ 'function($v) { $v.metadata.test.result = "failed" }' }} + + - name: Upload test trace + if: always() + uses: actions/upload-artifact@v3 + with: + name: test-trace + if-no-files-found: ignore + retention-days: 2 + path: | + test/traces + + testElectron: + name: Test Electron + runs-on: ubuntu-latest + needs: [build, build-native-test] + timeout-minutes: 10 + env: + NEXT_TELEMETRY_DISABLED: 1 + NEXT_TEST_JOB: 1 + TEST_ELECTRON: 1 + steps: + - name: Setup node + uses: actions/setup-node@v3 + if: ${{needs.build.outputs.docsChange == 'nope'}} + with: + node-version: 16 + check-latest: true + + - uses: actions/cache@v3 + if: ${{needs.build.outputs.docsChange == 'nope'}} + id: restore-build + with: + path: ./* + key: ${{ github.sha }}-${{ github.run_number }} + + - uses: actions/download-artifact@v3 + if: ${{needs.build.outputs.docsChange == 'nope'}} + with: + name: next-swc-test-binary + path: packages/next-swc/native + + - run: npm i -g pnpm@${PNPM_VERSION} + if: ${{needs.build.outputs.docsChange == 'nope'}} + + - run: cd test/integration/with-electron/app && yarn install + if: ${{needs.build.outputs.docsChange == 'nope'}} + + - run: xvfb-run node run-tests.js test/integration/with-electron/test/index.test.js + if: ${{needs.build.outputs.docsChange == 'nope'}} + + testsPass: + name: thank you, next + runs-on: ubuntu-latest + needs: + [ + lint, + check-examples, + test-native, + checkPrecompiled, + testIntegration, + testUnit, + testDev, + testProd, + ] + steps: + - run: exit 0 + + testFirefox: + name: Test Firefox (production) + runs-on: ubuntu-latest + needs: [build, build-native-test] + timeout-minutes: 10 + env: + BROWSER_NAME: 'firefox' + NEXT_TELEMETRY_DISABLED: 1 + steps: + - name: Setup node + uses: actions/setup-node@v3 + if: ${{needs.build.outputs.docsChange == 'nope'}} + with: + node-version: 16 + check-latest: true + + - uses: actions/cache@v3 + if: ${{needs.build.outputs.docsChange == 'nope'}} + id: restore-build + with: + path: ./* + key: ${{ github.sha }}-${{ github.run_number }} + - uses: actions/download-artifact@v3 + if: ${{needs.build.outputs.docsChange == 'nope'}} + with: + name: next-swc-test-binary + path: packages/next-swc/native + - run: npx playwright install-deps && npx playwright install firefox + if: ${{needs.build.outputs.docsChange == 'nope'}} + - run: node run-tests.js test/integration/production/test/index.test.js + if: ${{needs.build.outputs.docsChange == 'nope'}} + # test rsc hydration on firefox due to limited support of TransformStream api + - run: npm i -g pnpm@${PNPM_VERSION} + if: ${{needs.build.outputs.docsChange == 'nope'}} + + testSafari: + name: Test Safari (production) + runs-on: ubuntu-latest + needs: [build, build-native-test] + timeout-minutes: 15 + env: + BROWSER_NAME: 'safari' + NEXT_TEST_MODE: 'start' + NEXT_TELEMETRY_DISABLED: 1 + steps: + - name: Setup node + uses: actions/setup-node@v3 + if: ${{needs.build.outputs.docsChange == 'nope'}} + with: + node-version: 16 + check-latest: true + + # https://github.com/actions/virtual-environments/issues/1187 + - name: tune linux network + run: sudo ethtool -K eth0 tx off rx off + + - uses: actions/cache@v3 + if: ${{needs.build.outputs.docsChange == 'nope'}} + id: restore-build + with: + path: ./* + key: ${{ github.sha }}-${{ github.run_number }} + + - uses: actions/download-artifact@v3 + if: ${{needs.build.outputs.docsChange == 'nope'}} + with: + name: next-swc-test-binary + path: packages/next-swc/native + + - run: npm i -g pnpm@${PNPM_VERSION} + if: ${{needs.build.outputs.docsChange == 'nope'}} + + - run: npx playwright install-deps && npx playwright install webkit + if: ${{needs.build.outputs.docsChange == 'nope'}} + + - run: node run-tests.js -c 1 test/integration/production/test/index.test.js test/e2e/basepath.test.ts + if: ${{needs.build.outputs.docsChange == 'nope'}} + + - run: DEVICE_NAME='iPhone XR' node run-tests.js -c 1 test/production/prerender-prefetch/index.test.ts + if: ${{needs.build.outputs.docsChange == 'nope'}} + + testSafariOld: + name: Test Safari 10.1 (nav) + runs-on: ubuntu-latest + needs: [build, build-native-test] + timeout-minutes: 10 + env: + BROWSERSTACK: true + LEGACY_SAFARI: true + BROWSER_NAME: 'safari' + NEXT_TELEMETRY_DISABLED: 1 + SKIP_LOCAL_SELENIUM_SERVER: true + BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }} + BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }} + steps: + - name: Setup node + uses: actions/setup-node@v3 + if: ${{needs.build.outputs.docsChange == 'nope'}} + with: + node-version: 16 + check-latest: true + + # https://github.com/actions/virtual-environments/issues/1187 + - name: tune linux network + run: sudo ethtool -K eth0 tx off rx off + + - uses: actions/cache@v3 + if: ${{needs.build.outputs.docsChange == 'nope'}} + id: restore-build + with: + path: ./* + key: ${{ github.sha }}-${{ github.run_number }} + + - uses: actions/download-artifact@v3 + if: ${{needs.build.outputs.docsChange == 'nope'}} + with: + name: next-swc-test-binary + path: packages/next-swc/native + + - run: npm i -g pnpm@${PNPM_VERSION} + if: ${{needs.build.outputs.docsChange == 'nope'}} + + - run: '[[ -z "$BROWSERSTACK_ACCESS_KEY" ]] && echo "Skipping for PR" || npm i -g browserstack-local@1.4.0' + if: ${{needs.build.outputs.docsChange == 'nope'}} + + - run: '[[ -z "$BROWSERSTACK_ACCESS_KEY" ]] && echo "Skipping for PR" || node run-tests.js test/integration/production-nav/test/index.test.js' + if: ${{needs.build.outputs.docsChange == 'nope'}} + + testFirefoxNode18: + name: Test Firefox Node.js 18 + runs-on: ubuntu-latest + needs: [build, testFirefox, build-native-test] + timeout-minutes: 10 + env: + BROWSER_NAME: 'firefox' + NEXT_TELEMETRY_DISABLED: 1 + steps: + - name: Setup node + uses: actions/setup-node@v3 + if: ${{needs.build.outputs.docsChange == 'nope'}} + with: + node-version: 18 + check-latest: true + - uses: actions/cache@v3 + if: ${{needs.build.outputs.docsChange == 'nope'}} + id: restore-build + with: + path: ./* + key: ${{ github.sha }}-${{ github.run_number }} + - uses: actions/download-artifact@v3 + if: ${{needs.build.outputs.docsChange == 'nope'}} + with: + name: next-swc-test-binary + path: packages/next-swc/native + - run: npm i -g pnpm@${PNPM_VERSION} + if: ${{needs.build.outputs.docsChange == 'nope'}} + - run: npx playwright install-deps && npx playwright install firefox + if: ${{needs.build.outputs.docsChange == 'nope'}} + - run: node run-tests.js test/integration/production/test/index.test.js + if: ${{needs.build.outputs.docsChange == 'nope'}} + + publishRelease: + if: ${{ needs.build.outputs.isRelease == 'true' }} + name: Potentially publish release + runs-on: ubuntu-latest + needs: + - build + - build-wasm + - build-native + - build-native-freebsd + env: + NPM_TOKEN: ${{ secrets.NPM_TOKEN_ELEVATED }} + steps: + - name: Setup node + uses: actions/setup-node@v3 + if: ${{needs.build.outputs.docsChange == 'nope'}} + with: + node-version: 16 + check-latest: true + + # https://github.com/actions/virtual-environments/issues/1187 + - name: tune linux network + run: sudo ethtool -K eth0 tx off rx off + + - uses: actions/cache@v3 + id: restore-build + with: + path: ./* + key: ${{ github.sha }}-${{ github.run_number }} + + - uses: actions/download-artifact@v3 + with: + name: next-swc-binaries + path: packages/next-swc/native + + - uses: actions/download-artifact@v3 + with: + name: wasm-binaries + path: packages/next-swc/crates/wasm + + - run: npm i -g pnpm@${PNPM_VERSION} + - run: echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ~/.npmrc + - run: ./scripts/publish-native.js $GITHUB_REF + - run: ./scripts/publish-release.js + + testDeployE2E: + name: E2E (deploy) + runs-on: ubuntu-latest + needs: [publishRelease, build, build-native-test] + env: + NEXT_TELEMETRY_DISABLED: 1 + NEXT_TEST_JOB: 1 + VERCEL_TEST_TOKEN: ${{ secrets.VERCEL_TEST_TOKEN }} + VERCEL_TEST_TEAM: 'vtest314-next-e2e-tests' + NEXT_TEST_MODE: deploy + steps: + - name: Setup node + uses: actions/setup-node@v3 + with: + node-version: 16 + check-latest: true + + - uses: actions/cache@v3 + id: restore-build + with: + path: ./* + key: ${{ github.sha }}-${{ github.run_number }} + + - uses: actions/download-artifact@v3 + if: ${{needs.build.outputs.docsChange == 'nope'}} + with: + name: next-swc-test-binary + path: packages/next-swc/native + + - run: npm i -g pnpm@${PNPM_VERSION} + name: Install pnpm + + - run: npm i -g playwright-chromium@1.22.2 && npx playwright install-deps + timeout-minutes: 10 + name: Install playwright dependencies + + - run: RESET_VC_PROJECT=true node scripts/reset-vercel-project.mjs + name: Reset test project + + - run: node run-tests.js --type e2e + name: Run test/e2e (deploy) + + - name: Upload test trace + if: always() + uses: actions/upload-artifact@v3 + with: + name: test-trace + if-no-files-found: ignore + retention-days: 2 + path: | + test/traces + + releaseStats: + name: Release Stats + runs-on: ubuntu-latest + needs: [publishRelease, build-native-test] + steps: + - name: Setup node + uses: actions/setup-node@v3 + with: + node-version: 16 + check-latest: true + + - uses: actions/cache@v3 + id: restore-build + with: + path: ./* + key: ${{ github.sha }}-${{ github.run_number }} + + - uses: actions/download-artifact@v3 + with: + name: next-swc-test-binary + path: packages/next-swc/native + + - run: cp -r packages/next-swc/native .github/actions/next-stats-action/native + + - run: ./scripts/release-stats.sh + - uses: ./.github/actions/next-stats-action + env: + PR_STATS_COMMENT_TOKEN: ${{ secrets.PR_STATS_COMMENT_TOKEN }} + + build-native-test: + name: Build native binary for tests and metrics + runs-on: ubuntu-latest + steps: + # https://github.com/actions/virtual-environments/issues/1187 + - name: tune linux network + run: sudo ethtool -K eth0 tx off rx off + + - uses: actions/checkout@v3 + with: + fetch-depth: 25 + + - run: echo "::set-output name=DOCS_CHANGE::$(node scripts/run-for-change.js --not --type docs --exec echo 'nope')" + id: docs-change + + - name: Setup node + uses: actions/setup-node@v3 + if: ${{ steps.docs-change.outputs.DOCS_CHANGE == 'nope' }} + with: + node-version: 16 + check-latest: true + + - name: Install + uses: actions-rs/toolchain@v1 + if: ${{ steps.docs-change.outputs.DOCS_CHANGE == 'nope' }} + with: + profile: minimal + toolchain: ${{ env.RUST_TOOLCHAIN }} + + - name: Cache cargo registry + uses: actions/cache@v3 + if: ${{ steps.docs-change.outputs.DOCS_CHANGE == 'nope' }} + with: + path: ~/.cargo/registry + key: stable-ubuntu-latest-cargo-registry + + - name: Cache cargo index + uses: actions/cache@v3 + if: ${{ steps.docs-change.outputs.DOCS_CHANGE == 'nope' }} + with: + path: ~/.cargo/git + key: stable-ubuntu-latest-cargo-index + + # We use week in the turbo cache key to keep the cache from infinitely growing + - id: get-week + run: echo ::set-output name=WEEK::$(date +%U) + + - name: Turbo Cache + id: turbo-cache + uses: actions/cache@v3 + if: ${{ steps.docs-change.outputs.DOCS_CHANGE == 'nope' }} + with: + path: .turbo + key: turbo-${{ github.job }}-${{ github.ref_name }}-${{ steps.get-week.outputs.WEEK }}-${{ github.sha }} + restore-keys: | + turbo-${{ github.job }}- + turbo-${{ github.job }}-${{ github.ref_name }}-${{ steps.get-week.outputs.WEEK }}- + turbo-${{ github.job }}-canary-${{ steps.get-week.outputs.WEEK }}- + + # We use restore-key to pick latest cache. + # We will not get exact match, but doc says + # "If there are multiple partial matches for a restore key, the action returns the most recently created cache." + # So we get latest cache + - name: Cache built files + uses: actions/cache@v3 + with: + path: ./packages/next-swc/target + key: next-swc-cargo-cache-dev-ubuntu-latest-${{ hashFiles('**/Cargo.lock') }} + restore-keys: | + next-swc-cargo-cache-dev-ubuntu-latest + + # since the repo's dependencies aren't installed we need + # to install napi globally + - run: npm i -g @napi-rs/cli@${{ env.NAPI_CLI_VERSION }} turbo@${{ env.TURBO_VERSION }} pnpm@${PNPM_VERSION} + - name: Build + if: ${{ steps.docs-change.outputs.DOCS_CHANGE == 'nope' }} + run: turbo run build-native --cache-dir=".turbo" -- --release + env: + MACOSX_DEPLOYMENT_TARGET: '10.13' + + - name: Upload artifact + uses: actions/upload-artifact@v3 + with: + name: next-swc-test-binary + path: packages/next-swc/native/next-swc.linux-x64-gnu.node + + - name: Clear the cargo caches + if: ${{ steps.docs-change.outputs.DOCS_CHANGE == 'nope' }} + run: | + cargo install cargo-cache --no-default-features --features ci-autoclean + cargo-cache + + test-native: + name: Unit Test Native Code + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 25 + + - run: echo "::set-output name=SWC_CHANGE::$(node scripts/run-for-change.js --type next-swc --exec echo 'yup')" + id: swc-change + + - run: echo ${{ steps.swc-change.outputs.SWC_CHANGE }} + + - name: Install + if: ${{ steps.swc-change.outputs.SWC_CHANGE == 'yup' }} + uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ env.RUST_TOOLCHAIN }} + profile: minimal + + - run: cd packages/next-swc && cargo test + if: ${{ steps.swc-change.outputs.SWC_CHANGE == 'yup' }} + + test-wasm: + name: Test the wasm build + runs-on: ubuntu-latest + timeout-minutes: 15 + needs: [build, build-native-test, build-wasm-dev] + + steps: + - uses: actions/cache@v3 + if: ${{needs.build.outputs.docsChange == 'nope'}} + id: restore-build + with: + path: ./* + key: ${{ github.sha }} + + - uses: actions/download-artifact@v3 + if: ${{needs.build.outputs.docsChange == 'nope'}} + with: + name: wasm-dev-binary + path: packages/next-swc/crates/wasm/pkg-nodejs + + - run: ls packages/next-swc/crates/wasm + if: ${{needs.build.outputs.docsChange == 'nope'}} + + - uses: actions/download-artifact@v3 + if: ${{needs.build.outputs.docsChange == 'nope'}} + with: + name: next-swc-test-binary + path: packages/next-swc/native + + # node version needs to be 16+ to use --no-addons option + - name: Setup node + if: ${{needs.build.outputs.docsChange == 'nope'}} + uses: actions/setup-node@v3 + with: + node-version: 16 + check-latest: true + + - run: npm i -g playwright-chromium@1.22.2 && npx playwright install-deps + timeout-minutes: 10 + if: ${{needs.build.outputs.docsChange == 'nope'}} + + - run: node ./scripts/setup-wasm.mjs + if: ${{needs.build.outputs.docsChange == 'nope'}} + + - run: npm i -g pnpm@${PNPM_VERSION} + if: ${{needs.build.outputs.docsChange == 'nope'}} + + - run: TEST_WASM=true xvfb-run node run-tests.js test/integration/production/test/index.test.js + if: ${{needs.build.outputs.docsChange == 'nope'}} + + # test wasm parsing for runtime in page config + - run: TEST_WASM=true xvfb-run node run-tests.js test/e2e/streaming-ssr/index.test.ts + if: ${{needs.build.outputs.docsChange == 'nope'}} + + # Build binaries for publishing + build-native: + strategy: + fail-fast: false + matrix: + settings: + # pnpm is aliased here temporarily until the build docker + # image is updated past Node.js v14.19 (current 14.18.1) + - host: macos-latest + target: 'x86_64-apple-darwin' + build: | + npm i -g "@napi-rs/cli@${NAPI_CLI_VERSION}" "turbo@${TURBO_VERSION}" && if [ ! -f $(dirname $(which yarn))/pnpm ]; then ln -s $(which yarn) $(dirname $(which yarn))/pnpm;fi + turbo run build-native --cache-dir=".turbo" -- --release + strip -x packages/next-swc/native/next-swc.*.node + - host: windows-latest + build: | + npm i -g "@napi-rs/cli@${NAPI_CLI_VERSION}" "turbo@${TURBO_VERSION}" "pnpm@${PNPM_VERSION}" + turbo run build-native --cache-dir=".turbo" -- --release + target: 'x86_64-pc-windows-msvc' + - host: windows-latest + build: | + npm i -g "@napi-rs/cli@${NAPI_CLI_VERSION}" "turbo@${TURBO_VERSION}" "pnpm@${PNPM_VERSION}" + turbo run build-native-no-plugin --cache-dir=".turbo" -- --release --target i686-pc-windows-msvc + target: 'i686-pc-windows-msvc' + - host: ubuntu-latest + target: 'x86_64-unknown-linux-gnu' + docker: ghcr.io/napi-rs/napi-rs/nodejs-rust:stable-2022-10-24-x64 + build: >- + set -e && + rustup toolchain install "${RUST_TOOLCHAIN}" && + rustup default "${RUST_TOOLCHAIN}" && + rustup target add x86_64-unknown-linux-gnu && + npm i -g "@napi-rs/cli@${NAPI_CLI_VERSION}" "turbo@${TURBO_VERSION}" && if [ ! -f $(dirname $(which yarn))/pnpm ]; then ln -s $(which yarn) $(dirname $(which yarn))/pnpm;fi && + unset CC_x86_64_unknown_linux_gnu && unset CC && + turbo run build-native --cache-dir=".turbo" -- --release --target x86_64-unknown-linux-gnu && + strip packages/next-swc/native/next-swc.*.node + - host: ubuntu-latest + target: 'x86_64-unknown-linux-musl' + docker: ghcr.io/napi-rs/napi-rs/nodejs-rust:stable-2022-10-24-alpine + build: >- + set -e && + rustup toolchain install "${RUST_TOOLCHAIN}" && + rustup default "${RUST_TOOLCHAIN}" && + rustup target add x86_64-unknown-linux-musl && + npm i -g "@napi-rs/cli@${NAPI_CLI_VERSION}" "turbo@${TURBO_VERSION}" && if [ ! -f $(dirname $(which yarn))/pnpm ]; then ln -s $(which yarn) $(dirname $(which yarn))/pnpm;fi && + turbo run build-native --cache-dir=".turbo" -- --release --target x86_64-unknown-linux-musl && + strip packages/next-swc/native/next-swc.*.node + - host: macos-latest + target: 'aarch64-apple-darwin' + build: | + sudo rm -Rf /Library/Developer/CommandLineTools/SDKs/*; + export CC=$(xcrun -f clang); + export CXX=$(xcrun -f clang++); + SYSROOT=$(xcrun --sdk macosx --show-sdk-path); + export CFLAGS="-isysroot $SYSROOT -isystem $SYSROOT"; + npm i -g "@napi-rs/cli@${NAPI_CLI_VERSION}" "turbo@${TURBO_VERSION}" && if [ ! -f $(dirname $(which yarn))/pnpm ]; then ln -s $(which yarn) $(dirname $(which yarn))/pnpm;fi + turbo run build-native --cache-dir=".turbo" -- --release --target aarch64-apple-darwin + strip -x packages/next-swc/native/next-swc.*.node + - host: ubuntu-latest + target: 'aarch64-unknown-linux-gnu' + docker: ghcr.io/napi-rs/napi-rs/nodejs-rust:stable-2022-10-24-aarch64 + build: >- + set -e && + export JEMALLOC_SYS_WITH_LG_PAGE=16 && + rustup toolchain install "${RUST_TOOLCHAIN}" && + rustup default "${RUST_TOOLCHAIN}" && + rustup target add aarch64-unknown-linux-gnu && + npm i -g "@napi-rs/cli@${NAPI_CLI_VERSION}" "turbo@${TURBO_VERSION}" && if [ ! -f $(dirname $(which yarn))/pnpm ]; then ln -s $(which yarn) $(dirname $(which yarn))/pnpm;fi && + export CC_aarch64_unknown_linux_gnu=/usr/aarch64-unknown-linux-gnu/bin/aarch64-unknown-linux-gnu-gcc && + turbo run build-native --cache-dir=".turbo" -- --release --target aarch64-unknown-linux-gnu && + llvm-strip -x packages/next-swc/native/next-swc.*.node + - host: ubuntu-latest + target: 'armv7-unknown-linux-gnueabihf' + setup: | + sudo apt-get update + sudo apt-get install gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf -y + build: | + npm i -g "@napi-rs/cli@${NAPI_CLI_VERSION}" "turbo@${TURBO_VERSION}" && if [ ! -f $(dirname $(which yarn))/pnpm ]; then ln -s $(which yarn) $(dirname $(which yarn))/pnpm;fi + turbo run build-native-no-plugin --cache-dir=".turbo" -- --release --target armv7-unknown-linux-gnueabihf + arm-linux-gnueabihf-strip packages/next-swc/native/next-swc.*.node + - host: ubuntu-latest + target: aarch64-linux-android + build: | + export CLANG_VERSION=`ls ${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/linux-x86_64/lib64/clang | sed 's/ *$//g'` + export CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER="${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android24-clang" + export CC="${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android24-clang" + export CXX="${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android24-clang++" + export AR="${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ar" + export PATH="${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin:${PATH}" + touch "${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/linux-x86_64/lib64/clang/${CLANG_VERSION}/lib/linux/aarch64/libgcc.a" + chmod 777 "${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/linux-x86_64/lib64/clang/${CLANG_VERSION}/lib/linux/aarch64/libgcc.a" + echo "INPUT(-lunwind)" > "${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/linux-x86_64/lib64/clang/${CLANG_VERSION}/lib/linux/aarch64/libgcc.a" + npm i -g "@napi-rs/cli@${NAPI_CLI_VERSION}" "turbo@${TURBO_VERSION}" && if [ ! -f $(dirname $(which yarn))/pnpm ]; then ln -s $(which yarn) $(dirname $(which yarn))/pnpm;fi + turbo run build-native --cache-dir=".turbo" -- --release --target aarch64-linux-android + ${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip packages/next-swc/native/next-swc.*.node + - host: ubuntu-latest + target: armv7-linux-androideabi + build: | + export CLANG_VERSION=`ls ${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/linux-x86_64/lib64/clang | sed 's/ *$//g'` + export CARGO_TARGET_ARMV7_LINUX_ANDROIDEABI_LINKER="${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi24-clang" + export CC="${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi24-clang" + export CXX="${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi24-clang++" + export AR="${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ar" + export PATH="${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin:${PATH}" + touch "${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/linux-x86_64/lib64/clang/${CLANG_VERSION}/lib/linux/arm/libgcc.a" + chmod 777 "${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/linux-x86_64/lib64/clang/${CLANG_VERSION}/lib/linux/arm/libgcc.a" + echo "INPUT(-lunwind)" > "${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/linux-x86_64/lib64/clang/${CLANG_VERSION}/lib/linux/arm/libgcc.a" + npm i -g "@napi-rs/cli@${NAPI_CLI_VERSION}" "turbo@${TURBO_VERSION}" "pnpm@${PNPM_VERSION}" + turbo run build-native-no-plugin --cache-dir=".turbo" -- --release --target armv7-linux-androideabi + ${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip packages/next-swc/native/next-swc.*.node + - host: ubuntu-latest + target: 'aarch64-unknown-linux-musl' + docker: ghcr.io/napi-rs/napi-rs/nodejs-rust:stable-2022-10-24-alpine + build: >- + set -e && + export JEMALLOC_SYS_WITH_LG_PAGE=16 && + npm i -g "@napi-rs/cli@${NAPI_CLI_VERSION}" "turbo@${TURBO_VERSION}" && if [ ! -f $(dirname $(which yarn))/pnpm ]; then ln -s $(which yarn) $(dirname $(which yarn))/pnpm;fi && + rustup toolchain install "${RUST_TOOLCHAIN}" && + rustup default "${RUST_TOOLCHAIN}" && + rustup target add aarch64-unknown-linux-musl && + turbo run build-native --cache-dir=".turbo" -- --release --target aarch64-unknown-linux-musl && + llvm-strip -x packages/next-swc/native/next-swc.*.node + - host: windows-latest + target: 'aarch64-pc-windows-msvc' + build: | + npm i -g "@napi-rs/cli@${NAPI_CLI_VERSION}" "turbo@${TURBO_VERSION}" "pnpm@${PNPM_VERSION}" + turbo run build-native-no-plugin --cache-dir=".turbo" -- --release --target aarch64-pc-windows-msvc --cargo-flags=--no-default-features + if: ${{ needs.build.outputs.isRelease == 'true' }} + needs: build + name: stable - ${{ matrix.settings.target }} - node@16 + runs-on: ${{ matrix.settings.host }} + steps: + # https://github.com/actions/virtual-environments/issues/1187 + - name: tune linux network + run: sudo ethtool -K eth0 tx off rx off + if: ${{ matrix.settings.host == 'ubuntu-latest' }} + - name: tune linux network + run: sudo ethtool -K eth0 tx off rx off + if: ${{ matrix.settings.host == 'ubuntu-latest' }} + - name: tune windows network + run: Disable-NetAdapterChecksumOffload -Name * -TcpIPv4 -UdpIPv4 -TcpIPv6 -UdpIPv6 + if: ${{ matrix.settings.host == 'windows-latest' }} + - name: tune mac network + run: sudo sysctl -w net.link.generic.system.hwcksum_tx=0 && sudo sysctl -w net.link.generic.system.hwcksum_rx=0 + if: ${{ matrix.settings.host == 'macos-latest' }} + # we use checkout here instead of the build cache since + # it can fail to restore in different OS' + - uses: actions/checkout@v3 + # We use restore-key to pick latest cache. + # We will not get exact match, but doc says + # "If there are multiple partial matches for a restore key, the action returns the most recently created cache." + # So we get latest cache + - name: Cache built files + uses: actions/cache@v3 + with: + path: ./packages/next-swc/target + key: next-swc-cargo-cache-${{ matrix.settings.target }}--${{ hashFiles('**/Cargo.lock') }} + restore-keys: | + next-swc-cargo-cache-${{ matrix.settings.target }} + - name: Turbo Cache + id: turbo-cache + uses: actions/cache@v3 + with: + path: .turbo + key: turbo-${{ github.job }}-${{ github.ref_name }}-${{ matrix.settings.target }}-${{ needs.build.outputs.weekNum }}-${{ github.sha }} + restore-keys: | + turbo-${{ github.job }}-${{ github.ref_name }}-${{ matrix.settings.target }} + turbo-${{ github.job }}-${{ github.ref_name }}-${{ matrix.settings.target }}-${{ needs.build.outputs.weekNum }}- + turbo-${{ github.job }}-canary-${{ matrix.settings.target }}-${{ needs.build.outputs.weekNum }}- + + - name: Setup node + uses: actions/setup-node@v3 + if: ${{ !matrix.settings.docker }} + with: + node-version: 16 + check-latest: true + + - name: Install + uses: actions-rs/toolchain@v1 + if: ${{ !matrix.settings.docker }} + with: + profile: minimal + override: true + toolchain: ${{ env.RUST_TOOLCHAIN }} + target: ${{ matrix.settings.target }} + + - name: Cache cargo registry + uses: actions/cache@v3 + with: + path: ~/.cargo/registry + key: ${{ matrix.settings.target }}-cargo-registry + + - name: Cache cargo index + uses: actions/cache@v3 + with: + path: ~/.cargo/git + key: ${{ matrix.settings.target }}-cargo-index + + - name: Setup toolchain + run: ${{ matrix.settings.setup }} + if: ${{ matrix.settings.setup }} + shell: bash + + - name: Build in docker + uses: addnab/docker-run-action@v3 + if: ${{ matrix.settings.docker }} + with: + image: ${{ matrix.settings.docker }} + options: -e RUST_TOOLCHAIN=${{ env.RUST_TOOLCHAIN }} -e NAPI_CLI_VERSION=${{ env.NAPI_CLI_VERSION }} -e TURBO_VERSION=${{ env.TURBO_VERSION }} -v ${{ env.HOME }}/.cargo/git:/root/.cargo/git -v ${{ env.HOME }}/.cargo/registry:/root/.cargo/registry -v ${{ github.workspace }}:/build -w /build + run: ${{ matrix.settings.build }} + + - name: 'Build' + run: ${{ matrix.settings.build }} + if: ${{ !matrix.settings.docker }} + shell: bash + + - name: Upload artifact + uses: actions/upload-artifact@v3 + with: + name: next-swc-binaries + path: packages/next-swc/native/next-swc.*.node + + build-native-freebsd: + if: ${{ needs.build.outputs.isRelease == 'true' }} + needs: build + name: stable - x86_64-unknown-freebsd - node@16 + runs-on: macos-12 + steps: + - name: tune mac network + run: sudo sysctl -w net.link.generic.system.hwcksum_tx=0 && sudo sysctl -w net.link.generic.system.hwcksum_rx=0 + - uses: actions/checkout@v3 + - name: Delete useless files + run: | + rm -rf bench + rm -rf docs + rm -rf errors + rm -rf examples + rm -rf scripts + rm -rf test + - name: Build + id: build + uses: vmactions/freebsd-vm@v0.3.0 + env: + DEBUG: napi:* + RUSTUP_HOME: /usr/local/rustup + CARGO_HOME: /usr/local/cargo + RUSTUP_IO_THREADS: 1 + # Disable LTO, or the lld may crash with OOM + CARGO_PROFILE_RELEASE_LTO: false + with: + envs: DEBUG RUSTUP_HOME CARGO_HOME RUSTUP_IO_THREADS CARGO_PROFILE_RELEASE_LTO NAPI_CLI_VERSION TURBO_VERSION RUST_TOOLCHAIN PNPM_VERSION + usesh: true + mem: 6000 + prepare: | + pkg install -y curl node16 + curl -qL https://www.npmjs.com/install.sh | sh + npm i -g pnpm@${PNPM_VERSION} "@napi-rs/cli@${NAPI_CLI_VERSION}" + curl https://sh.rustup.rs -sSf --output rustup.sh + sh rustup.sh -y --profile minimal --default-toolchain stable + export PATH="/usr/local/cargo/bin:$PATH" + echo "~~~~ rustc --version ~~~~" + rustc --version + echo "~~~~ node -v ~~~~" + node -v + run: | + export PATH="/usr/local/cargo/bin:$PATH" + pwd + ls -lah + whoami + env + freebsd-version + pnpm --filter=@next/swc run build-native-no-plugin --platform --release --target x86_64-unknown-freebsd + rm -rf node_modules + rm -rf packages/next-swc/target + - name: Upload artifact + uses: actions/upload-artifact@v3 + with: + name: next-swc-binaries + path: packages/next-swc/native/next-swc.*.node + if-no-files-found: error + + build-wasm: + needs: build + if: ${{ needs.build.outputs.isRelease == 'true' }} + strategy: + matrix: + target: [web, nodejs] + runs-on: macos-latest + steps: + - uses: actions/cache@v3 + id: restore-build + with: + path: ./* + key: ${{ github.sha }}-${{ github.run_number }} + + - name: Setup node + uses: actions/setup-node@v3 + with: + node-version: 16 + check-latest: true + + - name: Install Rust + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: ${{ env.RUST_TOOLCHAIN }} + override: true + target: wasm32-unknown-unknown + + - run: npm i -g turbo@${{ env.TURBO_VERSION }} pnpm@${PNPM_VERSION} + + - name: Turbo cache + id: turbo-cache + uses: actions/cache@v3 + with: + path: .turbo + key: turbo-${{ github.job }}-${{ matrix.target }}-${{ github.ref_name }}-${{ needs.build.outputs.weekNum }}-${{ github.sha }} + restore-keys: | + turbo-${{ github.job }}-${{ matrix.target }}- + turbo-${{ github.job }}-${{ matrix.target }}-${{ github.ref_name }}-${{ needs.build.outputs.weekNum }}- + turbo-${{ github.job }}-${{ matrix.target }}-canary-${{ needs.build.outputs.weekNum }}- + + - name: Install wasm-pack + run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh + + - name: Build + run: turbo run build-wasm --cache-dir=".turbo" -- --target ${{ matrix.target }} + + - name: Add target to folder name + run: '[[ -d "packages/next-swc/crates/wasm/pkg" ]] && mv packages/next-swc/crates/wasm/pkg packages/next-swc/crates/wasm/pkg-${{ matrix.target }} || ls packages/next-swc/crates/wasm' + + - name: Upload artifact + uses: actions/upload-artifact@v3 + with: + name: wasm-binaries + path: packages/next-swc/crates/wasm/pkg-* + + build-wasm-dev: + needs: build + runs-on: ubuntu-latest + steps: + - uses: actions/cache@v3 + if: ${{needs.build.outputs.docsChange == 'nope'}} + id: restore-build + with: + path: ./* + key: ${{ github.sha }}-${{ github.run_number }} + + - name: Setup node + if: ${{needs.build.outputs.docsChange == 'nope'}} + uses: actions/setup-node@v3 + with: + node-version: 16 + check-latest: true + + - run: npm i -g turbo@${{ env.TURBO_VERSION }} pnpm@${PNPM_VERSION} + + - name: Install Rust + if: ${{needs.build.outputs.docsChange == 'nope'}} + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: ${{ env.RUST_TOOLCHAIN }} + override: true + target: wasm32-unknown-unknown + + - name: Turbo Cache + id: turbo-cache + uses: actions/cache@v3 + if: ${{needs.build.outputs.docsChange == 'nope'}} + + with: + path: .turbo + key: turbo-${{ github.job }}-${{ github.ref_name }}-${{ steps.get-week.outputs.WEEK }}-${{ github.sha }} + restore-keys: | + turbo-${{ github.job }}- + turbo-${{ github.job }}-${{ github.ref_name }}-${{ steps.get-week.outputs.WEEK }}- + turbo-${{ github.job }}-canary-${{ steps.get-week.outputs.WEEK }}- + + - name: Install wasm-pack + if: ${{needs.build.outputs.docsChange == 'nope'}} + run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh + + - name: Build + if: ${{needs.build.outputs.docsChange == 'nope'}} + run: turbo run build-wasm --cache-dir=".turbo" -- --target nodejs --dev + + - name: Add target to folder name + if: ${{needs.build.outputs.docsChange == 'nope'}} + run: '[[ -d "packages/next-swc/crates/wasm/pkg" ]] && mv packages/next-swc/crates/wasm/pkg packages/next-swc/crates/wasm/pkg-nodejs || ls packages/next-swc/crates/wasm' + + - name: Upload artifact + if: ${{needs.build.outputs.docsChange == 'nope'}} + uses: actions/upload-artifact@v3 + with: + name: wasm-dev-binary + path: packages/next-swc/crates/wasm/pkg-nodejs + + check-trace-secrests: + runs-on: ubuntu-latest + outputs: + trace-api-key: ${{ steps.trace-api-key.outputs.defined }} + steps: + - id: trace-api-key + env: + TRACE_API_KEY: ${{ secrets.DATA_DOG_API_KEY }} + if: "${{ env.TRACE_API_KEY != '' }}" + run: echo "::set-output name=defined::true" + + build-performance-metrics: + name: Performance Metrics for Release Build + runs-on: ubuntu-latest + needs: [build, build-native-test, check-trace-secrests] + if: needs.check-trace-secrests.outputs.trace-api-key == 'true' + env: + NEXT_TELEMETRY_DISABLED: 1 + steps: + - name: Setup node + uses: actions/setup-node@v3 + if: ${{needs.build.outputs.docsChange == 'nope'}} + with: + node-version: 16 + check-latest: true + + - uses: actions/cache@v3 + id: restore-build + if: ${{needs.build.outputs.docsChange == 'nope'}} + with: + path: ./* + key: ${{ github.sha }}-${{ github.run_number }} + + - name: Set Git Short sha Env + if: ${{needs.build.outputs.docsChange == 'nope'}} + run: echo "GIT_SHORT_SHA=`echo ${GITHUB_SHA} | cut -c1-8`" >> $GITHUB_ENV + + - name: Check Git Short sha Env + if: ${{needs.build.outputs.docsChange == 'nope'}} + run: echo ${GIT_SHORT_SHA} + + - uses: actions/download-artifact@v3 + if: ${{needs.build.outputs.docsChange == 'nope'}} + with: + name: next-swc-test-binary + path: packages/next-swc/native + + - name: Generate metrics data + if: ${{needs.build.outputs.docsChange == 'nope'}} + run: | + yarn --cwd bench/nested-deps install + node bench/nested-deps/bench.mjs build + - uses: datadog/agent-github-action@v1 + if: ${{needs.build.outputs.docsChange == 'nope'}} + with: + api_key: ${{ secrets.DATA_DOG_API_KEY }} + - name: Sending metrics data to Datadog + if: ${{needs.build.outputs.docsChange == 'nope'}} + run: | + node scripts/trace-dd.mjs bench/nested-deps/.next/trace build ${GIT_SHORT_SHA} ./bench/nested-deps/next.config.js + env: + DATA_DOG_API_KEY: ${{ secrets.DATA_DOG_API_KEY }} + DD_TRACE_PARTIAL_FLUSH_MIN_SPANS: 10 + DD_ENV: canary + DD_SERVICE: nextjs-dev-build + DD_TRACE_DEBUG: true diff --git a/.github/workflows/cancel.yml b/.github/workflows/cancel.yml new file mode 100644 index 0000000000000..093016a7beaf7 --- /dev/null +++ b/.github/workflows/cancel.yml @@ -0,0 +1,17 @@ +name: Cancel +on: + pull_request_target: + types: + - edited + - synchronize + +jobs: + cancel: + name: 'Cancel Previous Runs' + runs-on: ubuntu-latest + timeout-minutes: 2 + steps: + - uses: styfle/cancel-workflow-action@0.9.1 + with: + workflow_id: 444921, 444987 + access_token: ${{ github.token }} diff --git a/.github/workflows/lock.yml b/.github/workflows/lock.yml new file mode 100644 index 0000000000000..41b7e8c78beb4 --- /dev/null +++ b/.github/workflows/lock.yml @@ -0,0 +1,27 @@ +name: 'Lock Threads' + +on: + schedule: + # This runs twice a day: https://crontab.guru/#0_0,12_*_*_* + - cron: '0 0,12 * * *' + workflow_dispatch: + +permissions: + issues: write + pull-requests: write + +concurrency: + group: lock + +jobs: + action: + runs-on: ubuntu-latest + if: github.repository_owner == 'vercel' + steps: + - uses: dessant/lock-threads@v3 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + issue-inactive-days: 30 + issue-comment: 'This closed issue has been automatically locked because it had no new activity for a month. If you are running into a similar issue, please create a new issue with the steps to reproduce. Thank you.' + pr-inactive-days: 30 + log-output: true diff --git a/.github/workflows/pull_request_stats.yml b/.github/workflows/pull_request_stats.yml new file mode 100644 index 0000000000000..14fd026d4a91d --- /dev/null +++ b/.github/workflows/pull_request_stats.yml @@ -0,0 +1,134 @@ +on: + pull_request: + types: [opened, synchronize] + +name: Generate Pull Request Stats + +env: + NAPI_CLI_VERSION: 2.12.0 + TURBO_VERSION: 1.3.2-canary.1 + RUST_TOOLCHAIN: nightly-2022-11-04 + PNPM_VERSION: 7.3.0 + +jobs: + build-native-dev: + name: Build dev binary for tests + runs-on: ubuntu-latest + steps: + # https://github.com/actions/virtual-environments/issues/1187 + - name: tune linux network + run: sudo ethtool -K eth0 tx off rx off + + - uses: actions/checkout@v3 + with: + fetch-depth: 25 + + - name: Check non-docs only change + run: echo "::set-output name=DOCS_CHANGE::$(node scripts/run-for-change.js --not --type docs --exec echo 'nope')" + id: docs-change + + - name: Setup node + uses: actions/setup-node@v3 + if: ${{ steps.docs-change.outputs.DOCS_CHANGE == 'nope' }} + with: + node-version: 16 + check-latest: true + + - name: Install + uses: actions-rs/toolchain@v1 + if: ${{ steps.docs-change.outputs.DOCS_CHANGE == 'nope' }} + with: + profile: minimal + toolchain: nightly-2022-11-04 + + - name: Cache cargo registry + uses: actions/cache@v1 + if: ${{ steps.docs-change.outputs.DOCS_CHANGE == 'nope' }} + with: + path: ~/.cargo/registry + key: stable-ubuntu-latest-node@14-cargo-registry-trimmed-${{ hashFiles('**/Cargo.lock') }} + + - name: Cache cargo index + uses: actions/cache@v1 + if: ${{ steps.docs-change.outputs.DOCS_CHANGE == 'nope' }} + with: + path: ~/.cargo/git + key: stable-ubuntu-latest-node@14-cargo-index-trimmed-${{ hashFiles('**/Cargo.lock') }} + + # We use week in the turbo cache key to keep the cache from infinitely growing + - id: get-week + run: echo ::set-output name=WEEK::$(date +%U) + + - name: Turbo Cache + id: turbo-cache + uses: actions/cache@v3 + if: ${{ steps.docs-change.outputs.DOCS_CHANGE == 'nope' }} + with: + path: .turbo + key: turbo-${{ github.job }}-${{ github.ref_name }}-${{ steps.get-week.outputs.WEEK }}-${{ github.sha }} + restore-keys: | + turbo-${{ github.job }}-${{ github.ref_name }}-${{ steps.get-week.outputs.WEEK }}- + turbo-${{ github.job }}-canary-${{ steps.get-week.outputs.WEEK }}- + + # We use restore-key to pick latest cache. + # We will not get exact match, but doc says + # "If there are multiple partial matches for a restore key, the action returns the most recently created cache." + # So we get latest cache + - name: Cache built files + uses: actions/cache@v3 + with: + path: ./packages/next-target + key: next-swc-cargo-cache-ubuntu-latest--${{ hashFiles('**/Cargo.lock') }} + restore-keys: | + next-swc-cargo-cache-ubuntu-latest + + # since the repo's dependencies aren't installed we need + # to install napi globally + - run: npm i -g @napi-rs/cli@${NAPI_CLI_VERSION} + - run: npm i -g turbo@${TURBO_VERSION} pnpm@${PNPM_VERSION} + + - name: Build + if: ${{ steps.docs-change.outputs.DOCS_CHANGE == 'nope' }} + run: turbo run build-native --cache-dir=".turbo" + env: + MACOSX_DEPLOYMENT_TARGET: '10.13' + TURBO_TOKEN: ${{secrets.TURBO_TOKEN}} + TURBO_TEAM: nextjs + TURBO_PROJECT: nextjs + + - name: Upload artifact + uses: actions/upload-artifact@v3 + with: + name: next-swc-dev-binary + path: packages/next-swc/native/next-swc.linux-x64-gnu.node + + - name: Clear the cargo caches + if: ${{ steps.docs-change.outputs.DOCS_CHANGE == 'nope' }} + run: | + cargo install cargo-cache --no-default-features --features ci-autoclean + cargo-cache + + stats: + name: PR Stats + needs: build-native-dev + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 25 + + - name: Check non-docs only change + run: echo "::set-output name=DOCS_CHANGE::$(node scripts/run-for-change.js --not --type docs --exec echo 'nope')" + id: docs-change + + - uses: actions/download-artifact@v3 + if: ${{ steps.docs-change.outputs.DOCS_CHANGE == 'nope' }} + with: + name: next-swc-dev-binary + path: packages/next-swc/native + + - run: cp -r packages/next-swc/native .github/actions/next-stats-action/native + if: ${{ steps.docs-change.outputs.DOCS_CHANGE == 'nope' }} + + - uses: ./.github/actions/next-stats-action + if: ${{ steps.docs-change.outputs.DOCS_CHANGE == 'nope' }} diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 0000000000000..b514ba2b7baec --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,38 @@ +name: 'Stale issue handler' +on: + workflow_dispatch: + schedule: + # This runs every day 20 minutes before midnight: https://crontab.guru/#40_23_*_*_* + - cron: '40 23 * * *' + +jobs: + stale: + runs-on: ubuntu-latest + if: github.repository_owner == 'vercel' + steps: + - uses: actions/stale@v4 + id: stale-no-repro + name: 'Close stale issues with no reproduction' + with: + repo-token: ${{ secrets.STALE_TOKEN }} + only-labels: 'please add a complete reproduction' + close-issue-message: 'This issue has been automatically closed because it received no activity for a month and had no reproduction to investigate. If you think it was closed by accident, please leave a comment. If you are running into a similar issue, please open a new issue with a reproduction. Thank you.' + days-before-issue-close: 1 + days-before-issue-stale: 30 + days-before-pr-close: -1 + days-before-pr-stale: -1 + exempt-issue-labels: 'blocked,must,should,keep' + operations-per-run: 300 # 1 operation per 100 issues, the rest is to label/comment/close + - uses: actions/stale@v4 + id: stale-no-canary + name: 'Close issues not verified on canary' + with: + repo-token: ${{ secrets.STALE_TOKEN }} + only-labels: 'please verify canary' + close-issue-message: "This issue has been automatically closed because it wasn't verified against next@canary. If you think it was closed by accident, please leave a comment. If you are running into a similar issue, please open a new issue with a reproduction. Thank you." + days-before-issue-close: 1 + days-before-issue-stale: 30 + days-before-pr-close: -1 + days-before-pr-stale: -1 + exempt-issue-labels: 'blocked,must,should,keep' + operations-per-run: 300 # 1 operation per 100 issues, the rest is to label/comment/close diff --git a/.github/workflows/test_react_experimental.yml b/.github/workflows/test_react_experimental.yml new file mode 100644 index 0000000000000..f01a146964bdb --- /dev/null +++ b/.github/workflows/test_react_experimental.yml @@ -0,0 +1,51 @@ +on: + schedule: + # * is a special character in YAML so you have to quote this string + - cron: '0 0,12 * * *' + +name: Test react@experimental + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - run: yarn install --frozen-lockfile --check-files + env: + NEXT_TELEMETRY_DISABLED: 1 + + - run: yarn upgrade react@experimental react-dom@experimental -W --dev + + - run: node run-tests.js --timings --write-timings -g 1/1 + + - uses: actions/cache@v3 + id: cache-build + with: + path: ./* + key: ${{ github.sha }}-react-experimental + + testAll: + name: Test All + runs-on: ubuntu-latest + needs: build + env: + NEXT_TELEMETRY_DISABLED: 1 + NEXT_PRIVATE_REACT_ROOT: 1 + NEXT_PRIVATE_SKIP_SIZE_TESTS: true + strategy: + fail-fast: false + matrix: + group: [1, 2, 3, 4, 5, 6] + steps: + - uses: actions/cache@v3 + id: restore-build + with: + path: ./* + key: ${{ github.sha }}-react-experimental + + - run: npm i -g pnpm@latest + + - run: npm i -g playwright-chromium@1.22.2 && npx playwright install-deps + + - run: node run-tests.js --timings -g ${{ matrix.group }}/6 diff --git a/.github/workflows/test_react_next.yml b/.github/workflows/test_react_next.yml new file mode 100644 index 0000000000000..0a0a118bcd91f --- /dev/null +++ b/.github/workflows/test_react_next.yml @@ -0,0 +1,51 @@ +on: + schedule: + # * is a special character in YAML so you have to quote this string + - cron: '0 0,12 * * *' + +name: Test react@next + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - run: yarn install --frozen-lockfile --check-files + env: + NEXT_TELEMETRY_DISABLED: 1 + + - run: yarn upgrade react@next react-dom@next -W --dev + + - run: node run-tests.js --timings --write-timings -g 1/1 + + - uses: actions/cache@v3 + id: cache-build + with: + path: ./* + key: ${{ github.sha }}-react-next + + testAll: + name: Test All + runs-on: ubuntu-latest + needs: build + env: + NEXT_TELEMETRY_DISABLED: 1 + NEXT_PRIVATE_REACT_ROOT: 1 + NEXT_PRIVATE_SKIP_SIZE_TESTS: true + strategy: + fail-fast: false + matrix: + group: [1, 2, 3, 4, 5, 6] + steps: + - uses: actions/cache@v3 + id: restore-build + with: + path: ./* + key: ${{ github.sha }}-react-next + + - run: npm i -g pnpm@latest + + - run: npm i -g playwright-chromium@1.22.2 && npx playwright install-deps + + - run: node run-tests.js --timings -g ${{ matrix.group }}/6 diff --git a/.github/workflows/validate_issue.yml b/.github/workflows/validate_issue.yml new file mode 100644 index 0000000000000..abd6c0784d5f1 --- /dev/null +++ b/.github/workflows/validate_issue.yml @@ -0,0 +1,17 @@ +name: Validate issue +on: + issues: + types: [labeled] + +jobs: + validate: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: 18 + - name: 'Run issue validator' + run: node ./.github/actions/issue-validator/index.mjs + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index 86ecc6d4c46ba..ae752d2179622 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ # build output dist .next +target +packages/next/wasm/@next # dependencies node_modules @@ -12,6 +14,7 @@ test/node_modules # logs & pids *.log pids +*.cpuprofile # coverage .nyc_output @@ -19,11 +22,27 @@ coverage # test output test/**/out* +test/**/next-env.d.ts .DS_Store +/e2e-tests +test/tmp/** # Editors **/.idea +**/.#* +.nvmrc -# example output +# examples examples/**/out +examples/**/.env*.local +pr-stats.md +test-timings.json + +# Vercel +.vercel +.now + +# Cache +*.tsbuildinfo +.swc/ \ No newline at end of file diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100755 index 0000000000000..a5a29d9f7da2c --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,4 @@ +#!/usr/bin/env sh +. "$(dirname -- "$0")/_/husky.sh" + +pnpm lint-staged diff --git a/.npmrc b/.npmrc index d1504dcdecbad..7e6ce68381fb2 100644 --- a/.npmrc +++ b/.npmrc @@ -1,2 +1,5 @@ save-exact = true tag-version-prefix="" +strict-peer-dependencies = false +auto-install-peers = true +lockfile = true diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000000000..ae83ee559f424 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,28 @@ +node_modules +**/.next/** +**/_next/** +**/dist/** +packages/next/bundles/webpack/packages/*.runtime.js +packages/next/bundles/webpack/packages/lazy-compilation-*.js +packages/next/compiled/** +packages/react-refresh-utils/**/*.js +packages/react-refresh-utils/**/*.d.ts +packages/react-dev-overlay/lib/** +**/__tmp__/** +lerna.json +.github/actions/next-stats-action/.work +.github/actions/issue-validator/index.mjs +packages/next-swc/crates/**/* +packages/next-swc/target/**/* +packages/next-swc/native/**/* +packages/next-codemod/transforms/__testfixtures__/**/* +packages/next-codemod/transforms/__tests__/**/* +packages/next-codemod/**/*.js +packages/next-codemod/**/*.d.ts +packages/next-env/**/*.d.ts +test-timings.json +test/**/out/** +bench/nested-deps/pages/**/* +bench/nested-deps/components/**/* +pnpm-lock.yaml +**/convex/_generated/** diff --git a/.prettierignore_staged b/.prettierignore_staged new file mode 100644 index 0000000000000..9f3fd4864d607 --- /dev/null +++ b/.prettierignore_staged @@ -0,0 +1,12 @@ +**/.next/** +**/_next/** +**/dist/** +packages/next-swc/crates/** +packages/next/compiled/**/* +packages/next/bundles/webpack/packages/*.runtime.js +lerna.json +packages/next-codemod/transforms/__testfixtures__/**/* +packages/next-codemod/transforms/__tests__/**/* +pnpm-lock.yaml +.github/actions/issue-validator/index.mjs +**/convex/_generated/** diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000000000..fd496a820ea94 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,4 @@ +{ + "singleQuote": true, + "semi": false +} diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 077e500454d89..0000000000000 --- a/.travis.yml +++ /dev/null @@ -1,30 +0,0 @@ -{ - sudo: "required", - dist: "trusty", - addons: { - apt: { - sources: ["google-chrome"], - packages: ["google-chrome-stable"] - } - }, - env: { - HEADLESS: 'false' - }, - language: "node_js", - node_js: ["8", "10"], - cache: { - directories: ["node_modules"] - }, - before_install: [ - "curl -o- -L https://yarnpkg.com/install.sh | bash", - "export PATH=\"$HOME/.yarn/bin:$PATH\"", - "export DISPLAY=:99.0", - "sh -e /etc/init.d/xvfb start", - "sleep 3" - ], - before_cache: [ - "rm -rf node_modules/.cache" - ], - before_script: [], - after_script: ["yarn run coveralls"] -} diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000000000..31757a7345709 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,90 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Launch app-dir development", + "type": "node", + "request": "launch", + "cwd": "${workspaceFolder}", + "runtimeExecutable": "pnpm", + "runtimeArgs": ["debug", "dev", "test/e2e/app-dir/app"], + "skipFiles": ["/**"], + "env": { + "NEXT_PRIVATE_LOCAL_WEBPACK": "1" + } + }, + { + "name": "Launch app development", + "type": "node", + "request": "launch", + "cwd": "${workspaceFolder}", + "runtimeExecutable": "pnpm", + "runtimeArgs": ["debug", "dev", "examples/hello-world"], + "skipFiles": ["/**"], + "env": { + "NEXT_PRIVATE_LOCAL_WEBPACK": "1" + } + }, + { + "name": "Launch app build", + "type": "node", + "request": "launch", + "cwd": "${workspaceFolder}", + "runtimeExecutable": "pnpm", + "runtimeArgs": ["debug", "build", "examples/hello-world"], + "skipFiles": ["/**"], + "env": { + "NEXT_PRIVATE_LOCAL_WEBPACK": "1" + } + }, + { + "name": "Launch app production", + "type": "node", + "request": "launch", + "cwd": "${workspaceFolder}", + "runtimeExecutable": "pnpm", + "runtimeArgs": ["debug", "start", "examples/hello-world"], + "skipFiles": ["/**"], + "env": { + "NEXT_PRIVATE_LOCAL_WEBPACK": "1" + } + }, + { + "name": "Launch current directory in development", + "type": "node", + "request": "launch", + "cwd": "${workspaceFolder}", + "runtimeExecutable": "pnpm", + "runtimeArgs": ["debug", "dev", "${fileDirname}"], + "skipFiles": ["/**"], + "env": { + "NEXT_PRIVATE_LOCAL_WEBPACK": "1" + } + }, + { + "name": "Launch app build trace jaeger", + "type": "node", + "request": "launch", + "cwd": "${workspaceFolder}", + "runtimeExecutable": "pnpm", + "runtimeArgs": ["clean-trace-jaeger"], + "skipFiles": ["/**"], + "env": { + "NEXT_PRIVATE_LOCAL_WEBPACK": "1" + } + }, + { + "type": "node", + "request": "attach", + "name": "Attach to existing debugger", + "port": 9229, + "skipFiles": ["/**"], + "env": { + "NEXT_PRIVATE_LOCAL_WEBPACK": "1" + } + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000000000..bf06a491f6cae --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,14 @@ +{ + "eslint.validate": [ + "javascript", + "javascriptreact", + { "language": "typescript", "autoFix": true }, + { "language": "typescriptreact", "autoFix": true } + ], + "debug.javascript.unmapMissingSources": true, + "files.exclude": { + "**/node_modules": false, + "node_modules": true, + "*[!test]**/node_modules": true + } +} diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000000000..6714bc34d2f24 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,53 @@ +## Code of Conduct + +### Our Pledge + +We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, caste, color, religion, or sexual identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. + +### Our Standards + +Examples of behavior that contributes to a positive environment for our community include: + +- Demonstrating empathy and kindness toward other people +- Being respectful of differing opinions, viewpoints, and experiences +- Giving and gracefully accepting constructive feedback +- Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience +- Focusing on what is best not just for us as individuals, but for the overall community + +Examples of unacceptable behavior include: + +- The use of sexualized language or imagery, and sexual attention or advances of any kind +- Trolling, insulting or derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others’ private information, such as a physical or email address, without their explicit permission +- Other conduct which could reasonably be considered inappropriate in a professional setting + +### Enforcement Responsibilities + +Project maintainers are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. + +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate. + +### Scope + +This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. + +### Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the project team responsible for enforcement at [coc@vercel.com](mailto:coc@vercel.com). All complaints will be reviewed and investigated promptly and fairly. + +All project maintainers are obligated to respect the privacy and security of the reporter of any incident. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +### Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.1, +available at [https://www.contributor-covenant.org/version/2/1/code_of_conduct/][version] + +[homepage]: http://contributor-covenant.org +[version]: https://www.contributor-covenant.org/version/2/1 diff --git a/README-zh-CN.md b/README-zh-CN.md deleted file mode 100644 index d617f60e8cb13..0000000000000 --- a/README-zh-CN.md +++ /dev/null @@ -1,2102 +0,0 @@ -screen shot 2016-10-25 at 2 37 27 pm - -[![NPM version](https://img.shields.io/npm/v/next.svg)](https://www.npmjs.com/package/next) -[![Build Status](https://travis-ci.org/zeit/next.js.svg?branch=master)](https://travis-ci.org/zeit/next.js) -[![Build status](https://ci.appveyor.com/api/projects/status/gqp5hs71l3ebtx1r/branch/master?svg=true)](https://ci.appveyor.com/project/arunoda/next-js/branch/master) -[![Coverage Status](https://coveralls.io/repos/zeit/next.js/badge.svg?branch=master)](https://coveralls.io/r/zeit/next.js?branch=master) -[![Join the community on Spectrum](https://withspectrum.github.io/badge/badge.svg)](https://spectrum.chat/next-js) - -Next.js 是一个轻量级的 React 服务端渲染应用框架。 - -**可访问 [nextjs.org/learn](https://nextjs.org/learn) 开始学习 Next.js.** - -[README in English](README.md) - ---- - - - - - -- [怎么使用](#how-to-use) - - [安装](#setup) - - [代码自动分割](#automatic-code-splitting) - - [CSS](#css) - - [支持嵌入样式](#built-in-css-support) - - [内嵌样式](#css-in-js) - - [使用 CSS / Sass / Less / Stylus files](#importing-css--sass--less--stylus-files) - - [静态文件服务(如图像)](#static-file-serving-eg-images) - - [``](#populating-head) - - [获取数据以及组件生命周期](#fetching-data-and-component-lifecycle) - - [路由](#routing) - - [`` 用法](#with-link) - - [URL 对象](#with-url-object) - - [替换路由](#replace-instead-of-push-url) - - [组件支持点击事件`onClick`](#using-a-component-that-supports-onclick) - - [暴露`href`给子元素](#forcing-the-link-to-expose-href-to-its-child) - - [禁止滚动到页面顶部](#disabling-the-scroll-changes-to-top-on-page) - - [命令式](#imperatively) - - [拦截器 `popstate`](#intercepting-popstate) - - [URL 对象用法](#with-url-object-1) - - [路由事件](#router-events) - - [浅层路由](#shallow-routing) - - [高阶组件](#using-a-higher-order-component) - - [预加载页面](#prefetching-pages) - - [``用法](#with-link-1) - - [命令式 prefetch 写法](#imperatively-1) - - [自定义服务端路由](#custom-server-and-routing) - - [禁止文件路由](#disabling-file-system-routing) - - [动态前缀](#dynamic-assetprefix) - - [动态导入](#dynamic-import) - - [1. 基础支持 (同样支持 SSR)](#1-basic-usage-also-does-ssr) - - [2. 自定义加载组件](#2-with-custom-loading-component) - - [3. 禁止使用 SSR](#3-with-no-ssr) - - [4. 同时加载多个模块](#4-with-multiple-modules-at-once) - - [自定义 ``](#custom-app) - - [自定义 ``](#custom-document) - - [自定义错误处理](#custom-error-handling) - - [渲染内置错误页面](#reusing-the-built-in-error-page) - - [自定义配置](#custom-configuration) - - [设置自定义构建目录](#setting-a-custom-build-directory) - - [禁止 etag 生成](#disabling-etag-generation) - - [配置 onDemandEntries](#configuring-the-ondemandentries) - - [配置页面后缀名解析扩展](#configuring-extensions-looked-for-when-resolving-pages-in-pages) - - [配置构建 ID](#configuring-the-build-id) - - [自定义 webpack 配置](#customizing-webpack-config) - - [自定义 babel 配置](#customizing-babel-config) - - [暴露配置到服务端和客户端](#exposing-configuration-to-the-server--client-side) - - [启动服务选择 hostname](#starting-the-server-on-alternative-hostname) - - [CDN 支持前缀](#cdn-support-with-asset-prefix) -- [项目部署](#production-deployment) -- [浏览器支持](#browser-support) -- [导出静态页面](#static-html-export) - - [使用](#usage) - - [限制](#limitation) -- [多 zone](#multi-zones) - - [怎么定义一个 zone](#how-to-define-a-zone) - - [怎么合并他们](#how-to-merge-them) -- [技巧](#recipes) -- [FAQ](#faq) -- [贡献](#contributing) -- [作者](#authors) - - - - - -## 怎么使用 - - - -### 安装 - -在项目文件夹中运行: - -```bash -npm install --save next react react-dom -``` - -将下面脚本添加到 package.json 中: - -```json -{ - "scripts": { - "dev": "next", - "build": "next build", - "start": "next start" - } -} -``` - -下面, 文件系统是主要的 API. 每个`.js` 文件将变成一个路由,自动处理和渲染。 - -新建 `./pages/index.js` 到你的项目中: - -```jsx -export default () =>
Welcome to next.js!
; -``` - -运行 `npm run dev` 命令并打开 `http://localhost:3000`。 要使用其他端口,你可以运行 `npm run dev -- -p `. - -到目前为止,我们做到: - -- 自动打包编译 (使用 webpack 和 babel) -- 热加载 -- 以 `./pages`作为服务的渲染和索引 -- 静态文件服务. `./static/` 映射到 `/static/` (可以 [创建一个静态目录](#static-file-serving-eg-images) 在你的项目中) - -这里有个简单的案例,可以下载看看 [sample app - nextgram](https://github.com/zeit/nextgram) - - - -### 代码自动分割 - -每个页面只会导入`import`中绑定以及被用到的代码. 这意味着页面不会加载不必要的代码 - -```jsx -import cowsay from "cowsay-browser"; - -export default () =>
{cowsay.say({ text: "hi there!" })}
; -``` - - - -### CSS - - - -#### 支持嵌入样式 - -

- 案例 - -

- -我们绑定 [styled-jsx](https://github.com/zeit/styled-jsx) 来生成独立作用域的 CSS. 目标是支持 "shadow CSS",但是 [不支持独立模块作用域的 JS](https://github.com/w3c/webcomponents/issues/71). - -```jsx -export default () => ( -
- Hello world -

scoped!

- - -
-); -``` - -想查看更多案例可以点击 [styled-jsx documentation](https://www.npmjs.com/package/styled-jsx). - - - -#### 内嵌样式 - -

- - Examples - - -

- -有些情况可以使用 CSS 内嵌 JS 写法。如下所示: - -```jsx -export default () =>

hi there

; -``` - -更复杂的内嵌样式解决方案,特别是服务端渲染时的样式更改。我们可以通过包裹自定义 Document,来添加样式,案例如下:[custom ``](#user-content-custom-document) - - - -#### 使用 CSS / Sass / Less / Stylus files - -支持用`.css`, `.scss`, `.less` or `.styl`,需要配置默认文件 next.config.js,具体可查看下面链接 - -- [@zeit/next-css](https://github.com/zeit/next-plugins/tree/master/packages/next-css) -- [@zeit/next-sass](https://github.com/zeit/next-plugins/tree/master/packages/next-sass) -- [@zeit/next-less](https://github.com/zeit/next-plugins/tree/master/packages/next-less) -- [@zeit/next-stylus](https://github.com/zeit/next-plugins/tree/master/packages/next-stylus) - - - -### 静态文件服务(如图像) - -在根目录下新建文件夹叫`static`。代码可以通过`/static/`来引入相关的静态资源。 - -```jsx -export default () => my image; -``` - -_注意:不要自定义静态文件夹的名字,只能叫`static` ,因为只有这个名字 Next.js 才会把它当作静态资源。_ - - - -### 生成`` - -`` - -

- Examples - -

- -我们设置一个内置组件来装载``到页面中。 - -```jsx -import Head from "next/head"; - -export default () => ( -
- - My page title - - -

Hello world!

-
-); -``` - -我们定义`key`属性来避免重复的``标签,保证``只渲染一次,如下所示: - -```jsx -import Head from "next/head"; -export default () => ( -
- - My page title - - - - - -

Hello world!

-
-); -``` - -只有第二个``才被渲染。 - -_注意:在卸载组件时,``的内容将被清除。请确保每个页面都在其``定义了所需要的内容,而不是假设其他页面已经加过了_ - - - -### 获取数据以及组件生命周期 - -

- Examples - -

- -当你需要状态,生命周期钩子或初始数据填充时,你可以导出`React.Component`(而不是上面的无状态函数),如下所示: - -```jsx -import React from "react"; - -export default class extends React.Component { - static async getInitialProps({ req }) { - const userAgent = req ? req.headers["user-agent"] : navigator.userAgent; - return { userAgent }; - } - - render() { - return
Hello World {this.props.userAgent}
; - } -} -``` - -请注意,当页面渲染时加载数据,我们使用了一个异步静态方法`getInitialProps`。它能异步获取 JS 普通对象,并绑定在`props`上。 - -当服务渲染时,`getInitialProps`将会把数据序列化,就像`JSON.stringify`。所以确保`getInitialProps`返回的是一个普通 JS 对象,而不是`Date`, `Map` 或 `Set`类型。 - -当页面初次加载时,`getInitialProps`只会在服务端执行一次。`getInitialProps`只有在路由切换的时候(如`Link`组件跳转或路由自定义跳转)时,客户端的才会被执行。 - -当页面初始化加载时,`getInitialProps`仅在服务端上执行。只有当路由跳转(`Link`组件跳转或 API 方法跳转)时,客户端才会执行`getInitialProps`。 - -注意:`getInitialProps`将不能在子组件中使用。只能在`pages`页面中使用。 - -
- -> 只有服务端用到的模块放在`getInitialProps`里,请确保正确的导入了它们,可参考[import them properly](https://arunoda.me/blog/ssr-and-server-only-modules)。 -> 否则会拖慢你的应用速度。 - -
- -你也可以给无状态组件定义`getInitialProps`: - -```jsx -const Page = ({ stars }) =>
Next stars: {stars}
; - -Page.getInitialProps = async ({ req }) => { - const res = await fetch("https://api.github.com/repos/zeit/next.js"); - const json = await res.json(); - return { stars: json.stargazers_count }; -}; - -export default Page; -``` - -`getInitialProps`入参对象的属性如下: - -- `pathname` - URL 的 path 部分 -- `query` - URL 的 query 部分,并被解析成对象 -- `asPath` - 显示在浏览器中的实际路径(包含查询部分),为`String`类型 -- `req` - HTTP 请求对象 (仅限服务器端) -- `res` - HTTP 返回对象 (仅限服务器端) -- `jsonPageRes` - 获取响应对象(仅限客户端) -- `err` - 渲染过程中的任何错误 - - - -### 路由 - -Next.js不会随应用程序中每个可能的路由一起发布路由清单,因此当前页面不知道客户端上的任何其他页面。出于可扩展性考虑,所有后续路由都会惰性加载。 - - - -#### ``用法 - -

- Examples - -

- -可以用 `` 组件实现客户端的路由切换。 - -**基本例子** - -参考下面的两个页面: - -```jsx -// pages/index.js -import Link from 'next/link' - -function Home() { - return ( -
- Click{' '} - - here - {' '} - to read more -
- ) -} - -export default Home -``` - -```jsx -// pages/about.js -function About() { - return

Welcome to About!

-} - -export default About -``` - -**自定义路由 (使用URL中的props)** - -`` 组件有两个主要属性: - -- `href`: `pages`目录内的路径+查询字符串. -- `as`: 将在浏览器URL栏中呈现的路径. - -例子: - -1. 假设你有个这样的路由 `/post/:slug`. - -2. 你可以创建文件 `pages/post.js` - -```jsx -class Post extends React.Component { - static async getInitialProps({ query }) { - console.log('SLUG', query.slug) - return {} - } - render() { - return

My blog post

- } -} - -export default Post -``` - -3. 将路由添加到 `express` (或者其他服务端) 的 `server.js` 文件 (这仅适用于SSR). 这将解析`/post/:slug`到`pages/post.js`并在getInitialProps中提供`slug`作为查询的一部分。 - -```jsx -server.get('/post/:slug', (req, res) => { - return app.render(req, res, '/post', { slug: req.params.slug }) -}) -``` - -4. 对于客户端路由,使用 `next/link`: -```jsx - -``` - -_注意:可以使用[``](#prefetching-pages)使链接和预加载在后台同时进行,来达到页面的最佳性能。_ - -客户端路由行为与浏览器很相似: - -1. 获取组件 -2. 如果组件定义了`getInitialProps`,则获取数据。如果有错误情况将会渲染 `_error.js`。 -3. 1 和 2 都完成了,`pushState`执行,新组件被渲染。 - -如果需要注入`pathname`, `query` 或 `asPath`到你组件中,你可以使用[withRouter](#using-a-higher-order-component)。 - - - -##### URL 对象 - -

- Examples - -

- -组件``接收 URL 对象,而且它会自动格式化生成 URL 字符串 - -```jsx -// pages/index.js -import Link from "next/link"; - -export default () => ( -
- Click{" "} - - here - {" "} - to read more -
-); -``` - -将生成 URL 字符串`/about?name=Zeit`,你可以使用任何在[Node.js URL module documentation](https://nodejs.org/api/url.html#url_url_strings_and_url_objects)定义过的属性。 - - - -##### 替换路由 - -``组件默认将新 url 推入路由栈中。你可以使用`replace`属性来防止添加新输入。 - -```jsx -// pages/index.js -import Link from "next/link"; - -export default () => ( -
- Click{" "} - - here - {" "} - to read more -
-); -``` - - - -##### 组件支持点击事件 `onClick` - -``支持每个组件所支持的`onClick`事件。如果你不提供``标签,只会处理`onClick`事件而`href`将不起作用。 - -```jsx -// pages/index.js -import Link from "next/link"; - -export default () => ( -
- Click{" "} - - image - -
-); -``` - -
- -##### 暴露 `href` 给子元素 - -如子元素是一个没有 href 属性的``标签,我们将会指定它以免用户重复操作。然而有些时候,我们需要里面有``标签,但是`Link`组件不会被识别成*超链接*,结果不能将`href`传递给子元素。在这种场景下,你可以定义一个`Link`组件中的布尔属性`passHref`,强制将`href`传递给子元素。 - -**注意**: 使用`a`之外的标签而且没有通过`passHref`的链接可能会使导航看上去正确,但是当搜索引擎爬行检测时,将不会识别成链接(由于缺乏 href 属性),这会对你网站的 SEO 产生负面影响。 - -```jsx -import Link from "next/link"; -import Unexpected_A from "third-library"; - -export default ({ href, name }) => ( - - {name} - -); -``` - - - -##### 禁止滚动到页面顶部 - -``的默认行为就是滚到页面顶部。当有 hash 定义时(#),页面将会滚动到对应的 id 上,就像``标签一样。为了预防滚动到顶部,可以给``加 -`scroll={false}`属性: - -```jsx -Disables scrolling -Changes with scrolling to top -``` - - - -#### 命令式 - -

- Examples - -

- -你也可以用`next/router`实现客户端路由切换 - -```jsx -import Router from "next/router"; - -export default () => ( -
- Click Router.push("/about")}>here to read more -
-); -``` - - - -#### 拦截器 `popstate` - -有些情况(比如使用[custom router](#custom-server-and-routing)),你可能想监听[`popstate`](https://developer.mozilla.org/en-US/docs/Web/Events/popstate),在路由跳转前做一些动作。 -比如,你可以操作 request 或强制 SSR 刷新 - -```jsx -import Router from "next/router"; - -Router.beforePopState(({ url, as, options }) => { - // I only want to allow these two routes! - if (as !== "/" || as !== "/other") { - // Have SSR render bad routes as a 404. - window.location.href = as; - return false; - } - - return true; -}); -``` - -如果你在`beforePopState`中返回 false,`Router`将不会执行`popstate`事件。 -例如[Disabling File-System Routing](#disabling-file-system-routing)。 - -以上`Router`对象的 API 如下: - -- `route` - 当前路由,为`String`类型 -- `pathname` - 不包含查询内容的当前路径,为`String`类型 -- `query` - 查询内容,被解析成`Object`类型. 默认为`{}` -- `asPath` - 展现在浏览器上的实际路径,包含查询内容,为`String`类型 -- `push(url, as=url)` - 用给定的url调用`pushState` -- `replace(url, as=url)` - 用给定的url调用`replaceState` -- `beforePopState(cb=function)` - 在路由器处理事件之前拦截. - -`push` 和 `replace` 函数的第二个参数`as`,是为了装饰 URL 作用。如果你在服务器端设置了自定义路由将会起作用。 - - - -##### URL 对象用法 - -`push` 或 `replace`可接收的 URL 对象(``组件的 URL 对象一样)来生成 URL。 - -```jsx -import Router from "next/router"; - -const handler = () => - Router.push({ - pathname: "/about", - query: { name: "Zeit" } - }); - -export default () => ( -
- Click here to read more -
-); -``` - -也可以像``组件一样添加额外的参数。 - - - -##### 路由事件 - -你可以监听路由相关事件。 -下面是支持的事件列表: - -- `routeChangeStart(url)` - 路由开始切换时触发 -- `routeChangeComplete(url)` - 完成路由切换时触发 -- `routeChangeError(err, url)` - 路由切换报错时触发 -- `beforeHistoryChange(url)` - 浏览器 history 模式开始切换时触发 -- `hashChangeStart(url)` - 开始切换 hash 值但是没有切换页面路由时触发 -- `hashChangeComplete(url)` - 完成切换 hash 值但是没有切换页面路由时触发 - -> 这里的`url`是指显示在浏览器中的 url。如果你用了`Router.push(url, as)`(或类似的方法),那浏览器中的 url 将会显示 as 的值。 - -下面是如何正确使用路由事件`routeChangeStart`的例子: - -```js -const handleRouteChange = url => { - console.log("App is changing to: ", url); -}; - -Router.events.on("routeChangeStart", handleRouteChange); -``` - -如果你不想再监听该事件,你可以用`off`事件去取消监听: - -```js -Router.events.off("routeChangeStart", handleRouteChange); -``` - -如果路由加载被取消(比如快速连续双击链接),`routeChangeError`将触发。传递err,并且属性cancelled的值为true。 - -```js -Router.events.on("routeChangeError", (err, url) => { - if (err.cancelled) { - console.log(`Route to ${url} was cancelled!`); - } -}); -``` - - - -##### 浅层路由 - -

- Examples - -

- -浅层路由允许你改变 URL 但是不执行`getInitialProps`生命周期。你可以加载相同页面的 URL,得到更新后的路由属性`pathname`和`query`,并不失去 state 状态。 - -你可以给`Router.push` 或 `Router.replace`方法加`shallow: true`参数。如下面的例子所示: - -```js -// Current URL is "/" -const href = "/?counter=10"; -const as = href; -Router.push(href, as, { shallow: true }); -``` - -现在 URL 更新为`/?counter=10`。在组件里查看`this.props.router.query`你将会看到更新的 URL。 - -你可以在[`componentdidupdate`](https://facebook.github.io/react/docs/react-component.html#componentdidupdate)钩子函数中监听 URL 的变化。 - -```js -componentDidUpdate(prevProps) { - const { pathname, query } = this.props.router - // verify props have changed to avoid an infinite loop - if (query.id !== prevProps.router.query.id) { - // fetch data based on the new query - } -} -``` - -> 注意: -> -> 浅层路由只作用于相同 URL 的参数改变,比如我们假定有个其他路由`about`,而你向下面代码样运行: -> -> ```js -> Router.push("/?counter=10", "/about?counter=10", { shallow: true }); -> ``` -> -> 那么这将会出现新页面,即使我们加了浅层路由,但是它还是会卸载当前页,会加载新的页面并触发新页面的`getInitialProps`。 - - - -#### 高阶组件 - -

- Examples - -

- -如果你想应用里每个组件都处理路由对象,你可以使用`withRouter`高阶组件。下面是如何使用它: - -```jsx -import { withRouter } from "next/router"; - -const ActiveLink = ({ children, router, href }) => { - const style = { - marginRight: 10, - color: router.pathname === href ? "red" : "black" - }; - - const handleClick = e => { - e.preventDefault(); - router.push(href); - }; - - return ( - - {children} - - ); -}; - -export default withRouter(ActiveLink); -``` - -上面路由对象的 API 可以参考[`next/router`](#imperatively). - - - -### 预加载页面 - -⚠️ 只有生产环境才有此功能 ⚠️ - -

- Examples - -

- -Next.js 有允许你预加载页面的 API。 - -用 Next.js 服务端渲染你的页面,可以达到所有你应用里所有未来会跳转的路径即时响应,有效的应用 Next.js,可以通过预加载应用程序的功能,最大程度的初始化网站性能。[查看更多](https://zeit.co/blog/next#anticipation-is-the-key-to-performance). - -> Next.js 的预加载功能只预加载 JS 代码。当页面渲染时,你可能需要等待数据请求。 - - - -#### ``用法 - -你可以给添加 `prefetch` 属性,Next.js 将会在后台预加载这些页面。 - -```jsx -import Link from "next/link"; - -// example header component -export default () => ( - -); -``` - - - -#### 命令式 prefetch 写法 - -大多数预加载是通过处理的,但是我们还提供了命令式 API 用于更复杂的场景。 - -```jsx -import { withRouter } from "next/router"; - -export default withRouter(({ router }) => ( -
- setTimeout(() => router.push("/dynamic"), 100)}> - A route transition will happen after 100ms - - {// but we can prefetch it! - router.prefetch("/dynamic")} -
-)); -``` - -路由实例只允许在应用程序的客户端。以防服务端渲染发生错误,建议 prefetch 事件写在`componentDidMount()`生命周期里。 - -```jsx -import React from "react"; -import { withRouter } from "next/router"; - -class MyLink extends React.Component { - componentDidMount() { - const { router } = this.props; - router.prefetch("/dynamic"); - } - - render() { - const { router } = this.props; - return ( - - ); - } -} - -export default withRouter(MyLink); -``` - - - -### 自定义服务端路由 - -

- Examples - -

- -一般你使用`next start`命令来启动 next 服务,你还可以编写代码来自定义路由,如使用路由正则等。 - -当使用自定义服务文件,如下面例子所示叫 server.js 时,确保你更新了 package.json 中的脚本。 - -```json -{ - "scripts": { - "dev": "node server.js", - "build": "next build", - "start": "NODE_ENV=production node server.js" - } -} -``` - -下面这个例子使 `/a` 路由解析为`./pages/b`,以及`/b` 路由解析为`./pages/a`; - -```js -// This file doesn't go through babel or webpack transformation. -// Make sure the syntax and sources this file requires are compatible with the current node version you are running -// See https://github.com/zeit/next.js/issues/1245 for discussions on Universal Webpack or universal Babel -const { createServer } = require("http"); -const { parse } = require("url"); -const next = require("next"); - -const dev = process.env.NODE_ENV !== "production"; -const app = next({ dev }); -const handle = app.getRequestHandler(); - -app.prepare().then(() => { - createServer((req, res) => { - // Be sure to pass `true` as the second argument to `url.parse`. - // This tells it to parse the query portion of the URL. - const parsedUrl = parse(req.url, true); - const { pathname, query } = parsedUrl; - - if (pathname === "/a") { - app.render(req, res, "/b", query); - } else if (pathname === "/b") { - app.render(req, res, "/a", query); - } else { - handle(req, res, parsedUrl); - } - }).listen(3000, err => { - if (err) throw err; - console.log("> Ready on http://localhost:3000"); - }); -}); -``` - -`next`的 API 如下所示 - -- `next(opts: object)` - -opts 的属性如下: - -- `dev` (`boolean`) 判断 Next.js 应用是否在开发环境 - 默认`false` -- `dir` (`string`) Next 项目路径 - 默认`'.'` -- `quiet` (`boolean`) 是否隐藏包含服务端消息在内的错误信息 - 默认`false` -- `conf` (`object`) 与`next.config.js`的对象相同 - 默认`{}` - -生产环境的话,可以更改 package.json 里的`start`脚本为`NODE_ENV=production node server.js`。 - - - -#### 禁止文件路由 - -默认情况,`Next`将会把`/pages`下的所有文件匹配路由(如`/pages/some-file.js` 渲染为 `site.com/some-file`) - -如果你的项目使用自定义路由,那么有可能不同的路由会得到相同的内容,可以优化 SEO 和用户体验。 - -禁止路由链接到`/pages`下的文件,只需设置`next.config.js`文件如下所示: - -```js -// next.config.js -module.exports = { - useFileSystemPublicRoutes: false -}; -``` - -注意`useFileSystemPublicRoutes`只禁止服务端的文件路由;但是客户端的还是禁止不了。 - -你如果想配置客户端路由不能跳转文件路由,可以参考[Intercepting `popstate`](#intercepting-popstate)。 - - - -#### 动态前缀 - -有时你需要设置动态前缀,可以在请求时设置`assetPrefix`改变前缀。 - -使用方法如下: - -```js -const next = require("next"); -const micro = require("micro"); - -const dev = process.env.NODE_ENV !== "production"; -const app = next({ dev }); -const handleNextRequests = app.getRequestHandler(); - -app.prepare().then(() => { - const server = micro((req, res) => { - // Add assetPrefix support based on the hostname - if (req.headers.host === "my-app.com") { - app.setAssetPrefix("http://cdn.com/myapp"); - } else { - app.setAssetPrefix(""); - } - - handleNextRequests(req, res); - }); - - server.listen(port, err => { - if (err) { - throw err; - } - - console.log(`> Ready on http://localhost:${port}`); - }); -}); -``` - - - -### 动态导入 - -

- Examples - -

- -ext.js 支持 JavaScript 的 TC39 提议[dynamic import proposal](https://github.com/tc39/proposal-dynamic-import)。你可以动态导入 JavaScript 模块(如 React 组件)。 - -动态导入相当于把代码分成各个块管理。Next.js 服务端动态导入功能,你可以做很多炫酷事情。 - -下面介绍一些动态导入方式: - - - -#### 1. 基础用法 (也就是SSR) - -```jsx -import dynamic from "next/dynamic"; - -const DynamicComponent = dynamic(import("../components/hello")); - -export default () => ( -
-
- -

HOME PAGE is here!

-
-); -``` - - - -#### 2. 自定义加载组件 - -```jsx -import dynamic from "next/dynamic"; - -const DynamicComponentWithCustomLoading = dynamic( - import("../components/hello2"), - { - loading: () =>

...

- } -); - -export default () => ( -
-
- -

HOME PAGE is here!

-
-); -``` - - - -#### 3. 禁止使用 SSR - -```jsx -import dynamic from "next/dynamic"; - -const DynamicComponentWithNoSSR = dynamic(import("../components/hello3"), { - ssr: false -}); - -export default () => ( -
-
- -

HOME PAGE is here!

-
-); -``` - - - -#### 4. 同时加载多个模块 - -```jsx -import dynamic from "next/dynamic"; - -const HelloBundle = dynamic({ - modules: () => { - const components = { - Hello1: import("../components/hello1"), - Hello2: import("../components/hello2") - }; - - return components; - }, - render: (props, { Hello1, Hello2 }) => ( -
-

{props.title}

- - -
- ) -}); - -export default () => ; -``` - - - -### 自定义 `` - -

- Examples - - -

- -组件来初始化页面。你可以重写它来控制页面初始化,如下面的事: - -- 当页面变化时保持页面布局 -- 当路由变化时保持页面状态 -- 使用`componentDidCatch`自定义处理错误 -- 注入额外数据到页面里 (如 GraphQL 查询) - -重写的话,新建`./pages/_app.js`文件,重写 App 模块如下所示: - -```js -import App, { Container } from "next/app"; -import React from "react"; - -export default class MyApp extends App { - static async getInitialProps({ Component, router, ctx }) { - let pageProps = {}; - - if (Component.getInitialProps) { - pageProps = await Component.getInitialProps(ctx); - } - - return { pageProps }; - } - - render() { - const { Component, pageProps } = this.props; - return ( - - - - ); - } -} -``` - - - -### 自定义 `` - -

- Examples - - -

- -- 在服务端呈现 -- 初始化服务端时添加文档标记元素 -- 通常实现服务端渲染会使用一些 css-in-js 库,如[styled-components](./examples/with-styled-components), [glamorous](./examples/with-glamorous) 或 [emotion](with-emotion)。[styled-jsx](https://github.com/zeit/styled-jsx)是 Next.js 自带默认使用的 css-in-js 库 - -`Next.js`会自动定义文档标记,比如,你从来不需要添加``, ``等。如果想自定义文档标记,你可以新建`./pages/_document.js`,然后扩展`Document`类: - -```jsx -// _document is only rendered on the server side and not on the client side -// Event handlers like onClick can't be added to this file - -// ./pages/_document.js -import Document, { Head, Main, NextScript } from "next/document"; - -export default class MyDocument extends Document { - static async getInitialProps(ctx) { - const initialProps = await Document.getInitialProps(ctx); - return { ...initialProps }; - } - - render() { - return ( - - - - - -
- - - - ); - } -} -``` - -钩子[`getInitialProps`](#fetching-data-and-component-lifecycle)接收到的参数`ctx`对象都是一样的 - -- 回调函数`renderPage`是会执行 React 渲染逻辑的函数(同步),这种做法有助于此函数支持一些类似于 Aphrodite 的 renderStatic 等一些服务器端渲染容器。 - -**注意:`
`外的 React 组件将不会渲染到浏览器中,所以那添加应用逻辑代码。如果你页面需要公共组件(菜单或工具栏),可以参照上面说的`App`组件代替。** - - - -#### 自定义 `renderPage` - -🚧 应该注意的是,您应该定制“renderPage”的唯一原因是使用css-in-js库,需要将应用程序包装起来以正确使用服务端渲染。 🚧 - -- 它将一个选项对象作为参数进行进一步的自定义: - -```js -import Document from 'next/document' - -class MyDocument extends Document { - static async getInitialProps(ctx) { - const originalRenderPage = ctx.renderPage - - ctx.renderPage = () => - originalRenderPage({ - // useful for wrapping the whole react tree - enhanceApp: App => App, - // useful for wrapping in a per-page basis - enhanceComponent: Component => Component - }) - - // Run the parent `getInitialProps` using `ctx` that now includes our custom `renderPage` - const initialProps = await Document.getInitialProps(ctx) - - return initialProps - } -} - -export default MyDocument -``` - - -### 自定义错误处理 - -404 和 500 错误客户端和服务端都会通过`error.js`组件处理。如果你想改写它,则新建`_error.js`在文件夹中: - -⚠️ 该`pages/_error.js`组件仅用于生产。在开发过程中,您会收到调用堆栈错误,以了解错误源自何处。 ⚠️ - -```jsx -import React from "react"; - -export default class Error extends React.Component { - static getInitialProps({ res, err }) { - const statusCode = res ? res.statusCode : err ? err.statusCode : null; - return { statusCode }; - } - - render() { - return ( -

- {this.props.statusCode - ? `An error ${this.props.statusCode} occurred on server` - : "An error occurred on client"} -

- ); - } -} -``` - - - -### 渲染内置错误页面 - -如果你想渲染内置错误页面,你可以使用`next/error`: - -```jsx -import React from "react"; -import Error from "next/error"; -import fetch from "isomorphic-unfetch"; - -export default class Page extends React.Component { - static async getInitialProps() { - const res = await fetch("https://api.github.com/repos/zeit/next.js"); - const statusCode = res.statusCode > 200 ? res.statusCode : false; - const json = await res.json(); - - return { statusCode, stars: json.stargazers_count }; - } - - render() { - if (this.props.statusCode) { - return ; - } - - return
Next stars: {this.props.stars}
; - } -} -``` - -> 如果你自定义了个错误页面,你可以引入自己的错误页面来代替`next/error` - - - -### 自定义配置 - -如果你想自定义 Next.js 的高级配置,可以在根目录下新建`next.config.js`文件(与`pages/` 和 `package.json`一起) - -注意:`next.config.js`是一个 Node.js 模块,不是一个 JSON 文件,可以用于 Next 启动服务已经构建阶段,但是不作用于浏览器端。 - -```js -// next.config.js -module.exports = { - /* config options here */ -}; -``` - -或使用一个函数: - -```js -module.exports = (phase, { defaultConfig }) => { - // - // https://github.com/zeit/ - return { - /* config options here */ - }; -}; -``` - -`phase`是配置文件被加载时的当前内容。你可看到所有的 phases 常量:[constants](./lib/constants.js) -这些常量可以通过`next/constants`引入: - -```js -const { PHASE_DEVELOPMENT_SERVER } = require("next/constants"); -module.exports = (phase, { defaultConfig }) => { - if (phase === PHASE_DEVELOPMENT_SERVER) { - return { - /* development only config options here */ - }; - } - - return { - /* config options for all phases except development here */ - }; -}; -``` - - - -#### 设置自定义构建目录 - -你可以自定义一个构建目录,如新建`build`文件夹来代替`.next` 文件夹成为构建目录。如果没有配置构建目录,构建时将会自动新建`.next`文件夹 - -```js -// next.config.js -module.exports = { - distDir: "build" -}; -``` - - - -#### 禁止 etag 生成 - -你可以禁止 etag 生成根据你的缓存策略。如果没有配置,Next 将会生成 etags 到每个页面中。 - -```js -// next.config.js -module.exports = { - generateEtags: false -}; -``` - - - -#### 配置 onDemandEntries - -Next 暴露一些选项来给你控制服务器部署以及缓存页面: - -```js -module.exports = { - onDemandEntries: { - // period (in ms) where the server will keep pages in the buffer - maxInactiveAge: 25 * 1000, - // number of pages that should be kept simultaneously without being disposed - pagesBufferLength: 2 - } -}; -``` - -这个只是在开发环境才有的功能。如果你在生成环境中想缓存 SSR 页面,请查看[SSR-caching](https://github.com/zeit/next.js/tree/canary/examples/ssr-caching) - - - -#### 配置解析路由时的页面文件后缀名 - -如 typescript 模块[`@zeit/next-typescript`](https://github.com/zeit/next-plugins/tree/master/packages/next-typescript),需要支持解析后缀名为`.ts`的文件。`pageExtensions` 允许你扩展后缀名来解析各种 pages 下的文件。 - -```js -// next.config.js -module.exports = { - pageExtensions: ["jsx", "js"] -}; -``` - - - -#### 配置构建 ID - -Next.js 使用构建时生成的常量来标识你的应用服务是哪个版本。在每台服务器上运行构建命令时,可能会导致多服务器部署出现问题。为了保持同一个构建 ID,可以配置`generateBuildId`函数: - -```js -// next.config.js -module.exports = { - generateBuildId: async () => { - // For example get the latest git commit hash here - return "my-build-id"; - } -}; -``` - - - -### 自定义 webpack 配置 - -

- Examples - -

- -可以使用些一些常见的模块 - -- [@zeit/next-css](https://github.com/zeit/next-plugins/tree/master/packages/next-css) -- [@zeit/next-sass](https://github.com/zeit/next-plugins/tree/master/packages/next-sass) -- [@zeit/next-less](https://github.com/zeit/next-plugins/tree/master/packages/next-less) -- [@zeit/next-preact](https://github.com/zeit/next-plugins/tree/master/packages/next-preact) -- [@zeit/next-typescript](https://github.com/zeit/next-plugins/tree/master/packages/next-typescript) - -_注意: `webpack`方法将被执行两次,一次在服务端一次在客户端。你可以用`isServer`属性区分客户端和服务端来配置_ - -多配置可以组合在一起,如: - -```js -const withTypescript = require("@zeit/next-typescript"); -const withSass = require("@zeit/next-sass"); - -module.exports = withTypescript( - withSass({ - webpack(config, options) { - // Further custom configuration here - return config; - } - }) -); -``` - -为了扩展`webpack`使用,可以在`next.config.js`定义函数。 - -```js -// next.config.js is not transformed by Babel. So you can only use javascript features supported by your version of Node.js. - -module.exports = { - webpack: (config, { buildId, dev, isServer, defaultLoaders }) => { - // Perform customizations to webpack config - // Important: return the modified config - return config; - }, - webpackDevMiddleware: config => { - // Perform customizations to webpack dev middleware config - // Important: return the modified config - return config; - } -}; -``` - -`webpack`的第二个参数是个对象,你可以自定义配置它,对象属性如下所示: - -- `buildId` - 字符串类型,构建的唯一标示 -- `dev` - `Boolean`型,判断你是否在开发环境下 -- `isServer` - `Boolean` 型,为`true`使用在服务端, 为`false`使用在客户端. -- `defaultLoaders` - 对象型 ,内部加载器, 你可以如下配置 - - `babel` - 对象型,配置`babel-loader`. - - `hotSelfAccept` - 对象型, `hot-self-accept-loader`配置选项.这个加载器只能用于高阶案例。如 [`@zeit/next-typescript`](https://github.com/zeit/next-plugins/tree/master/packages/next-typescript)添加顶层 typescript 页面。 - -`defaultLoaders.babel`使用案例如下: - -```js -// Example next.config.js for adding a loader that depends on babel-loader -// This source was taken from the @zeit/next-mdx plugin source: -// https://github.com/zeit/next-plugins/blob/master/packages/next-mdx -module.exports = { - webpack: (config, {}) => { - config.module.rules.push({ - test: /\.mdx/, - use: [ - options.defaultLoaders.babel, - { - loader: "@mdx-js/loader", - options: pluginOptions.options - } - ] - }); - - return config; - } -}; -``` - - - -### 自定义 babel 配置 - -

- Examples - -

- -为了扩展方便我们使用`babel`,可以在应用根目录新建`.babelrc`文件,该文件可配置。 - -如果有该文件,我们将会考虑数据源,因此也需要定义 next 项目需要的东西,也就是 `next/babel`预设。 - -这种设计方案将会使你不诧异于我们可以定制 babel 配置。 - -下面是`.babelrc`文件案例: - -```json -{ - "presets": ["next/babel"], - "plugins": [] -} -``` - -`next/babel`预设可处理各种 React 应用所需要的情况。包括: - -- preset-env -- preset-react -- plugin-proposal-class-properties -- plugin-proposal-object-rest-spread -- plugin-transform-runtime -- styled-jsx - -presets / plugins 不允许添加到`.babelrc`中,然而你可以配置`next/babel`预设: - -```json -{ - "presets": [ - [ - "next/babel", - { - "preset-env": {}, - "transform-runtime": {}, - "styled-jsx": {}, - "class-properties": {} - } - ] - ], - "plugins": [] -} -``` - -`"preset-env"`模块选项应该保持为 false,否则 webpack 代码分割将被禁用。 - - - -### 暴露配置到服务端和客户端 - -在应用程序中通常需要提供配置值 - -Next.js支持2种提供配置的方式: - -- 构建时配置 -- 运行时配置 - -#### 构建时配置 - -构建时配置的工作方式是将提供的值内联到Javascript包中。 - -你可以在`next.config.js`设置`env`: - -```js -// next.config.js -module.exports = { - env: { - customKey: 'value' - } -} -``` - -这将允许你在代码中使用`process.env.customKey`,例如: - -```jsx -// pages/index.js -function Index() { - return

The value of customKey is: {process.env.customKey}

-} - -export default Index -``` - -#### 运行时配置 - -> ⚠️ 请注意,使用此选项时不可用 `target: 'serverless'` - -> ⚠️ 通常,您希望使用构建时配置来提供配置。原因是运行时配置增加了一个小的rendering/initialization开销。 - -`next/config`模块使你应用运行时可以读取些存储在`next.config.js`的配置项。`serverRuntimeConfig`属性只在服务器端可用,`publicRuntimeConfig`属性在服务端和客户端可用。 - -```js -// next.config.js -module.exports = { - serverRuntimeConfig: { - // Will only be available on the server side - mySecret: 'secret', - secondSecret: process.env.SECOND_SECRET // Pass through env variables - }, - publicRuntimeConfig: { - // Will be available on both server and client - staticFolder: '/static' - } -} -``` - -```js -// pages/index.js -import getConfig from 'next/config' -// Only holds serverRuntimeConfig and publicRuntimeConfig from next.config.js nothing else. -const { serverRuntimeConfig, publicRuntimeConfig } = getConfig() - -console.log(serverRuntimeConfig.mySecret) // Will only be available on the server side -console.log(publicRuntimeConfig.staticFolder) // Will be available on both server and client - -function MyImage() { - return ( -
- logo -
- ) -} - -export default MyImage -``` - -### 启动服务选择 hostname - -启动开发环境服务可以设置不同的 hostname,你可以在启动命令后面加上`--hostname 主机名` 或 `-H 主机名`。它将会启动一个 TCP 服务器来监听连接所提供的主机。 - - - -### CDN 支持前缀 - -建立一个 CDN,你能配置`assetPrefix`选项,去配置你的 CDN 源。 - -```js -const isProd = process.env.NODE_ENV === "production"; -module.exports = { - // You may only need to add assetPrefix in the production. - assetPrefix: isProd ? "https://cdn.mydomain.com" : "" -}; -``` - -注意:Next.js 运行时将会自动添加前缀,但是对于`/static`是没有效果的,如果你想这些静态资源也能使用 CDN,你需要自己添加前缀。有一个方法可以判断你的环境来加前缀,如 [in this example](https://github.com/zeit/next.js/tree/master/examples/with-universal-configuration-build-time)。 - - - -## 项目部署 - -部署中,你可以先构建打包生成环境代码,再启动服务。因此,构建和启动分为下面两条命令: - -```bash -next build -next start -``` - -例如,使用[`now`](https://zeit.co/now)去部署`package.json`配置文件如下: - -```json -{ - "name": "my-app", - "dependencies": { - "next": "latest" - }, - "scripts": { - "dev": "next", - "build": "next build", - "start": "next start" - } -} -``` - -然后就可以直接运行`now`了。 - -Next.js 也有其他托管解决方案。请查考 wiki 章节['Deployment'](https://github.com/zeit/next.js/wiki/Deployment) 。 - -注意:`NODE_ENV`可以通过`next`命令配置,如果没有配置,会最大渲染,如果你使用编程式写法的话[programmatically](#custom-server-and-routing),你需要手动设置`NODE_ENV=production`。 - -注意:推荐将`.next`或自定义打包文件夹[custom dist folder](https://github.com/zeit/next.js#custom-configuration)放入`.gitignore` 或 `.npmignore`中。否则,使用`files` 或 `now.files` -添加部署白名单,并排除`.next`或自定义打包文件夹。 - - - - -### 无服务器部署 - -
- 例子 - -
- -无服务器部署通过将应用程序拆分为更小的部分(也称为[**lambdas**](https://zeit.co/docs/v2/deployments/concepts/lambdas/))来显着提高可靠性和可伸缩性。在Next.js中,`pages`目录中的每个页面都变成了无服务器的lambda。 -对于无服务器的人来说,有[许多好处](https://zeit.co/blog/serverless-express-js-lambdas-with-now-2#benefits-of-serverless-express)。引用的链接在Express的上下文中讨论了其中的一些,但这些原则普遍适用:无服务器允许分布式故障点,无限的可扩展性,并且通过“为您使用的内容付费”的模式来提供难以置信的价格。 - -要在Next.js中启用**无服务器模式**,可在`Next.config.js`中配置`target`值为`serverless`: - -```js -// next.config.js -module.exports = { - target: 'serverless' -} -``` - -`serverless`将每页输出一个lambda。此文件是完全独立的,不需要运行任何依赖项: - -- `pages/index.js` => `.next/serverless/pages/index.js` -- `pages/about.js` => `.next/serverless/pages/about.js` - -Next.js无服务器功能的签名类似于Node.js HTTP服务器回调: - -```ts -export function render(req: http.IncomingMessage, res: http.ServerResponse) => void -``` - -- [http.IncomingMessage](https://nodejs.org/api/http.html#http_class_http_incomingmessage) -- [http.ServerResponse](https://nodejs.org/api/http.html#http_class_http_serverresponse) -- `void` 指的是没有返回值的函数,它等同于JavaScript`undefined`。调用该函数将完成请求。 - -使用无服务配置, 你可以讲Next.js部署到[ZEIT Now](https://zeit.co/now) 并提供所有的好处和易于控制; [custom routes](https://zeit.co/guides/custom-next-js-server-to-routes/) 缓存头. 要了解更多信息,请参阅 [ZEIT Guide for Deploying Next.js with Now](https://zeit.co/guides/deploying-nextjs-with-now/) - -#### 降级部署 - -Next.js为无服务器部署提供低级API,因为托管平台具有不同的功能签名。通常,您需要使用兼容性层包装Next.js无服务器构建的输出。 - -例如,如果平台支持Node.js[`http.Server`](https://nodejs.org/api/http.html#http_class_http_server)类: - -```js -const http = require('http') -const page = require('./.next/serverless/pages/about.js') -const server = new http.Server((req, res) => page.render(req, res)) -server.listen(3000, () => console.log('Listening on http://localhost:3000')) -``` - -有关特定平台示例,请参阅[the examples section above](#serverless-deployment). - -#### 摘要 - -- 用于实现无服务器部署的Low-level API -- `pages`目录中的每个页面都成为无服务器功能(lambda) -- 创建最小的无服务器功能 (50Kb base zip size) -- 针对功能的快速[cold start](https://zeit.co/blog/serverless-ssr#cold-start) 进行了优化 -- 无服务器函数有0个依赖项 (依赖项包含在函数包中) -- 使用Node.js中的[http.IncomingMessage](https://nodejs.org/api/http.html#http_class_http_incomingmessage)和[http.ServerResponse](https://nodejs.org/api/http.html#http_class_http_serverresponse) -- 选择使用`target: 'serverless'` in `next.config.js` -- 在执行函数时不要加载`next.config.js`,请注意这意味着`publicRuntimeConfig` / `serverRuntimeConfig`不支持。 - -## 浏览器支持 - -Next.js 支持 IE11 和所有的现代浏览器使用了[`@babel/preset-env`](https://new.babeljs.io/docs/en/next/babel-preset-env.html)。为了支持 IE11,Next.js 需要全局添加`Promise`的 polyfill。有时你的代码或引入的其他 NPM 包的部分功能现代浏览器不支持,则需要用 polyfills 去实现。 - -ployflls 实现案例为[polyfills](https://github.com/zeit/next.js/tree/canary/examples/with-polyfills)。 - - - -## 导出静态页面 - -

- Examples - -

- -`next export`可以输出一个 Next.js 应用作为静态资源应用而不依靠 Node.js 服务。 -这个输出的应用几乎支持 Next.js 的所有功能,包括动态路由,预获取,预加载以及动态导入。 - -`next export`将把所有有可能渲染出的 HTML 都生成。这是基于映射对象的`pathname`关键字关联到页面对象。这个映射叫做`exportPathMap`。 - -页面对象有 2 个属性: - -- `page` - 字符串类型,页面生成目录 -- `query` - 对象类型,当预渲染时,`query`对象将会传入页面的生命周期`getInitialProps`中。默认为`{}`。 - - - -### 使用 - -通常开发 Next.js 应用你将会运行: - -``` -next build -next export -``` - -`next export`命令默认不需要任何配置,将会自动生成默认`exportPathMap`生成`pages`目录下的路由你页面。 - -如果你想动态配置路由,可以在`next.config.js`中添加异步函数`exportPathMap`。 - -```js -// next.config.js -module.exports = { - exportPathMap: async function(defaultPathMap) { - return { - "/": { page: "/" }, - "/about": { page: "/about" }, - "/readme.md": { page: "/readme" }, - "/p/hello-nextjs": { page: "/post", query: { title: "hello-nextjs" } }, - "/p/learn-nextjs": { page: "/post", query: { title: "learn-nextjs" } }, - "/p/deploy-nextjs": { page: "/post", query: { title: "deploy-nextjs" } } - }; - } -}; -``` - -> 注意:如果 path 的结尾是目录名,则将导出`/dir-name/index.html`,但是如果结尾有扩展名,将会导出对应的文件,如上`/readme.md`。如果你使用`.html`以外的扩展名解析文件时,你需要设置 header 的`Content-Type`头为"text/html". - -输入下面命令: - -```sh -next build -next export -``` - -你可以在`package.json`添加一个 NPM 脚本,如下所示: - -```json -{ - "scripts": { - "build": "next build", - "export": "npm run build && next export" - } -} -``` - -接着只用执行一次下面命令: - -```sh -npm run export -``` - -然后你将会有一个静态页面应用在`out` 目录下。 - -> 你也可以自定义输出目录。可以运行`next export -h`命令查看帮助。 - -现在你可以部署`out`目录到任意静态资源服务器上。注意如果部署 GitHub Pages 需要加个额外的步骤,[文档如下](https://github.com/zeit/next.js/wiki/Deploying-a-Next.js-app-into-GitHub-Pages) - -例如,访问`out`目录并用下面命令部署应用[ZEIT Now](https://zeit.co/now). - -```sh -now -``` - - - -### 复制自定义文件 - -如果您必须复制robots.txt等自定义文件或生成sitemap.xml,您可以在其中执行此操作`exportPathMap`。 `exportPathMap`获取一些上下文参数来帮助您创建/复制文件: - -- `dev` - `true`表示在开发环境下使用`exportPathMap`. `false`表示运行于`next export`. 在开发中,“exportpathmap”用于定义路由,不需要复制文件等行为。 -- `dir` - 项目目录的绝对路径 -- `outDir` - 指向`out`目录的绝对路径(可配置为`-o`或`--outdir`)。当`dev`为`true`时,`outdir`的值将为`null`。 -- `distDir` - `.next`目录的绝对路径(可使用`distDir`配置键配置) -- `buildId` - 导出正在运行的buildId - -```js -// next.config.js -const fs = require('fs') -const { join } = require('path') -const { promisify } = require('util') -const copyFile = promisify(fs.copyFile) - -module.exports = { - exportPathMap: async function( - defaultPathMap, - { dev, dir, outDir, distDir, buildId } - ) { - if (dev) { - return defaultPathMap - } - // This will copy robots.txt from your project root into the out directory - await copyFile(join(dir, 'robots.txt'), join(outDir, 'robots.txt')) - return defaultPathMap - } -} -``` - -### 限制 - -使用`next export`,我们创建了个静态 HTML 应用。构建时将会运行页面里生命周期`getInitialProps` 函数。 - -`req`和`res`只在服务端可用,不能通过`getInitialProps`。 - -> 所以你不能预构建 HTML 文件时动态渲染 HTML 页面。如果你想动态渲染可以运行`next start`或其他自定义服务端 API。 - - - -## 多 zone - -

- Examples - -

- -一个 zone 时一个单独的 Next.js 应用。如果你有很多 zone,你可以合并成一个应用。 - -例如,你如下有两个 zone: - -- https://docs.my-app.com 服务于路由 `/docs/**` -- https://ui.my-app.com 服务于所有页面 - -有多 zone 应用技术支持,你可以将几个应用合并到一个,而且可以自定义 URL 路径,使你能同时单独开发各个应用。 - -> 与 microservices 观念类似, 只是应用于前端应用. - - - -### 怎么定义一个 zone - -zone 没有单独的 API 文档。你需要做下面事即可: - -- 确保你的应用里只有需要的页面 (例如, https://ui.my-app.com 不包含 `/docs/**`) -- 确保你的应用有个前缀[assetPrefix](https://github.com/zeit/next.js#cdn-support-with-asset-prefix)。(你也可以定义动态前缀[dynamically](https://github.com/zeit/next.js#dynamic-assetprefix)) - - - -### 怎么合并他们 - -你能使用 HTTP 代理合并 zone - -你能使用代理[micro proxy](https://github.com/zeit/micro-proxy)来作为你的本地代理服务。它允许你定义路由规则如下: - -```json -{ - "rules": [ - { - "pathname": "/docs**", - "method": ["GET", "POST", "OPTIONS"], - "dest": "https://docs.my-app.com" - }, - { "pathname": "/**", "dest": "https://ui.my-app.com" } - ] -} -``` - -生产环境部署,如果你使用了[ZEIT now](https://zeit.co/now),可以它的使用[path alias](https://zeit.co/docs/features/path-aliases) 功能。否则,你可以设置你已使用的代理服务编写上面规则来路由 HTML 页面 - - - -## 技巧 - -- [设置 301 重定向](https://www.raygesualdo.com/posts/301-redirects-with-nextjs/) -- [只处理服务器端模块](https://arunoda.me/blog/ssr-and-server-only-modules) -- [构建项目 React-Material-UI-Next-Express-Mongoose-Mongodb](https://github.com/builderbook/builderbook) -- [构建一个 SaaS 产品 React-Material-UI-Next-MobX-Express-Mongoose-MongoDB-TypeScript](https://github.com/async-labs/saas) - - - -## 问答 - -
- 这个产品可以用于生产环境吗? - https://zeit.co 都是一直用 Next.js 写的。 - -它的开发体验和终端用户体验都很好,所以我们决定开源出来给大家共享。 - -
- -
- 体积多大? - -客户端大小根据应用需求不一样大小也不一样。 - -一个最简单 Next 应该用 gzip 压缩后大约 65kb - -
- -
- 这个像 `create-react-app`? - -是或不是. - -是,因为它让你的 SSR 开发更简单。 - -不是,因为它规定了一定的目录结构,使我们能做以下更高级的事: - -- 服务端渲染 -- 自动代码分割 - -此外,Next.js 还提供两个内置特性: - -- 路由与懒加载组件: `` (通过引入 `next/link`) -- 修改``的组件: `` (通过引入 `next/head`) - -如果你想写共用组件,可以嵌入 Next.js 应用和 React 应用中,推荐使用`create-react-app`。你可以更改`import`保持代码清晰。 - -
- -
- 怎么解决 CSS 嵌入 JS 问题? - -Next.js 自带[styled-jsx](https://github.com/zeit/styled-jsx)库支持 CSS 嵌入 JS。而且你可以选择其他嵌入方法到你的项目中,可参考文档[as mentioned before](#css-in-js)。 - -
- -
- 哪些语法会被转换?怎么转换它们? - -我们遵循 V8 引擎的,如今 V8 引擎广泛支持 ES6 语法以及`async`和`await`语法,所以我们支持转换它们。但是 V8 引擎不支持修饰器语法,所以我们也不支持转换这语法。 - -可以参照[这些](https://github.com/zeit/next.js/blob/master/server/build/webpack.js#L79) 以及 [这些](https://github.com/zeit/next.js/issues/26) - -
- -
- 为什么使用新路由? - -Next.js 的特别之处如下所示: - -- 路由不需要被提前知道 -- 路由总是被懒加载 -- 顶层组件可以定义生命周期`getInitialProps`来阻止路由加载(当服务端渲染或路由懒加载时) - -因此,我们可以介绍一个非常简单的路由方法,它由下面两部分组成: - -- 每个顶层组件都将会收到一个`url`对象,来检查 url 或修改历史记录 -- ``组件用于包装如(``)标签的元素容器,来执行客户端转换。 - -我们使用了些有趣的场景来测试路由的灵活性,例如,可查看[nextgram](https://github.com/zeit/nextgram)。 - -
- -
-我怎么定义自定义路由? - -我们通过请求处理来[添加](#custom-server-and-routing)任意 URL 与任意组件之前的映射关系。 - -在客户端,我们``组件有个属性`as`,可以装饰改变获取到的 URL。 - -
- -
-怎么获取数据? - -这由你决定。`getInitialProps`是一个异步函数`async`(也就是函数将会返回个`Promise`)。你可以在任意位置获取数据。 - -
- -
- 我可以使用 GraphQL 吗? - -是的! 这里有个例子[Apollo](./examples/with-apollo). - -
- -
-我可以使用 Redux 吗? - -是的! 这里有个[例子](./examples/with-redux) - -
- -
-我可以在 Next 应用中使用我喜欢的 Javascript 库或工具包吗? - -从我们第一次发版就已经提供**很多**例子,你可以查看这些[例子](./examples)。 - -
- -
-什么启发我们做这个? - -我们实现的大部分目标都是通过 Guillermo Rauch 的[Web 应用的 7 原则](http://rauchg.com/2014/7-principles-of-rich-web-applications/)来启发出的。 - -PHP 的易用性也是个很好的灵感来源,我们觉得 Next.js 可以替代很多需要用 PHP 输出 HTML 的场景。 - -与 PHP 不同的是,我们得利于 ES6 模块系统,每个文件会输出一个**组件或方法**,以便可以轻松的导入用于懒加载和测试 - -我们研究 React 的服务器渲染时并没有花费很大的步骤,因为我们发现一个类似于 Next.js 的产品,React 作者 Jordan Walke 写的[react-page](https://github.com/facebookarchive/react-page) (现在已经废弃) - -
- -
- -## 贡献 - -可查看 [contributing.md](./contributing.md) - - - -## 作者 - -- Arunoda Susiripala ([@arunoda](https://twitter.com/arunoda)) – [ZEIT](https://zeit.co) -- Tim Neutkens ([@timneutkens](https://twitter.com/timneutkens)) – [ZEIT](https://zeit.co) -- Naoyuki Kanezawa ([@nkzawa](https://twitter.com/nkzawa)) – [ZEIT](https://zeit.co) -- Tony Kovanen ([@tonykovanen](https://twitter.com/tonykovanen)) – [ZEIT](https://zeit.co) -- Guillermo Rauch ([@rauchg](https://twitter.com/rauchg)) – [ZEIT](https://zeit.co) -- Dan Zajdband ([@impronunciable](https://twitter.com/impronunciable)) – Knight-Mozilla / Coral Project diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000000000..7ef6aaaaa6d0f --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,9 @@ +# Reporting Security Issues + +If you believe you have found a security vulnerability in Next.js, we encourage you to let us know right away. + +We will investigate all legitimate reports and do our best to quickly fix the problem. + +Email `security@vercel.com` to disclose any security vulnerabilities. + +https://vercel.com/security diff --git a/UPGRADING.md b/UPGRADING.md new file mode 100644 index 0000000000000..83373e6ae0ad5 --- /dev/null +++ b/UPGRADING.md @@ -0,0 +1 @@ +This document has been moved to [nextjs.org/docs/upgrading](https://nextjs.org/docs/upgrading). It's also available in this repository on [/docs/upgrading.md](/docs/upgrading.md). diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 74b306bf0e683..4ca6149a8a4a7 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -1,24 +1,109 @@ -pool: - vmImage: 'vs2017-win2016' - -strategy: - maxParallel: 10 - matrix: - node-10: - node_version: ^10.10.0 - node-8: - node_version: ^8.12.0 - -steps: -- task: NodeTool@0 - inputs: - versionSpec: $(node_version) - displayName: 'Install Node.js' - -- script: | - yarn install - displayName: 'Install dependencies' - -- script: | - yarn test - displayName: 'Run tests' +trigger: + # Only run latest commit for branches: + batch: true + # Do not run Azure CI for docs-only/example-only changes: + paths: + include: + - '*' + exclude: + - bench + - docs + - errors + - examples + # Do not run Azure on `canary`, `main`, or release tags. This unnecessarily + # increases the backlog, and the change was already tested on the PR. + branches: + include: + - '*' + exclude: + - canary + - main + - refs/tags/* + +pr: + # Do not run Azure CI for docs-only/example-only changes: + paths: + include: + - '*' + exclude: + - bench + - docs + - errors + - examples + +variables: + PNPM_CACHE_FOLDER: $(Pipeline.Workspace)/.pnpm-store + PNPM_VERSION: 7.3.0 + NEXT_TELEMETRY_DISABLED: '1' + node_version: ^14.19.0 + +stages: + - stage: Test + jobs: + - job: test_integration + pool: + vmImage: 'windows-2019' + steps: + - task: NodeTool@0 + inputs: + versionSpec: $(node_version) + displayName: 'Install Node.js' + + - bash: | + node scripts/run-for-change.js --not --type docs --exec echo "##vso[task.setvariable variable=isDocsOnly]No" + displayName: 'Check Docs Only Change' + + - script: npm i -g pnpm@$(PNPM_VERSION) + condition: eq(variables['isDocsOnly'], 'No') + + - script: pnpm config set store-dir $(PNPM_CACHE_FOLDER) + condition: eq(variables['isDocsOnly'], 'No') + + - script: pnpm store path + condition: eq(variables['isDocsOnly'], 'No') + + - script: pnpm install && pnpm run build + condition: eq(variables['isDocsOnly'], 'No') + displayName: 'Install and build' + + - script: npx playwright install chromium + condition: eq(variables['isDocsOnly'], 'No') + + - script: | + node run-tests.js -c 1 test/integration/production/test/index.test.js test/integration/css-client-nav/test/index.test.js test/integration/rewrites-has-condition/test/index.test.js + condition: eq(variables['isDocsOnly'], 'No') + displayName: 'Run tests' + + - job: test_unit + pool: + vmImage: 'windows-2019' + steps: + - script: | + wmic datafile where name="C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe" get Version /value + displayName: 'List Chrome version' + + - task: NodeTool@0 + inputs: + versionSpec: $(node_version) + displayName: 'Install Node.js' + + - bash: | + node scripts/run-for-change.js --not --type docs --exec echo "##vso[task.setvariable variable=isDocsOnly]No" + displayName: 'Check Docs Only Change' + + - script: npm i -g pnpm@$(PNPM_VERSION) + condition: eq(variables['isDocsOnly'], 'No') + + - script: pnpm config set store-dir $(PNPM_CACHE_FOLDER) + condition: eq(variables['isDocsOnly'], 'No') + + - script: pnpm store path + condition: eq(variables['isDocsOnly'], 'No') + + - script: pnpm install && pnpm run build + condition: eq(variables['isDocsOnly'], 'No') + displayName: 'Install and build' + + - script: node run-tests.js --type unit + condition: eq(variables['isDocsOnly'], 'No') + displayName: 'Run tests' diff --git a/bench/minimal-server/benchmark-app/app/layout.js b/bench/minimal-server/benchmark-app/app/layout.js new file mode 100644 index 0000000000000..bf15eff980a84 --- /dev/null +++ b/bench/minimal-server/benchmark-app/app/layout.js @@ -0,0 +1,10 @@ +import * as React from 'react' + +export default function Root({ children }) { + return ( + + + {children} + + ) +} diff --git a/bench/minimal-server/benchmark-app/app/rsc/page.js b/bench/minimal-server/benchmark-app/app/rsc/page.js new file mode 100644 index 0000000000000..395757bacd944 --- /dev/null +++ b/bench/minimal-server/benchmark-app/app/rsc/page.js @@ -0,0 +1,5 @@ +import * as React from 'react' + +export default function page() { + return
hello
+} diff --git a/bench/minimal-server/benchmark-app/next.config.js b/bench/minimal-server/benchmark-app/next.config.js new file mode 100644 index 0000000000000..cfa3ac3d7aa94 --- /dev/null +++ b/bench/minimal-server/benchmark-app/next.config.js @@ -0,0 +1,5 @@ +module.exports = { + experimental: { + appDir: true, + }, +} diff --git a/bench/minimal-server/benchmark-app/package.json b/bench/minimal-server/benchmark-app/package.json new file mode 100644 index 0000000000000..883a323075faf --- /dev/null +++ b/bench/minimal-server/benchmark-app/package.json @@ -0,0 +1,14 @@ +{ + "name": "stats-app", + "private": true, + "license": "MIT", + "dependencies": { + "webpack-bundle-analyzer": "^4.6.1", + "webpack-stats-plugin": "^1.1.0" + }, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start" + } +} diff --git a/bench/minimal-server/benchmark-app/pages/index.js b/bench/minimal-server/benchmark-app/pages/index.js new file mode 100644 index 0000000000000..a41e3b2fd91bf --- /dev/null +++ b/bench/minimal-server/benchmark-app/pages/index.js @@ -0,0 +1,9 @@ +import * as React from 'react' + +export default function page() { + return
hello world
+} + +export async function getServerSideProps() { + return {} +} diff --git a/bench/minimal-server/package.json b/bench/minimal-server/package.json new file mode 100644 index 0000000000000..cdb7cecb8f611 --- /dev/null +++ b/bench/minimal-server/package.json @@ -0,0 +1,7 @@ +{ + "name": "next-minimal-server", + "description": "Minimal server for Next.js for benchmarking/perf analysis purposes.", + "scripts": { + "start": "node start.js" + } +} diff --git a/bench/minimal-server/start.js b/bench/minimal-server/start.js new file mode 100644 index 0000000000000..da3f91839d0c3 --- /dev/null +++ b/bench/minimal-server/start.js @@ -0,0 +1,39 @@ +process.env.NODE_ENV = 'production' + +require('../../test/lib/react-channel-require-hook') + +console.time('next-cold-start') +const NextServer = require('next/dist/server/next-server').default +const path = require('path') + +const appDir = path.join(__dirname, 'benchmark-app') +const distDir = '.next' + +const compiledConfig = require(path.join( + appDir, + distDir, + 'required-server-files.json' +)).config + +process.chdir(appDir) + +const nextServer = new NextServer({ + conf: compiledConfig, + dir: appDir, + distDir, + minimalMode: true, + customServer: false, +}) + +const requestHandler = nextServer.getRequestHandler() + +require('http') + .createServer((req, res) => { + console.time('next-request') + return requestHandler(req, res).finally(() => { + console.timeEnd('next-request') + }) + }) + .listen(3000, () => { + console.timeEnd('next-cold-start') + }) diff --git a/bench/nested-deps/.gitignore b/bench/nested-deps/.gitignore new file mode 100644 index 0000000000000..9df615af05dab --- /dev/null +++ b/bench/nested-deps/.gitignore @@ -0,0 +1,2 @@ +components/* +CPU* \ No newline at end of file diff --git a/bench/nested-deps/bench.mjs b/bench/nested-deps/bench.mjs new file mode 100644 index 0000000000000..6703aab8206b2 --- /dev/null +++ b/bench/nested-deps/bench.mjs @@ -0,0 +1,253 @@ +import { execSync, spawn } from 'child_process' +import { join } from 'path' +import { fileURLToPath } from 'url' +import fetch from 'node-fetch' +import { + existsSync, + readFileSync, + writeFileSync, + unlinkSync, + promises as fs, +} from 'fs' +import prettyMs from 'pretty-ms' +import treeKill from 'tree-kill' + +const ROOT_DIR = join(fileURLToPath(import.meta.url), '..', '..', '..') +const CWD = join(ROOT_DIR, 'bench', 'nested-deps') + +const NEXT_BIN = join(ROOT_DIR, 'packages', 'next', 'dist', 'bin', 'next') + +const [, , command = 'all'] = process.argv + +async function killApp(instance) { + await new Promise((resolve, reject) => { + treeKill(instance.pid, (err) => { + if (err) { + if ( + process.platform === 'win32' && + typeof err.message === 'string' && + (err.message.includes(`no running instance of the task`) || + err.message.includes(`not found`)) + ) { + // Windows throws an error if the process is already stopped + // + // Command failed: taskkill /pid 6924 /T /F + // ERROR: The process with PID 6924 (child process of PID 6736) could not be terminated. + // Reason: There is no running instance of the task. + return resolve() + } + return reject(err) + } + + resolve() + }) + }) +} + +class File { + constructor(path) { + this.path = path + this.originalContent = existsSync(this.path) + ? readFileSync(this.path, 'utf8') + : null + } + + write(content) { + if (!this.originalContent) { + this.originalContent = content + } + writeFileSync(this.path, content, 'utf8') + } + + replace(pattern, newValue) { + const currentContent = readFileSync(this.path, 'utf8') + if (pattern instanceof RegExp) { + if (!pattern.test(currentContent)) { + throw new Error( + `Failed to replace content.\n\nPattern: ${pattern.toString()}\n\nContent: ${currentContent}` + ) + } + } else if (typeof pattern === 'string') { + if (!currentContent.includes(pattern)) { + throw new Error( + `Failed to replace content.\n\nPattern: ${pattern}\n\nContent: ${currentContent}` + ) + } + } else { + throw new Error(`Unknown replacement attempt type: ${pattern}`) + } + + const newContent = currentContent.replace(pattern, newValue) + this.write(newContent) + } + + prepend(line) { + const currentContent = readFileSync(this.path, 'utf8') + this.write(line + '\n' + currentContent) + } + + delete() { + unlinkSync(this.path) + } + + restore() { + this.write(this.originalContent) + } +} + +function runNextCommandDev(argv, opts = {}) { + const env = { + ...process.env, + NODE_ENV: undefined, + __NEXT_TEST_MODE: 'true', + FORCE_COLOR: 3, + ...opts.env, + } + + const nodeArgs = opts.nodeArgs || [] + return new Promise((resolve, reject) => { + const instance = spawn(NEXT_BIN, [...nodeArgs, ...argv], { + cwd: CWD, + env, + }) + let didResolve = false + + function handleStdout(data) { + const message = data.toString() + const bootupMarkers = { + dev: /compiled .*successfully/i, + start: /started server/i, + } + if ( + (opts.bootupMarker && opts.bootupMarker.test(message)) || + bootupMarkers[opts.nextStart ? 'start' : 'dev'].test(message) + ) { + if (!didResolve) { + didResolve = true + resolve(instance) + instance.removeListener('data', handleStdout) + } + } + + if (typeof opts.onStdout === 'function') { + opts.onStdout(message) + } + + if (opts.stdout !== false) { + process.stdout.write(message) + } + } + + function handleStderr(data) { + const message = data.toString() + if (typeof opts.onStderr === 'function') { + opts.onStderr(message) + } + + if (opts.stderr !== false) { + process.stderr.write(message) + } + } + + instance.stdout.on('data', handleStdout) + instance.stderr.on('data', handleStderr) + + instance.on('close', () => { + instance.stdout.removeListener('data', handleStdout) + instance.stderr.removeListener('data', handleStderr) + if (!didResolve) { + didResolve = true + resolve() + } + }) + + instance.on('error', (err) => { + reject(err) + }) + }) +} + +function waitFor(millis) { + return new Promise((resolve) => setTimeout(resolve, millis)) +} + +await fs.rm('.next', { recursive: true }).catch(() => {}) +const file = new File(join(CWD, 'pages/index.jsx')) +const results = [] + +try { + if (command === 'dev' || command === 'all') { + const instance = await runNextCommandDev(['dev', '--port', '3000']) + + function waitForCompiled() { + return new Promise((resolve) => { + function waitForOnData(data) { + const message = data.toString() + const compiledRegex = + /compiled client and server successfully in (\d*[.]?\d+)\s*(m?s) \((\d+) modules\)/gm + const matched = compiledRegex.exec(message) + if (matched) { + resolve({ + 'time (ms)': (matched[2] === 's' ? 1000 : 1) * Number(matched[1]), + modules: Number(matched[3]), + }) + instance.stdout.removeListener('data', waitForOnData) + } + } + instance.stdout.on('data', waitForOnData) + }) + } + + const [res, initial] = await Promise.all([ + fetch('http://localhost:3000/'), + waitForCompiled(), + ]) + if (res.status !== 200) { + throw new Error('Fetching / failed') + } + + results.push(initial) + + file.prepend('// First edit') + + results.push(await waitForCompiled()) + + await waitFor(1000) + + file.prepend('// Second edit') + + results.push(await waitForCompiled()) + + await waitFor(1000) + + file.prepend('// Third edit') + + results.push(await waitForCompiled()) + + console.table(results) + + await killApp(instance) + } + if (command === 'build' || command === 'all') { + // ignore error + await fs.rm('.next', { recursive: true, force: true }).catch(() => {}) + + execSync(`node ${NEXT_BIN} build ./bench/nested-deps`, { + cwd: ROOT_DIR, + stdio: 'inherit', + env: { + ...process.env, + TRACE_TARGET: 'jaeger', + }, + }) + const traceString = await fs.readFile(join(CWD, '.next', 'trace'), 'utf8') + const traces = traceString + .split('\n') + .filter((line) => line) + .map((line) => JSON.parse(line)) + const { duration } = traces.pop().find(({ name }) => name === 'next-build') + console.info('next build duration: ', prettyMs(duration / 1000)) + } +} finally { + file.restore() +} diff --git a/bench/nested-deps/fuzzponent.js b/bench/nested-deps/fuzzponent.js new file mode 100755 index 0000000000000..d28fd1976eb41 --- /dev/null +++ b/bench/nested-deps/fuzzponent.js @@ -0,0 +1,182 @@ +#!/usr/bin/env node +const path = require('path') +const fs = require('fs') + +const getSequenceGenerator = require('random-seed') +const generate = require('@babel/generator').default +const t = require('@babel/types') + +const MIN_COMPONENT_NAME_LEN = 18 +const MAX_COMPONENT_NAME_LEN = 24 +const MIN_CHILDREN = 4 +const MAX_CHILDREN = 80 + +const arrayUntil = (len) => [...Array(len)].map((_, i) => i) + +const generateFunctionalComponentModule = (componentName, children = []) => { + const body = [ + generateImport('React', 'react'), + ...children.map((childName) => generateImport(childName, `./${childName}`)), + t.variableDeclaration('const', [ + t.variableDeclarator( + t.identifier(componentName), + t.arrowFunctionExpression( + [], + t.parenthesizedExpression( + generateJSXElement( + 'div', + children.map((childName) => generateJSXElement(childName)) + ) + ) + ) + ), + ]), + t.exportDefaultDeclaration(t.identifier(componentName)), + ] + + return t.program(body, [], 'module') +} + +const generateJSXElement = (componentName, children = null) => + t.JSXElement( + t.JSXOpeningElement(t.JSXIdentifier(componentName), [], !children), + children ? t.JSXClosingElement(t.JSXIdentifier(componentName)) : null, + children || [], + !children + ) + +const generateImport = (componentName, requireString) => + t.importDeclaration( + [t.importDefaultSpecifier(t.identifier(componentName))], + t.stringLiteral(requireString) + ) + +const validFirstChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' +const validOtherChars = validFirstChars.toLowerCase() +function generateComponentName(seqGenerator, opts) { + const numOtherChars = seqGenerator.intBetween(opts.minLen, opts.maxLen) + const firstChar = validFirstChars[seqGenerator.range(validFirstChars.length)] + const otherChars = arrayUntil(numOtherChars).map( + () => validOtherChars[seqGenerator.range(validOtherChars.length)] + ) + return `${firstChar}${otherChars.join('')}` +} + +function* generateModules(name, remainingDepth, seqGenerator, opts) { + const filename = `${name}.${opts.extension}` + let ast + + if (name === 'index') { + name = 'RootComponent' + } + + if (remainingDepth === 0) { + ast = generateFunctionalComponentModule(name) + } else { + const numChildren = seqGenerator.intBetween(opts.minChild, opts.maxChild) + const children = arrayUntil(numChildren).map(() => + generateComponentName(seqGenerator, opts) + ) + ast = generateFunctionalComponentModule(name, children) + + for (const child of children) { + yield* generateModules(child, remainingDepth - 1, seqGenerator, opts) + } + } + + yield { + filename, + content: generate(ast).code, + } +} + +function generateFuzzponents(outdir, seed, depth, opts) { + const seqGenerator = getSequenceGenerator(seed) + + const filenames = new Set() + for (const { filename, content } of generateModules( + 'index', + depth, + seqGenerator, + opts + )) { + if (filenames.has(filename)) { + throw new Error( + `Seed "${seed}" generates output with filename collisions.` + ) + } else { + filenames.add(filename) + } + const fpath = path.join(outdir, filename) + fs.writeFileSync(fpath, `// ${filename}\n\n${content}`) + } +} + +if (require.main === module) { + const { outdir, seed, depth, ...opts } = require('yargs') + .option('depth', { + alias: 'd', + demandOption: true, + describe: 'component hierarchy depth', + type: 'number', + }) + .option('seed', { + alias: 's', + demandOption: true, + describe: 'prng seed', + type: 'number', + }) + .option('outdir', { + alias: 'o', + demandOption: false, + default: process.cwd(), + describe: 'the directory where components should be written', + type: 'string', + normalize: true, + }) + .option('minLen', { + demandOption: false, + default: MIN_COMPONENT_NAME_LEN, + describe: 'the smallest acceptable component name length', + type: 'number', + }) + .option('maxLen', { + demandOption: false, + default: MAX_COMPONENT_NAME_LEN, + describe: 'the largest acceptable component name length', + type: 'number', + }) + .option('minLen', { + demandOption: false, + default: MIN_COMPONENT_NAME_LEN, + describe: 'the smallest acceptable component name length', + type: 'number', + }) + .option('maxLen', { + demandOption: false, + default: MAX_COMPONENT_NAME_LEN, + describe: 'the largest acceptable component name length', + type: 'number', + }) + .option('minChild', { + demandOption: false, + default: MIN_CHILDREN, + describe: 'the smallest number of acceptable component children', + type: 'number', + }) + .option('maxChild', { + demandOption: false, + default: MAX_CHILDREN, + describe: 'the largest number of acceptable component children', + type: 'number', + }) + .option('extension', { + default: 'jsx', + describe: 'extension to use for generated components', + type: 'string', + }).argv + + generateFuzzponents(outdir, seed, depth, opts) +} + +module.exports = generateFuzzponents diff --git a/bench/nested-deps/next.config.js b/bench/nested-deps/next.config.js new file mode 100644 index 0000000000000..004e6c18198b6 --- /dev/null +++ b/bench/nested-deps/next.config.js @@ -0,0 +1,9 @@ +const idx = process.execArgv.indexOf('--cpu-prof') +if (idx >= 0) process.execArgv.splice(idx, 1) + +module.exports = { + eslint: { + ignoreDuringBuilds: true, + }, + swcMinify: true, +} diff --git a/bench/nested-deps/package.json b/bench/nested-deps/package.json new file mode 100644 index 0000000000000..91c3fca60c60c --- /dev/null +++ b/bench/nested-deps/package.json @@ -0,0 +1,20 @@ +{ + "scripts": { + "prepare": "rimraf components && mkdir components && node ./fuzzponent.js -d 2 -s 206 -o components", + "dev": "cross-env NEXT_PRIVATE_LOCAL_WEBPACK=1 node ../../node_modules/next/dist/bin/next dev", + "build": "cross-env NEXT_PRIVATE_LOCAL_WEBPACK=1 node ../../node_modules/next/dist/bin/next build", + "start": "cross-env NEXT_PRIVATE_LOCAL_WEBPACK=1 node ../../node_modules/next/dist/bin/next start", + "dev-nocache": "rimraf .next && yarn dev", + "dev-cpuprofile-nocache": "rimraf .next && cross-env NEXT_PRIVATE_LOCAL_WEBPACK=1 node --cpu-prof ../../node_modules/next/dist/bin/next", + "build-nocache": "rimraf .next && yarn build" + }, + "devDependencies": { + "@babel/types": "7.18.0", + "@babel/generator": "7.18.0", + "random-seed": "0.3.0", + "cross-env": "^7.0.3", + "pretty-ms": "^7.0.1", + "rimraf": "^3.0.2", + "yargs": "16.2.0" + } +} diff --git a/bench/nested-deps/pages/index.jsx b/bench/nested-deps/pages/index.jsx new file mode 100644 index 0000000000000..f199ad545abfd --- /dev/null +++ b/bench/nested-deps/pages/index.jsx @@ -0,0 +1,5 @@ +import Comp from '../components/index.jsx' + +export default function Home() { + return +} diff --git a/bench/package.json b/bench/package.json deleted file mode 100644 index d39122c7c9976..0000000000000 --- a/bench/package.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "name": "next-bench", - "scripts": { - "build": "next build", - "start": "NODE_ENV=production npm run build && NODE_ENV=production next start", - "bench:stateless": "ab -c1 -n3000 http://0.0.0.0:3000/stateless", - "bench:stateless-big": "ab -c1 -n500 http://0.0.0.0:3000/stateless-big" - } -} diff --git a/bench/pages/stateless.js b/bench/pages/stateless.js deleted file mode 100644 index 256b5e8f006c8..0000000000000 --- a/bench/pages/stateless.js +++ /dev/null @@ -1 +0,0 @@ -export default () =>

My component!

diff --git a/bench/readdir/glob.js b/bench/readdir/glob.js index b871f0779d695..170f4fb050eb5 100644 --- a/bench/readdir/glob.js +++ b/bench/readdir/glob.js @@ -1,20 +1,21 @@ -const { join } = require('path') -const { promisify } = require('util') -const globMod = require('glob') +import { join } from 'path' +import { promisify } from 'util' +import globMod from 'glob' + const glob = promisify(globMod) const resolveDataDir = join(__dirname, 'fixtures', '**/*') -async function test () { +async function test() { const time = process.hrtime() await glob(resolveDataDir) const hrtime = process.hrtime(time) - const nanoseconds = (hrtime[0] * 1e9) + hrtime[1] + const nanoseconds = hrtime[0] * 1e9 + hrtime[1] const milliseconds = nanoseconds / 1e6 console.log(milliseconds) } -async function run () { +async function run() { for (let i = 0; i < 50; i++) { await test() } diff --git a/bench/readdir/recursive-readdir.js b/bench/readdir/recursive-readdir.js index 43877bb73a0b7..2167f30336553 100644 --- a/bench/readdir/recursive-readdir.js +++ b/bench/readdir/recursive-readdir.js @@ -1,18 +1,18 @@ -const { join } = require('path') -const { recursiveReadDir } = require('next/dist/lib/recursive-readdir') +import { join } from 'path' +import { recursiveReadDir } from 'next/dist/lib/recursive-readdir' const resolveDataDir = join(__dirname, 'fixtures') -async function test () { +async function test() { const time = process.hrtime() await recursiveReadDir(resolveDataDir, /\.js$/) const hrtime = process.hrtime(time) - const nanoseconds = (hrtime[0] * 1e9) + hrtime[1] + const nanoseconds = hrtime[0] * 1e9 + hrtime[1] const milliseconds = nanoseconds / 1e6 console.log(milliseconds) } -async function run () { +async function run() { for (let i = 0; i < 50; i++) { await test() } diff --git a/bench/readme.md b/bench/readme.md deleted file mode 100644 index 0ac01e4b1b426..0000000000000 --- a/bench/readme.md +++ /dev/null @@ -1,27 +0,0 @@ -# Next.js server-side benchmarks - -## Installation - -Follow the steps in [contributing.md](../contributing.md) - -Both benchmarks use `ab`. So make sure you have that installed. - -## Usage - -Before running the test: - -``` -npm run start -``` - -Then run one of these tests: - -- Stateless application which renders `

My component!

`. Runs 3000 http requests. -``` -npm run bench:stateless -``` - -- Stateless application which renders `
  • This is row {i}
  • ` 10.000 times. Runs 500 http requests. -``` -npm run bench:stateless-big -``` diff --git a/bench/recursive-copy/run.js b/bench/recursive-copy/run.js new file mode 100644 index 0000000000000..ab12258004724 --- /dev/null +++ b/bench/recursive-copy/run.js @@ -0,0 +1,59 @@ +import { join } from 'path' +import { ensureDir, outputFile, remove } from 'fs-extra' +import recursiveCopyNpm from 'recursive-copy' +import { recursiveCopy as recursiveCopyCustom } from 'next/dist/lib/recursive-copy' + +const fixturesDir = join(__dirname, 'fixtures') +const srcDir = join(fixturesDir, 'src') +const destDir = join(fixturesDir, 'dest') + +const createSrcFolder = async () => { + await ensureDir(srcDir) + + const files = new Array(100) + .fill(undefined) + .map((x, i) => + join(srcDir, `folder${i % 5}`, `folder${i + (1 % 5)}`, `file${i}`) + ) + + await Promise.all(files.map((file) => outputFile(file, 'hello'))) +} + +async function run(fn) { + async function test() { + const start = process.hrtime() + + await fn(srcDir, destDir) + + const timer = process.hrtime(start) + const ms = (timer[0] * 1e9 + timer[1]) / 1e6 + return ms + } + + const ts = [] + + for (let i = 0; i < 10; i++) { + const t = await test() + await remove(destDir) + ts.push(t) + } + + const sum = ts.reduce((a, b) => a + b) + const nb = ts.length + const avg = sum / nb + console.log({ sum, nb, avg }) +} + +async function main() { + await createSrcFolder() + + console.log('test recursive-copy npm module') + await run(recursiveCopyNpm) + + console.log('test recursive-copy custom implementation') + await run(recursiveCopyCustom) + + await remove(fixturesDir) +} + +main() diff --git a/bench/recursive-delete/recursive-delete.js b/bench/recursive-delete/recursive-delete.js index 8b43044e396d4..e23ed3e6f26a6 100644 --- a/bench/recursive-delete/recursive-delete.js +++ b/bench/recursive-delete/recursive-delete.js @@ -1,13 +1,13 @@ -const { join } = require('path') -const { recursiveDelete } = require('next/dist/lib/recursive-delete') +import { join } from 'path' +import { recursiveDelete } from 'next/dist/lib/recursive-delete' const resolveDataDir = join(__dirname, `fixtures-${process.argv[2]}`) -async function test () { +async function test() { const time = process.hrtime() await recursiveDelete(resolveDataDir) const hrtime = process.hrtime(time) - const nanoseconds = (hrtime[0] * 1e9) + hrtime[1] + const nanoseconds = hrtime[0] * 1e9 + hrtime[1] const milliseconds = nanoseconds / 1e6 console.log(milliseconds) } diff --git a/bench/recursive-delete/rimraf.js b/bench/recursive-delete/rimraf.js index d3163afc97006..827cdaae77484 100644 --- a/bench/recursive-delete/rimraf.js +++ b/bench/recursive-delete/rimraf.js @@ -1,15 +1,16 @@ -const { join } = require('path') -const { promisify } = require('util') -const rimrafMod = require('rimraf') -const resolveDataDir = join(__dirname, `fixtures-${process.argv[2]}`, '**/*') +import { join } from 'path' +import { promisify } from 'util' +import rimrafMod from 'rimraf' + const rimraf = promisify(rimrafMod) +const resolveDataDir = join(__dirname, `fixtures-${process.argv[2]}`, '**/*') -async function test () { +async function test() { const time = process.hrtime() await rimraf(resolveDataDir) const hrtime = process.hrtime(time) - const nanoseconds = (hrtime[0] * 1e9) + hrtime[1] + const nanoseconds = hrtime[0] * 1e9 + hrtime[1] const milliseconds = nanoseconds / 1e6 console.log(milliseconds) } diff --git a/bench/rendering/package.json b/bench/rendering/package.json new file mode 100644 index 0000000000000..d311332afdefa --- /dev/null +++ b/bench/rendering/package.json @@ -0,0 +1,14 @@ +{ + "name": "next-bench", + "scripts": { + "build": "next build", + "start": "NODE_ENV=production npm run build && NODE_ENV=production next start", + "bench:stateless": "ab -c1 -n3000 http://0.0.0.0:3000/stateless", + "bench:stateless-big": "ab -c1 -n500 http://0.0.0.0:3000/stateless-big", + "bench:recursive-copy": "node recursive-copy/run" + }, + "dependencies": { + "fs-extra": "10.0.0", + "recursive-copy": "2.0.11" + } +} diff --git a/bench/pages/stateless-big.js b/bench/rendering/pages/stateless-big.js similarity index 81% rename from bench/pages/stateless-big.js rename to bench/rendering/pages/stateless-big.js index 5b14df331c2f6..87f340d66d7e9 100644 --- a/bench/pages/stateless-big.js +++ b/bench/rendering/pages/stateless-big.js @@ -1,11 +1,7 @@ import React from 'react' export default () => { - return ( -
      - {items()} -
    - ) + return
      {items()}
    } const items = () => { diff --git a/bench/rendering/pages/stateless.js b/bench/rendering/pages/stateless.js new file mode 100644 index 0000000000000..620ef04822f5f --- /dev/null +++ b/bench/rendering/pages/stateless.js @@ -0,0 +1,2 @@ +import React from 'react' +export default () =>

    My component!

    diff --git a/bench/rendering/readme.md b/bench/rendering/readme.md new file mode 100644 index 0000000000000..71c7070d8f691 --- /dev/null +++ b/bench/rendering/readme.md @@ -0,0 +1,29 @@ +# Next.js server-side benchmarks + +## Installation + +Follow the steps in [contributing.md](../contributing.md) + +Both benchmarks use `ab`. So make sure you have that installed. + +## Usage + +Before running the test: + +``` +npm run start +``` + +Then run one of these tests: + +- Stateless application which renders `

    My component!

    `. Runs 3000 http requests. + +``` +npm run bench:stateless +``` + +- Stateless application which renders `
  • This is row {i}
  • ` 10.000 times. Runs 500 http requests. + +``` +npm run bench:stateless-big +``` diff --git a/bench/vercel/.env.dev b/bench/vercel/.env.dev new file mode 100644 index 0000000000000..aaa722fd03652 --- /dev/null +++ b/bench/vercel/.env.dev @@ -0,0 +1,8 @@ +# The Vercel team you want to deploy the project too +VERCEL_TEST_TEAM= + +# The corresponding Vercel token +VERCEL_TEST_TOKEN= + +# The Vercel project you want to deploy the test project too +VERCEL_TEST_PROJECT_NAME= diff --git a/bench/vercel/.gitignore b/bench/vercel/.gitignore new file mode 100644 index 0000000000000..753f9c1a3b423 --- /dev/null +++ b/bench/vercel/.gitignore @@ -0,0 +1,5 @@ +.vercel +.next +*.tgz +yarn.lock +.env \ No newline at end of file diff --git a/bench/vercel/README.md b/bench/vercel/README.md new file mode 100644 index 0000000000000..5d8bce4c7e53b --- /dev/null +++ b/bench/vercel/README.md @@ -0,0 +1,22 @@ +# Benchmarking Next.js on production + +This script allows you to measure some performance metrics of your local build of Next.js on production by uploading your current build to Vercel with an example app and running some basic benchmarks on it. + +## Requirements + +- the Vercel CLI + +## Setup + +Rename the provided `./env.local` file to `./env` and fill in the required `VERCEL_TEST_TOKEN` and `VERCEL_TEST_TEAM` values. You can find and generate those from vercel.com. + +Run `pnpm install`, `pnpm bench` and profit. + +Note: if you made some changes to Next.js, make sure you compiled them by running at the root of the monorepo either `pnpm dev` or `pnpm build --force`. + +## How it works + +- with the Vercel CLI, we setup a project +- we `npm pack` the local Next build and add it to the repo +- we upload the repo to Vercel and let it build +- once it builds, we get the deployment url and run some tests diff --git a/bench/vercel/bench.js b/bench/vercel/bench.js new file mode 100644 index 0000000000000..7aea7631acb16 --- /dev/null +++ b/bench/vercel/bench.js @@ -0,0 +1,70 @@ +import { Command } from 'commander' +import console from 'console' + +import chalk from 'chalk' + +import PQueue from 'p-queue' +import { generateProjects, cleanupProjectFolders } from './project-utils.js' +import { printBenchmarkResults } from './chart.js' +import { genRetryableRequest } from './gen-request.js' + +const program = new Command() + +const queue = new PQueue({ concurrency: 25 }) +const TTFB_OUTLIERS_THRESHOLD = 250 + +program.option('-p, --path ') + +program.parse(process.argv) + +const options = program.opts() + +if (options.path) { + console.log('Running benchmark for path: ', options.path) +} + +try { + const [originDeploymentURL, headDeploymentURL] = await generateProjects() + + const originBenchmarkURL = `${originDeploymentURL}${options.path || ''}` + const headBenchmarkURL = `${headDeploymentURL}${options.path || ''}` + + console.log(`Origin deployment URL: ${originBenchmarkURL}`) + console.log(`Head deployment URL: ${headBenchmarkURL}`) + console.log(`Running benchmark...`) + + const benchResults = await runBenchmark(originBenchmarkURL) + + const headBenchResults = await runBenchmark(headBenchmarkURL) + + console.log(chalk.bold('Benchmark results for cold:')) + printBenchmarkResults( + { + origin: benchResults, + head: headBenchResults, + }, + (r) => r.cold && r.firstByte <= TTFB_OUTLIERS_THRESHOLD && r.firstByte + ) + console.log(chalk.bold('Benchmark results for hot:')) + printBenchmarkResults( + { + origin: benchResults, + head: headBenchResults, + }, + (r) => !r.cold && r.firstByte <= TTFB_OUTLIERS_THRESHOLD && r.firstByte + ) +} catch (err) { + console.log(chalk.red('Benchmark failed: ', err)) +} finally { + await cleanupProjectFolders() +} + +async function runBenchmark(url) { + return ( + await Promise.all( + Array.from({ length: 500 }).map(() => + queue.add(() => genRetryableRequest(url)) + ) + ) + ).filter(Boolean) +} diff --git a/bench/vercel/benchmark-app/.gitignore b/bench/vercel/benchmark-app/.gitignore new file mode 100644 index 0000000000000..9a0fb41c7f85d --- /dev/null +++ b/bench/vercel/benchmark-app/.gitignore @@ -0,0 +1,2 @@ +.vercel +webpack-stats-client.json \ No newline at end of file diff --git a/bench/vercel/benchmark-app/app/layout.js b/bench/vercel/benchmark-app/app/layout.js new file mode 100644 index 0000000000000..3a313aa413fdb --- /dev/null +++ b/bench/vercel/benchmark-app/app/layout.js @@ -0,0 +1,14 @@ +import * as React from 'react' + +export default function Root({ children }) { + return ( + + + {children} + + ) +} + +export const config = { + runtime: 'experimental-edge', +} diff --git a/bench/vercel/benchmark-app/app/rsc/page.js b/bench/vercel/benchmark-app/app/rsc/page.js new file mode 100644 index 0000000000000..7598427263e55 --- /dev/null +++ b/bench/vercel/benchmark-app/app/rsc/page.js @@ -0,0 +1,14 @@ +import * as React from 'react' + +// if (!('hot' in Math)) Math.hot = false + +export default function page() { + // const previous = Math.hot + // Math.hot = true + // return
    {previous ? 'HOT' : 'COLD'}
    + return
    hello
    +} + +export const config = { + runtime: 'experimental-edge', +} diff --git a/bench/vercel/benchmark-app/next.config.js b/bench/vercel/benchmark-app/next.config.js new file mode 100644 index 0000000000000..7041c1b531d4f --- /dev/null +++ b/bench/vercel/benchmark-app/next.config.js @@ -0,0 +1,34 @@ +const { StatsWriterPlugin } = require('webpack-stats-plugin') +const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer') + +module.exports = { + experimental: { + appDir: true, + }, + webpack: (config, options) => { + const { nextRuntime = 'client' } = options + if (process.env.ANALYZE) { + if (nextRuntime === 'edge') + config.plugins.push( + new BundleAnalyzerPlugin({ + analyzerMode: 'static', + openAnalyzer: true, + reportFilename: options.isServer + ? '../analyze/server.html' + : './analyze/client.html', + }) + ) + config.plugins.push( + new StatsWriterPlugin({ + filename: `../webpack-stats-${nextRuntime}.json`, + stats: { + assets: true, + chunks: true, + modules: true, + }, + }) + ) + } + return config + }, +} diff --git a/bench/vercel/benchmark-app/package.json b/bench/vercel/benchmark-app/package.json new file mode 100644 index 0000000000000..883a323075faf --- /dev/null +++ b/bench/vercel/benchmark-app/package.json @@ -0,0 +1,14 @@ +{ + "name": "stats-app", + "private": true, + "license": "MIT", + "dependencies": { + "webpack-bundle-analyzer": "^4.6.1", + "webpack-stats-plugin": "^1.1.0" + }, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start" + } +} diff --git a/bench/vercel/benchmark-app/pages/index.js b/bench/vercel/benchmark-app/pages/index.js new file mode 100644 index 0000000000000..7ab1d4cea6d4b --- /dev/null +++ b/bench/vercel/benchmark-app/pages/index.js @@ -0,0 +1,20 @@ +if (!('hot' in Math)) Math.hot = false + +export default function page({ hot }) { + return `${hot ? 'HOT' : 'COLD'}` +} + +export async function getServerSideProps() { + const wasHot = Math.hot + Math.hot = true + + return { + props: { + hot: wasHot, + }, + } +} + +export const config = { + runtime: 'experimental-edge', +} diff --git a/bench/vercel/chart.js b/bench/vercel/chart.js new file mode 100644 index 0000000000000..c35e07e9d74ad --- /dev/null +++ b/bench/vercel/chart.js @@ -0,0 +1,99 @@ +import downsampler from 'downsample-lttb' +import asciichart from 'asciichart' +import terminalSize from 'term-size' + +const CHART_WIDTH = terminalSize().columns - 15 // space for the labels + +function getMetrics(data) { + const sorted = [...data].sort((a, b) => a - b) + const getPercentile = (percentile) => { + const index = Math.floor((sorted.length - 1) * percentile) + return sorted[index] + } + return { + hits: sorted.length, + confidenceInterval: round(getConfidenceInterval(sorted)), + median: getPercentile(0.5), + avg: sorted.reduce((a, b) => a + b, 0) / sorted.length, + p75: getPercentile(0.75), + p95: getPercentile(0.95), + p99: getPercentile(0.99), + p25: getPercentile(0.25), + min: sorted[0], + max: sorted[sorted.length - 1], + } +} + +function round(num) { + return Math.round(num * 100) / 100 +} + +// thanks Copilot +function getConfidenceInterval(data) { + const n = data.length + const m = data.reduce((a, b) => a + b) / n + const s = Math.sqrt( + data.map((x) => Math.pow(x - m, 2)).reduce((a, b) => a + b) / n + ) + const z = 1.96 // 95% confidence + const e = z * (s / Math.sqrt(n)) + return e +} + +export function downsample(data, maxPoints) { + const sortedData = [...data].sort((a, b) => a - b) + return downsampler + .processData( + // the downsampler expects a 2d array of [x, y] values, so we need to add an index + sortedData.map((p, i) => [p, i]), + maxPoints + ) + .map((p) => p[0]) +} + +export function printBenchmarkResults({ origin, head }, metricSelector) { + const [processedOriginData, processedHeadData] = [origin, head].map( + (results) => results.map(metricSelector).filter(Boolean) + ) + + const [originMetrics, headMetrics] = [ + processedOriginData, + processedHeadData, + ].map(getMetrics) + + const deltaMetrics = { + min: headMetrics.min - originMetrics.min, + max: headMetrics.max - originMetrics.max, + avg: headMetrics.avg - originMetrics.avg, + median: headMetrics.median - originMetrics.median, + p95: headMetrics.p95 - originMetrics.p95, + p99: headMetrics.p99 - originMetrics.p99, + p75: headMetrics.p75 - originMetrics.p75, + p25: headMetrics.p25 - originMetrics.p25, + } + + console.table({ + origin: originMetrics, + head: headMetrics, + delta: deltaMetrics, + }) + + const [originData, headData] = [processedOriginData, processedHeadData].map( + (data) => + downsample( + data, + Math.min( + CHART_WIDTH, + processedOriginData.length, + processedHeadData.length + ) + ) + ) + + console.log( + asciichart.plot([originData, headData], { + height: 15, + colors: [asciichart.blue, asciichart.red], + }) + ) +} diff --git a/bench/vercel/gen-request.js b/bench/vercel/gen-request.js new file mode 100644 index 0000000000000..ac99226f55a96 --- /dev/null +++ b/bench/vercel/gen-request.js @@ -0,0 +1,41 @@ +import https from 'https' +import timer from '@szmarczak/http-timer' + +// a wrapper around genAsyncRequest that will retry the request 5 times if it fails +export async function genRetryableRequest(url) { + let retries = 0 + while (retries < 5) { + try { + return await genAsyncRequest(url) + } catch (err) {} + retries++ + await new Promise((r) => setTimeout(r, 1000)) + } + throw new Error(`Failed to fetch ${url}, too many retries`) +} + +// a wrapper around http.request that is enhanced with timing information +async function genAsyncRequest(url) { + return new Promise((resolve, reject) => { + const request = https.get(url) + timer(request) + request.on('response', (response) => { + let body = '' + response.on('data', (data) => { + body += data + }) + response.on('end', () => { + resolve({ + ...response.timings.phases, + cold: !body.includes('HOT'), + }) + }) + response.on('error', (err) => { + reject(err) + }) + }) + request.on('error', (err) => { + reject(err) + }) + }) +} diff --git a/bench/vercel/generate-package-json.js b/bench/vercel/generate-package-json.js new file mode 100644 index 0000000000000..1d449d460c1ca --- /dev/null +++ b/bench/vercel/generate-package-json.js @@ -0,0 +1,52 @@ +import execa from 'execa' +import fs from 'fs/promises' +import path from 'path' + +export async function generatePackageJson(folder, withLocalNext = false) { + const packageJson = JSON.parse( + await fs.readFile(path.join(folder, 'package.json')) + ) + + const currentVersions = await getCurrentRootReactPackagesVersions() + + packageJson.dependencies = packageJson.dependencies || {} + packageJson.dependencies['react'] = currentVersions.react + packageJson.dependencies['react-dom'] = currentVersions['react-dom'] + if (withLocalNext) { + packageJson.dependencies.next = await packNextBuild(folder) + } else { + packageJson.dependencies.next = await getCurrentNextVersion() + } + + await fs.writeFile( + path.join(folder, 'package.json'), + JSON.stringify(packageJson, null, 2) + ) +} + +export async function packNextBuild(folder) { + const process = await execa('npm', [ + 'pack', + '../../packages/next', + `--pack-destination=${folder}`, + ]) + + return `file:./${process.stdout}` +} + +async function getCurrentNextVersion() { + const packageJson = JSON.parse( + await fs.readFile('../../packages/next/package.json', 'utf8') + ) + return packageJson.version +} + +async function getCurrentRootReactPackagesVersions() { + const packageJson = JSON.parse( + await fs.readFile('../../package.json', 'utf8') + ) + return { + react: packageJson.devDependencies['react-exp'], + 'react-dom': packageJson.devDependencies['react-dom-exp'], + } +} diff --git a/bench/vercel/package.json b/bench/vercel/package.json new file mode 100644 index 0000000000000..4ce5045df58f6 --- /dev/null +++ b/bench/vercel/package.json @@ -0,0 +1,24 @@ +{ + "name": "bench-production", + "version": "1.0.0", + "description": "Scripts for benchmarking in production.", + "main": "bench.js", + "type": "module", + "scripts": { + "bench": "node bench.js" + }, + "author": "", + "license": "ISC", + "dependencies": { + "@szmarczak/http-timer": "5.0.1", + "asciichart": "1.5.25", + "commander": "2.20.0", + "dotenv": "10.0.0", + "downsample-lttb": "0.0.1", + "listr2": "5.0.5", + "p-queue": "7.3.0", + "term-size": "3.0.2", + "webpack-bundle-analyzer": "^4.6.1", + "webpack-stats-plugin": "^1.1.0" + } +} diff --git a/bench/vercel/project-utils.js b/bench/vercel/project-utils.js new file mode 100644 index 0000000000000..6d069dc0a619c --- /dev/null +++ b/bench/vercel/project-utils.js @@ -0,0 +1,218 @@ +import { config } from 'dotenv' + +import fetch from 'node-fetch' +import chalk from 'chalk' +import execa from 'execa' +import path from 'path' +import url from 'url' +import { generatePackageJson } from './generate-package-json.js' +import { Listr } from 'listr2' + +config() + +const TEST_PROJECT_NAME = process.env.VERCEL_TEST_PROJECT_NAME +const ORIGIN_PROJECT_NAME = TEST_PROJECT_NAME + '-origin' +const HEAD_PROJECT_NAME = TEST_PROJECT_NAME + '-head' + +const TEST_TEAM_NAME = process.env.VERCEL_TEST_TEAM +const TEST_TOKEN = process.env.VERCEL_TEST_TOKEN +const VERCEL_EDGE_FUNCTIONS_BRIDGE_PKG = + process.env.VERCEL_EDGE_FUNCTIONS_BRIDGE_PKG +const __dirname = url.fileURLToPath(new URL('.', import.meta.url)) + +const appFolder = path.join(__dirname, 'benchmark-app') +const originAppFolder = path.join(__dirname, 'benchmark-app-origin') +const headAppFolder = path.join(__dirname, 'benchmark-app-head') + +export async function generateProjects() { + const { originUrl, headUrl } = await new Listr( + [ + { + title: 'Origin project', + task: (ctx, task) => + task.newListr( + (parent) => [ + { + title: 'Resetting project', + task: async () => { + await resetProject(ORIGIN_PROJECT_NAME) + }, + }, + { + title: 'copying app', + task: async () => { + await execa('cp', ['-f', '-R', appFolder, originAppFolder]) + }, + }, + { + title: 'Set Next.js version in package.json', + task: async () => { + await generatePackageJson(originAppFolder) + }, + }, + { + title: 'deploying project', + task: async () => { + const url = await deployProject( + ORIGIN_PROJECT_NAME, + originAppFolder + ) + ctx.originUrl = url + }, + }, + ], + { concurrent: false } + ), + }, + { + title: 'Head project', + task: (ctx, task) => + task.newListr( + (parent) => [ + { + title: 'Resetting project', + task: async () => { + await resetProject(HEAD_PROJECT_NAME) + }, + }, + { + title: 'copying app', + task: async () => { + await execa('cp', ['-f', '-R', appFolder, headAppFolder]) + }, + }, + { + title: 'pack local Next.js version', + task: async () => { + await generatePackageJson(headAppFolder, true) + }, + }, + { + title: 'deploying project', + task: async () => { + const url = await deployProject( + HEAD_PROJECT_NAME, + headAppFolder + ) + ctx.headUrl = url + }, + }, + ], + { concurrent: false } + ), + }, + ], + { concurrent: true } + ).run() + + return [originUrl, headUrl] +} + +export async function cleanupProjectFolders() { + await Promise.all([ + execa('rm', ['-rf', originAppFolder]), + execa('rm', ['-rf', headAppFolder]), + ]) +} + +async function resetProject(projectName) { + const deleteRes = await fetch( + `https://vercel.com/api/v8/projects/${encodeURIComponent( + projectName + )}?teamId=${TEST_TEAM_NAME}`, + { + method: 'DELETE', + headers: { + Authorization: `Bearer ${TEST_TOKEN}`, + }, + } + ) + + if (!deleteRes.ok && deleteRes.status !== 404) { + throw new Error( + `Failed to delete project got status ${ + deleteRes.status + }, ${await deleteRes.text()}` + ) + } + + const createRes = await fetch( + `https://vercel.com/api/v8/projects?teamId=${TEST_TEAM_NAME}`, + { + method: 'POST', + headers: { + 'content-type': 'application/json', + Authorization: `Bearer ${TEST_TOKEN}`, + }, + body: JSON.stringify({ + framework: 'nextjs', + name: projectName, + }), + } + ) + + if (!createRes.ok) { + throw new Error( + `Failed to create project got status ${ + createRes.status + }, ${await createRes.text()}` + ) + } +} + +export async function deployProject(projectName, appFolder) { + try { + const vercelFlags = ['--scope', TEST_TEAM_NAME] + const vercelEnv = { ...process.env, TOKEN: TEST_TOKEN } + + // link the project + const linkRes = await execa( + 'vercel', + ['link', '-p', projectName, '--confirm', ...vercelFlags], + { + cwd: appFolder, + env: vercelEnv, + } + ) + + if (linkRes.exitCode !== 0) { + throw new Error( + `Failed to link project ${linkRes.stdout} ${linkRes.stderr} (${linkRes.exitCode})` + ) + } + + const deployRes = await execa( + 'vercel', + [ + 'deploy', + '--build-env', + 'NEXT_PRIVATE_TEST_MODE=1', + '--build-env', + 'NEXT_TELEMETRY_DISABLED=1', + ...(VERCEL_EDGE_FUNCTIONS_BRIDGE_PKG + ? [ + '--build-env', + `VERCEL_EDGE_FUNCTIONS_BRIDGE_PKG=${VERCEL_EDGE_FUNCTIONS_BRIDGE_PKG}`, + ] + : []), + '--force', + ...vercelFlags, + ], + { + cwd: appFolder, + env: vercelEnv, + } + ) + + if (deployRes.exitCode !== 0) { + throw new Error( + `Failed to deploy project ${linkRes.stdout} ${linkRes.stderr} (${linkRes.exitCode})` + ) + } + + return deployRes.stdout + } catch (err) { + console.log(chalk.red('Deployment failed: ', err)) + throw err + } +} diff --git a/contributing.md b/contributing.md index 2d9a0da2ddde6..32733ca5e4002 100644 --- a/contributing.md +++ b/contributing.md @@ -1,54 +1,33 @@ # Contributing to Next.js -Our Commitment to Open Source can be found [here](https://zeit.co/blog/oss) +[Watch the 40-minute walkthrough video on how to contribute to Next.js.](https://www.youtube.com/watch?v=cuoNzXFLitc) -1. [Fork](https://help.github.com/articles/fork-a-repo/) this repository to your own GitHub account and then [clone](https://help.github.com/articles/cloning-a-repository/) it to your local device. -1. Install yarn: `npm install -g yarn` -1. Install the dependencies: `yarn` -1. Run `yarn run dev` to build and watch for code changes +- Read about our [Commitment to Open Source](https://vercel.com/oss). +- Before jumping into a PR be sure to search [existing PRs](https://github.com/vercel/next.js/pulls) or [issues](https://github.com/vercel/next.js/issues) for an open or closed item that relates to your submission. -## To run tests +## Repository -Running all tests: +- [Triaging](./contributing/repository/triaging.md) +- [Linting](./contributing/repository/linting.md) +- [Release Channels and Publishing](./contributing/repository/release-channels-publishing.md) +- [Pull Request Descriptions](./contributing/repository/pull-request-descriptions.md) -``` -yarn testonly -``` +## Documentation -Running a specific test suite inside of the `test/integration` directory: +- [Adding Documentation](./contributing/docs/adding-documentation.md) -``` -yarn testonly --testPathPattern "production" -``` +## Examples -Running just one test in the `production` test suite: +To contribute to [our examples](./examples), please see -``` -yarn testonly --testPathPattern "production" -t "should allow etag header support" -``` +- [Adding Examples](./contributing/examples/adding-examples.md) +- [Run Example Apps](./contributing/examples/run-example-apps.md) -## Running the integration test apps without running tests +## Core -``` -./node_modules/.bin/next ./test/integration/basic -``` - -## Testing in your own app - -Because of the way Node.js resolves modules the easiest way to test your own application is copying it into the `test` directory. - -``` -cp -r yourapp /test/integration/yourapp -``` - -Make sure you remove `react` `react-dom` and `next` from `test/integration/yourapp/node_modules` as otherwise they will be overwritten. - -```bash -rm -rf /test/integration/yourapp/{react,react-dom,next,next-server} -``` - -Then run your app using: - -``` -./node_modules/.bin/next ./test/integration/yourapp -``` +- [Developing](./contributing/core/developing.md) +- [Building](./contributing/core/building.md) +- [Testing](./contributing/core/testing.md) +- [Adding Error Links](./contributing/core/adding-error-links.md) +- [Developing Using Local App](./contributing/core/developing-using-local-app.md) + diff --git a/contributing/core/adding-error-links.md b/contributing/core/adding-error-links.md new file mode 100644 index 0000000000000..0f6ccb3d66227 --- /dev/null +++ b/contributing/core/adding-error-links.md @@ -0,0 +1,12 @@ +# Adding Warning and Error Descriptions + +Next.js has a system to add helpful links to warnings and errors. + +This allows the logged message to be short while giving a broader description and instructions on how to solve the warning/error on the documentation. + +In general, all warnings and errors added should have these links attached. + +Below are the steps to add a new link: + +1. Run `pnpm new-error` which will create the error document and update the manifest automatically. +2. At the end of the command the URL for the error will be provided, add that to your error. diff --git a/contributing/core/building.md b/contributing/core/building.md new file mode 100644 index 0000000000000..bce370380cbc8 --- /dev/null +++ b/contributing/core/building.md @@ -0,0 +1,13 @@ +# Building + +You can build Next.js, including all type definitions and packages, with: + +```bash +pnpm build +``` + +By default, the latest canary of the `next-swc` binaries will be installed and used. If you are actively working on Rust code or you need to test out the most recent Rust code that hasn't been published as a canary yet, you can [install Rust](https://www.rust-lang.org/tools/install) and run `pnpm --filter=@next/swc build-native`. + +If you want to test out the wasm build locally, you will need to [install wasm-pack](https://rustwasm.github.io/wasm-pack/installer/). Run `pnpm --filter=@next/swc build-wasm --target ` to build and `node ./scripts/setup-wasm.mjs` to copy it into your `node_modules`. Run next with `NODE_OPTIONS='--no-addons'` to force it to use the wasm binary. + +If you need to clean the project for any reason, use `pnpm clean`. diff --git a/contributing/core/developing-using-local-app.md b/contributing/core/developing-using-local-app.md new file mode 100644 index 0000000000000..bb7a5e51c35f2 --- /dev/null +++ b/contributing/core/developing-using-local-app.md @@ -0,0 +1,50 @@ +# Developing Using Your Local Version of Next.js + +There are two options to develop with your local version of the codebase: + +## Develop inside the monorepo + +This will use the version of `next` built inside of the Next.js monorepo. You can also let `pnpm dev` run in a separate terminal. This will let you make changes to Next.js at the same time (note that some changes might require re-running `pnpm next-with-deps` or `pnpm next`). + +If your app does not have dependencies, you can create a directory inside the monorepo (eg.: `dev-app`) and run `pnpm next ./dev-app` without creating a `package.json` file. + +If you already have an app and it has dependencies, you can follow these steps: + +1. Move your app inside of the Next.js monorepo. + +2. Run with `pnpm next-with-deps ./app-path-in-monorepo` + +## Set as a local dependency in package.json + +1. Run `pnpm dev` in the background in the Next.js monorepo. + +2. In your app's root directory, run: + + ```sh + pnpm add ./path/to/next.js/{packages/next,node_modules/{react,react-dom}} + ``` + + to re-install all of the dependencies and point `next`, `react` and `react-dom` to the monorepo versions. + + Note that Next.js will be copied from the locally compiled version as opposed to being downloaded from the NPM registry. + +3. Run your application as you normally would. + +### Troubleshooting + +- If you see the below error while running `pnpm dev` with `next`: + +``` +Failed to load SWC binary, see more info here: https://nextjs.org/docs/messages/failed-loading-swc +``` + +Try to add the below section to your `package.json`, then run again + +```json +"optionalDependencies": { + "@next/swc-linux-x64-gnu": "canary", + "@next/swc-win32-x64-msvc": "canary", + "@next/swc-darwin-x64": "canary", + "@next/swc-darwin-arm64": "canary" +}, +``` diff --git a/contributing/core/developing.md b/contributing/core/developing.md new file mode 100644 index 0000000000000..3614fe22e96b9 --- /dev/null +++ b/contributing/core/developing.md @@ -0,0 +1,44 @@ +# Developing + +- The development branch is `canary`. +- All pull requests should be opened against `canary`. +- The changes on the `canary` branch are published to the `@canary` tag on npm regularly. + +To develop locally: + +1. Install the [GitHub CLI](https://github.com/cli/cli#installation). +1. Clone the Next.js repository: + ``` + gh repo clone vercel/next.js + ``` +1. Create a new branch: + ``` + git checkout -b MY_BRANCH_NAME origin/canary + ``` +1. Enable pnpm: + ``` + corepack enable pnpm + ``` +1. Install the dependencies with: + ``` + pnpm install + ``` +1. Start developing and watch for code changes: + ``` + pnpm dev + ``` +1. In a new terminal, run `pnpm types` to compile declaration files from + TypeScript. + _Note: You may need to repeat this step if your types get outdated._ +1. When your changes are finished, commit them to the branch: + ``` + git add . + git commit -m "DESCRIBE_YOUR_CHANGES_HERE" + ``` +1. To open a pull request you can use the GitHub CLI which automatically forks and sets up a remote branch. Follow the prompts when running: + ``` + gh pr create + ``` + +For instructions on how to build a project with your local version of the CLI, +see **[Developing Using Your Local Version of Next.js](./developing-using-local-app.md)** as linking the package is not sufficient to develop locally. diff --git a/contributing/core/testing.md b/contributing/core/testing.md new file mode 100644 index 0000000000000..ae3e8db7bfd59 --- /dev/null +++ b/contributing/core/testing.md @@ -0,0 +1,71 @@ +# Testing + +## Running tests + +Before you start to run tests, you need to build project first: + +```bash +pnpm build +``` + +And for more detail about building, you can check out [building.md](./building.md) + +We recommend running the tests in headless mode (with the browser windows hidden) and with a specific directory pattern and/or test name (`-t`) which ensures only a small part of the test suite is run locally: + +For example, running one test in the production test suite: + +Running one test in the `test/integration/production` test suite: + +```sh +pnpm testheadless test/integration/production/ -t "should allow etag header support" +``` + +Running all tests in the `test/integration/production` test suite: + +```sh +pnpm testheadless test/integration/production/ +``` + +When you want to debug a particular test you can replace `pnpm testheadless` with `pnpm testonly` to opt out of the headless browser. +When the test runs it will open the browser that is in the background by default, allowing you to inspect what is on the screen. + +```sh +pnpm testonly test/integration/production/ -t "should allow etag header support" +``` + +## Writing tests for Next.js + +### Getting Started + +You can set up a new test using `pnpm new-test` which will start from a template related to the test type. + +### Test Types in Next.js + +- e2e: Runs against `next dev`, `next start`, and deployed to Vercel. +- development: Runs against `next dev`. +- production: Runs against `next start`. +- integration: Historical location of tests. Runs misc checks and modes. Ideally, we don't add new test suites here anymore as these tests are not isolated from the monorepo. +- unit: Very fast tests that should run without a browser or run `next` and should be testing a specific utility. + +For the e2e, development, and production tests the `createNext` utility should be used and an example is available [here](../../test/e2e/example.txt). This creates an isolated Next.js install to ensure nothing in the monorepo is relied on accidentally causing incorrect tests. + +All new test suites should be written in TypeScript either `.ts` (or `.tsx` for unit tests). This will help ensure we catch smaller issues in tests that could cause flakey or incorrect tests. + +If a test suite already exists that relates closely to the item being tested (e.g. hash navigation relates to existing navigation test suites) the new checks can be added to the existing test suite. + +### Best Practices + +- When checking for a condition that might take time, ensure it is waited for either using the browser `waitForElement` or using the `check` util in `next-test-utils`. +- When applying a fix, ensure the test fails without the fix. This makes sure the test will properly catch regressions. + +### Helpful environment variables + +Some test-specific environment variables can be used to help debug isolated tests better, these can be leveraged by prefixing the `pnpm test` command. + +- When investigating failures in isolated tests you can use `NEXT_TEST_SKIP_CLEANUP=1` to prevent deleting the temp folder created for the test, then you can run `pnpm next` while inside of the temp folder to debug the fully set-up test project. +- You can also use `NEXT_SKIP_ISOLATE=1` if the test doesn't need to be installed to debug and it will run inside of the Next.js repo instead of the temp directory, this can also reduce test times locally but is not compatible with all tests. +- The `NEXT_TEST_MODE` env variable allows toggling specific test modes for the `e2e` folder, it can be used when not using `pnpm test-dev` or `pnpm test-start` directly. Valid test modes can be seen here: https://github.com/vercel/next.js/blob/aa664868c102ddc5adc618415162d124503ad12e/test/lib/e2e-utils.ts#L46 + +### Debugging + +When tests are run in CI and a test failure occurs we attempt to capture traces of the playwright run to make debugging the failure easier. A test-trace artifact should be uploaded after the workflow completes which can be downloaded, unzipped, and then inspected with `pnpm playwright show-trace ./path/to/trace` diff --git a/contributing/core/vscode-debugger.md b/contributing/core/vscode-debugger.md new file mode 100644 index 0000000000000..d240b0af040bb --- /dev/null +++ b/contributing/core/vscode-debugger.md @@ -0,0 +1,27 @@ +# Using the VS Code Debugger + +## Debug configurations + +The Next.js monorepo provides configurations in the [`.vscode/launch.json`](../../.vscode/launch.json) file to help you [debug Next.js from VS Code](https://code.visualstudio.com/docs/editor/debugging). + +The common configurations are: + +- Launch app development: Run `next dev` with an attached debugger +- Launch app build: Run `next build` with an attached debugger +- Launch app production: Run `next start` with an attached debugger + +### Run a specific app + +Any Next.js app inside the monorepo can be debugged with these configurations. For example to run "Launch app development" against `examples/hello-world`: + +1. Open the [`.vscode/launch.json`](../../.vscode/launch.json) file. +2. Find the configuration "Launch app development". +3. Edit the `runtimeArgs` array's last item to be `"examples/hello-world"`. +4. Save the file. +5. Now you can start the debugger and it will run against the `examples/hello-world` app! + +To see the changes you make to the Next.js codebase during development, you can run `pnpm dev` in the root directory, which will watch for file changes in `packages/next` and recompile the Next.js source code on any file saves. + +## Breakpoints + +When developing/debugging Next.js, you can set breakpoints anywhere in the `packages/next` source code that will stop the debugger at certain locations so you can examine the behavior. Read more about [breakpoints in the VS Code documentation](https://code.visualstudio.com/docs/nodejs/nodejs-debugging#_breakpoints). diff --git a/contributing/docs/adding-documentation.md b/contributing/docs/adding-documentation.md new file mode 100644 index 0000000000000..f89bd836dd565 --- /dev/null +++ b/contributing/docs/adding-documentation.md @@ -0,0 +1,40 @@ +# Updating Documentation Paths + +Our documentation currently leverages a [manifest file](/docs/manifest.json), which is how documentation entries are checked. + +When adding a new entry under an existing category you only need to add an entry with `{title: '', path: '/docs/path/to/file.md'}`. The "title" is what is shown on the sidebar. + +When moving the location/url of an entry, the "title" field can be removed from the existing entry and the ".md" extension removed from the "path", then a "redirect" field with the shape of `{permanent: true/false, destination: '/some-url'}` can be added. A new entry should be added with the "title" and "path" fields if the document is renamed within the [`docs` folder](/docs) that points to the new location in the folder, e.g. `/docs/some-url.md` + +Example of moving documentation file: + +Before: + +```json +[ + { + "path": "/docs/original.md", + "title": "Hello world" + } +] +``` + +After: + +```json +[ + { + "path": "/docs/original", + "redirect": { + "permanent": false, + "destination": "/new" + } + } + { + "path": "/docs/new.md", + "title": "Hello world" + }, +] +``` + +Note: the manifest is checked automatically in the "lint" step in CI when opening a PR. diff --git a/contributing/examples/adding-examples.md b/contributing/examples/adding-examples.md new file mode 100644 index 0000000000000..c6f4cebc822ad --- /dev/null +++ b/contributing/examples/adding-examples.md @@ -0,0 +1,57 @@ +## Adding examples + +When you add an example to the [examples](examples) directory, please follow these guidelines to ensure high-quality examples: + +- TypeScript should be leveraged for new examples (no need for separate JavaScript and TypeScript examples, converting old JavaScript examples is preferred) +- Examples should not add custom ESLint configuration (we have specific templates for ESLint) +- If API routes aren't used in an example, they should be omitted +- If an example exists for a certain library and you would like to showcase a specific feature of that library, the existing example should be updated (instead of adding a new example) +- Package manager specific config should not be added (e.g. `resolutions` in `package.json`) +- In `package.json` the version of `next` should be `latest` +- In `package.json` the dependency versions should be up-to-date +- Use `export default function` for page components and API Routes instead of `const`/`let` (The exception is if the page has `getInitialProps`, in which case [`NextPage`](https://nextjs.org/docs/api-reference/data-fetching/get-initial-props#typescript) could be useful) +- CMS example directories should be prefixed with `cms-` +- Example directories should not be prefixed with `with-` +- Make sure linting passes (you can run `pnpm lint-fix`) + +Also, don’t forget to add a `README.md` file with the following format: + +- Replace `DIRECTORY_NAME` with the directory name you’re adding. +- Fill in `Example Name` and `Description`. +- Examples should be TypeScript first, if possible. +- Omit the `name` and `version` fields from your `package.json`. +- Ensure all your dependencies are up to date. +- Ensure you’re using [`next/image`](https://nextjs.org/docs/api-reference/next/image). +- To add additional installation instructions, please add them where appropriate. +- To add additional notes, add `## Notes` section at the end. +- Remove the `Deploy your own` section if your example can’t be immediately deployed to Vercel. + +````markdown +# Example Name + +Description + +## Deploy your own + +Deploy the example using [Vercel](https://vercel.com?utm_source=github&utm_medium=readme&utm_campaign=next-example): + +[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/git/external?repository-url=https://github.com/vercel/next.js/tree/canary/examples/DIRECTORY_NAME&project-name=DIRECTORY_NAME&repository-name=DIRECTORY_NAME) + +## How to use + +Execute [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app) with [npm](https://docs.npmjs.com/cli/init), [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/), or [pnpm](https://pnpm.io) to bootstrap the example: + +```bash +npx create-next-app --example DIRECTORY_NAME DIRECTORY_NAME-app +``` + +```bash +yarn create next-app --example DIRECTORY_NAME DIRECTORY_NAME-app +``` + +```bash +pnpm create next-app --example DIRECTORY_NAME DIRECTORY_NAME-app +``` + +Deploy it to the cloud with [Vercel](https://vercel.com/new?utm_source=github&utm_medium=readme&utm_campaign=next-example) ([Documentation](https://nextjs.org/docs/deployment)). +```` diff --git a/contributing/examples/run-example-apps.md b/contributing/examples/run-example-apps.md new file mode 100644 index 0000000000000..c95c20cd35a5c --- /dev/null +++ b/contributing/examples/run-example-apps.md @@ -0,0 +1,22 @@ +# Running Example Apps + +Running examples can be done with: + +```sh +pnpm next ./examples/basic-css/ +``` + +To figure out which pages are available for the given example, you can run: + +```sh +EXAMPLE=./test/integration/basic +(\ + cd $EXAMPLE/pages; \ + find . -type f \ + | grep -v '\.next' \ + | sed 's#^\.##' \ + | sed 's#index\.js##' \ + | sed 's#\.js$##' \ + | xargs -I{} echo localhost:3000{} \ +) +``` diff --git a/contributing/repository/linting.md b/contributing/repository/linting.md new file mode 100644 index 0000000000000..513797ce7e280 --- /dev/null +++ b/contributing/repository/linting.md @@ -0,0 +1,37 @@ +# Linting + +The Next.js repository runs [ESLint](https://eslint.org), [Prettier](https://prettier.io) and [alex](https://alexjs.com) to lint and format all code and documentation. + +To lint all code you can run: + +```sh +pnpm lint +``` + +If you get errors, you can run the ESLint and Prettier auto-fix using: + +```sh +pnpm lint-fix +``` + +Not all rules can be auto-fixed, those require manual changes. + +If you get a warning by alex, follow the instructions to correct the language. + +## ESLint + +We recommend installing the [ESLint plugin for VS Code](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint). + +You can find the enabled rules in the [ESLint config](../../.eslintrc.json). + +## Prettier + +We recommend installing the [Prettier plugin for VS Code](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode). + +You can find the format configuration in the [Prettier config](../../.prettierrc.json). + +## alex + +We recommend installing the [AlexJS Linter extension for VSCode](https://marketplace.visualstudio.com/items?itemName=TLahmann.alex-linter) + +You can find the configuration in the [alex config](../../.alexrc). diff --git a/contributing/repository/pull-request-descriptions.md b/contributing/repository/pull-request-descriptions.md new file mode 100644 index 0000000000000..ab217c271b0d4 --- /dev/null +++ b/contributing/repository/pull-request-descriptions.md @@ -0,0 +1,7 @@ +# Pull Request Descriptions + +Pull Requests opened against the repository have a default template to guide you in creating a descriptive explanation of what the PR is for. + +Please make sure to explain the purpose and link any related issues. + +Check out the [`pull_request_template.md`](../../.github/pull_request_template.md) for more details. diff --git a/contributing/repository/release-channels-publishing.md b/contributing/repository/release-channels-publishing.md new file mode 100644 index 0000000000000..8eb4dce091f43 --- /dev/null +++ b/contributing/repository/release-channels-publishing.md @@ -0,0 +1,25 @@ +# Release Channels and Publishing + +Next.js has two release channels: `stable` and `canary`. + +## Stable + +The stable release is what is installed when you `npm install next`. This channel is used by the majority of Next.js users. + +This channel is published at a regular cadence and follows [semantic versioning](https://semver.org). + +Repository maintainers can publish a new stable version using: `pnpm publish-stable`. +The command will ask what version to publish `major`, `minor`, or `patch`. + +## Canary + +The canary channel has to be explicitly installed by users through `npm install next@canary`. + +This channel is published early based on the `canary` branch. It holds all changes that are waiting to be published to the stable channel. + +`canary` is used to test the latest features and bugfixes on real-world applications. + +By installing `next@canary` from time to time you can check if your application is affected by any changes that have not been published yet. + +Repository maintainers can publish a new canary version using: `pnpm publish-canary`. +The command will automatically decide the new version tag as it's an increment from the previous version. diff --git a/contributing/repository/triaging.md b/contributing/repository/triaging.md new file mode 100644 index 0000000000000..71eb1636c63fa --- /dev/null +++ b/contributing/repository/triaging.md @@ -0,0 +1,21 @@ +# Triaging + +Repository maintainers triage every issue and PR opened in the repository. + +Issues are opened with one of these labels: + +- `template: story` - a feature request, converted to an [💡 Ideas discussion](https://github.com/vercel/next.js/discussions/categories/ideas) +- `template: bug` - unverified issue with Next.js itself, or one of the examples in the [`examples`](https://github.com/vercel/next.js/tree/canary/examples) folder +- `template: documentation` - feedback for improvement or an unverified issue with the Next.js documentation + +In the case of a bug report, a maintainer looks at the provided reproduction. If the reproduction is missing or insufficient, a `please add a complete reproduction` label is added. If a reproduction is not provided for more than 30 days, the issue becomes stale and will be automatically closed. If a reproduction is provided within 30 days, the `please add a complete reproduction` label is removed and the issue will not become stale anymore. + +Bug reports must be verified against the `next@canary` release. The canary version of Next.js ships daily and includes all features and fixes that have not been released to the stable version yet. Think of canary as a public beta. Some issues may already be fixed in the canary version, so please verify that your issue reproduces before opening a new issue. Issues not verified against `next@canary` will be closed after 30 days. + +If the issue is specific to the project and not to Next.js itself, it might be converted to a [🎓️ Help discussion](https://github.com/vercel/next.js/discussions/categories/help) + +If the bug is verified, it will receive the `kind: bug` label and will be tracked by the maintainers. An `area:` label can be added to indicate which part of Next.js is affected. + +Confirmed issues never become stale or are closed before resolution. + +All **closed** PRs and Issues will be locked after 30 days of inactivity (eg.: comment, referencing from elsewhere). diff --git a/docs/accessibility.md b/docs/accessibility.md new file mode 100644 index 0000000000000..dc4b4a40ab751 --- /dev/null +++ b/docs/accessibility.md @@ -0,0 +1,48 @@ +--- +description: Learn about the built-in accessibility features of Next.js. +--- + +# Accessibility + +The Next.js team is committed to making Next.js accessible to all developers (and their end-users). By adding accessibility features to Next.js by default, we aim to make the Web more inclusive for everyone. + +## Route Announcements + +When transitioning between pages rendered on the server (e.g. using the `` tag) screen readers and other assistive technology announce the page title when the page loads so that users understand that the page has changed. + +In addition to traditional page navigations, Next.js also supports client-side transitions for improved performance (using `next/link`). To ensure that client-side transitions are also announced to assistive technology, Next.js includes a route announcer by default. + +The Next.js route announcer looks for the page name to announce by first inspecting `document.title`, then the `

    ` element, and finally the URL pathname. For the most accessible user experience, ensure that each page in your application has a unique and descriptive title. + +## Linting + +Next.js provides an [integrated ESLint experience](/docs/basic-features/eslint.md) out of the box, including custom rules for Next.js. By default, Next.js includes `eslint-plugin-jsx-a11y` to help catch accessibility issues early, including warning on: + +- [aria-props](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/HEAD/docs/rules/aria-props.md?rgh-link-date=2021-06-04T02%3A10%3A36Z) +- [aria-proptypes](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/HEAD/docs/rules/aria-proptypes.md?rgh-link-date=2021-06-04T02%3A10%3A36Z) +- [aria-unsupported-elements](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/HEAD/docs/rules/aria-unsupported-elements.md?rgh-link-date=2021-06-04T02%3A10%3A36Z) +- [role-has-required-aria-props](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/HEAD/docs/rules/role-has-required-aria-props.md?rgh-link-date=2021-06-04T02%3A10%3A36Z) +- [role-supports-aria-props](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/HEAD/docs/rules/role-supports-aria-props.md?rgh-link-date=2021-06-04T02%3A10%3A36Z) + +For example, this plugin helps ensure you add alt text to `img` tags, use correct `aria-*` attributes, use correct `role` attributes, and more. + +## Disabling JavaScript + +By default, Next.js prerenders pages to static HTML files. This means that JavaScript is not required to view the HTML markup from the server and is instead used to add interactivity on the client side. + +If your application requires JavaScript to be disabled, and only HTML to be used, you can remove all JavaScript from your application using an experimental flag: + +```js +// next.config.js +export const config = { + unstable_runtimeJS: false, +} +``` + +## Accessibility Resources + +- [WebAIM WCAG checklist](https://webaim.org/standards/wcag/checklist) +- [WCAG 2.1 Guidelines](https://www.w3.org/TR/WCAG21/) +- [The A11y Project](https://www.a11yproject.com/) +- Check [color contrast ratios](https://developer.mozilla.org/en-US/docs/Web/Accessibility/Understanding_WCAG/Perceivable/Color_contrast) between foreground and background elements +- Use [`prefers-reduced-motion`](https://web.dev/prefers-reduced-motion/) when working with animations diff --git a/docs/advanced-features/amp-support/adding-amp-components.md b/docs/advanced-features/amp-support/adding-amp-components.md new file mode 100644 index 0000000000000..63305791d5448 --- /dev/null +++ b/docs/advanced-features/amp-support/adding-amp-components.md @@ -0,0 +1,70 @@ +--- +description: Add components from the AMP community to AMP pages, and make your pages more interactive. +--- + +# Adding AMP Components + +The AMP community provides [many components](https://amp.dev/documentation/components/) to make AMP pages more interactive. Next.js will automatically import all components used on a page and there is no need to manually import AMP component scripts: + +```jsx +export const config = { amp: true } + +function MyAmpPage() { + const date = new Date() + + return ( +
    +

    Some time: {date.toJSON()}

    + + . + +
    + ) +} + +export default MyAmpPage +``` + +The above example uses the [`amp-timeago`](https://amp.dev/documentation/components/amp-timeago/?format=websites) component. + +By default, the latest version of a component is always imported. If you want to customize the version, you can use `next/head`, as in the following example: + +```jsx +import Head from 'next/head' + +export const config = { amp: true } + +function MyAmpPage() { + const date = new Date() + + return ( +
    + + +``` + +Or by using the `dangerouslySetInnerHTML` property: + +```jsx + +``` + +The HTML [script](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script) tag is used to embed any client-side JavaScript. It can either contain inline scripting statements (as shown in the example above) or point to an external script file via the `src` attribute. +This example validates the name and roll number of a user. The `validateFormWithJS()` function does not allow an empty name field, and the roll number must be at least three digits long. The validation is performed when you hit the Submit button. You are not redirected to the next page until the given values are correct. + +![js-validation](https://assets.vercel.com/image/upload/dpr_auto,q_auto,f_auto/nextjs/guides/building-forms/js-validation.jpg) + +#### Form Validation Using Regular Expressions + +JavaScript validation with Regular Expressions uses the `pattern` HTML attribute. A regular expression (commonly known as RegEx) is an object that describes a pattern of characters. You can only apply the `pattern` attribute to the `` element. This way, you can validate the input value using Regular Expressions (RegEx) by defining your own rules. Once again, if the value does not match the defined pattern, the input will give an error. +The below example shows using the `pattern` attribute on an `input` element: + +```html +
    + + + + +
    +``` + +The password form field must only contain digits (0 to 9), lowercase alphabets (a to z) and it must be no more than 15 characters in length. No other characters (#,$,&, etc.) are allowed. The rule in RegEx is written as `[a-z0-9]{1,15}`. + +![form-validate-regex](https://assets.vercel.com/image/upload/dpr_auto,q_auto,f_auto/nextjs/guides/building-forms/form-validate-regex.jpg) + +> To learn more about HTML forms, check out the [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Learn/Forms). + +## Part 2: Project Setup + +In the following section you will be creating forms in React using Next.js. + +Create a new Next.js app. You can use the [create-next-app](https://nextjs.org/docs/getting-started#setup) for a quick start. In your command line terminal, run the following: + +``` +npx create-next-app +``` + +Answer the questions to create your project, and give it a name, this example uses [`next-forms`](https://github.com/vercel/next.js/tree/canary/examples/next-forms). Next `cd` into this directory, and run `npm run dev` or `yarn dev` command to start the development server. + +Open the URL printed in the terminal to ensure that your app is running successfully. + +## Part 3: Setting up a Next.js Form API Route + +Both the client and the server will be built using Next.js. For the server part, create an API endpoint where you will send the form data. + +Next.js offers a file-based system for routing that's built on the [concept of pages](/docs/basic-features/pages). Any file inside the folder `pages/api` is mapped to `/api/*` and will be treated as an API endpoint instead of a page. This [API endpoint](/docs/api-routes/introduction) is going to be server-side only. + +Go to `pages/api`, create a file called `form.js` and paste this code written in Node.js: + +```js +export default function handler(req, res) { + // Get data submitted in request's body. + const body = req.body + + // Optional logging to see the responses + // in the command line where next.js app is running. + console.log('body: ', body) + + // Guard clause checks for first and last name, + // and returns early if they are not found + if (!body.first || !body.last) { + // Sends a HTTP bad request error code + return res.status(400).json({ data: 'First or last name not found' }) + } + + // Found the name. + // Sends a HTTP success code + res.status(200).json({ data: `${body.first} ${body.last}` }) +} +``` + +This form `handler` function will receive the request `req` from the client (i.e. submitted form data). And in return, it'll send a response `res` as JSON that will have both the first and the last name. You can access this API endpoint at `http://localhost:3000/api/form` or replace the localhost URL with an actual Vercel deployment when you deploy. + +> Moreover, you can also attach this API to a database like MongoDB or Google Sheets. This way, your submitted form data will be securely stored for later use. For this guide, no database is used. Instead, the same data is returned to the user to demo how it's done. + +### Form Submission without JavaScript + +You can now use `/api/form` relative endpoint inside the `action` attribute of the form. You are sending form data to the server when the form is submitted via `POST` HTTP method (which is used to send data). + +```html +
    + + + + + +
    +``` + +If you submit this form, it will submit the data to the forms API endpoint `/api/form`. The server then responds, generally handling the data and loading the URL defined by the action attribute, causing a new page load. So in this case you'll be redirected to `http://localhost:3000/api/form` with the following response from the server. + +![form-no-js](https://assets.vercel.com/image/upload/dpr_auto,q_auto,f_auto/nextjs/guides/building-forms/form-no-js.jpg) + +## Part 4: Configuring Forms in Next.js + +You have created a Next.js API Route for form submission. Now it's time to configure the client (the form itself) inside Next.js using React. The first step will be extending your knowledge of HTML forms and converting it into React (using [JSX](https://reactjs.org/docs/introducing-jsx.html)). + +Here's the same form in a [React function component](https://reactjs.org/docs/components-and-props.html) written using [JSX](https://reactjs.org/docs/introducing-jsx.html). + +```js +export default function Form() { + return ( +
    + + + + + + + +
    + ) +} +``` + +Here's what changed: + +- The `for` attribute is changed to `htmlFor`. (Since `for` is a keyword associated with the "for" loop in JavaScript, React elements use `htmlFor` instead.) +- The `action` attribute now has a relative URL which is the form API endpoint. + +This completes the basic structure of your Next.js-based form. + +> You can view the entire source code of [next-forms](https://github.com/vercel/next.js/tree/canary/examples/next-forms) example repo that we're creating here as a working example. Feel free to clone it and start right away. This demo is built with create-next-app, and you can preview the basic form CSS styles inside `/styles/global.css` file. + +![forms with nextjs](https://assets.vercel.com/image/upload/dpr_auto,q_auto,f_auto/nextjs/guides/building-forms/forms-with-nextjs.png) + +## Part 5: Form Submission without JavaScript + +JavaScript brings interactivity to our web applications, but sometimes you need to control the JavaScript bundle from being too large, or your sites visitors might have JavaScript disabled. + +There are several reasons why users disable JavaScript: + +- Addressing bandwidth constraints +- Increasing device (phone or laptop) battery life +- For privacy so they won’t be tracked with analytical scripts + +Regardless of the reason, disabling JavaScript will impact site functionality partially, if not completely. + +Next open the `next-forms` directory. Inside the `/pages` directory, create a file `no-js-form.js`. + +> **Quick Tip**: In Next.js, a page is a React Component exported from a `.js`, `.jsx`, `.ts`, or `.tsx` file in the pages directory. Each page is associated with a route based on its file name. +> +> Example: If you create `pages/no-js-form.js`, it will be accessible at `your-domain.tld/no-js-form`. + +Let's use the same code from above: + +```js +export default function PageWithoutJSbasedForm() { + return ( +
    + + + + + + + +
    + ) +} +``` + +With JavaScript disabled, when you hit the Submit button, an event is triggered, which collects the form data and sends it to our forms API endpoint as defined in the `action` attribute and using `POST` HTTP `method`. You'll be redirected to the `/api/form` endpoint since that's how form `action` works. + +The form data will be submitted on the server as a request `req` to the form handler function written above. It will process the data and return a JSON string as a response `res` with your submitted name included. + +> To improve the experience here, as a response you can redirect the user to a page and thank them for submitting the form. + +## Part 6: Form Submission with JavaScript Enabled + +Inside `/pages`, you'll create another file called `js-form.js`. This will create a `/js-form` page on your Next.js app. + +Now, as soon as the form is submitted, we prevent the form's default behavior of reloading the page. We'll take the form data, convert it to JSON string, and send it to our server, the API endpoint. Finally, our server will respond with the name submitted. All of this with a basic JavaScript `handleSubmit()` function. + +Here's what this function looks like. It's well documented for you to understand each step: + +```js +export default function PageWithJSbasedForm() { + // Handles the submit event on form submit. + const handleSubmit = async (event) => { + // Stop the form from submitting and refreshing the page. + event.preventDefault() + + // Get data from the form. + const data = { + first: event.target.first.value, + last: event.target.last.value, + } + + // Send the data to the server in JSON format. + const JSONdata = JSON.stringify(data) + + // API endpoint where we send form data. + const endpoint = '/api/form' + + // Form the request for sending data to the server. + const options = { + // The method is POST because we are sending data. + method: 'POST', + // Tell the server we're sending JSON. + headers: { + 'Content-Type': 'application/json', + }, + // Body of the request is the JSON data we created above. + body: JSONdata, + } + + // Send the form data to our forms API on Vercel and get a response. + const response = await fetch(endpoint, options) + + // Get the response data from server as JSON. + // If server returns the name submitted, that means the form works. + const result = await response.json() + alert(`Is this your full name: ${result.data}`) + } + return ( + // We pass the event to the handleSubmit() function on submit. +
    + + + + + + + +
    + ) +} +``` + +It's a Next.js page with a React function component called `PageWithJSbasedForm` with a `
    ` element written in JSX. There's no action on the `` element. Instead, we use the `onSubmit` event handler to send data to our `{handleSubmit}` function. + +The `handleSubmit()` function processes your form data through a series of steps: + +- The `event.preventDefault()` stops the `` element from refreshing the entire page. +- We created a JavaScript object called `data` with the `first` and `last` values from the form. +- JSON is a language-agnostic data transfer format. So we use `JSON.stringify(data)` to convert the data to JSON. +- We then use `fetch()` to send the data to our `/api/form` endpoint using JSON and HTTP `POST` method. +- Server sends back a response with the name submitted. Woohoo! 🥳 + +## Conclusion + +This guide has covered the following: + +- The basic HTML `form` element +- Understanding forms with React.js +- Validating forms data with and without JavaScript +- Using Next.js API Routes to handle `req` and `res` from the client and server + +For more details go through [Next.js Learn Course](https://nextjs.org/learn/basics/create-nextjs-app). diff --git a/docs/manifest.json b/docs/manifest.json new file mode 100644 index 0000000000000..15178b9b8f146 --- /dev/null +++ b/docs/manifest.json @@ -0,0 +1,580 @@ +{ + "routes": [ + { + "title": "Documentation", + "heading": true, + "routes": [ + { + "title": "Getting Started", + "path": "/docs/getting-started.md" + }, + { + "title": "Basic Features", + "open": true, + "routes": [ + { + "title": "Pages", + "path": "/docs/basic-features/pages.md" + }, + { + "title": "Data Fetching", + "routes": [ + { + "path": "/docs/basic-features/data-fetching", + "redirect": { + "destination": "/docs/basic-features/data-fetching/overview" + } + }, + { + "path": "/docs/basic-features/data-fetching/index", + "redirect": { + "destination": "/docs/basic-features/data-fetching/overview" + } + }, + { + "title": "Overview", + "path": "/docs/basic-features/data-fetching/overview.md" + }, + { + "title": "getServerSideProps", + "path": "/docs/basic-features/data-fetching/get-server-side-props.md" + }, + { + "title": "getStaticPaths", + "path": "/docs/basic-features/data-fetching/get-static-paths.md" + }, + { + "title": "getStaticProps", + "path": "/docs/basic-features/data-fetching/get-static-props.md" + }, + { + "title": "Incremental Static Regeneration", + "path": "/docs/basic-features/data-fetching/incremental-static-regeneration.md" + }, + { + "title": "Client side", + "path": "/docs/basic-features/data-fetching/client-side.md" + } + ] + }, + { + "title": "Built-in CSS Support", + "path": "/docs/basic-features/built-in-css-support.md" + }, + { + "title": "Layouts", + "path": "/docs/basic-features/layouts.md" + }, + { + "title": "Image Optimization", + "path": "/docs/basic-features/image-optimization.md" + }, + { + "title": "Font Optimization", + "path": "/docs/basic-features/font-optimization.md" + }, + { + "title": "Static File Serving", + "path": "/docs/basic-features/static-file-serving.md" + }, + { + "title": "Fast Refresh", + "path": "/docs/basic-features/fast-refresh.md" + }, + { + "title": "ESLint", + "path": "/docs/basic-features/eslint.md" + }, + { + "title": "TypeScript", + "path": "/docs/basic-features/typescript.md" + }, + { + "title": "Environment Variables", + "path": "/docs/basic-features/environment-variables.md" + }, + { + "title": "Supported Browsers and Features", + "path": "/docs/basic-features/supported-browsers-features.md" + }, + { + "title": "Handling Scripts", + "path": "/docs/basic-features/script.md" + } + ] + }, + { + "title": "Routing", + "routes": [ + { + "title": "Introduction", + "path": "/docs/routing/introduction.md" + }, + { + "title": "Dynamic Routes", + "path": "/docs/routing/dynamic-routes.md" + }, + { + "title": "Imperatively", + "path": "/docs/routing/imperatively.md" + }, + { + "title": "Shallow Routing", + "path": "/docs/routing/shallow-routing.md" + } + ] + }, + { + "title": "API Routes", + "routes": [ + { + "path": "/docs/api-routes/api-middlewares", + "redirect": { + "destination": "/docs/api-routes/request-helpers", + "permanent": true + } + }, + { + "title": "Introduction", + "path": "/docs/api-routes/introduction.md" + }, + { + "title": "Dynamic API Routes", + "path": "/docs/api-routes/dynamic-api-routes.md" + }, + { + "title": "Request Helpers", + "path": "/docs/api-routes/request-helpers.md" + }, + { + "title": "Response Helpers", + "path": "/docs/api-routes/response-helpers.md" + }, + { + "title": "Edge API Routes (Beta)", + "path": "/docs/api-routes/edge-api-routes.md" + } + ] + }, + { + "path": "/docs/middleware", + "redirect": { + "destination": "/docs/advanced-features/middleware", + "permanent": true + } + }, + { + "title": "Going to Production", + "path": "/docs/going-to-production.md" + }, + { + "title": "Deployment", + "path": "/docs/deployment.md" + }, + { + "title": "Authentication", + "path": "/docs/authentication.md" + }, + { + "title": "Testing", + "path": "/docs/testing.md" + }, + { + "title": "Accessibility", + "path": "/docs/accessibility.md" + }, + { + "title": "Guides", + "routes": [ + { + "title": "Building Forms", + "path": "/docs/guides/building-forms.md" + } + ] + }, + { + "title": "Advanced Features", + "routes": [ + { + "title": "Next.js Compiler", + "path": "/docs/advanced-features/compiler.md" + }, + { + "title": "Turbopack", + "path": "/docs/advanced-features/turbopack.md" + }, + { + "title": "Preview Mode", + "path": "/docs/advanced-features/preview-mode.md" + }, + { + "title": "Dynamic Import", + "path": "/docs/advanced-features/dynamic-import.md" + }, + { + "title": "Automatic Static Optimization", + "path": "/docs/advanced-features/automatic-static-optimization.md" + }, + { + "title": "Static HTML Export", + "path": "/docs/advanced-features/static-html-export.md" + }, + { + "title": "Absolute Imports and Module Path Aliases", + "path": "/docs/advanced-features/module-path-aliases.md" + }, + { + "title": "Using MDX", + "path": "/docs/advanced-features/using-mdx.md" + }, + { + "title": "AMP Support", + "routes": [ + { + "title": "Introduction", + "path": "/docs/advanced-features/amp-support/introduction.md" + }, + { + "title": "Adding AMP Components", + "path": "/docs/advanced-features/amp-support/adding-amp-components.md" + }, + { + "title": "AMP Validation", + "path": "/docs/advanced-features/amp-support/amp-validation.md" + }, + { + "title": "AMP in Static HTML export", + "path": "/docs/advanced-features/amp-support/amp-in-static-html-export.md" + }, + { + "title": "TypeScript", + "path": "/docs/advanced-features/amp-support/typescript.md" + } + ] + }, + { + "title": "Customizing Babel Config", + "path": "/docs/advanced-features/customizing-babel-config.md" + }, + { + "title": "Customizing PostCSS Config", + "path": "/docs/advanced-features/customizing-postcss-config.md" + }, + { + "title": "Custom Server", + "path": "/docs/advanced-features/custom-server.md" + }, + { + "title": "Custom `App`", + "path": "/docs/advanced-features/custom-app.md" + }, + { + "title": "Custom `Document`", + "path": "/docs/advanced-features/custom-document.md" + }, + { + "title": "Custom Error Page", + "path": "/docs/advanced-features/custom-error-page.md" + }, + { + "title": "`src` Directory", + "path": "/docs/advanced-features/src-directory.md" + }, + { + "title": "CI Build Caching", + "path": "/docs/advanced-features/ci-build-caching.md" + }, + { + "title": "Multi Zones", + "path": "/docs/advanced-features/multi-zones.md" + }, + { + "title": "Measuring performance", + "path": "/docs/advanced-features/measuring-performance.md" + }, + { + "title": "Middleware", + "path": "/docs/advanced-features/middleware.md" + }, + { + "title": "Debugging", + "path": "/docs/advanced-features/debugging.md" + }, + { + "title": "Error Handling", + "path": "/docs/advanced-features/error-handling.md" + }, + { + "title": "Source Maps", + "path": "/docs/advanced-features/source-maps.md" + }, + { + "title": "Codemods", + "path": "/docs/advanced-features/codemods.md" + }, + { + "title": "Internationalized Routing", + "path": "/docs/advanced-features/i18n-routing.md" + }, + { + "title": "Output File Tracing", + "path": "/docs/advanced-features/output-file-tracing.md" + }, + { + "title": "Security Headers", + "path": "/docs/advanced-features/security-headers.md" + }, + { + "title": "React 18", + "routes": [ + { + "path": "/docs/advanced-features/react-18", + "redirect": { + "destination": "/docs/advanced-features/react-18/overview" + } + }, + { + "title": "Overview", + "path": "/docs/advanced-features/react-18/overview.md" + }, + { + "title": "Streaming SSR", + "path": "/docs/advanced-features/react-18/streaming.md" + }, + { + "title": "React Server Components", + "path": "/docs/advanced-features/react-18/server-components.md" + }, + { + "title": "Switchable Runtime", + "path": "/docs/advanced-features/react-18/switchable-runtime.md" + } + ] + } + ] + }, + { + "title": "Upgrade Guide", + "path": "/docs/upgrading.md" + }, + { + "title": "Migrating to Next.js", + "routes": [ + { + "title": "Incrementally Adopting Next.js", + "path": "/docs/migrating/incremental-adoption.md" + }, + { + "title": "Migrating from Gatsby", + "path": "/docs/migrating/from-gatsby.md" + }, + { + "title": "Migrating from Create React App", + "path": "/docs/migrating/from-create-react-app.md" + }, + { + "title": "Migrating from React Router", + "path": "/docs/migrating/from-react-router.md" + } + ] + }, + { + "title": "FAQ", + "path": "/docs/faq.md" + } + ] + }, + { + "title": "API Reference", + "heading": true, + "routes": [ + { + "title": "CLI", + "path": "/docs/api-reference/cli.md" + }, + { + "title": "Create Next App", + "path": "/docs/api-reference/create-next-app.md" + }, + { + "title": "next/router", + "path": "/docs/api-reference/next/router.md" + }, + { + "title": "next/link", + "path": "/docs/api-reference/next/link.md" + }, + { + "title": "next/image", + "path": "/docs/api-reference/next/image.md" + }, + { + "title": "next/script", + "path": "/docs/api-reference/next/script.md" + }, + { + "title": "next/head", + "path": "/docs/api-reference/next/head.md" + }, + { + "title": "next/amp", + "path": "/docs/api-reference/next/amp.md" + }, + { + "title": "next/server", + "path": "/docs/api-reference/next/server.md" + }, + { + "title": "@next/font", + "path": "/docs/api-reference/next/font.md" + }, + { + "path": "/docs/api-reference/next/streaming", + "redirect": { + "destination": "/docs/advanced-features/react-18" + } + }, + { + "path": "/docs/api-reference/next/future/image", + "redirect": { + "destination": "/docs/api-reference/next/image" + } + }, + { + "title": "next/legacy/image", + "path": "/docs/api-reference/next/legacy/image.md" + }, + { + "title": "Edge Runtime", + "path": "/docs/api-reference/edge-runtime.md" + }, + { + "title": "Data Fetching", + "routes": [ + { + "title": "getInitialProps", + "path": "/docs/api-reference/data-fetching/get-initial-props.md" + }, + { + "title": "getServerSideProps", + "path": "/docs/api-reference/data-fetching/get-server-side-props.md" + }, + { + "title": "getStaticPaths", + "path": "/docs/api-reference/data-fetching/get-static-paths.md" + }, + { + "title": "getStaticProps", + "path": "/docs/api-reference/data-fetching/get-static-props.md" + } + ] + }, + { + "title": "Static Optimization Indicator", + "path": "/docs/api-reference/next.config.js/static-optimization-indicator.md" + }, + { + "title": "next.config.js", + "routes": [ + { + "title": "Introduction", + "path": "/docs/api-reference/next.config.js/introduction.md" + }, + { + "title": "Environment Variables", + "path": "/docs/api-reference/next.config.js/environment-variables.md" + }, + { + "title": "Base Path", + "path": "/docs/api-reference/next.config.js/basepath.md" + }, + { + "title": "Rewrites", + "path": "/docs/api-reference/next.config.js/rewrites.md" + }, + { + "title": "Redirects", + "path": "/docs/api-reference/next.config.js/redirects.md" + }, + { + "title": "Custom Headers", + "path": "/docs/api-reference/next.config.js/headers.md" + }, + { + "title": "Custom Page Extensions", + "path": "/docs/api-reference/next.config.js/custom-page-extensions.md" + }, + { + "title": "CDN Support with Asset Prefix", + "path": "/docs/api-reference/next.config.js/cdn-support-with-asset-prefix.md" + }, + { + "title": "Custom Webpack Config", + "path": "/docs/api-reference/next.config.js/custom-webpack-config.md" + }, + { + "title": "Compression", + "path": "/docs/api-reference/next.config.js/compression.md" + }, + { + "title": "Runtime Configuration", + "path": "/docs/api-reference/next.config.js/runtime-configuration.md" + }, + { + "title": "Disabling x-powered-by", + "path": "/docs/api-reference/next.config.js/disabling-x-powered-by.md" + }, + { + "title": "Disabling ETag Generation", + "path": "/docs/api-reference/next.config.js/disabling-etag-generation.md" + }, + { + "title": "Disabling HTTP Keep-Alive", + "path": "/docs/api-reference/next.config.js/disabling-http-keep-alive.md" + }, + { + "title": "Setting a custom build directory", + "path": "/docs/api-reference/next.config.js/setting-a-custom-build-directory.md" + }, + { + "title": "Configuring the Build ID", + "path": "/docs/api-reference/next.config.js/configuring-the-build-id.md" + }, + { + "title": "Configuring onDemandEntries", + "path": "/docs/api-reference/next.config.js/configuring-onDemandEntries.md" + }, + { + "title": "Ignoring ESLint", + "path": "/docs/api-reference/next.config.js/ignoring-eslint.md" + }, + { + "title": "Ignoring TypeScript Errors", + "path": "/docs/api-reference/next.config.js/ignoring-typescript-errors.md" + }, + { + "title": "exportPathMap", + "path": "/docs/api-reference/next.config.js/exportPathMap.md" + }, + { + "title": "Trailing Slash", + "path": "/docs/api-reference/next.config.js/trailing-slash.md" + }, + { + "title": "React Strict Mode", + "path": "/docs/api-reference/next.config.js/react-strict-mode.md" + }, + { + "title": "URL Imports", + "path": "/docs/api-reference/next.config.js/url-imports.md" + }, + { + "title": "Build indicator", + "path": "/docs/api-reference/next.config.js/build-indicator.md" + } + ] + } + ] + } + ] +} diff --git a/docs/migrating/from-create-react-app.md b/docs/migrating/from-create-react-app.md new file mode 100644 index 0000000000000..6e49541072941 --- /dev/null +++ b/docs/migrating/from-create-react-app.md @@ -0,0 +1,241 @@ +--- +description: Learn how to transition an existing Create React App project to Next.js. +--- + +# Migrating from Create React App + +This guide will help you understand how to transition from an existing non-ejected Create React App project to Next.js. Migrating to Next.js will allow you to: + +- Choose which [data fetching](/docs/basic-features/data-fetching/overview.md) strategy you want on a per-page basis. +- Use [Incremental Static Regeneration](/docs/basic-features/data-fetching/incremental-static-regeneration.md) to update _existing_ pages by re-rendering them in the background as traffic comes in. +- Use [API Routes](/docs/api-routes/introduction.md). + +And more! Let’s walk through a series of steps to complete the migration. + +## Updating `package.json` and dependencies + +The first step towards migrating to Next.js is to update `package.json` and dependencies. You should: + +- Remove `react-scripts` (but keep `react` and `react-dom`). If you're using React Router, you can also remove `react-router-dom`. +- Install `next`. +- Add Next.js related commands to `scripts`. One is `next dev`, which runs a development server at `localhost:3000`. You should also add `next build` and `next start` for creating and starting a production build. + +Here's an example `package.json`: + +```json +{ + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start" + }, + "dependencies": { + "next": "latest", + "react": "latest", + "react-dom": "latest" + } +} +``` + +## Static Assets and Compiled Output + +Create React App uses the `public` directory for the [entry HTML file](https://create-react-app.dev/docs/using-the-public-folder) as well as static assets, but Next.js only uses it for static assets. When migrating from Create React App, the location of the `public` directory remains the same. + +- Move any images, fonts, or other static assets to `public`. +- Convert `index.html` (the entry point of your application) to Next.js. Any `` code should be moved to a [custom `_document.js`](/docs/advanced-features/custom-document.md). Any shared layout between all pages should be moved to a [custom `_app.js`](/docs/advanced-features/custom-app.md). +- See [Styling](#styling) for CSS/Sass files. +- Add `.next` to `.gitignore`. + +## Creating Routes & Linking + +With Create React App, you're likely using React Router. Instead of using a third-party library, Next.js includes its own [file-system based routing](/docs/routing/introduction.md). + +- Create a [`pages`](/docs/basic-features/pages.md) directory at the root of your project. +- Then, move the `src/App.js` file to `pages/index.js`. This file is the [index page](https://nextjs.org/docs/routing/introduction#index-routes) of your Next.js application. Populate this file with code that is used to display the index route in your Create React App. +- Convert all other `Route` components to new files in the `pages` directory. +- For routes that require dynamic content (e.g. `/blog/:slug`), you can use [Dynamic Routes](/docs/routing/dynamic-routes.md) with Next.js (e.g. `pages/blog/[slug].js`). The value of `slug` is accessible through a [query parameter](/docs/routing/dynamic-routes.md). For example, the route `/blog/first-post` would forward the query object `{ 'slug': 'first-post' }` to `pages/blog/[slug].js` ([learn more here](/docs/basic-features/data-fetching/get-static-paths.md)). + +For more information, see [Migrating from React Router](/docs/migrating/from-react-router.md). + +## Styling + +Next.js has built-in support for [CSS](/docs/basic-features/built-in-css-support.md), [Sass](/docs/basic-features/built-in-css-support.md#sass-support) and [CSS-in-JS](/docs/basic-features/built-in-css-support.md#css-in-js). + +With Create React App, you can import `.css` files directly inside React components. Next.js allows you to do the same, but requires these files to be [CSS Modules](/docs/basic-features/built-in-css-support.md). For global styles, you'll need a [custom `_app.js`](/docs/advanced-features/custom-app.md) to add a [global stylesheet](/docs/basic-features/built-in-css-support.md#adding-a-global-stylesheet). + +## Safely Accessing Web APIs + +With client-side rendered applications (like Create React App), you can access `window`, `localStorage`, `navigator`, and other [Web APIs](https://developer.mozilla.org/en-US/docs/Web/API) out of the box. + +Since Next.js uses [pre-rendering](/docs/basic-features/pages.md#pre-rendering), you'll need to safely access those Web APIs only when you're on the client-side. For example, the following code snippet will allow access to `window` only on the client-side. + +```jsx +if (typeof window !== 'undefined') { + // You now have access to `window` +} +``` + +A recommended way of accessing Web APIs safely is by using the [`useEffect`](https://reactjs.org/docs/hooks-effect.html) hook, which only executes client-side: + +```jsx +import { useEffect } from 'react' + +useEffect(() => { + // You now have access to `window` +}, []) +``` + +## Image Component and Image Optimization + +Since version **10.0.0**, Next.js has a built-in [Image Component and Automatic Image Optimization](/docs/basic-features/image-optimization.md). + +The Next.js Image Component, [`next/image`](/docs/api-reference/next/image.md), is an extension of the HTML `` element, evolved for the modern web. + +The Automatic Image Optimization allows for resizing, optimizing, and serving images in modern formats like [WebP](https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Image_types) when the browser supports it. This avoids shipping large images to devices with a smaller viewport. It also allows Next.js to automatically adopt future image formats and serve them to browsers that support those formats. + +Instead of optimizing images at build time, Next.js optimizes images on-demand, as users request them. Your build times aren't increased, whether shipping 10 images or 10 million images. + +```jsx +import Image from 'next/image' + +export default function Home() { + return ( + <> +

    My Homepage

    + Picture of the author +

    Welcome to my homepage!

    + + ) +} +``` + +## Environment Variables + +Next.js has support for `.env` [Environment Variables](/docs/basic-features/environment-variables.md) similar to Create React App. The main difference is the prefix used to expose environment variables on the client-side. + +- Change all environment variables with the `REACT_APP_` prefix to `NEXT_PUBLIC_`. +- Server-side environment variables will be available at build-time and in [API Routes](/docs/api-routes/introduction.md). + +## Search Engine Optimization + +Most Create React App examples use `react-helmet` to assist with adding `meta` tags for proper SEO. With Next.js, we use [`next/head`](/docs/api-reference/next/head.md) to add `meta` tags to your `` element. For example, here's an SEO component with Create React App: + +```jsx +// src/components/seo.js + +import { Helmet } from 'react-helmet' + +export default function SEO({ description, title, siteTitle }) { + return ( + + ) +} +``` + +And here's the same example using Next.js. + +```jsx +// src/components/seo.js + +import Head from 'next/head' + +export default function SEO({ description, title, siteTitle }) { + return ( + + {`${title} | ${siteTitle}`} + + + + + + + + + + + ) +} +``` + +## Single-Page App (SPA) + +If you want to move your existing Create React App to Next.js and keep a Single-Page App, you can move your old application's entry point to an [Optional Catch-All Route](/docs/routing/dynamic-routes.md#optional-catch-all-routes) named `pages/[[...app]].js`. + +```jsx +// pages/[[...app]].js + +import { useState, useEffect } from 'react' +import CreateReactAppEntryPoint from '../components/app' + +function App() { + const [isMounted, setIsMounted] = useState(false) + + useEffect(() => { + setIsMounted(true) + }, []) + + if (!isMounted) { + return null + } + + return +} + +export default App +``` + +## Ejected Create React App + +If you've ejected Create React App, here are some things to consider: + +- If you have custom file loaders set up for CSS, Sass, or other assets, this is all built-in with Next.js. +- If you've manually added [new JavaScript features](/docs/basic-features/supported-browsers-features.md#javascript-language-features) (e.g. Optional Chaining) or [Polyfills](/docs/basic-features/supported-browsers-features.md#polyfills), check to see what's included by default with Next.js. +- If you have a custom code splitting setup, you can remove that. Next.js has automatic code splitting on a [per-page basis](/docs/basic-features/pages.md). +- You can [customize your PostCSS setup](/docs/advanced-features/customizing-postcss-config.md#default-behavior) with Next.js without ejecting from the framework. +- You should reference the default [Babel config](/docs/advanced-features/customizing-babel-config.md) and [Webpack config](/docs/api-reference/next.config.js/custom-webpack-config.md) of Next.js to see what's included by default. + +## Learn More + +You can learn more about Next.js by completing our [starter tutorial](https://nextjs.org/learn/basics/create-nextjs-app). If you have questions or if this guide didn't work for you, feel free to reach out to our community on [GitHub Discussions](https://github.com/vercel/next.js/discussions). diff --git a/docs/migrating/from-gatsby.md b/docs/migrating/from-gatsby.md new file mode 100644 index 0000000000000..98031267bc2ff --- /dev/null +++ b/docs/migrating/from-gatsby.md @@ -0,0 +1,319 @@ +--- +description: Learn how to transition an existing Gatsby project to Next.js. +--- + +# Migrating from Gatsby + +This guide will help you understand how to transition from an existing Gatsby project to Next.js. Migrating to Next.js will allow you to: + +- Choose which [data fetching](/docs/basic-features/data-fetching/overview.md) strategy you want on a per-page basis. +- Use [Incremental Static Regeneration](/docs/basic-features/data-fetching/incremental-static-regeneration.md) to update _existing_ pages by re-rendering them in the background as traffic comes in. +- Use [API Routes](/docs/api-routes/introduction.md). + +And more! Let’s walk through a series of steps to complete the migration. + +## Updating `package.json` and dependencies + +The first step towards migrating to Next.js is to update `package.json` and dependencies. You should: + +- Remove all Gatsby-related packages (but keep `react` and `react-dom`). +- Install `next`. +- Add Next.js related commands to `scripts`. One is `next dev`, which runs a development server at `localhost:3000`. You should also add `next build` and `next start` for creating and starting a production build. + +Here's an example `package.json` ([view diff](https://github.com/leerob/gatsby-to-nextjs/pull/1/files#diff-b9cfc7f2cdf78a7f4b91a753d10865a2)): + +```json +{ + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start" + }, + "dependencies": { + "next": "latest", + "react": "latest", + "react-dom": "latest" + } +} +``` + +## Static Assets and Compiled Output + +Gatsby uses the `public` directory for the compiled output, whereas Next.js uses it for static assets. Here are the steps for migration ([view diff](https://github.com/leerob/gatsby-to-nextjs/pull/1/files#diff-a084b794bc0759e7a6b77810e01874f2)): + +- Remove `.cache/` and `public` from `.gitignore` and delete both directories. +- Rename Gatsby’s `static` directory as `public`. +- Add `.next` to `.gitignore`. + +## Creating Routes + +Both Gatsby and Next support a `pages` directory, which uses [file-system based routing](/docs/routing/introduction.md). Gatsby's directory is `src/pages`, which is also [supported by Next.js](/docs/advanced-features/src-directory.md). + +Gatsby creates dynamic routes using the `createPages` API inside of `gatsby-node.js`. With Next, we can use [Dynamic Routes](/docs/routing/dynamic-routes.md) inside of `pages` to achieve the same effect. Rather than having a `template` directory, you can use the React component inside your dynamic route file. For example: + +- **Gatsby:** `createPages` API inside `gatsby-node.js` for each blog post, then have a template file at `src/templates/blog-post.js`. +- **Next:** Create `pages/blog/[slug].js` which contains the blog post template. The value of `slug` is accessible through a [query parameter](/docs/routing/dynamic-routes.md). For example, the route `/blog/first-post` would forward the query object `{ 'slug': 'first-post' }` to `pages/blog/[slug].js` ([learn more here](/docs/basic-features/data-fetching/get-static-paths.md)). + +## Styling + +With Gatsby, global CSS imports are included in `gatsby-browser.js`. With Next, you should create a [custom `_app.js`](/docs/advanced-features/custom-app.md) for global CSS. When migrating, you can copy over your CSS imports directly and update the relative file path, if necessary. Next.js has [built-in CSS support](/docs/basic-features/built-in-css-support.md). + +## Links + +The Gatsby `Link` and Next.js [`Link`](/docs/api-reference/next/link.md) component have a slightly different API. + +```jsx +// Gatsby + +import { Link } from 'gatsby' + +export default function Home() { + return blog +} +``` + +```jsx +// Next.js + +import Link from 'next/link' + +export default function Home() { + return blog +} +``` + +Update any import statements, switch `to` to `href`. + +## Data Fetching + +The largest difference between Gatsby and Next.js is how data fetching is implemented. Gatsby is opinionated with GraphQL being the default strategy for retrieving data across your application. With Next.js, you get to choose which strategy you want (GraphQL is one supported option). + +Gatsby uses the `graphql` tag to query data in the pages of your site. This may include local data, remote data, or information about your site configuration. Gatsby only allows the creation of static pages. With Next.js, you can choose on a [per-page basis](/docs/basic-features/pages.md) which [data fetching strategy](/docs/basic-features/data-fetching/overview.md) you want. For example, `getServerSideProps` allows you to do server-side rendering. If you wanted to generate a static page, you'd export `getStaticProps` / `getStaticPaths` inside the page, rather than using `pageQuery`. For example: + +```js +// src/pages/[slug].js + +// Install remark and remark-html +import { remark } from 'remark' +import html from 'remark-html' +import { getPostBySlug, getAllPosts } from '../lib/blog' + +export async function getStaticProps({ params }) { + const post = getPostBySlug(params.slug) + const markdown = await remark() + .use(html) + .process(post.content || '') + const content = markdown.toString() + + return { + props: { + ...post, + content, + }, + } +} + +export async function getStaticPaths() { + const posts = getAllPosts() + + return { + paths: posts.map((post) => { + return { + params: { + slug: post.slug, + }, + } + }), + fallback: false, + } +} +``` + +You'll commonly see Gatsby plugins used for reading the file system (`gatsby-source-filesystem`), handling markdown files (`gatsby-transformer-remark`), and so on. For example, the popular starter blog example has [15 Gatsby specific packages](https://github.com/gatsbyjs/gatsby-starter-blog/blob/master/package.json). Next takes a different approach. It includes common features directly inside the framework, and gives the user full control over integrations with external packages. For example, rather than abstracting reading from the file system to a plugin, you can use the native Node.js `fs` package inside `getStaticProps` / `getStaticPaths` to read from the file system. + +```js +// src/lib/blog.js + +// Install gray-matter and date-fns +import matter from 'gray-matter' +import { parseISO, format } from 'date-fns' +import fs from 'fs' +import { join } from 'path' + +// Add markdown files in `src/content/blog` +const postsDirectory = join(process.cwd(), 'src', 'content', 'blog') + +export function getPostBySlug(slug) { + const realSlug = slug.replace(/\.md$/, '') + const fullPath = join(postsDirectory, `${realSlug}.md`) + const fileContents = fs.readFileSync(fullPath, 'utf8') + const { data, content } = matter(fileContents) + const date = format(parseISO(data.date), 'MMMM dd, yyyy') + + return { slug: realSlug, frontmatter: { ...data, date }, content } +} + +export function getAllPosts() { + const slugs = fs.readdirSync(postsDirectory) + const posts = slugs.map((slug) => getPostBySlug(slug)) + + return posts +} +``` + +## Image Component and Image Optimization + +Next.js has a built-in [Image Component and Automatic Image Optimization](/docs/basic-features/image-optimization.md). + +The Next.js Image Component, [`next/image`](/docs/api-reference/next/image.md), is an extension of the HTML `` element, evolved for the modern web. + +The Automatic Image Optimization allows for resizing, optimizing, and serving images in modern formats like [WebP](https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Image_types) when the browser supports it. This avoids shipping large images to devices with a smaller viewport. It also allows Next.js to automatically adopt future image formats and serve them to browsers that support those formats. + +### Migrating from Gatsby Image + +Instead of optimizing images at build time, Next.js optimizes images on-demand, as users request them. Unlike static site generators and static-only solutions, your build times aren't increased, whether shipping 10 images or 10 million images. + +This means you can remove common Gatsby plugins like: + +- `gatsby-image` +- `gatsby-transformer-sharp` +- `gatsby-plugin-sharp` + +Instead, use the built-in [`next/image`](/docs/api-reference/next/image.md) component and [Automatic Image Optimization](/docs/basic-features/image-optimization.md). + +> The `next/image` component's default loader is not supported when using [`next export`](/docs/advanced-features/static-html-export.md). However, other loader options will work. + +```jsx +import Image from 'next/image' +import profilePic from '../public/me.png' + +export default function Home() { + return ( + <> +

    My Homepage

    + Picture of the author +

    Welcome to my homepage!

    + + ) +} +``` + +## Site Configuration + +With Gatsby, your site's metadata (name, description, etc.) is located inside `gatsby-config.js`. This is then exposed through the GraphQL API and consumed through a `pageQuery` or a static query inside a component. + +With Next.js, we recommend creating a config file similar to below. You can then import this file anywhere without having to use GraphQL to access your site's metadata. + +```js +// src/config.js + +export default { + title: 'Starter Blog', + author: { + name: 'Lee Robinson', + summary: 'who loves Next.js.', + }, + description: 'A starter blog converting Gatsby -> Next.', + social: { + twitter: 'leeerob', + }, +} +``` + +## Search Engine Optimization + +Most Gatsby examples use `react-helmet` to assist with adding `meta` tags for proper SEO. With Next.js, we use [`next/head`](/docs/api-reference/next/head.md) to add `meta` tags to your `` element. For example, here's an SEO component with Gatsby: + +```js +// src/components/seo.js + +import { Helmet } from 'react-helmet' + +export default function SEO({ description, title, siteTitle }) { + return ( + + ) +} +``` + +And here's the same example using Next.js, including reading from a site config file. + +```js +// src/components/seo.js + +import Head from 'next/head' +import config from '../config' + +export default function SEO({ description, title }) { + const siteTitle = config.title + + return ( + + {`${title} | ${siteTitle}`} + + + + + + + + + + + ) +} +``` + +## Learn more + +Take a look at [this pull request](https://github.com/leerob/gatsby-to-nextjs/pull/1) for more details on how an app can be migrated from Gatsby to Next.js. If you have questions or if this guide didn't work for you, feel free to reach out to our community on [GitHub Discussions](https://github.com/vercel/next.js/discussions). diff --git a/docs/migrating/from-react-router.md b/docs/migrating/from-react-router.md new file mode 100644 index 0000000000000..1174eaa5ed3c9 --- /dev/null +++ b/docs/migrating/from-react-router.md @@ -0,0 +1,175 @@ +--- +description: Learn how to migrate from React Router to file-system based routes with Next.js. +--- + +# Migrating from React Router + +This guide will help you understand how to transition from [React Router](https://reactrouter.com) to [file-system based](/docs/routing/introduction.md) routes with Next.js. Using [`next/link`](/docs/api-reference/next/link.md) and [`next/router`](/docs/api-reference/next/router.md) will allow you to: + +- Decrease bundle size by removing React Router as a dependency. +- Define your application routes through the file system. +- Utilize the latest improvements to the Next.js framework. + +## Basics + +First, uninstall React Router. You'll be migrating to the built-in routing with Next.js. + +```jsx +npm uninstall react-router-dom +``` + +The `Link` component for performing client-side route transitions is slightly different from React Router. + +```jsx +// Before (React Router) +import { Link } from 'react-router-dom' + +export default function App() { + return About +} + +// After (Next.js) +import Link from 'next/link' + +export default function App() { + return ( + + About + + ) +} +``` + +Most React applications that use React Router have a top-level navigation file, containing a list of routes. For example: + +```jsx +import { BrowserRouter as Router, Switch, Route } from 'react-router-dom' + +export default function App() { + return ( + + + +

    About

    +
    + +

    Blog

    +
    + +

    Home

    +
    +
    +
    + ) +} +``` + +With Next.js, you can express the same application structure in the file system. When a file is added to the [`pages`](/docs/basic-features/pages.md) directory it's automatically available as a route. + +- `pages/about.js` → `/about` +- `pages/blog.js` → `/blog` +- `pages/index.js` → `/` + +## Nested Routes + +In the example below, routes like `/blog/my-post` would render the `Post` component. If a slug was not provided, it would render the list of all blog posts. + +```jsx +import { + BrowserRouter as Router, + Switch, + Route, + useRouteMatch, + useParams, +} from 'react-router-dom' + +export default function Blog() { + // Nested route under /blog + const match = useRouteMatch() + + return ( + + + + + + +

    All Blog Posts

    +
    +
    +
    + ) +} + +function Post() { + const { slug } = useParams() + return

    Post Slug: {slug}

    +} +``` + +Rather than using the `:slug` syntax inside your `Route` component, Next.js uses the `[slug]` syntax in the file name for [Dynamic Routes](/docs/routing/dynamic-routes.md). We can transform this to Next.js by creating two new files, `pages/blog/index.js` (showing all pages) and `pages/blog/[slug].js` (showing an individual post). + +```jsx +// pages/blog/index.js + +export default function Blog() { + return

    All Blog Posts

    +} + +// pages/blog/[slug].js + +import { useRouter } from 'next/router' + +export default function Post() { + const router = useRouter() + const { slug } = router.query + + return

    Post Slug: {slug}

    +} +``` + +## Server Rendering + +Next.js has built-in support for [Server-side Rendering](/docs/basic-features/pages#server-side-rendering.md). This means you can remove any instances of `StaticRouter` in your code. + +## Code Splitting + +Next.js has built-in support for [Code Splitting](https://reactrouter.com/web/guides/code-splitting). This means you can remove any instances of: + +- `@loadable/server`, `@loadable/babel-plugin`, and `@loadable/webpack-plugin` +- Modifications to your `.babelrc` for `@loadable/babel-plugin` + +Each file inside your `pages/` directory will be code split into its own JavaScript bundle during the build process. Next.js [also supports](/docs/basic-features/supported-browsers-features.md#javascript-language-features) ES2020 dynamic `import()` for JavaScript. With it you can import JavaScript modules dynamically and work with them. They also work with SSR. + +For more information, read about [Dynamic Imports](https://nextjs.org/docs/advanced-features/dynamic-import). + +## Scroll Restoration + +Next.js has built-in support for [Scroll Restoration](https://reactrouter.com/web/guides/scroll-restoration). This means you can remove any custom `ScrollToTop` components you have defined. + +The default behavior of `next/link` and `next/router` is to scroll to the top of the page. You can also [disable this](https://nextjs.org/docs/api-reference/next/link#disable-scrolling-to-the-top-of-the-page) if you prefer. + +## Learn More + +For more information on what to do next, we recommend the following sections: + +
    + + + + diff --git a/docs/migrating/incremental-adoption.md b/docs/migrating/incremental-adoption.md new file mode 100644 index 0000000000000..e7bf9ae047635 --- /dev/null +++ b/docs/migrating/incremental-adoption.md @@ -0,0 +1,93 @@ +--- +description: Learn different strategies for incrementally adopting Next.js into your development workflow. +--- + +# Incrementally Adopting Next.js + +
    + Examples + +
    + +Next.js has been designed for gradual adoption. With Next.js, you can continue using your existing code and add as much (or as little) React as you need. By starting small and incrementally adding more pages, you can prevent derailing feature work by avoiding a complete rewrite. + +## Strategies + +### Subpath + +The first strategy is to configure your server or proxy such that, everything under a specific subpath points to a Next.js app. For example, your existing website might be at `example.com`, and you might configure your proxy such that `example.com/store` serves a Next.js e-commerce store. + +Using [`basePath`](/docs/api-reference/next.config.js/basepath.md), you can configure your Next.js application's assets and links to automatically work with your new subpath `/store`. Since each page in Next.js is its own [standalone route](/docs/routing/introduction.md), pages like `pages/products.js` will route to `example.com/store/products` in your application. + +```jsx +// next.config.js + +module.exports = { + basePath: '/store', +} +``` + +To learn more about `basePath`, take a look at our [documentation](/docs/api-reference/next.config.js/basepath.md). + +### Rewrites + +The second strategy is to create a new Next.js app that points to the root URL of your domain. Then, you can use [`rewrites`](/docs/api-reference/next.config.js/rewrites.md) inside `next.config.js` to have some subpaths to be proxied to your existing app. + +For example, let's say you created a Next.js app to be served from `example.com` with the following `next.config.js`. Now, requests for the pages you’ve added to this Next.js app (e.g. `/about` if you’ve added `pages/about.js`) will be handled by Next.js, and requests for any other route (e.g. `/dashboard`) will be proxied to `proxy.example.com`. + +> **Note:** If you use [fallback: true/'blocking'](/docs/api-reference/data-fetching/get-static-paths#fallback-true) in `getStaticPaths`, the catch-all fallback `rewrites` defined in `next.config.js` will not be run. They are instead caught by the `getStaticPaths` fallback. + +```jsx +// next.config.js + +module.exports = { + async rewrites() { + return { + // After checking all Next.js pages (including dynamic routes) + // and static files we proxy any other requests + fallback: [ + { + source: '/:path*', + destination: `https://proxy.example.com/:path*`, + }, + ], + } + + // For versions of Next.js < v10.1 you can use a no-op rewrite instead + return [ + // we need to define a no-op rewrite to trigger checking + // all pages/static files before we attempt proxying + { + source: '/:path*', + destination: '/:path*', + }, + { + source: '/:path*', + destination: `https://proxy.example.com/:path*`, + }, + ] + }, +} +``` + +To learn more about rewrites, take a look at our [documentation](/docs/api-reference/next.config.js/rewrites.md). + +> **Note:** If you are incrementally migrating to a dynamic route (e.g. `[slug].js`) and using `fallback: true` or `fallback: 'blocking'` along with a fallback `rewrite`, ensure you consider the case where pages are not found. When Next.js matches the dynamic route it stops checking any further routes. Using `notFound: true` in `getStaticProps` will return the 404 page without applying the fallback `rewrite`. If this is not desired, you can use `getServerSideProps` with `stale-while-revalidate` Cache-Control headers when returning your props. Then, you can _manually_ proxy to your existing backend using something like [http-proxy](https://github.com/vercel/next.js/discussions/38839#discussioncomment-3744442) instead of returning `notFound: true`. + +### Micro-Frontends with Monorepos and Subdomains + +Next.js and [Vercel](https://vercel.com) make it straightforward to adopt micro frontends and deploy as a [monorepo](https://vercel.com/blog/monorepos-are-changing-how-teams-build-software?utm_source=next-site&utm_medium=docs&utm_campaign=next-website). This allows you to use [subdomains](https://demo.vercel.pub/platforms-starter-kit) to adopt new applications incrementally. Some benefits of micro-frontends: + +- Smaller, more cohesive and maintainable codebases. +- More scalable organizations with decoupled, autonomous teams. +- The ability to upgrade, update, or even rewrite parts of the frontend in a more incremental fashion. + +Once your monorepo is set up, push changes to your Git repository as usual and you'll see the commits deployed to the Vercel projects you've connected. + +## Conclusion + +To learn more, read about [subpaths](/docs/api-reference/next.config.js/basepath.md) and [rewrites](/docs/api-reference/next.config.js/rewrites.md) or [deploy a Next.js monorepo](https://vercel.com/templates/next.js/monorepo-turborepo). diff --git a/docs/routing/dynamic-routes.md b/docs/routing/dynamic-routes.md new file mode 100644 index 0000000000000..87c02e856791d --- /dev/null +++ b/docs/routing/dynamic-routes.md @@ -0,0 +1,154 @@ +--- +description: Dynamic Routes are pages that allow you to add custom params to your URLs. Start creating Dynamic Routes and learn more here. +--- + +# Dynamic Routes + +
    + Examples + +
    + +Defining routes by using predefined paths is not always enough for complex applications. In Next.js you can add brackets to a page (`[param]`) to create a dynamic route (a.k.a. url slugs, pretty urls, and others). + +Consider the following page `pages/post/[pid].js`: + +```jsx +import { useRouter } from 'next/router' + +const Post = () => { + const router = useRouter() + const { pid } = router.query + + return

    Post: {pid}

    +} + +export default Post +``` + +Any route like `/post/1`, `/post/abc`, etc. will be matched by `pages/post/[pid].js`. The matched path parameter will be sent as a query parameter to the page, and it will be merged with the other query parameters. + +For example, the route `/post/abc` will have the following `query` object: + +```json +{ "pid": "abc" } +``` + +Similarly, the route `/post/abc?foo=bar` will have the following `query` object: + +```json +{ "foo": "bar", "pid": "abc" } +``` + +However, route parameters will override query parameters with the same name. For example, the route `/post/abc?pid=123` will have the following `query` object: + +```json +{ "pid": "abc" } +``` + +Multiple dynamic route segments work the same way. The page `pages/post/[pid]/[comment].js` will match the route `/post/abc/a-comment` and its `query` object will be: + +```json +{ "pid": "abc", "comment": "a-comment" } +``` + +Client-side navigations to dynamic routes are handled with [`next/link`](/docs/api-reference/next/link.md). If we wanted to have links to the routes used above it will look like this: + +```jsx +import Link from 'next/link' + +function Home() { + return ( +
      +
    • + Go to pages/post/[pid].js +
    • +
    • + Also goes to pages/post/[pid].js +
    • +
    • + + Go to pages/post/[pid]/[comment].js + +
    • +
    + ) +} + +export default Home +``` + +Read our docs for [Linking between pages](/docs/routing/introduction.md#linking-between-pages) to learn more. + +### Catch all routes + +
    + Examples + +
    + +Dynamic routes can be extended to catch all paths by adding three dots (`...`) inside the brackets. For example: + +- `pages/post/[...slug].js` matches `/post/a`, but also `/post/a/b`, `/post/a/b/c` and so on. + +> **Note**: You can use names other than `slug`, such as: `[...param]` + +Matched parameters will be sent as a query parameter (`slug` in the example) to the page, and it will always be an array, so, the path `/post/a` will have the following `query` object: + +```json +{ "slug": ["a"] } +``` + +And in the case of `/post/a/b`, and any other matching path, new parameters will be added to the array, like so: + +```json +{ "slug": ["a", "b"] } +``` + +### Optional catch all routes + +Catch all routes can be made optional by including the parameter in double brackets (`[[...slug]]`). + +For example, `pages/post/[[...slug]].js` will match `/post`, `/post/a`, `/post/a/b`, and so on. + +The main difference between catch all and optional catch all routes is that with optional, the route without the parameter is also matched (`/post` in the example above). + +The `query` objects are as follows: + +```json +{ } // GET `/post` (empty object) +{ "slug": ["a"] } // `GET /post/a` (single-element array) +{ "slug": ["a", "b"] } // `GET /post/a/b` (multi-element array) +``` + +## Caveats + +- Predefined routes take precedence over dynamic routes, and dynamic routes over catch all routes. Take a look at the following examples: + - `pages/post/create.js` - Will match `/post/create` + - `pages/post/[pid].js` - Will match `/post/1`, `/post/abc`, etc. But not `/post/create` + - `pages/post/[...slug].js` - Will match `/post/1/2`, `/post/a/b/c`, etc. But not `/post/create`, `/post/abc` +- Pages that are statically optimized by [Automatic Static Optimization](/docs/advanced-features/automatic-static-optimization.md) will be hydrated without their route parameters provided, i.e `query` will be an empty object (`{}`). + + After hydration, Next.js will trigger an update to your application to provide the route parameters in the `query` object. + +## Related + +For more information on what to do next, we recommend the following sections: + + + + diff --git a/docs/routing/imperatively.md b/docs/routing/imperatively.md new file mode 100644 index 0000000000000..f3c4e589bb83b --- /dev/null +++ b/docs/routing/imperatively.md @@ -0,0 +1,30 @@ +--- +description: Client-side navigations are also possible using the Next.js Router instead of the Link component. Learn more here. +--- + +# Imperatively + +
    + Examples + +
    + +[`next/link`](/docs/api-reference/next/link.md) should be able to cover most of your routing needs, but you can also do client-side navigations without it, take a look at the [documentation for `next/router`](/docs/api-reference/next/router.md). + +The following example shows how to do basic page navigations with [`useRouter`](/docs/api-reference/next/router.md#useRouter): + +```jsx +import { useRouter } from 'next/router' + +export default function ReadMore() { + const router = useRouter() + + return ( + + ) +} +``` diff --git a/docs/routing/introduction.md b/docs/routing/introduction.md new file mode 100644 index 0000000000000..c19a59486c9b1 --- /dev/null +++ b/docs/routing/introduction.md @@ -0,0 +1,160 @@ +--- +description: Next.js has a built-in, opinionated, and file-system based Router. You can learn how it works here. +--- + +# Routing + +Next.js has a file-system based router built on the [concept of pages](/docs/basic-features/pages.md). + +When a file is added to the `pages` directory, it's automatically available as a route. + +The files inside the `pages` directory can be used to define most common patterns. + +#### Index routes + +The router will automatically route files named `index` to the root of the directory. + +- `pages/index.js` → `/` +- `pages/blog/index.js` → `/blog` + +#### Nested routes + +The router supports nested files. If you create a nested folder structure, files will automatically be routed in the same way still. + +- `pages/blog/first-post.js` → `/blog/first-post` +- `pages/dashboard/settings/username.js` → `/dashboard/settings/username` + +#### Dynamic route segments + +To match a dynamic segment, you can use the bracket syntax. This allows you to match named parameters. + +- `pages/blog/[slug].js` → `/blog/:slug` (`/blog/hello-world`) +- `pages/[username]/settings.js` → `/:username/settings` (`/foo/settings`) +- `pages/post/[...all].js` → `/post/*` (`/post/2020/id/title`) + +> Check out the [Dynamic Routes documentation](/docs/routing/dynamic-routes.md) to learn more about how they work. + +## Linking between pages + +The Next.js router allows you to do client-side route transitions between pages, similar to a single-page application. + +A React component called `Link` is provided to do this client-side route transition. + +```jsx +import Link from 'next/link' + +function Home() { + return ( +
      +
    • + Home +
    • +
    • + About Us +
    • +
    • + Blog Post +
    • +
    + ) +} + +export default Home +``` + +The example above uses multiple links. Each one maps a path (`href`) to a known page: + +- `/` → `pages/index.js` +- `/about` → `pages/about.js` +- `/blog/hello-world` → `pages/blog/[slug].js` + +Any `` in the viewport (initially or through scroll) will be prefetched by default (including the corresponding data) for pages using [Static Generation](/docs/basic-features/data-fetching/get-static-props.md). The corresponding data for [server-rendered](/docs/basic-features/data-fetching/get-server-side-props.md) routes is fetched _only when_ the `` is clicked. + +### Linking to dynamic paths + +You can also use interpolation to create the path, which comes in handy for [dynamic route segments](#dynamic-route-segments). For example, to show a list of posts which have been passed to the component as a prop: + +```jsx +import Link from 'next/link' + +function Posts({ posts }) { + return ( +
      + {posts.map((post) => ( +
    • + + {post.title} + +
    • + ))} +
    + ) +} + +export default Posts +``` + +> [`encodeURIComponent`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent) is used in the example to keep the path utf-8 compatible. + +Alternatively, using a URL Object: + +```jsx +import Link from 'next/link' + +function Posts({ posts }) { + return ( +
      + {posts.map((post) => ( +
    • + + {post.title} + +
    • + ))} +
    + ) +} + +export default Posts +``` + +Now, instead of using interpolation to create the path, we use a URL object in `href` where: + +- `pathname` is the name of the page in the `pages` directory. `/blog/[slug]` in this case. +- `query` is an object with the dynamic segment. `slug` in this case. + +## Injecting the router + +
    + Examples + +
    + +To access the [`router` object](/docs/api-reference/next/router.md#router-object) in a React component you can use [`useRouter`](/docs/api-reference/next/router.md#useRouter) or [`withRouter`](/docs/api-reference/next/router.md#withRouter). + +In general we recommend using [`useRouter`](/docs/api-reference/next/router.md#useRouter). + +## Learn more + +The router is divided in multiple parts: + + + + diff --git a/docs/routing/shallow-routing.md b/docs/routing/shallow-routing.md new file mode 100644 index 0000000000000..dbb3f13250ab5 --- /dev/null +++ b/docs/routing/shallow-routing.md @@ -0,0 +1,65 @@ +--- +description: You can use shallow routing to change the URL without triggering a new page change. Learn more here. +--- + +# Shallow Routing + +
    + Examples + +
    + +Shallow routing allows you to change the URL without running data fetching methods again, that includes [`getServerSideProps`](/docs/basic-features/data-fetching/get-server-side-props.md), [`getStaticProps`](/docs/basic-features/data-fetching/get-static-props.md), and [`getInitialProps`](/docs/api-reference/data-fetching/get-initial-props.md). + +You'll receive the updated `pathname` and the `query` via the [`router` object](/docs/api-reference/next/router.md#router-object) (added by [`useRouter`](/docs/api-reference/next/router.md#useRouter) or [`withRouter`](/docs/api-reference/next/router.md#withRouter)), without losing state. + +To enable shallow routing, set the `shallow` option to `true`. Consider the following example: + +```jsx +import { useEffect } from 'react' +import { useRouter } from 'next/router' + +// Current URL is '/' +function Page() { + const router = useRouter() + + useEffect(() => { + // Always do navigations after the first render + router.push('/?counter=10', undefined, { shallow: true }) + }, []) + + useEffect(() => { + // The counter changed! + }, [router.query.counter]) +} + +export default Page +``` + +The URL will get updated to `/?counter=10`. and the page won't get replaced, only the state of the route is changed. + +You can also watch for URL changes via [`componentDidUpdate`](https://reactjs.org/docs/react-component.html#componentdidupdate) as shown below: + +```jsx +componentDidUpdate(prevProps) { + const { pathname, query } = this.props.router + // verify props have changed to avoid an infinite loop + if (query.counter !== prevProps.router.query.counter) { + // fetch data based on the new query + } +} +``` + +## Caveats + +Shallow routing **only** works for URL changes in the current page. For example, let's assume we have another page called `pages/about.js`, and you run this: + +```jsx +router.push('/?counter=10', '/about?counter=10', { shallow: true }) +``` + +Since that's a new page, it'll unload the current page, load the new one and wait for data fetching even though we asked to do shallow routing. + +When shallow routing is used with middleware it will not ensure the new page matches the current page like previously done without middleware. This is due to middleware being able to rewrite dynamically and can't be verified client-side without a data fetch which is skipped with shallow, so a shallow route change must always be treated as shallow. diff --git a/docs/testing.md b/docs/testing.md new file mode 100644 index 0000000000000..7414761d37c3b --- /dev/null +++ b/docs/testing.md @@ -0,0 +1,503 @@ +--- +description: Learn how to set up Next.js with three commonly used testing tools — Cypress, Playwright, Jest, and React Testing Library. +--- + +# Testing + +
    + Examples + +
    + +Learn how to set up Next.js with commonly used testing tools: [Cypress](https://nextjs.org/docs/testing#cypress), [Playwright](https://nextjs.org/docs/testing#playwright), and [Jest with React Testing Library](https://nextjs.org/docs/testing#jest-and-react-testing-library). + +## Cypress + +Cypress is a test runner used for **End-to-End (E2E)** and **Integration Testing**. + +### Quickstart + +You can use `create-next-app` with the [with-cypress example](https://github.com/vercel/next.js/tree/canary/examples/with-cypress) to quickly get started. + +```bash +npx create-next-app@latest --example with-cypress with-cypress-app +``` + +### Manual setup + +To get started with Cypress, install the `cypress` package: + +```bash +npm install --save-dev cypress +``` + +Add Cypress to the `package.json` scripts field: + +```json +"scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "cypress": "cypress open", +} +``` + +Run Cypress for the first time to generate examples that use their recommended folder structure: + +```bash +npm run cypress +``` + +You can look through the generated examples and the [Writing Your First Test](https://docs.cypress.io/guides/getting-started/writing-your-first-test) section of the Cypress Documentation to help you get familiar with Cypress. + +### Creating your first Cypress integration test + +Assuming the following two Next.js pages: + +```jsx +// pages/index.js +import Link from 'next/link' + +export default function Home() { + return ( + + ) +} +``` + +```jsx +// pages/about.js +export default function About() { + return ( +
    +

    About Page

    +
    + ) +} +``` + +Add a test to check your navigation is working correctly: + +```jsx +// cypress/integration/app.spec.js + +describe('Navigation', () => { + it('should navigate to the about page', () => { + // Start from the index page + cy.visit('http://localhost:3000/') + + // Find a link with an href attribute containing "about" and click it + cy.get('a[href*="about"]').click() + + // The new url should include "/about" + cy.url().should('include', '/about') + + // The new page should contain an h1 with "About page" + cy.get('h1').contains('About Page') + }) +}) +``` + +You can use `cy.visit("/")` instead of `cy.visit("http://localhost:3000/")` if you add `baseUrl: 'http://localhost:3000'` to the `cypress.config.js` configuration file. + +### Running your Cypress tests + +Since Cypress is testing a real Next.js application, it requires the Next.js server to be running prior to starting Cypress. We recommend running your tests against your production code to more closely resemble how your application will behave. + +Run `npm run build` and `npm run start`, then run `npm run cypress` in another terminal window to start Cypress. + +> **Note:** Alternatively, you can install the `start-server-and-test` package and add it to the `package.json` scripts field: `"test": "start-server-and-test start http://localhost:3000 cypress"` to start the Next.js production server in conjunction with Cypress. Remember to rebuild your application after new changes. + +### Getting ready for Continuous Integration (CI) + +You will have noticed that running Cypress so far has opened an interactive browser which is not ideal for CI environments. You can also run Cypress headlessly using the `cypress run` command: + +```json +// package.json + +"scripts": { + //... + "cypress": "cypress open", + "cypress:headless": "cypress run", + "e2e": "start-server-and-test start http://localhost:3000 cypress", + "e2e:headless": "start-server-and-test start http://localhost:3000 cypress:headless" +} +``` + +You can learn more about Cypress and Continuous Integration from these resources: + +- [Cypress Continuous Integration Docs](https://docs.cypress.io/guides/continuous-integration/introduction) +- [Cypress GitHub Actions Guide](https://on.cypress.io/github-actions) +- [Official Cypress GitHub Action](https://github.com/cypress-io/github-action) + +## Playwright + +Playwright is a testing framework that lets you automate Chromium, Firefox, and WebKit with a single API. You can use it to write **End-to-End (E2E)** and **Integration** tests across all platforms. + +### Quickstart + +The fastest way to get started is to use `create-next-app` with the [with-playwright example](https://github.com/vercel/next.js/tree/canary/examples/with-playwright). This will create a Next.js project complete with Playwright all set up. + +```bash +npx create-next-app@latest --example with-playwright with-playwright-app +``` + +### Manual setup + +You can also use `npm init playwright` to add Playwright to an existing `NPM` project. + +To manually get started with Playwright, install the `@playwright/test` package: + +```bash +npm install --save-dev @playwright/test +``` + +Add Playwright to the `package.json` scripts field: + +```json +"scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "test:e2e": "playwright test", +} +``` + +### Creating your first Playwright end-to-end test + +Assuming the following two Next.js pages: + +```jsx +// pages/index.js +import Link from 'next/link' + +export default function Home() { + return ( + + ) +} +``` + +```jsx +// pages/about.js +export default function About() { + return ( +
    +

    About Page

    +
    + ) +} +``` + +Add a test to verify that your navigation is working correctly: + +```jsx +// e2e/example.spec.ts + +import { test, expect } from '@playwright/test' + +test('should navigate to the about page', async ({ page }) => { + // Start from the index page (the baseURL is set via the webServer in the playwright.config.ts) + await page.goto('http://localhost:3000/') + // Find an element with the text 'About Page' and click on it + await page.click('text=About') + // The new URL should be "/about" (baseURL is used there) + await expect(page).toHaveURL('http://localhost:3000/about') + // The new page should contain an h1 with "About Page" + await expect(page.locator('h1')).toContainText('About Page') +}) +``` + +You can use `page.goto("/")` instead of `page.goto("http://localhost:3000/")`, if you add [`"baseURL": "http://localhost:3000"`](https://playwright.dev/docs/api/class-testoptions#test-options-base-url) to the `playwright.config.ts` configuration file. + +### Running your Playwright tests + +Since Playwright is testing a real Next.js application, it requires the Next.js server to be running prior to starting Playwright. It is recommended to run your tests against your production code to more closely resemble how your application will behave. + +Run `npm run build` and `npm run start`, then run `npm run test:e2e` in another terminal window to run the Playwright tests. + +> **Note:** Alternatively, you can use the [`webServer`](https://playwright.dev/docs/test-advanced#launching-a-development-web-server-during-the-tests) feature to let Playwright start the development server and wait until it's fully available. + +### Running Playwright on Continuous Integration (CI) + +Playwright will by default run your tests in the [headless mode](https://playwright.dev/docs/ci#running-headed). To install all the Playwright dependencies, run `npx playwright install-deps`. + +You can learn more about Playwright and Continuous Integration from these resources: + +- [Getting started with Playwright](https://playwright.dev/docs/intro) +- [Use a development server](https://playwright.dev/docs/test-advanced#launching-a-development-web-server-during-the-tests) +- [Playwright on your CI provider](https://playwright.dev/docs/ci) + +## Jest and React Testing Library + +Jest and React Testing Library are frequently used together for **Unit Testing**. There are three ways you can start using Jest within your Next.js application: + +1. Using one of our [quickstart examples](https://nextjs.org/docs/testing#quickstart-2) +2. With the [Next.js Rust Compiler](https://nextjs.org/docs/testing#setting-up-jest-with-the-rust-compiler) +3. With [Babel](https://nextjs.org/docs/testing#setting-up-jest-with-babel) + +The following sections will go through how you can set up Jest with each of these options: + +### Quickstart + +You can use `create-next-app` with the [with-jest](https://github.com/vercel/next.js/tree/canary/examples/with-jest) example to quickly get started with Jest and React Testing Library: + +```bash +npx create-next-app@latest --example with-jest with-jest-app +``` + +### Setting up Jest (with the Rust Compiler) + +Since the release of [Next.js 12](https://nextjs.org/blog/next-12), Next.js now has built-in configuration for Jest. + +To set up Jest, install `jest`, `jest-environment-jsdom`, `@testing-library/react`, `@testing-library/jest-dom`: + +```bash +npm install --save-dev jest jest-environment-jsdom @testing-library/react @testing-library/jest-dom +``` + +Create a `jest.config.js` file in your project's root directory and add the following: + +```jsx +// jest.config.js +const nextJest = require('next/jest') + +const createJestConfig = nextJest({ + // Provide the path to your Next.js app to load next.config.js and .env files in your test environment + dir: './', +}) + +// Add any custom config to be passed to Jest +/** @type {import('jest').Config} */ +const customJestConfig = { + // Add more setup options before each test is run + // setupFilesAfterEnv: ['/jest.setup.js'], + // if using TypeScript with a baseUrl set to the root directory then you need the below for alias' to work + moduleDirectories: ['node_modules', '/'], + testEnvironment: 'jest-environment-jsdom', +} + +// createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async +module.exports = createJestConfig(customJestConfig) +``` + +Under the hood, `next/jest` is automatically configuring Jest for you, including: + +- Setting up `transform` using [SWC](https://nextjs.org/docs/advanced-features/compiler) +- Auto mocking stylesheets (`.css`, `.module.css`, and their scss variants), image imports and [`@next/font`](https://nextjs.org/docs/basic-features/font-optimization) +- Loading `.env` (and all variants) into `process.env` +- Ignoring `node_modules` from test resolving and transforms +- Ignoring `.next` from test resolving +- Loading `next.config.js` for flags that enable SWC transforms + +> **Note**: To test environment variables directly, load them manually in a separate setup script or in your `jest.config.js` file. For more information, please see [Test Environment Variables](https://nextjs.org/docs/basic-features/environment-variables#test-environment-variables). + +### Setting up Jest (with Babel) + +If you opt out of the [Rust Compiler](https://nextjs.org/docs/advanced-features/compiler), you will need to manually configure Jest and install `babel-jest` and `identity-obj-proxy` in addition to the packages above. + +Here are the recommended options to configure Jest for Next.js: + +```jsx +// jest.config.js +module.exports = { + collectCoverage: true, + // on node 14.x coverage provider v8 offers good speed and more or less good report + coverageProvider: 'v8', + collectCoverageFrom: [ + '**/*.{js,jsx,ts,tsx}', + '!**/*.d.ts', + '!**/node_modules/**', + '!/out/**', + '!/.next/**', + '!/*.config.js', + '!/coverage/**', + ], + moduleNameMapper: { + // Handle CSS imports (with CSS modules) + // https://jestjs.io/docs/webpack#mocking-css-modules + '^.+\\.module\\.(css|sass|scss)$': 'identity-obj-proxy', + + // Handle CSS imports (without CSS modules) + '^.+\\.(css|sass|scss)$': '/__mocks__/styleMock.js', + + // Handle image imports + // https://jestjs.io/docs/webpack#handling-static-assets + '^.+\\.(png|jpg|jpeg|gif|webp|avif|ico|bmp|svg)$/i': `/__mocks__/fileMock.js`, + + // Handle module aliases + '^@/components/(.*)$': '/components/$1', + }, + // Add more setup options before each test is run + // setupFilesAfterEnv: ['/jest.setup.js'], + testPathIgnorePatterns: ['/node_modules/', '/.next/'], + testEnvironment: 'jsdom', + transform: { + // Use babel-jest to transpile tests with the next/babel preset + // https://jestjs.io/docs/configuration#transform-objectstring-pathtotransformer--pathtotransformer-object + '^.+\\.(js|jsx|ts|tsx)$': ['babel-jest', { presets: ['next/babel'] }], + }, + transformIgnorePatterns: [ + '/node_modules/', + '^.+\\.module\\.(css|sass|scss)$', + ], +} +``` + +You can learn more about each configuration option in the [Jest docs](https://jestjs.io/docs/configuration). + +**Handling stylesheets and image imports** + +Stylesheets and images aren't used in the tests but importing them may cause errors, so they will need to be mocked. Create the mock files referenced in the configuration above - `fileMock.js` and `styleMock.js` - inside a `__mocks__` directory: + +```js +// __mocks__/fileMock.js +module.exports = { + src: '/img.jpg', + height: 24, + width: 24, + blurDataURL: 'data:image/png;base64,imagedata', +} +``` + +```js +// __mocks__/styleMock.js +module.exports = {} +``` + +For more information on handling static assets, please refer to the [Jest Docs](https://jestjs.io/docs/webpack#handling-static-assets). + +**Optional: Extend Jest with custom matchers** + +`@testing-library/jest-dom` includes a set of convenient [custom matchers](https://github.com/testing-library/jest-dom#custom-matchers) such as `.toBeInTheDocument()` making it easier to write tests. You can import the custom matchers for every test by adding the following option to the Jest configuration file: + +```js +// jest.config.js +setupFilesAfterEnv: ['/jest.setup.js'] +``` + +Then, inside `jest.setup.js`, add the following import: + +```jsx +// jest.setup.js +import '@testing-library/jest-dom/extend-expect' +``` + +If you need to add more setup options before each test, it's common to add them to the `jest.setup.js` file above. + +**Optional: Absolute Imports and Module Path Aliases** + +If your project is using [Module Path Aliases](https://nextjs.org/docs/advanced-features/module-path-aliases), you will need to configure Jest to resolve the imports by matching the paths option in the `jsconfig.json` file with the `moduleNameMapper` option in the `jest.config.js` file. For example: + +```json +// tsconfig.json or jsconfig.json +{ + "compilerOptions": { + "baseUrl": ".", + "paths": { + "@/components/*": ["components/*"] + } + } +} +``` + +```jsx +// jest.config.js +moduleNameMapper: { + '^@/components/(.*)$': '/components/$1', +} +``` + +### Creating your tests: + +**Add a test script to package.json** + +Add the Jest executable in watch mode to the `package.json` scripts: + +```jsx +"scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "test": "jest --watch" +} +``` + +`jest --watch` will re-run tests when a file is changed. For more Jest CLI options, please refer to the [Jest Docs](https://jestjs.io/docs/cli#reference). + +**Create your first tests** + +Your project is now ready to run tests. Follow Jest's convention by adding tests to the `__tests__` folder in your project's root directory. + +For example, we can add a test to check if the `` component successfully renders a heading: + +```jsx +// __tests__/index.test.jsx + +import { render, screen } from '@testing-library/react' +import Home from '../pages/index' +import '@testing-library/jest-dom' + +describe('Home', () => { + it('renders a heading', () => { + render() + + const heading = screen.getByRole('heading', { + name: /welcome to next\.js!/i, + }) + + expect(heading).toBeInTheDocument() + }) +}) +``` + +Optionally, add a [snapshot test](https://jestjs.io/docs/snapshot-testing) to keep track of any unexpected changes to your `` component: + +```jsx +// __tests__/snapshot.js + +import { render } from '@testing-library/react' +import Home from '../pages/index' + +it('renders homepage unchanged', () => { + const { container } = render() + expect(container).toMatchSnapshot() +}) +``` + +> **Note**: Test files should not be included inside the pages directory because any files inside the pages directory are considered routes. + +**Running your test suite** + +Run `npm run test` to run your test suite. After your tests pass or fail, you will notice a list of interactive Jest commands that will be helpful as you add more tests. + +For further reading, you may find these resources helpful: + +- [Jest Docs](https://jestjs.io/docs/getting-started) +- [React Testing Library Docs](https://testing-library.com/docs/react-testing-library/intro/) +- [Testing Playground](https://testing-playground.com/) - use good testing practices to match elements. + +## Community Packages and Examples + +The Next.js community has created packages and articles you may find helpful: + +- [next-router-mock](https://github.com/scottrippey/next-router-mock) for Storybook. +- [Test Preview Vercel Deploys with Cypress](https://glebbahmutov.com/blog/develop-preview-test/) by Gleb Bahmutov. + +For more information on what to read next, we recommend: + + diff --git a/docs/upgrading.md b/docs/upgrading.md new file mode 100644 index 0000000000000..7bddae8b3dbf1 --- /dev/null +++ b/docs/upgrading.md @@ -0,0 +1,576 @@ +--- +description: Learn how to upgrade Next.js. +--- + +# Upgrade Guide + +## Upgrading from 12 to 13 + +To update to Next.js version 13, run the following command using your preferred package manager: + +```bash +npm i next@latest react@latest react-dom@latest eslint-config-next@latest +# or +yarn upgrade next react react-dom eslint-config-next --latest +# or +pnpm up next react react-dom eslint-config-next --latest +``` + +### v13 Summary + +- The [Supported Browsers](/docs/basic-features/supported-browsers-features.md) have been changed to drop Internet Explorer and target modern browsers. +- The minimum Node.js version has been bumped from 12.22.0 to 14.0.0, since 12.x has reached end-of-life. +- The minimum React version has been bumped from 17.0.2 to 18.2.0. +- The `swcMinify` configuration property was changed from `false` to `true`. See [Next.js Compiler](/docs/advanced-features/compiler.md) for more info. +- The `next/image` import was renamed to `next/legacy/image`. The `next/future/image` import was renamed to `next/image`. A [codemod is available](/docs/advanced-features/codemods.md#next-image-to-legacy-image) to safely and automatically rename your imports. +- The `next/link` child can no longer be ``. Add the `legacyBehavior` prop to use the legacy behavior or remove the `` to upgrade. A [codemod is available](/docs/advanced-features/codemods.md#new-link) to automatically upgrade your code. +- The `target` configuration property has been removed and superseded by [Output File Tracing](https://nextjs.org/docs/advanced-features/output-file-tracing). + +## Migrating shared features + +Next.js 13 introduces a new [`app` directory](https://beta.nextjs.org/docs/routing/fundamentals) with new features and conventions. However, upgrading to Next.js 13 does **not** require using the new [`app` directory](https://beta.nextjs.org/docs/routing/fundamentals#the-app-directory). + +You can continue using `pages` with new features that work in both directories, such as the updated [Image component](#image-component), [Link component](#link-component), [Script component](#script-component), and [Font optimization](#font-optimization). + +### `` Component + +Next.js 12 introduced many improvements to the Image Component with a temporary import: `next/future/image`. These improvements included less client-side JavaScript, easier ways to extend and style images, better accessibility, and native browser lazy loading. + +Starting in Next.js 13, this new behavior is now the default for `next/image`. + +There are two codemods to help you migrate to the new Image Component: + +- [next-image-to-legacy-image](/docs/advanced-features/codemods.md#rename-instances-of-nextimage): This codemod will safely and automatically rename `next/image` imports to `next/legacy/image` to maintain the same behavior as Next.js 12. We recommend running this codemod to quickly update to Next.js 13 automatically. +- [next-image-experimental](/docs/advanced-features/codemods.md#next-image-experimental-experimental): After running the previous codemod, you can optionally run this experimental codemod to upgrade `next/legacy/image` to the new `next/image`, which will remove unused props and add inline styles. Please note this codemod is experimental and only covers static usage (such as ``) but not dynamic usage (such as ``). + +Alternatively, you can manually update by following the [migration guide](/docs/advanced-features/codemods.md#next-image-experimental-experimental) and also see the [legacy comparison](/docs/api-reference/next/legacy/image.md#comparison). + +### `` Component + +The [`` Component](/docs/api-reference/next/link.md) no longer requires manually adding an `` tag as a child. This behavior was added as an experimental option in [version 12.2](https://nextjs.org/blog/next-12-2) and is now the default. In Next.js 13, `` always renders `` and allows you to forward props to the underlying tag. + +For example: + +```jsx +import Link from 'next/link' + +// Next.js 12: `` has to be nested otherwise it's excluded + + About + + +// Next.js 13: `` always renders `` under the hood + + About + +``` + +To upgrade your links to Next.js 13, you can use the [`new-link` codemod](/docs/advanced-features/codemods.md#new-link). + +### ` + + + ) +} +``` + +## Useful links + +- [Docs for Next.js Script component](https://nextjs.org/docs/basic-features/script) diff --git a/errors/install-sass.md b/errors/install-sass.md new file mode 100644 index 0000000000000..91b7d1301f395 --- /dev/null +++ b/errors/install-sass.md @@ -0,0 +1,19 @@ +# Install `sass` to Use Built-In Sass Support + +#### Why This Error Occurred + +Using Next.js' [built-in Sass support](https://nextjs.org/docs/basic-features/built-in-css-support#sass-support) requires that you bring your own version of Sass. + +#### Possible Ways to Fix It + +Please install the `sass` package in your project. + +```bash +npm i sass +# or +yarn add sass +``` + +### Useful Links + +- [Sass Support in Documentation](https://nextjs.org/docs/basic-features/built-in-css-support#sass-support) diff --git a/errors/install-sharp.md b/errors/install-sharp.md new file mode 100644 index 0000000000000..5c35f95b7357d --- /dev/null +++ b/errors/install-sharp.md @@ -0,0 +1,25 @@ +# Install `sharp` to Use Built-In Image Optimization + +#### Why This Error Occurred + +Using Next.js' built-in [Image Optimization](https://nextjs.org/docs/basic-features/image-optimization) requires [sharp](https://www.npmjs.com/package/sharp) as a dependency. + +You are seeing this error because your OS was unable to [install sharp](https://sharp.pixelplumbing.com/install) properly, either using pre-built binaries or building from source. + +#### Possible Ways to Fix It + +Option 1: Use a different version of Node.js and try to install `sharp` again. + +```bash +npm i sharp +# or +yarn add sharp +``` + +Option 2: If using macOS, ensure XCode Build Tools are installed and try to install `sharp` again. + +For example, see [macOS Catalina instructions](https://github.com/nodejs/node-gyp/blob/66c0f0446749caa591ad841cd029b6d5b5c8da42/macOS_Catalina.md). + +Option 3: Use a different OS and try to install `sharp` again. + +For example, if you're using Windows, try using [WSL](https://docs.microsoft.com/en-us/windows/wsl/about) (Windows Subsystem for Linux). diff --git a/errors/invalid-api-status-body.md b/errors/invalid-api-status-body.md new file mode 100644 index 0000000000000..45f86c0b3684e --- /dev/null +++ b/errors/invalid-api-status-body.md @@ -0,0 +1,32 @@ +# Invalid API Route Status/Body Response + +#### Why This Error Occurred + +In one of your API routes a 204 or 304 status code was used as well as sending a response body. + +This is invalid as a 204 or 304 status code dictates no response body should be present. + +#### Possible Ways to Fix It + +Send an empty body when using a 204 or 304 status code or use a different status code while sending a response body. + +Before + +```js +export default function handler(req, res) { + res.status(204).send('invalid body') +} +``` + +After + +```js +export default function handler(req, res) { + res.status(204).end() +} +``` + +### Useful Links + +- [204 status code documentation](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/204) +- [304 status code documentation](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/304) diff --git a/errors/invalid-assetprefix.md b/errors/invalid-assetprefix.md new file mode 100644 index 0000000000000..a1415cfe0320b --- /dev/null +++ b/errors/invalid-assetprefix.md @@ -0,0 +1,17 @@ +# Invalid assetPrefix + +#### Why This Error Occurred + +The value of `assetPrefix` in `next.config.js` is set to something that is not a `string`. + +#### Possible Ways to Fix It + +Ensure that `assetPrefix` is a `string`. + +Example: + +```js +module.exports = { + assetPrefix: '/', +} +``` diff --git a/errors/invalid-dynamic-options-type.md b/errors/invalid-dynamic-options-type.md new file mode 100644 index 0000000000000..e5aa443ac58ec --- /dev/null +++ b/errors/invalid-dynamic-options-type.md @@ -0,0 +1,31 @@ +# Invalid options type in a `next/dynamic` call + +#### Why This Error Occurred + +You have an invalid options type in a `next/dynamic` call. The options must be an object literal. + +#### Possible Ways to Fix It + +**Before** + +```jsx +import dynamic from 'next/dynamic' + +const options = { loading: () =>

    ...

    , ssr: false } +const DynamicComponent = dynamic(() => import('../components/hello'), options) +``` + +**After** + +```jsx +import dynamic from 'next/dynamic' + +const DynamicComponent = dynamic(() => import('../components/hello'), { + loading: () =>

    ...

    , + ssr: false, +}) +``` + +### Useful Links + +- [Dynamic Import](https://nextjs.org/docs/advanced-features/dynamic-import) diff --git a/errors/invalid-dynamic-suspense.md b/errors/invalid-dynamic-suspense.md new file mode 100644 index 0000000000000..f016eec897d41 --- /dev/null +++ b/errors/invalid-dynamic-suspense.md @@ -0,0 +1,31 @@ +# Invalid Usage of `suspense` Option of `next/dynamic` + +#### Why This Error Occurred + +- You are using `{ suspense: true }` with React version older than 18. +- You are using `{ suspense: true, ssr: false }`. +- You are using `{ suspense: true, loading }`. + +#### Possible Ways to Fix It + +**If you are using `{ suspense: true }` with React version older than 18** + +- You can try upgrading to React 18 or newer +- If upgrading React is not an option, remove `{ suspense: true }` from `next/dynamic` usages. + +**If you are using `{ suspense: true, ssr: false }`** + +Next.js will use `React.lazy` when `suspense` is set to true. React 18 or newer will always try to resolve the Suspense boundary on the server. This behavior can not be disabled, thus the `ssr: false` is ignored with `suspense: true`. + +- You should write code that works in both client-side and server-side. +- If rewriting the code is not an option, remove `{ suspense: true }` from `next/dynamic` usages. + +**If you are using `{ suspense: true, loading }`** + +Next.js will use `React.lazy` when `suspense` is set to true, when your dynamic-imported component is loading, React will use the closest suspense boundary's fallback. + +You should remove `loading` from `next/dynamic` usages, and use ``'s `fallback` prop. + +### Useful Links + +- [Dynamic Import Suspense Usage](https://nextjs.org/docs/advanced-features/dynamic-import#with-suspense) diff --git a/errors/invalid-external-rewrite.md b/errors/invalid-external-rewrite.md new file mode 100644 index 0000000000000..89d114af835bf --- /dev/null +++ b/errors/invalid-external-rewrite.md @@ -0,0 +1,13 @@ +# Invalid External Rewrite + +#### Why This Error Occurred + +A rewrite was defined with both `basePath: false` and an internal `destination`. Rewrites that capture urls outside of the `basePath` must route externally, as they are intended for proxying in the case of incremental adoption of Next.js in a project. + +#### Possible Ways to Fix It + +Look for any rewrite where `basePath` is `false` and make sure its `destination` starts with `http://` or `https://`. + +### Useful Links + +- [Rewrites section in Documentation](https://nextjs.org/docs/api-reference/next.config.js/rewrites) diff --git a/errors/invalid-getserversideprops-value.md b/errors/invalid-getserversideprops-value.md new file mode 100644 index 0000000000000..8a05d9d5d635b --- /dev/null +++ b/errors/invalid-getserversideprops-value.md @@ -0,0 +1,21 @@ +# Invalid getServerSideProps Return Value + +#### Why This Error Occurred + +In one of the page's `getServerSideProps` the return value had the incorrect shape. + +#### Possible Ways to Fix It + +Make sure to return the following shape from `getServerSideProps`: + +```ts +export async function getServerSideProps(ctx: GetServerSidePropsContext) { + return { + props: { [key: string]: any } + } +} +``` + +### Useful Links + +- [getServerSideProps](https://nextjs.org/docs/api-reference/data-fetching/get-server-side-props) diff --git a/errors/invalid-getstaticpaths-value.md b/errors/invalid-getstaticpaths-value.md new file mode 100644 index 0000000000000..f34640ba00afd --- /dev/null +++ b/errors/invalid-getstaticpaths-value.md @@ -0,0 +1,41 @@ +# Invalid getStaticPaths Return Value + +#### Why This Error Occurred + +In one of the page's `getStaticPaths` the return value had the incorrect shape. + +#### Possible Ways to Fix It + +Make sure to return the following shape from `getStaticPaths`: + +```js +export async function getStaticPaths() { + return { + paths: Array, + fallback: boolean + } +} +``` + +There are two required properties: + +1. `paths`: this property is an [Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array) of URLs ("paths") that should be statically generated at build-time. The returned paths must match the dynamic route shape. + - You may return a [String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String) or an [Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object) that explicitly defines all URL `params`. + ```js + // pages/blog/[slug].js + export async function getStaticPaths() { + return { + paths: [ + // String variant: + '/blog/first-post', + // Object variant: + { params: { slug: 'second-post' } }, + ], + fallback: true, + } + } + ``` +1. `fallback`: this property can be a [Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean), specifying whether or not a fallback version of this page should be generated, or a string `'blocking'` to wait for the generation: + - Enabling `fallback` (via `true`) allows you to return a subset of all the possible paths that should be statically generated. At runtime, Next.js will statically generate the remaining paths the **first time they are requested**. Consecutive calls to the path will be served as-if it was statically generated at build-time. This reduces build times when dealing with thousands or millions of pages. + - Disabling `fallback` (via `false`) requires you return the full collection of paths you would like to statically generate at build-time. At runtime, any path that was not generated at build-time **will 404**. + - If `fallback` is `'blocking'`, new paths not returned by getStaticPaths will wait for the HTML to be generated, identical to SSR (hence why blocking), and then be cached for future requests so it only happens once per path. diff --git a/errors/invalid-getstaticprops-value.md b/errors/invalid-getstaticprops-value.md new file mode 100644 index 0000000000000..24a86c8498d24 --- /dev/null +++ b/errors/invalid-getstaticprops-value.md @@ -0,0 +1,25 @@ +# Invalid getStaticProps Return Value + +#### Why This Error Occurred + +In one of the page's `getStaticProps` the return value had the incorrect shape. + +#### Possible Ways to Fix It + +Make sure to return the following shape from `getStaticProps`: + +```ts +export async function getStaticProps(ctx: { + params?: ParsedUrlQuery; + preview?: boolean; + previewData?: PreviewData; +}) { + return { + props: { [key: string]: any } + } +} +``` + +### Useful Links + +- [`getStaticProps` Documentation](https://nextjs.org/docs/api-reference/data-fetching/get-static-props) diff --git a/errors/invalid-href-passed.md b/errors/invalid-href-passed.md new file mode 100644 index 0000000000000..9d84c9da006d0 --- /dev/null +++ b/errors/invalid-href-passed.md @@ -0,0 +1,18 @@ +# Invalid href passed to router + +#### Why This Error Occurred + +Next.js provides a router which can be utilized via a component imported via `next/link`, a wrapper `withRouter(Component)`, and now a hook `useRouter()`. +When using any of these, it is expected they are only used for internal navigation, i.e. navigating between pages in the same Next.js application. + +Either you passed a non-internal `href` to a `next/link` component or you called `Router#push` or `Router#replace` with one. + +Invalid `href`s include external sites (https://google.com) and `mailto:` links. In the past, usage of these invalid `href`s could have gone unnoticed, but since they can cause unexpected behavior we now show a warning in development for them. + +#### Possible Ways to Fix It + +Look for any usage of `next/link` or `next/router` that is being passed a non-internal `href` and replace them with either an anchor tag (`
    `) or `window.location.href = YOUR_HREF`. + +### Useful Links + +- [Routing section in Documentation](https://nextjs.org/docs/routing/introduction) diff --git a/errors/invalid-i18n-config.md b/errors/invalid-i18n-config.md new file mode 100644 index 0000000000000..b840d14a61f98 --- /dev/null +++ b/errors/invalid-i18n-config.md @@ -0,0 +1,44 @@ +# Invalid i18n config + +#### Why This Error + +In your `next.config.js` file you provided an invalid config for the `i18n` field. This could mean the limit for 100 locale items was exceeded. + +#### Possible Ways to Fix It + +Make sure your `i18n` field follows the allowed config shape, limits, and values: + +```js +module.exports = { + i18n: { + // These are all the locales you want to support in + // your application + locales: ['en-US', 'es', 'fr', 'nl-NL'], + // This is the default locale you want to be used when visiting + // a non-locale prefixed path e.g. `/hello` + defaultLocale: 'en-US', + // This is a list of locale domains and the default locale they + // should handle (these are only required when setting up domain routing) + domains: [ + { + domain: 'example.com', + defaultLocale: 'en-US', + // other locales that should be handled on this domain + locales: ['es'], + }, + { + domain: 'example.nl', + defaultLocale: 'nl-NL', + }, + { + domain: 'example.fr', + defaultLocale: 'fr', + }, + ], + }, +} +``` + +### Useful Links + +- [Internationalized Routing Documentation](https://nextjs.org/docs/advanced-features/i18n-routing) diff --git a/errors/invalid-images-config.md b/errors/invalid-images-config.md new file mode 100644 index 0000000000000..badaa4f3e1a88 --- /dev/null +++ b/errors/invalid-images-config.md @@ -0,0 +1,47 @@ +# Invalid images config + +#### Why This Error Occurred + +In your `next.config.js` file you provided an invalid config for the `images` field. + +#### Possible Ways to Fix It + +Make sure your `images` field follows the allowed config shape and values: + +```js +module.exports = { + images: { + // limit of 25 deviceSizes values + deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840], + // limit of 25 imageSizes values + imageSizes: [16, 32, 48, 64, 96, 128, 256, 384], + // limit of 50 domains values + domains: [], + // path prefix for Image Optimization API, useful with `loader` + path: '/_next/image', + // loader can be 'default', 'imgix', 'cloudinary', 'akamai', or 'custom' + loader: 'default', + // file with `export default function loader({src, width, quality})` + loaderFile: '', + // disable static imports for image files + disableStaticImages: false, + // minimumCacheTTL is in seconds, must be integer 0 or more + minimumCacheTTL: 60, + // ordered list of acceptable optimized image formats (mime types) + formats: ['image/webp'], + // enable dangerous use of SVG images + dangerouslyAllowSVG: false, + // set the Content-Security-Policy header + contentSecurityPolicy: "default-src 'self'; script-src 'none'; sandbox;", + // limit of 50 objects + remotePatterns: [], + // when true, every image will be unoptimized + unoptimized: false, + }, +} +``` + +### Useful Links + +- [Image Optimization Documentation](https://nextjs.org/docs/basic-features/image-optimization) +- [`next/image` Documentation](https://nextjs.org/docs/api-reference/next/image) diff --git a/errors/invalid-multi-match.md b/errors/invalid-multi-match.md new file mode 100644 index 0000000000000..cf9596b23d77a --- /dev/null +++ b/errors/invalid-multi-match.md @@ -0,0 +1,27 @@ +# Invalid Multi-match + +#### Why This Error Occurred + +In one of your custom-routes you specified a multi-match `/:path*` and used it in your `destination` without adding the `*` in your `destination` e.g. `destination: '/another/:path'` + +#### Possible Ways to Fix It + +Add `*` to your usage of the multi-match param in your `destination`. + +**Before** + +```js +{ + source: '/:path*', + destination: '/another/:path' +} +``` + +**After** + +```js +{ + source: '/:path*', + destination: '/another/:path*' +} +``` diff --git a/errors/invalid-new-link-with-extra-anchor.md b/errors/invalid-new-link-with-extra-anchor.md new file mode 100644 index 0000000000000..c91b53f7acf66 --- /dev/null +++ b/errors/invalid-new-link-with-extra-anchor.md @@ -0,0 +1,21 @@ +# Invalid <Link> with <a> child + +#### Why This Error Occurred + +Starting with Next.js 13, `` renders as ``, so attempting to use `` as a child is invalid. + +#### Possible Ways to Fix It + +Run the `new-link` codemod to automatically upgrade previous versions of Next.js to the new `` usage: + +```sh +npx @next/codemod new-link . +``` + +This will change `Home` to `Home`. + +Alternatively, you can add the `legacyBehavior` prop `Home`. + +### Useful Links + +- [next/link](https://nextjs.org/docs/api-reference/next/link) diff --git a/errors/invalid-next-config.md b/errors/invalid-next-config.md new file mode 100644 index 0000000000000..ffe6a08e491c8 --- /dev/null +++ b/errors/invalid-next-config.md @@ -0,0 +1,24 @@ +# Invalid next.config.js + +#### Why This Error Occurred + +In your `next.config.js` file you passed invalid options that either are the incorrect type or an unknown field. + +#### Possible Ways to Fix It + +Fixing the listed config errors will remove this warning. You can also leverage the `NextConfig` type by importing from `next` to help ensure your config is correct. + +```ts +/** + * @type {import('next').NextConfig} + */ +const nextConfig = { + /* config options here */ +} + +module.exports = nextConfig +``` + +### Useful Links + +- [`next.config.js`](https://nextjs.org/docs/api-reference/next.config.js/introduction) diff --git a/errors/invalid-page-config.md b/errors/invalid-page-config.md new file mode 100644 index 0000000000000..a535a29056c7d --- /dev/null +++ b/errors/invalid-page-config.md @@ -0,0 +1,119 @@ +# Invalid Page / API Route Config + +#### Why This Error Occurred + +In one of your pages or API Routes you did `export const config` with an invalid value. + +#### Possible Ways to Fix It + +The page's config must be an object initialized directly when being exported and not modified dynamically. +The config object must only contain static constant literals without expressions. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Not AllowedAllowed
    + +```js +// `config` should be an object +export const config = 'hello world' +``` + + + +```js +export const config = {} +``` + +
    + +```js +export const config = {} +// `config.amp` is defined after `config` is exported +config.amp = true + +// `config.amp` contains a dynamic expression +export const config = { + amp: 1 + 1 > 2, +} +``` + + + +```js +export const config = { + amp: true, +} + +export const config = { + amp: false, +} +``` + +
    + +```js +// `config.runtime` contains a dynamic expression +export const config = { + runtime: `node${'js'}`, +} +``` + + + +```js +export const config = { + runtime: 'nodejs', +} +export const config = { + runtime: `nodejs`, +} +``` + +
    + +```js +// Re-exported `config` is not allowed +export { config } from '../config' +``` + + + +```js +export const config = {} +``` + +
    + +### Useful Links + +- [Enabling AMP Support](https://nextjs.org/docs/advanced-features/amp-support/introduction) +- [API Routes Request Helpers](https://nextjs.org/docs/api-routes/request-helpers) +- [Switchable Runtime](https://nextjs.org/docs/advanced-features/react-18/switchable-runtime) diff --git a/errors/invalid-project-dir-casing.md b/errors/invalid-project-dir-casing.md new file mode 100644 index 0000000000000..8f650f95acac6 --- /dev/null +++ b/errors/invalid-project-dir-casing.md @@ -0,0 +1,16 @@ +# Invalid Project Directory Casing + +#### Why This Error Occurred + +When starting Next.js, the current directory is a different casing than the actual directory on your filesystem. This can cause files to resolve inconsistently. + +This can occur when using a case-insensitive filesystem. For example, opening PowerShell on Windows navigating to `cd path/to/myproject` instead of `cd path/to/MyProject`. + +#### Possible Ways to Fix It + +Ensure the casing for the current working directory matches the actual case of the real directory. Use a terminal that enforces case-sensitivity. + +### Useful Links + +- [Next.js CLI documentation](https://nextjs.org/docs/api-reference/cli) +- [Case sensitivity in filesystems](https://en.wikipedia.org/wiki/Case_sensitivity#In_filesystems) diff --git a/errors/invalid-redirect-gssp.md b/errors/invalid-redirect-gssp.md new file mode 100644 index 0000000000000..d75fbb9fe4d0b --- /dev/null +++ b/errors/invalid-redirect-gssp.md @@ -0,0 +1,32 @@ +# Invalid Redirect getStaticProps/getServerSideProps + +#### Why This Error Occurred + +The `redirect` value returned from your `getStaticProps` or `getServerSideProps` function had invalid values. + +#### Possible Ways to Fix It + +Make sure you return the proper values for the `redirect` value. + +```js +export const getStaticProps = ({ params }) => { + if (params.slug === 'deleted-post') { + return { + redirect: { + permanent: true, // or false + destination: '/some-location', + }, + } + } + + return { + props: { + // data + }, + } +} +``` + +### Useful Links + +- [Data Fetching Documentation](https://nextjs.org/docs/basic-features/data-fetching/get-static-props) diff --git a/errors/invalid-relative-url-external-as.md b/errors/invalid-relative-url-external-as.md new file mode 100644 index 0000000000000..848b56d422e46 --- /dev/null +++ b/errors/invalid-relative-url-external-as.md @@ -0,0 +1,47 @@ +# Invalid relative `href` and external `as` values + +#### Why This Error Occurred + +Somewhere you are utilizing the `next/link` component, `Router#push`, or `Router#replace` with a relative route in your `href` that has an external `as` value. The `as` value must be relative also or only `href` should be used with an external URL. + +Note: this error will only show when the `next/link` component is clicked not when only rendered. + +**Incompatible `href` and `as`** + +```jsx +import Link from 'next/link' + +export default function Page(props) { + return ( + <> + +
    Invalid link + + + ) +} +``` + +**Compatible `href` and `as`** + +```jsx +import Link from 'next/link' + +export default function Page(props) { + return ( + <> + + Invalid link + + + ) +} +``` + +#### Possible Ways to Fix It + +Look for any usage of the `next/link` component, `Router#push`, or `Router#replace` and make sure that the provided `href` and `as` values are compatible + +### Useful Links + +- [Routing section in Documentation](https://nextjs.org/docs/routing/introduction) diff --git a/errors/invalid-resolve-alias.md b/errors/invalid-resolve-alias.md new file mode 100644 index 0000000000000..4c2c88aa1fcf4 --- /dev/null +++ b/errors/invalid-resolve-alias.md @@ -0,0 +1,22 @@ +# Invalid webpack resolve alias + +#### Why This Error Occurred + +When overriding `config.resolve.alias` incorrectly in `next.config.js` webpack will throw an error because private-next-pages is not defined. + +#### Possible Ways to Fix It + +This is not a bug in Next.js, it's related to the user adding a custom webpack(config) config to next.config.js and overriding internals by not applying Next.js' aliases. Solution would be: + +```js +webpack(config) { + config.resolve.alias = { + ...config.resolve.alias, + // your aliases + } +} +``` + +### Useful Links + +- [Related issue](https://github.com/vercel/next.js/issues/6681) diff --git a/errors/invalid-route-source.md b/errors/invalid-route-source.md new file mode 100644 index 0000000000000..0995c5714b5d1 --- /dev/null +++ b/errors/invalid-route-source.md @@ -0,0 +1,58 @@ +# Invalid Custom Route `source` + +#### Why This Error Occurred + +When defining custom routes, or a middleware `matcher`, a pattern could not be parsed. + +This could have been due to trying to use normal `RegExp` syntax like negative lookaheads (`?!exclude`) without following [`path-to-regexp`](https://github.com/pillarjs/path-to-regexp)'s syntax for it. + +#### Possible Ways to Fix It + +Wrap the `RegExp` part of your `source` as an un-named parameter. + +--- + +Custom routes: + +**Before** + +```js +{ + source: '/feedback/(?!general)', + destination: '/feedback/general' +} +``` + +**After** + +```js +{ + source: '/feedback/((?!general).*)', + destination: '/feedback/general' +} +``` + +--- + +Middleware: + +**Before** + +```js +const config = { + matcher: '/feedback/(?!general)', +} +``` + +**After** + +```js +const config = { + matcher: '/feedback/((?!general).*)', +} +``` + +### Useful Links + +- [path-to-regexp](https://github.com/pillarjs/path-to-regexp) +- [un-named parameters](https://github.com/pillarjs/path-to-regexp#unnamed-parameters) diff --git a/errors/invalid-script.md b/errors/invalid-script.md new file mode 100644 index 0000000000000..f919a25a00873 --- /dev/null +++ b/errors/invalid-script.md @@ -0,0 +1,38 @@ +# Invalid Script + +#### Why This Error Occurred + +Somewhere in your application, you are using the `next/script` component without including an inline script or `src` attribute. + +#### Possible Ways to Fix It + +Look for any usage of the `next/script` component and make sure that `src` is provided or an inline script is used. + +**Compatible `src` attribute** + +```jsx + +``` + +**Compatible inline script with `dangerouslySetInnerHtml`** + +```jsx + +
    + ) +} + +export default Home +``` + +#### Using analytics.js + +If you are using the [analytics.js](https://developers.google.com/analytics/devguides/collection/analyticsjs) script to add analytics: + +```jsx +import Script from 'next/script' + +function Home() { + return ( +
    + +
    + ) +} + +export default Home +``` + +If you are using the [alternative async variant](https://developers.google.com/analytics/devguides/collection/analyticsjs#alternative_async_tag): + +```jsx +import Script from 'next/script' + +function Home() { + return ( +
    + + + + + ) +} +``` + +- [next-script](https://nextjs.org/docs/basic-features/script#usage) diff --git a/errors/no-cache.md b/errors/no-cache.md new file mode 100644 index 0000000000000..4e4b9963d9227 --- /dev/null +++ b/errors/no-cache.md @@ -0,0 +1,14 @@ +# No Cache Detected + +### Why This Error Occurred + +A Next.js build was triggered in a continuous integration environment, but no cache was detected. + +This results in slower builds and can hurt Next.js' persistent caching of client-side bundles across builds. + +### Possible Ways to Fix It + +> **Note**: If this is a new project, or being built for the first time in your CI, you can ignore this error. +> However, you'll want to make sure it doesn't continue to happen and fix it if it does! + +Follow the instructions in [CI Build Caching](https://nextjs.org/docs/advanced-features/ci-build-caching) to ensure your Next.js cache is persisted between builds. diff --git a/errors/no-css-tags.md b/errors/no-css-tags.md new file mode 100644 index 0000000000000..c9926b25b81f1 --- /dev/null +++ b/errors/no-css-tags.md @@ -0,0 +1,40 @@ +# No CSS Tags + +> Prevent manual stylesheet tags. + +### Why This Error Occurred + +A `link` element was used to link to an external stylesheet. This can negatively affect CSS resource loading on your webpage. + +### Possible Ways to Fix It + +There are multiple ways to include styles using Next.js' built-in CSS support, including the option to use `@import` within the root stylesheet that is imported in `pages/_app.js`: + +```css +/* Root stylesheet */ +@import 'extra.css'; + +body { + /* ... */ +} +``` + +Another option is to use CSS Modules to import the CSS file scoped specifically to the component. + +```jsx +import styles from './extra.module.css' + +export class Home { + render() { + return ( +
    + +
    + ) + } +} +``` + +Refer to the [Built-In CSS Support](https://nextjs.org/docs/basic-features/built-in-css-support) documentation to learn about all the ways to include CSS to your application. diff --git a/errors/no-document-import-in-page.md b/errors/no-document-import-in-page.md new file mode 100644 index 0000000000000..bb2713856f9a6 --- /dev/null +++ b/errors/no-document-import-in-page.md @@ -0,0 +1,26 @@ +# No Document Import in Page + +> Prevent importing `next/document` outside of `pages/_document.js`. + +### Why This Error Occurred + +`next/document` was imported in a page outside of `pages/_document.js` (or `pages/_document.tsx` if you are using TypeScript). This can cause unexpected issues in your application. + +### Possible Ways to Fix It + +Only import and use `next/document` within `pages/_document.js` (or `pages/_document.tsx`) to override the default `Document` component: + +```jsx +// pages/_document.js +import Document, { Html, Head, Main, NextScript } from 'next/document' + +class MyDocument extends Document { + //... +} + +export default MyDocument +``` + +### Useful Links + +- [Custom Document](https://nextjs.org/docs/advanced-features/custom-document) diff --git a/errors/no-document-title.md b/errors/no-document-title.md index c71b6d5f6d007..e83b856defe92 100644 --- a/errors/no-document-title.md +++ b/errors/no-document-title.md @@ -1,4 +1,4 @@ -# `` should not be used in _document.js's `<Head>` +# `<title>` should not be used in \_document.js's `<Head>` #### Why This Error Occurred @@ -10,37 +10,23 @@ Set `<title>` in `pages/_app.js` instead: ```js // pages/_app.js -import App, {Container} from 'next/app' -import Head from 'next/head' import React from 'react' +import Head from 'next/head' -export default class MyApp extends App { - static async getInitialProps ({ Component, ctx }) { - let pageProps = {} - - if (Component.getInitialProps) { - pageProps = await Component.getInitialProps(ctx) - } - - return { pageProps } - } - - render () { - const { Component, pageProps } = this.props - - return ( - <Container> - <Head> - <title>My new cool app - - - - ) - } +function MyApp({ Component, pageProps }) { + return ( + <> + + My new cool app + + + + ) } -``` +export default MyApp +``` ### Useful Links -- [The issue this was reported in: #4596](https://github.com/zeit/next.js/issues/4596) +- [The issue this was reported in: #4596](https://github.com/vercel/next.js/issues/4596) diff --git a/errors/no-document-viewport-meta.md b/errors/no-document-viewport-meta.md new file mode 100644 index 0000000000000..bf5131b348a24 --- /dev/null +++ b/errors/no-document-viewport-meta.md @@ -0,0 +1,32 @@ +# Viewport `meta` tags should not be used in `_document.js`'s `` + +#### Why This Error Occurred + +Adding `` in `pages/_document.js` will lead to unexpected results since it cannot be deduped. +The viewport tag should be handled by `next/head` in `pages/_app.js`. + +#### Possible Ways to Fix It + +Set your viewport `meta` tag in `pages/_app.js` instead: + +```tsx +// pages/_app.js +import Head from 'next/head' + +function MyApp({ Component, pageProps }) { + return ( + <> + + + + + + ) +} + +export default MyApp +``` + +### Useful Links + +- [Issue #13230](https://github.com/vercel/next.js/issues/13230), which led to the creation of this warning. diff --git a/errors/no-duplicate-head.md b/errors/no-duplicate-head.md new file mode 100644 index 0000000000000..4ba4314f18456 --- /dev/null +++ b/errors/no-duplicate-head.md @@ -0,0 +1,40 @@ +# No Duplicate Head + +> Prevent duplicate usage of `` in `pages/_document.js`. + +### Why This Error Occurred + +More than a single instance of the `` component was used in a single custom document. This can cause unexpected behavior in your application. + +### Possible Ways to Fix It + +Only use a single `` component in your custom document in `pages/_document.js`. + +```jsx +// pages/_document.js +import Document, { Html, Head, Main, NextScript } from 'next/document' + +class MyDocument extends Document { + static async getInitialProps(ctx) { + //... + } + + render() { + return ( + + + +
    + + + + ) + } +} + +export default MyDocument +``` + +### Useful Links + +- [Custom Document](https://nextjs.org/docs/advanced-features/custom-document) diff --git a/errors/no-head-element.md b/errors/no-head-element.md new file mode 100644 index 0000000000000..f3bd5ca349270 --- /dev/null +++ b/errors/no-head-element.md @@ -0,0 +1,32 @@ +# No Head Element + +> Prevent usage of `` element. + +### Why This Error Occurred + +A `` element was used to include page-level metadata, but this can cause unexpected behavior in a Next.js application. Use Next.js' built-in `next/head` component instead. + +### Possible Ways to Fix It + +Import and use the `` component: + +```jsx +import Head from 'next/head' + +function Index() { + return ( + <> + + My page title + + + + ) +} + +export default Index +``` + +### Useful Links + +- [next/head](https://nextjs.org/docs/api-reference/next/head) diff --git a/errors/no-head-import-in-document.md b/errors/no-head-import-in-document.md new file mode 100644 index 0000000000000..cb9c18e08eb88 --- /dev/null +++ b/errors/no-head-import-in-document.md @@ -0,0 +1,36 @@ +# No Head Import in Document + +> Prevent usage of `next/head` in `pages/_document.js`. + +### Why This Error Occurred + +`next/head` was imported in `pages/_document.js`. This can cause unexpected issues in your application. + +### Possible Ways to Fix It + +Only import and use `next/document` within `pages/_document.js` to override the default `Document` component. If you are importing `next/head` to use the `Head` component, import it from `next/document` instead in order to modify `` code across all pages: + +```jsx +// pages/_document.js +import Document, { Html, Head, Main, NextScript } from 'next/document' + +class MyDocument extends Document { + static async getInitialProps(ctx) { + //... + } + + render() { + return ( + + + + ) + } +} + +export default MyDocument +``` + +### Useful Links + +- [Custom Document](https://nextjs.org/docs/advanced-features/custom-document) diff --git a/errors/no-html-link-for-pages.md b/errors/no-html-link-for-pages.md new file mode 100644 index 0000000000000..32e124dc11fda --- /dev/null +++ b/errors/no-html-link-for-pages.md @@ -0,0 +1,65 @@ +# No HTML link for pages + +> Prevent usage of `` elements to navigate to internal Next.js pages. + +### Why This Error Occurred + +An `` element was used to navigate to a page route without using the `next/link` component, causing unnecessary full page refreshes. + +The `Link` component is required in order to enable client-side route transitions between pages and provide a single-page app experience. + +### Possible Ways to Fix It + +Make sure to import the `Link` component and wrap anchor elements that route to different page routes. + +**Before:** + +```jsx +function Home() { + return ( + + ) +} +``` + +**After:** + +```jsx +import Link from 'next/link' + +function Home() { + return ( +
    + + About Us + +
    + ) +} + +export default Home +``` + +### Options + +#### `pagesDir` + +This rule can normally locate your `pages` directory automatically. + +If you're working in a monorepo, we recommend configuring the [`rootDir`](/docs/basic-features/eslint.md#rootDir) setting in `eslint-plugin-next`, which `pagesDir` will use to locate your `pages` directory. + +In some cases, you may also need to configure this rule directly by providing a `pages` directory. This can be a path or an array of paths. + +```json +{ + "rules": { + "@next/next/no-html-link-for-pages": ["error", "packages/my-app/pages/"] + } +} +``` + +### Useful Links + +- [next/link API Reference](https://nextjs.org/docs/api-reference/next/link) diff --git a/errors/no-img-element.md b/errors/no-img-element.md new file mode 100644 index 0000000000000..1e4d9f651442a --- /dev/null +++ b/errors/no-img-element.md @@ -0,0 +1,51 @@ +# No Img Element + +> Prevent usage of `` element to prevent layout shift. + +### Why This Error Occurred + +An `` element was used to display an image. Use either `` in conjunction with `` element, or use `next/image` that has better performance and automatic Image Optimization over ``. + +### Possible Ways to Fix It + +Import and use the `` component: + +```jsx +import Image from 'next/image' + +function Home() { + return ( + <> + Landscape picture + + ) +} + +export default Home +``` + +
    + +Use `` in conjunction with `` element: + +```jsx +function Home() { + return ( + <> + + + Landscape picture + + + ) +} +``` + +### Useful Links + +- [Image Component and Image Optimization](https://nextjs.org/docs/basic-features/image-optimization) diff --git a/errors/no-on-app-updated-hook.md b/errors/no-on-app-updated-hook.md index ec0e0524f88fe..5437cef9d4f71 100644 --- a/errors/no-on-app-updated-hook.md +++ b/errors/no-on-app-updated-hook.md @@ -1,11 +1,11 @@ # Router.onAppUpdated is removed -Due to [this bug fix](https://github.com/zeit/next.js/pull/3849), we had to remove the `Router.onAppUpdated` hook. But the default functionality of this feature is still in effect. +Due to [this bug fix](https://github.com/vercel/next.js/pull/3849), we had to remove the `Router.onAppUpdated` hook. But the default functionality of this feature is still in effect. We use this hook to detect a new app deployment when switching pages and act accordingly. Although there are many things you can do in this hook, it's often used to navigate the page via the server as shown below: ```js -Router.onAppUpdated = function(nextRoute) { +Router.onAppUpdated = function (nextRoute) { location.href = nextRoute } ``` @@ -17,9 +17,9 @@ One real use of this hook is to persist your application state to local-storage This is code for that: ```js -window.onbeforeunload = function(e) { +window.onbeforeunload = function (e) { // Get the application state (usually from a store like Redux) const appState = {} - localStorage.setItem('app-state', JSON.stringify(appState)); -}; + localStorage.setItem('app-state', JSON.stringify(appState)) +} ``` diff --git a/errors/no-page-custom-font.md b/errors/no-page-custom-font.md new file mode 100644 index 0000000000000..2f2e51e3d28e5 --- /dev/null +++ b/errors/no-page-custom-font.md @@ -0,0 +1,73 @@ +# No Page Custom Font + +> Prevent page-only custom fonts. + +### Why This Error Occurred + +- The custom font you're adding was added to a page - this only adds the font to the specific page and not the entire application. +- The custom font you're adding was added to a separate component within `pages/_document.js` - this disables automatic font optimization. + +### Possible Ways to Fix It + +Create the file `./pages/_document.js` and add the font to a custom Document: + +```jsx +// pages/_document.js + +import Document, { Html, Head, Main, NextScript } from 'next/document' + +class MyDocument extends Document { + render() { + return ( + + + + + +
    + + + + ) + } +} + +export default MyDocument +``` + +Or as a function component: + +```js +// pages/_document.js + +import { Html, Head, Main, NextScript } from 'next/document' + +export default function Document() { + return ( + + + + + +
    + + + + ) +} +``` + +### When Not To Use It + +If you have a reason to only load a font for a particular page or don't care about font optimization, then you can disable this rule. + +### Useful Links + +- [Custom Document](https://nextjs.org/docs/advanced-features/custom-document) +- [Font Optimization](https://nextjs.org/docs/basic-features/font-optimization) diff --git a/errors/no-router-instance.md b/errors/no-router-instance.md index 2381b0e72d6d1..2beaafbc71cbb 100644 --- a/errors/no-router-instance.md +++ b/errors/no-router-instance.md @@ -2,8 +2,12 @@ #### Why This Error Occurred -During SSR you might have tried to access a router method `push`, `replace`, `back`, which is not supported. +During Pre-rendering (SSR or SSG) you tried to access a router method `push`, `replace`, `back`, which is not supported. #### Possible Ways to Fix It -Move any calls to router methods to `componentDidMount` or add a check such as `typeof window !== 'undefined'` before calling the methods \ No newline at end of file +In a function Component you can move the code into the `useEffect` hook. + +In a class Component, move any calls to router methods to the `componentDidMount` lifecycle method. + +This way the calls to the router methods are only executed in the browser. diff --git a/errors/no-script-component-in-head.md b/errors/no-script-component-in-head.md new file mode 100644 index 0000000000000..50503ebe0eaec --- /dev/null +++ b/errors/no-script-component-in-head.md @@ -0,0 +1,50 @@ +# No Script Component in Head + +> Prevent usage of `next/script` in `next/head` component. + +#### Why This Error Occurred + +The `next/script` component should not be used in a `next/head` component. + +#### Possible Ways to Fix It + +Move the ` +
    Home Page
    +
    + ) +} + +export default Home +``` + +#### Use `async` or `defer` + +```html + + + ``` + + Instead you need to use `dangerouslySetInnerHTML` to initialize the string. can use the [`/components/amp/AmpState.js`](components/amp/AmpState.js) component to see how it works. The same approach works for `amp-access` and `amp-consent` as well: + + ```html + + ``` + +- **amp-list & amp-mustache:** mustache templates conflict with JSX and it's template literals need to be escaped. A simple approach is to escape them via back ticks: `` src={`{{imageUrl}}`} ``. +- **amp-script:** you can use [amp-script](https://amp.dev/documentation/components/amp-script/) to add custom JavaScript to your AMP pages. The boilerplate includes a helper [`components/amp/AmpScript.js`](components/amp/AmpScript.js) to simplify using `amp-script`. The helper also supports embedding inline scripts. Good to know: Next.js uses [AMP Optimizer](https://github.com/ampproject/amp-toolbox/tree/master/packages/optimizer) under the hood, which automatically adds the needed script hashes for [inline amp-scripts](https://amp.dev/documentation/components/amp-script/#load-javascript-from-a-local-element). + +## Deployment + +For production builds, you need to run (the app will be build into the `.next` folder): + +```shell +$ yarn build +``` + +To start the application in production mode, run: + +```shell +$ yarn start +``` + +Deploy it to the cloud with [Vercel](https://vercel.com/new?utm_source=github&utm_medium=readme&utm_campaign=next-example) ([Documentation](https://nextjs.org/docs/deployment)). diff --git a/examples/amp-first/amp.d.ts b/examples/amp-first/amp.d.ts new file mode 100644 index 0000000000000..d70f506154f78 --- /dev/null +++ b/examples/amp-first/amp.d.ts @@ -0,0 +1,107 @@ +// Allow AMP elements to be a property on JSX.IntrinsicElements + +// Only the intrinsic elements defined here will be accepted, and only with the attributes defined here +declare namespace JSX { + interface AmpImg { + alt?: string + src?: string + width?: string + height?: string + layout?: string + } + interface AmpInstallServiceWorker { + src: string + 'data-iframe-src': string + layout: string + } + interface AmpState { + id?: string + children?: any + src?: string + } + interface AmpScript { + id?: string + children?: any + layout?: string + width?: string + height?: string + script?: any + src?: string + } + interface AmpCarousel { + children: React.ReactNode + layout?: + | 'fill' + | 'fixed' + | 'fixed-height' + | 'flex-item' + | 'intrinsic' + | 'nodisplay' + | 'responsive' + width: string + height: string + type: 'slides' | 'carousel' + role?: 'region' | 'list' | 'listitem' + controls?: '' + loop?: '' + autoplay?: '' + delay?: string + id?: string + } + interface AmpList { + layout?: + | 'fill' + | 'fixed' + | 'fixed-height' + | 'flex-item' + | 'nodisplay' + | 'responsive' + temlate?: string + width?: string + height?: string + credentials?: 'omit' | 'include' + children: React.ReactNode + src?: string + binding?: string + } + interface IntrinsicElements { + 'amp-img': AmpImg + 'amp-install-serviceworker': AmpInstallServiceWorker + 'amp-state': AmpState + 'amp-script': AmpScript + 'amp-carousel': AmpCarousel + 'amp-list': AmpList + } +} + +// Only the intrinsic elements defined here will be accepted, attributes don't matter +// declare namespace JSX { +// interface IntrinsicElements { +// 'amp-img': any +// 'amp-install-serviceworker': any +// 'amp-state': any +// 'amp-script': any +// 'amp-carousel': any +// 'amp-list': any +// } +// } + +// All intrinsic elements will be accepted +// declare namespace JSX { +// interface IntrinsicElements { +// [elemName: string]: any +// } +// } + +// Allow custom AMP attributes on HTML elements +declare namespace React { + interface ScriptHTMLAttributes { + target?: string + } + + interface HTMLAttributes { + submitting?: string + type?: string + on?: string + } +} diff --git a/examples/amp-first/components/Layout.tsx b/examples/amp-first/components/Layout.tsx new file mode 100644 index 0000000000000..86f68fdfb26ac --- /dev/null +++ b/examples/amp-first/components/Layout.tsx @@ -0,0 +1,52 @@ +import Head from 'next/head' +import { AmpIncludeAmpInstallServiceworker } from './amp/AmpCustomElement' + +// Your app's theme color +const THEME_COLOR = '#005af0' + +type LayoutProps = { + title: string + children?: React.ReactNode + description: string +} + +/** + * A sample page layout installing the AMP Serviceworker by default. + */ +const Layout: React.FC = ({ title, children, description }) => ( + <> + + {title || ''} + + + + + + + + + {children} + + + + + + +) + +export default Layout diff --git a/examples/amp-first/components/amp/AmpCustomElement.tsx b/examples/amp-first/components/amp/AmpCustomElement.tsx new file mode 100644 index 0000000000000..0c02ae55668c4 --- /dev/null +++ b/examples/amp-first/components/amp/AmpCustomElement.tsx @@ -0,0 +1,535 @@ +/** + * @file An AMP Component import helper. This file is auto-generated using + * https://www.npmjs.com/package/@ampproject/toolbox-validator-rules. + */ +import Head from 'next/head' + +interface Props { + name: string + version: string +} + +export function AmpIncludeCustomElement(props: Props) { + return ( + + +`} + Instead you need to use dangerouslySetInnerHTML to + initialize the string. can use the{' '} + /components/amp/AmpState.js component to see how it + works. The same approach works for amp-access and{' '} + amp-consent as well + + + Demo: +

    + + + <> + {{ + message: 'Hello World', + }} + + + + + + +
    +

    amp-list & amp-mustache

    +

    + Mustache templates conflict with JSX and it's template literals need + to be escaped. A simple approach is to escape them via back ticks:{' '} + src={`{{imageUrl}}`}. +

    + + + + + +
    + +
    +

    amp-script

    +

    + Checkout the{' '} + + amp-script + {' '} + helper here: components/amp/AmpScript.js for embedding + custom JavaScript. +

    + + + + + +

    + The helper also supports embedding inline scripts. Good to know: + Next.js uses{' '} + + AMP Optimizer + {' '} + under the hood, which automatically adds the needed script hashes + for{' '} + + inline amp-scripts + + . +

    + + + +
    +

    + + + +) + +// amp-script requires absolute URLs, so we create a property `host` which we can use to calculate the script URL. +export const getServerSideProps: GetServerSideProps = async ({ req }) => { + const getProtocol = (req: any) => { + if (req.connection.encrypted) { + return 'https' + } + const forwardedProto = req.headers['x-forwarded-proto'] + if (forwardedProto) { + return forwardedProto.split(/\s*,\s*/)[0] + } + return 'http' + } + + // WARNING: This is a generally unsafe application unless you're deploying to a managed platform like Vercel. + // Be sure your load balancer is configured to not allow spoofed host headers. + return { props: { host: `${getProtocol(req)}://${req.headers.host}` } } +} + +export default Home diff --git a/examples/amp-first/pages/offline.tsx b/examples/amp-first/pages/offline.tsx new file mode 100644 index 0000000000000..ec987b8882d63 --- /dev/null +++ b/examples/amp-first/pages/offline.tsx @@ -0,0 +1,12 @@ +import Layout from '../components/Layout' + +export const config = { amp: true } + +const Home = () => ( + +

    Offline

    +

    Please try again later.

    +
    +) + +export default Home diff --git a/examples/amp-first/public/install-serviceworker.html b/examples/amp-first/public/install-serviceworker.html new file mode 100644 index 0000000000000..cffb15dd6caa8 --- /dev/null +++ b/examples/amp-first/public/install-serviceworker.html @@ -0,0 +1,7 @@ + +installing service worker + diff --git a/examples/amp-first/public/manifest.json b/examples/amp-first/public/manifest.json new file mode 100644 index 0000000000000..a4371e83d0db7 --- /dev/null +++ b/examples/amp-first/public/manifest.json @@ -0,0 +1,20 @@ +{ + "short_name": "My page", + "name": "My fantastic page", + "icons": [ + { + "src": "/static/images/icons-192.png", + "type": "image/png", + "sizes": "192x192" + }, + { + "src": "/static/images/icons-512.png", + "type": "image/png", + "sizes": "512x512" + } + ], + "display": "standalone", + "scope": "/", + "theme_color": "#005af0", + "background_color": "#ffffff" +} diff --git a/examples/amp-first/public/serviceworker.js b/examples/amp-first/public/serviceworker.js new file mode 100644 index 0000000000000..d5f58a9fda3a6 --- /dev/null +++ b/examples/amp-first/public/serviceworker.js @@ -0,0 +1,28 @@ +/* global importScripts, AMP_SW */ +importScripts('https://cdn.ampproject.org/sw/amp-sw.js') + +/* + This configures the AMP service worker to enhance network resiliency and + optimizes asset caching. This configuration will: + + - Cache AMP scripts with a stale-while-revalidate strategy for a longer duration + than the default http response headers indicate. + - Cache valid visited AMP documents, and serve only in case of flaky network conditions. + - Cache and serve an offline page. + - Serve static assets with a cache first strategy. + + Checkout https://github.com/ampproject/amp-sw/ to learn more about how to configure + asset caching and link prefetching. +*/ +AMP_SW.init({ + assetCachingOptions: [ + { + regexp: /\.(png|jpg|woff2|woff|css|js)/, + cachingStrategy: 'CACHE_FIRST', // options are NETWORK_FIRST | CACHE_FIRST | STALE_WHILE_REVALIDATE + }, + ], + offlinePageOptions: { + url: '/offline', + assets: [], + }, +}) diff --git a/examples/amp-first/public/static/amp-script/hello.js b/examples/amp-first/public/static/amp-script/hello.js new file mode 100644 index 0000000000000..448ebac5466fa --- /dev/null +++ b/examples/amp-first/public/static/amp-script/hello.js @@ -0,0 +1,4 @@ +const btn = document.querySelector('button') +btn.addEventListener('click', () => { + document.body.textContent = 'Hello World!' +}) diff --git a/examples/with-algolia-react-instantsearch/static/favicon.ico b/examples/amp-first/public/static/favicon.ico similarity index 100% rename from examples/with-algolia-react-instantsearch/static/favicon.ico rename to examples/amp-first/public/static/favicon.ico diff --git a/examples/amp-first/public/static/images/icons-192.png b/examples/amp-first/public/static/images/icons-192.png new file mode 100755 index 0000000000000..b6405c4c0e6f6 Binary files /dev/null and b/examples/amp-first/public/static/images/icons-192.png differ diff --git a/examples/amp-first/public/static/images/icons-512.png b/examples/amp-first/public/static/images/icons-512.png new file mode 100755 index 0000000000000..dd1ff4acfde53 Binary files /dev/null and b/examples/amp-first/public/static/images/icons-512.png differ diff --git a/examples/amp-first/tsconfig.json b/examples/amp-first/tsconfig.json new file mode 100644 index 0000000000000..114d0ab4942b1 --- /dev/null +++ b/examples/amp-first/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "target": "es5", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": false, + "forceConsistentCasingInFileNames": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true + }, + "include": ["next-env.d.ts", "amp.d.ts", "**/*.ts", "**/*.tsx"], + "exclude": ["node_modules"] +} diff --git a/examples/amp-story/.gitignore b/examples/amp-story/.gitignore new file mode 100644 index 0000000000000..c87c9b392c020 --- /dev/null +++ b/examples/amp-story/.gitignore @@ -0,0 +1,36 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# local env files +.env*.local + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/examples/amp-story/README.md b/examples/amp-story/README.md new file mode 100644 index 0000000000000..b36649135cc1c --- /dev/null +++ b/examples/amp-story/README.md @@ -0,0 +1,27 @@ +# Google AMP Story + +This example shows how to create an AMP page with `amp-story` using Next.js and the AMP feature. + +## Deploy your own + +Deploy the example using [Vercel](https://vercel.com?utm_source=github&utm_medium=readme&utm_campaign=next-example): + +[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/git/external?repository-url=https://github.com/vercel/next.js/tree/canary/examples/amp-story&project-name=amp-story&repository-name=amp-story) + +## How to use + +Execute [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app) with [npm](https://docs.npmjs.com/cli/init), [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/), or [pnpm](https://pnpm.io) to bootstrap the example: + +```bash +npx create-next-app --example amp-story amp-story-app +``` + +```bash +yarn create next-app --example amp-story amp-story-app +``` + +```bash +pnpm create next-app --example amp-story amp-story-app +``` + +Deploy it to the cloud with [Vercel](https://vercel.com/new?utm_source=github&utm_medium=readme&utm_campaign=next-example) ([Documentation](https://nextjs.org/docs/deployment)). diff --git a/examples/amp-story/amp.d.ts b/examples/amp-story/amp.d.ts new file mode 100644 index 0000000000000..37f3315282f35 --- /dev/null +++ b/examples/amp-story/amp.d.ts @@ -0,0 +1,75 @@ +// Allow AMP elements to be a property on JSX.IntrinsicElements + +// Only the intrinsic elements defined here will be accepted, and only with the attributes defined here +declare namespace JSX { + interface AmpImg { + alt: string + src: string + width?: string + height?: string + layout: string + } + interface AmpVideo { + width: string + height: string + layout: string + poster: string + children: React.ReactNode + autoplay: string + loop?: string + } + interface AmpStory { + standalone: string + title: string + publisher: string + 'publisher-logo-src': string + 'poster-portrait-src': string + children: React.ReactNode + } + interface AmpStoryPage { + id: string + children: React.ReactNode + } + interface AmpStoryGridLayer { + template: 'fill' | 'vertical' | 'horizontal' | 'thirds' + children: React.ReactNode + } + interface AmpStoryBookend { + src: string + layout?: + | 'fill' + | 'fixed' + | 'fixed-height' + | 'flex-item' + | 'intrinsic' + | 'nodisplay' + | 'responsive' + } + interface IntrinsicElements { + 'amp-img': AmpImg + 'amp-video': AmpVideo + 'amp-story': AmpStory + 'amp-story-grid-layer': AmpStoryGridLayer + 'amp-story-page': AmpStoryPage + 'amp-story-bookend': AmpStoryBookend + } +} + +// Only the intrinsic elements defined here will be accepted, attributes don't matter +// declare namespace JSX { +// interface IntrinsicElements { +// 'amp-img': any +// 'amp-video': any +// 'amp-story': any +// 'amp-story-grid-layer': any +// 'amp-story-page': any +// 'amp-story-bookend': any +// } +// } + +// All intrinsic elements will be accepted +// declare namespace JSX { +// interface IntrinsicElements { +// [elemName: string]: any +// } +// } diff --git a/examples/amp-story/package.json b/examples/amp-story/package.json new file mode 100644 index 0000000000000..9fa64a28dc61a --- /dev/null +++ b/examples/amp-story/package.json @@ -0,0 +1,19 @@ +{ + "private": true, + "scripts": { + "dev": "next", + "build": "next build", + "start": "next start" + }, + "dependencies": { + "next": "latest", + "react": "^18.2.0", + "react-dom": "^18.2.0" + }, + "devDependencies": { + "@types/node": "^18.0.0", + "@types/react": "^18.0.14", + "@types/react-dom": "^18.0.5", + "typescript": "^4.7.4" + } +} diff --git a/examples/amp-story/pages/index.tsx b/examples/amp-story/pages/index.tsx new file mode 100644 index 0000000000000..0676d56db01bf --- /dev/null +++ b/examples/amp-story/pages/index.tsx @@ -0,0 +1,132 @@ +import Head from 'next/head' + +export const config = { amp: true } + +const Home = () => { + return ( + <> + + Example AMP Story in Next.js + + + + ` + ) + res.end() +} diff --git a/examples/cms-contentful/pages/api/revalidate.js b/examples/cms-contentful/pages/api/revalidate.js new file mode 100644 index 0000000000000..fd5a7078d37f8 --- /dev/null +++ b/examples/cms-contentful/pages/api/revalidate.js @@ -0,0 +1,31 @@ +// pages/api/revalidate.js + +export default async function handler(req, res) { + // should be secret, custom header coming in from Contentful + let inboundRevalToken = req.headers['x-vercel-reval-key'] + + // Check for secret to confirm this is a valid request + if (!inboundRevalToken) { + return res + .status(401) + .json({ message: 'x-vercel-reval-key header not defined' }) + } else if (inboundRevalToken !== process.env.CONTENTFUL_REVALIDATE_SECRET) { + return res.status(401).json({ message: 'Invalid token' }) + } + + try { + // Note: if this fails to parse you may have forget to set the + // "content-type" header correctly as mentioned here https://github.com/vercel/next.js/blob/canary/examples/cms-contentful/README.md#step-9-try-using-on-demand-revalidation + let postSlug = req.body.fields.slug['en-US'] + + // revalidate the individual post and the home page + await res.revalidate(`/posts/${postSlug}`) + await res.revalidate('/') + + return res.json({ revalidated: true }) + } catch (err) { + // If there was an error, Next.js will continue + // to show the last successfully generated page + return res.status(500).send('Error revalidating') + } +} diff --git a/examples/cms-contentful/pages/index.js b/examples/cms-contentful/pages/index.js new file mode 100644 index 0000000000000..ce7c079c45424 --- /dev/null +++ b/examples/cms-contentful/pages/index.js @@ -0,0 +1,43 @@ +import Container from '../components/container' +import MoreStories from '../components/more-stories' +import HeroPost from '../components/hero-post' +import Intro from '../components/intro' +import Layout from '../components/layout' +import { getAllPostsForHome } from '../lib/api' +import Head from 'next/head' +import { CMS_NAME } from '../lib/constants' + +export default function Index({ preview, allPosts }) { + const heroPost = allPosts[0] + const morePosts = allPosts.slice(1) + return ( + <> + + + Next.js Blog Example with {CMS_NAME} + + + + {heroPost && ( + + )} + {morePosts.length > 0 && } + + + + ) +} + +export async function getStaticProps({ preview = false }) { + const allPosts = (await getAllPostsForHome(preview)) ?? [] + return { + props: { preview, allPosts }, + } +} diff --git a/examples/cms-contentful/pages/posts/[slug].js b/examples/cms-contentful/pages/posts/[slug].js new file mode 100644 index 0000000000000..c14841bd9ca0b --- /dev/null +++ b/examples/cms-contentful/pages/posts/[slug].js @@ -0,0 +1,74 @@ +import { useRouter } from 'next/router' +import Head from 'next/head' +import ErrorPage from 'next/error' +import Container from '../../components/container' +import PostBody from '../../components/post-body' +import MoreStories from '../../components/more-stories' +import Header from '../../components/header' +import PostHeader from '../../components/post-header' +import SectionSeparator from '../../components/section-separator' +import Layout from '../../components/layout' +import { getAllPostsWithSlug, getPostAndMorePosts } from '../../lib/api' +import PostTitle from '../../components/post-title' +import { CMS_NAME } from '../../lib/constants' + +export default function Post({ post, morePosts, preview }) { + const router = useRouter() + + if (!router.isFallback && !post) { + return + } + + return ( + + +
    + {router.isFallback ? ( + Loading… + ) : ( + <> +
    + + + {post.title} | Next.js Blog Example with {CMS_NAME} + + + + + +
    + + {morePosts && morePosts.length > 0 && ( + + )} + + )} + + + ) +} + +export async function getStaticProps({ params, preview = false }) { + const data = await getPostAndMorePosts(params.slug, preview) + + return { + props: { + preview, + post: data?.post ?? null, + morePosts: data?.morePosts ?? null, + }, + } +} + +export async function getStaticPaths() { + const allPosts = await getAllPostsWithSlug() + return { + paths: allPosts?.map(({ slug }) => `/posts/${slug}`) ?? [], + fallback: true, + } +} diff --git a/examples/cms-contentful/postcss.config.js b/examples/cms-contentful/postcss.config.js new file mode 100644 index 0000000000000..3fa0a9514dc9d --- /dev/null +++ b/examples/cms-contentful/postcss.config.js @@ -0,0 +1,8 @@ +// If you want to use other PostCSS plugins, see the following: +// https://tailwindcss.com/docs/using-with-preprocessors +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/examples/cms-contentful/public/favicon/android-chrome-192x192.png b/examples/cms-contentful/public/favicon/android-chrome-192x192.png new file mode 100644 index 0000000000000..2f07282a59cda Binary files /dev/null and b/examples/cms-contentful/public/favicon/android-chrome-192x192.png differ diff --git a/examples/cms-contentful/public/favicon/android-chrome-512x512.png b/examples/cms-contentful/public/favicon/android-chrome-512x512.png new file mode 100644 index 0000000000000..dbb0faea84049 Binary files /dev/null and b/examples/cms-contentful/public/favicon/android-chrome-512x512.png differ diff --git a/examples/cms-contentful/public/favicon/apple-touch-icon.png b/examples/cms-contentful/public/favicon/apple-touch-icon.png new file mode 100644 index 0000000000000..8f4033b2a8b35 Binary files /dev/null and b/examples/cms-contentful/public/favicon/apple-touch-icon.png differ diff --git a/examples/cms-contentful/public/favicon/browserconfig.xml b/examples/cms-contentful/public/favicon/browserconfig.xml new file mode 100644 index 0000000000000..9824d87b11517 --- /dev/null +++ b/examples/cms-contentful/public/favicon/browserconfig.xml @@ -0,0 +1,9 @@ + + + + + + #000000 + + + diff --git a/examples/cms-contentful/public/favicon/favicon-16x16.png b/examples/cms-contentful/public/favicon/favicon-16x16.png new file mode 100644 index 0000000000000..29deaf6716e77 Binary files /dev/null and b/examples/cms-contentful/public/favicon/favicon-16x16.png differ diff --git a/examples/cms-contentful/public/favicon/favicon-32x32.png b/examples/cms-contentful/public/favicon/favicon-32x32.png new file mode 100644 index 0000000000000..e3b4277bf093d Binary files /dev/null and b/examples/cms-contentful/public/favicon/favicon-32x32.png differ diff --git a/examples/cms-contentful/public/favicon/favicon.ico b/examples/cms-contentful/public/favicon/favicon.ico new file mode 100644 index 0000000000000..ea2f437d9db65 Binary files /dev/null and b/examples/cms-contentful/public/favicon/favicon.ico differ diff --git a/examples/cms-contentful/public/favicon/mstile-150x150.png b/examples/cms-contentful/public/favicon/mstile-150x150.png new file mode 100644 index 0000000000000..f2dfd904bf1be Binary files /dev/null and b/examples/cms-contentful/public/favicon/mstile-150x150.png differ diff --git a/examples/cms-contentful/public/favicon/safari-pinned-tab.svg b/examples/cms-contentful/public/favicon/safari-pinned-tab.svg new file mode 100644 index 0000000000000..72ab6e050cb11 --- /dev/null +++ b/examples/cms-contentful/public/favicon/safari-pinned-tab.svg @@ -0,0 +1,33 @@ + + + + +Created by potrace 1.11, written by Peter Selinger 2001-2013 + + + + + + + + + + diff --git a/examples/cms-contentful/public/favicon/site.webmanifest b/examples/cms-contentful/public/favicon/site.webmanifest new file mode 100644 index 0000000000000..a672d9a233c59 --- /dev/null +++ b/examples/cms-contentful/public/favicon/site.webmanifest @@ -0,0 +1,19 @@ +{ + "name": "Next.js", + "short_name": "Next.js", + "icons": [ + { + "src": "/favicons/android-chrome-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/favicons/android-chrome-512x512.png", + "sizes": "512x512", + "type": "image/png" + } + ], + "theme_color": "#000000", + "background_color": "#000000", + "display": "standalone" +} diff --git a/examples/cms-contentful/styles/index.css b/examples/cms-contentful/styles/index.css new file mode 100644 index 0000000000000..b63c4592cb2e1 --- /dev/null +++ b/examples/cms-contentful/styles/index.css @@ -0,0 +1,5 @@ +/* purgecss start ignore */ +@tailwind base; +@tailwind components; +/* purgecss end ignore */ +@tailwind utilities; diff --git a/examples/cms-contentful/tailwind.config.js b/examples/cms-contentful/tailwind.config.js new file mode 100644 index 0000000000000..ea0efae6d75f5 --- /dev/null +++ b/examples/cms-contentful/tailwind.config.js @@ -0,0 +1,38 @@ +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: [ + './pages/**/*.{js,ts,jsx,tsx}', + './components/**/*.{js,ts,jsx,tsx}', + ], + theme: { + extend: { + colors: { + 'accent-1': '#FAFAFA', + 'accent-2': '#EAEAEA', + 'accent-7': '#333', + success: '#0070f3', + cyan: '#79FFE1', + }, + spacing: { + 28: '7rem', + }, + letterSpacing: { + tighter: '-.04em', + }, + lineHeight: { + tight: 1.2, + }, + fontSize: { + '5xl': '2.5rem', + '6xl': '2.75rem', + '7xl': '4.5rem', + '8xl': '6.25rem', + }, + boxShadow: { + small: '0 5px 10px rgba(0, 0, 0, 0.12)', + medium: '0 8px 30px rgba(0, 0, 0, 0.12)', + }, + }, + }, + plugins: [], +} diff --git a/examples/cms-contentful/types/author.json b/examples/cms-contentful/types/author.json new file mode 100644 index 0000000000000..c9659d4b3aa33 --- /dev/null +++ b/examples/cms-contentful/types/author.json @@ -0,0 +1,18 @@ +{ + "Main": { + "name": { + "type": "Text", + "config": { + "label": "name" + } + }, + "picture": { + "type": "Image", + "config": { + "constraint": {}, + "thumbnails": [], + "label": "picture" + } + } + } +} diff --git a/examples/cms-contentful/types/post.json b/examples/cms-contentful/types/post.json new file mode 100644 index 0000000000000..303fe0eeafc22 --- /dev/null +++ b/examples/cms-contentful/types/post.json @@ -0,0 +1,52 @@ +{ + "Main": { + "title": { + "type": "StructuredText", + "config": { + "single": "heading1, heading2, heading3, heading4, heading5, heading6", + "label": "title" + } + }, + "content": { + "type": "StructuredText", + "config": { + "multi": "paragraph, preformatted, heading1, heading2, heading3, heading4, heading5, heading6, strong, em, hyperlink, image, embed, list-item, o-list-item, o-list-item", + "label": "content" + } + }, + "excerpt": { + "type": "Text", + "config": { + "label": "excerpt" + } + }, + "coverimage": { + "type": "Image", + "config": { + "constraint": {}, + "thumbnails": [], + "label": "coverimage" + } + }, + "date": { + "type": "Date", + "config": { + "label": "date" + } + }, + "author": { + "type": "Link", + "config": { + "select": "document", + "customtypes": ["author"], + "label": "author" + } + }, + "uid": { + "type": "UID", + "config": { + "label": "slug" + } + } + } +} diff --git a/examples/cms-cosmic/.env.local.example b/examples/cms-cosmic/.env.local.example new file mode 100644 index 0000000000000..4f11f19a6288b --- /dev/null +++ b/examples/cms-cosmic/.env.local.example @@ -0,0 +1,3 @@ +COSMIC_BUCKET_SLUG= +COSMIC_READ_KEY= +COSMIC_PREVIEW_SECRET= \ No newline at end of file diff --git a/examples/cms-cosmic/.gitignore b/examples/cms-cosmic/.gitignore new file mode 100644 index 0000000000000..c87c9b392c020 --- /dev/null +++ b/examples/cms-cosmic/.gitignore @@ -0,0 +1,36 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# local env files +.env*.local + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/examples/cms-cosmic/README.md b/examples/cms-cosmic/README.md new file mode 100644 index 0000000000000..8f5bd84111c5d --- /dev/null +++ b/examples/cms-cosmic/README.md @@ -0,0 +1,142 @@ +# A statically generated blog example using Next.js and Cosmic + +This example showcases Next.js's [Static Generation](https://nextjs.org/docs/basic-features/pages) feature using [Cosmic](https://cosmicjs.com/) as the data source. + +## Demo + +[https://cosmic-next-blog.vercel.app/](https://cosmic-next-blog.vercel.app/) + +## Deploy your own + +Once you have access to [the environment variables you'll need](#step-3-set-up-environment-variables), deploy the example using [Vercel](https://vercel.com?utm_source=github&utm_medium=readme&utm_campaign=next-example): + +[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/git/external?repository-url=https://github.com/vercel/next.js/tree/canary/examples/cms-cosmic&project-name=cms-cosmic&repository-name=cms-cosmic&env=COSMIC_BUCKET_SLUG,COSMIC_READ_KEY,COSMIC_PREVIEW_SECRET&envDescription=Required%20to%20connect%20the%20app%20with%20Cosmic&envLink=https://vercel.link/cms-cosmic-env) + +### Related examples + +- [WordPress](/examples/cms-wordpress) +- [DatoCMS](/examples/cms-datocms) +- [Sanity](/examples/cms-sanity) +- [TakeShape](/examples/cms-takeshape) +- [Prismic](/examples/cms-prismic) +- [Contentful](/examples/cms-contentful) +- [Strapi](/examples/cms-strapi) +- [Agility CMS](/examples/cms-agilitycms) +- [ButterCMS](/examples/cms-buttercms) +- [Storyblok](/examples/cms-storyblok) +- [GraphCMS](/examples/cms-graphcms) +- [Kontent](/examples/cms-kontent) +- [Ghost](/examples/cms-ghost) +- [Umbraco Heartcore](/examples/cms-umbraco-heartcore) +- [Blog Starter](/examples/blog-starter) +- [Builder.io](/examples/cms-builder-io) +- [DotCMS](/examples/cms-dotcms) +- [Enterspeed](/examples/cms-enterspeed) + +## How to use + +Execute [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app) with [npm](https://docs.npmjs.com/cli/init), [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/), or [pnpm](https://pnpm.io) to bootstrap the example: + +```bash +npx create-next-app --example cms-cosmic cms-cosmic-app +``` + +```bash +yarn create next-app --example cms-cosmic cms-cosmic-app +``` + +```bash +pnpm create next-app --example cms-cosmic cms-cosmic-app +``` + +## Configuration + +### Step 1. Create an account and a project on Cosmic + +First, [create an account on Cosmic](https://cosmicjs.com). + +### Step 2. Install the Next.js Static Blog app + +After creating an account, install the [Next.js Static Blog](https://www.cosmicjs.com/apps/nextjs-static-blog) app from the Cosmic App Marketplace. + +### Step 3. Set up environment variables + +Go to the **Settings** menu at the sidebar and click **Basic Settings**. + +Next, copy the `.env.local.example` file in this directory to `.env.local` (which will be ignored by Git): + +```bash +cp .env.local.example .env.local +``` + +Then set each variable on `.env.local`: + +- `COSMIC_BUCKET_SLUG` should be the **Bucket slug** key under **API Access**. +- `COSMIC_READ_KEY` should be the **Read Key** under **API Access**. +- `COSMIC_PREVIEW_SECRET` can be any random string (but avoid spaces) - this is used for [Preview Mode](https://nextjs.org/docs/advanced-features/preview-mode). + +Your `.env.local` file should look like this: + +```bash +COSMIC_BUCKET_SLUG=... +COSMIC_READ_KEY=... +COSMIC_PREVIEW_SECRET=... +``` + +### Step 4. Run Next.js in development mode + +```bash +npm install +npm run dev + +# or + +yarn install +yarn dev +``` + +Your blog should be up and running on [http://localhost:3000](http://localhost:3000)! If it doesn't work, post on [GitHub discussions](https://github.com/vercel/next.js/discussions). + +### Step 5. Try preview mode + +To add the ability to preview content from your Cosmic dashboard go to **Posts > Edit Settings** and scroll down to the "Preview Link" section. (Screenshot below) + +![Image](https://cdn.cosmicjs.com/14e6c0f0-a07b-11ea-829b-5b458b05d525-preview-link.png) + +Add your live URL or localhost development URL which includes your chosen preview secret and `[object_slug]` shortcode. It should look like the following: + +``` +http://localhost:3000/api/preview?secret=&slug=[object_slug] +``` + +- `` is the string you entered for `COSMIC_PREVIEW_SECRET`. +- `[object_slug]` shortcode will automatically be converted to the post's `slug` attribute. + +On Cosmic, go to one of the posts you've created and: + +- **Update the title**. For example, you can add `[Draft]` in front of the title. +- Click **Save Draft**, but **DO NOT** click **Publish**. By doing this, the post will be in the draft state. + +Now, if you go to the post page directly on localhost, you won't see the updated title. However, if you use the **Preview Mode**, you'll be able to see the change ([Documentation](https://nextjs.org/docs/advanced-features/preview-mode)). + +Next, click the Preview Link button on the Post to see the updated title. (Screenshot below) + + + +To exit preview mode, you can click on **Click here to exit preview mode** at the top. + +### Step 6. Deploy on Vercel + +You can deploy this app to the cloud with [Vercel](https://vercel.com?utm_source=github&utm_medium=readme&utm_campaign=next-example) ([Documentation](https://nextjs.org/docs/deployment)). + +#### Deploy Your Local Project + +To deploy your local project to Vercel, push it to GitHub/GitLab/Bitbucket and [import to Vercel](https://vercel.com/new?utm_source=github&utm_medium=readme&utm_campaign=next-example). + +**Important**: When you import your project on Vercel, make sure to click on **Environment Variables** and set them to match your `.env.local` file. + +#### Deploy from Our Template + +Alternatively, you can deploy using our template by clicking on the Deploy button below. + +[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/git/external?repository-url=https://github.com/vercel/next.js/tree/canary/examples/cms-cosmic&project-name=cms-cosmic&repository-name=cms-cosmic&env=COSMIC_BUCKET_SLUG,COSMIC_READ_KEY,COSMIC_PREVIEW_SECRET&envDescription=Required%20to%20connect%20the%20app%20with%20Cosmic&envLink=https://vercel.link/cms-cosmic-env) diff --git a/examples/cms-cosmic/components/alert.tsx b/examples/cms-cosmic/components/alert.tsx new file mode 100644 index 0000000000000..a7ae558e52d9c --- /dev/null +++ b/examples/cms-cosmic/components/alert.tsx @@ -0,0 +1,49 @@ +import Container from './container' +import cn from 'classnames' +import { EXAMPLE_PATH } from '@/lib/constants' + +type AlertProps = { + preview: boolean +} + +const Alert = (props: AlertProps) => { + const { preview } = props + return ( +
    + +
    + {preview ? ( + <> + This page is a preview.{' '} + + Click here + {' '} + to exit preview mode. + + ) : ( + <> + The source code for this blog is{' '} + + available on GitHub + + . + + )} +
    +
    +
    + ) +} + +export default Alert diff --git a/examples/cms-cosmic/components/avatar.tsx b/examples/cms-cosmic/components/avatar.tsx new file mode 100644 index 0000000000000..2a331968ebdab --- /dev/null +++ b/examples/cms-cosmic/components/avatar.tsx @@ -0,0 +1,28 @@ +import Image from 'next/image' + +type AvatarProps = { + name: string + picture: string +} + +const Avatar = (props: AvatarProps) => { + const { name, picture } = props + + return ( +
    +
    + {picture && ( + {name} + )} +
    +
    {name}
    +
    + ) +} + +export default Avatar diff --git a/examples/cms-cosmic/components/container.tsx b/examples/cms-cosmic/components/container.tsx new file mode 100644 index 0000000000000..9e96a9db022f0 --- /dev/null +++ b/examples/cms-cosmic/components/container.tsx @@ -0,0 +1,12 @@ +import { ReactNode } from 'react' + +type ContainerProps = { + children: ReactNode +} + +const Container = (props: ContainerProps) => { + const { children } = props + return
    {children}
    +} + +export default Container diff --git a/examples/cms-cosmic/components/cover-image.tsx b/examples/cms-cosmic/components/cover-image.tsx new file mode 100644 index 0000000000000..ff28720b0f220 --- /dev/null +++ b/examples/cms-cosmic/components/cover-image.tsx @@ -0,0 +1,43 @@ +import cn from 'classnames' +import Link from 'next/link' +import Imgix from 'react-imgix' + +type CoverImageProps = { + title + url + slug +} +const CoverImage = (props: CoverImageProps) => { + const { title, url, slug } = props + + const image = ( + + ) + return ( +
    + {slug ? ( + + {image} + + ) : ( + image + )} +
    + ) +} +export default CoverImage diff --git a/examples/cms-cosmic/components/date.tsx b/examples/cms-cosmic/components/date.tsx new file mode 100644 index 0000000000000..f47148b75fb2a --- /dev/null +++ b/examples/cms-cosmic/components/date.tsx @@ -0,0 +1,13 @@ +import { parseISO, format } from 'date-fns' + +type DateProps = { + dateString: string +} + +const Date = (props: DateProps) => { + const { dateString } = props + const date = parseISO(dateString) + return +} + +export default Date diff --git a/examples/cms-cosmic/components/footer.tsx b/examples/cms-cosmic/components/footer.tsx new file mode 100644 index 0000000000000..21af8f901a8b6 --- /dev/null +++ b/examples/cms-cosmic/components/footer.tsx @@ -0,0 +1,32 @@ +import Container from './container' +import { EXAMPLE_PATH } from '@/lib/constants' + +const Footer = () => { + return ( + + ) +} + +export default Footer diff --git a/examples/cms-cosmic/components/header.tsx b/examples/cms-cosmic/components/header.tsx new file mode 100644 index 0000000000000..6ecbe1ad0c545 --- /dev/null +++ b/examples/cms-cosmic/components/header.tsx @@ -0,0 +1,14 @@ +import Link from 'next/link' + +const Header = () => { + return ( +

    + + Blog + + . +

    + ) +} + +export default Header diff --git a/examples/cms-cosmic/components/hero-post.tsx b/examples/cms-cosmic/components/hero-post.tsx new file mode 100644 index 0000000000000..25faf0cdf1701 --- /dev/null +++ b/examples/cms-cosmic/components/hero-post.tsx @@ -0,0 +1,47 @@ +import Avatar from './avatar' +import Date from './date' +import CoverImage from './cover-image' +import Link from 'next/link' +import { AuthorType, ImgixType } from 'interfaces' + +type HeroPostProps = { + title: string + coverImage: ImgixType + date: string + excerpt: string + author: AuthorType + slug: string +} + +const HeroPost = (props: HeroPostProps) => { + const { title, coverImage, date, excerpt, author, slug } = props + + return ( +
    +
    + +
    +
    +
    +

    + + {title} + +

    +
    + +
    +
    +
    +

    {excerpt}

    + +
    +
    +
    + ) +} + +export default HeroPost diff --git a/examples/cms-cosmic/components/intro.tsx b/examples/cms-cosmic/components/intro.tsx new file mode 100644 index 0000000000000..041acb864be4e --- /dev/null +++ b/examples/cms-cosmic/components/intro.tsx @@ -0,0 +1,30 @@ +import { CMS_NAME, CMS_URL } from '@/lib/constants' + +const Intro = () => { + return ( +
    +

    + Blog. +

    +

    + A statically generated blog example using{' '} + + Next.js + {' '} + and{' '} + + {CMS_NAME} + + . +

    +
    + ) +} + +export default Intro diff --git a/examples/cms-cosmic/components/layout.tsx b/examples/cms-cosmic/components/layout.tsx new file mode 100644 index 0000000000000..40299b54ea819 --- /dev/null +++ b/examples/cms-cosmic/components/layout.tsx @@ -0,0 +1,26 @@ +import Alert from './alert' +import Footer from './footer' +import Meta from './meta' +import 'lazysizes' +import 'lazysizes/plugins/parent-fit/ls.parent-fit' +import { ReactNode } from 'react' + +type LayoutProps = { + preview: boolean + children: ReactNode +} + +const Layout = ({ preview, children }: LayoutProps) => { + return ( + <> + +
    + +
    {children}
    +
    +