diff --git a/.eslintrc.cjs b/.eslintrc.cjs index a09c094..8ff7d74 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -70,19 +70,35 @@ module.exports = { }, { - files: ['packages/solid-js/**/*.ts', 'packages/solid-js/**/*.tsx'], + files: ['**/*.config.*'], - extends: [ - '@gridventures/eslint-config-solid-js/typescript', - '@gridventures/eslint-config-base/prettier', - ], + rules: { + 'import/no-extraneous-dependencies': 'off', + }, }, { - files: ['**/*.config.*'], + // Standalone build/check scripts: they print their results (that's + // the point), their tooling deps are correctly devDependencies, and + // sequential awaits in a loop are intentional (measuring bundles one + // at a time keeps esbuild's output from interleaving). + files: ['scripts/**/*.mjs'], rules: { + 'no-console': 'off', 'import/no-extraneous-dependencies': 'off', + 'no-await-in-loop': 'off', + }, + }, + + { + // Testing Library's `screen` export shadows the browser global of the + // same name; test files legitimately import it, so relax the rule + // only here rather than for the whole codebase. + files: ['packages/*/tests/**'], + + rules: { + 'no-shadow': 'off', }, }, ], diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 5174198..d436b97 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -1,38 +1,68 @@ -name: Run unit tests +name: CI on: push: - branches: ['*'] + branches: ['**'] pull_request: - branches: ['*'] + branches: ['**'] + jobs: - build: - name: 'Run tests' + test: + name: Lint & test (node ${{ matrix.node-version }}) runs-on: ubuntu-latest strategy: + fail-fast: false matrix: - node-version: [16, 18, 20, 21] - # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ + node-version: [20, 22, 24] + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + cache: 'npm' + + - name: Install + run: npm ci + + - name: Build + run: npm run build + + - name: Lint + run: npm run lint:all + + - name: Test + run: npm test + + package: + name: Package & size checks + runs-on: ubuntu-latest steps: - - name: checkout - uses: actions/checkout@v4 - - name: restore caches - uses: actions/cache@v2 - with: - path: | - ./node_modules - ./.coverage - ./.eslintcache - key: ${{ runner.os }}-${{ matrix.node-version }}-${{ hashFiles('package-lock.json') }} - - name: Run tests - uses: actions/setup-node@v2 - with: - node-version: ${{ matrix.node-version }} - cache: 'npm' - - run: npm install && cd packages/core && npm run coverage - # - name: Publish code coverage - # uses: paambaati/codeclimate-action@v3.0.0 - # env: - # CC_TEST_REPORTER_ID: ${{ secrets.CODE_CLIMATE_REPORTER_ID }} + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: 24 + cache: 'npm' + + - name: Install + run: npm ci + + - name: Build + run: npm run build + + - name: Lint package manifests + run: npm run publint + + - name: Check type resolution + run: npm run attw + + - name: Check bundle size budget + run: npm run size diff --git a/.gitignore b/.gitignore index 2658d06..1fef22e 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,6 @@ uploads/* !uploads/.init .husky/_ + +# Claude Code agent scratch (worktrees, session state) +.claude/ diff --git a/README.md b/README.md index eb7773a..a74cb98 100644 --- a/README.md +++ b/README.md @@ -7,4 +7,7 @@ A tiny library to translate or transform strings, dates and numbers based on nat - [@tiny-intl/core](./packages/core) ![Core Size](https://deno.bundlejs.com/badge?q=@tiny-intl/core&treeshake=%5B*%5D) - [@tiny-intl/react](./packages/react) ![React Adapter Size](https://deno.bundlejs.com/badge?q=@tiny-intl/react&treeshake=%5B*%5D&config=%7B%22esbuild%22:%7B%22external%22:%5B%22react%22%5D%7D%7D) - [@tiny-intl/preact](./packages/preact) ![Preact Adapter Size](https://deno.bundlejs.com/badge?q=@tiny-intl/preact&treeshake=%5B*%5D&config=%7B%22esbuild%22:%7B%22external%22:%5B%22preact%22,%22preact/compat%22%5D%7D%7D) -- [@tiny-intl/solid](./packages/solid) ![Solid Adater Size](https://deno.bundlejs.com/badge?q=@tiny-intl/solid-js&treeshake=%5B*%5D&config=%7B%22esbuild%22:%7B%22external%22:%5B%22solid-js%22%5D%7D%7D) + +> [!NOTE] +> `@tiny-intl/solid-js` has been removed. Published versions up +> to 1.2.0 remain installable from npm but are no longer maintained. diff --git a/commitlint.config.js b/commitlint.config.js index 7dfaf90..ec72210 100644 --- a/commitlint.config.js +++ b/commitlint.config.js @@ -5,7 +5,7 @@ module.exports = { // If you want to implement custom scopes uncomment this rules: { 'scope-enum': async () => { - return [2, 'always', ['core', 'react', 'solid-js', 'preact']]; + return [2, 'always', ['core', 'react', 'preact']]; }, }, }; diff --git a/package-lock.json b/package-lock.json index 5836267..382b592 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,21 +12,22 @@ "packages/*" ], "devDependencies": { + "@arethetypeswrong/cli": "^0.18.5", "@commitlint/cli": "^17.4.4", "@commitlint/config-conventional": "^17.4.4", "@gridventures/eslint-config-base": "^1.3.3", "@gridventures/eslint-config-react": "^1.4.0", - "@gridventures/eslint-config-solid-js": "^1.4.1", "@gridventures/eslint-config-typescript": "^1.3.3", "@types/node": "^20.10.3", + "esbuild": "^0.28.2", "eslint": "^8.35.0", - "eslint-plugin-solid": "^0.13.0", "husky": "^8.0.3", "lerna": "^7.4.1", "lint-staged": "^15.0.1", "patch-package": "^8.0.0", "path": "^0.12.7", "prettier": "^2.8.4", + "publint": "^0.3.23", "typescript": "^5.2.2" } }, @@ -52,6 +53,89 @@ "node": ">=6.0.0" } }, + "node_modules/@andrewbranch/untar.js": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@andrewbranch/untar.js/-/untar.js-1.0.4.tgz", + "integrity": "sha512-pVXSwPsLuw8IGLo2Di0EaOfsk+ntVvpkk942J/sHYIkwvtKUakEcPh7HBgZ6tuimgzKSEHgCvO4XgQ05DEbwDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@arethetypeswrong/cli": { + "version": "0.18.5", + "resolved": "https://registry.npmjs.org/@arethetypeswrong/cli/-/cli-0.18.5.tgz", + "integrity": "sha512-gM+8vRsQOD/Uc7EnBedUhkG5OCsDWE4uoak5QvomGpMpaky0Eh41p04nIMgrWb8EOmqZUJGc6zz9hsP6E56R7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@arethetypeswrong/core": "0.18.5", + "chalk": "^4.1.2", + "cli-table3": "^0.6.3", + "commander": "^10.0.1", + "marked": "^9.1.2", + "marked-terminal": "^7.1.0", + "semver": "^7.5.4" + }, + "bin": { + "attw": "dist/index.js" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@arethetypeswrong/cli/node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/@arethetypeswrong/core": { + "version": "0.18.5", + "resolved": "https://registry.npmjs.org/@arethetypeswrong/core/-/core-0.18.5.tgz", + "integrity": "sha512-9ytjzGwxjm9Uz7I9avfbt5vlQt6uk9uRRESzJjqrznl6WKvI6dwYTo+vJ3U02Wrq/mR3iql/PzhvHhKdJIAjDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@andrewbranch/untar.js": "^1.0.3", + "@loaderkit/resolve": "^1.0.2", + "cjs-module-lexer": "^1.2.3", + "fflate": "^0.8.3", + "lru-cache": "^11.0.1", + "semver": "^7.5.4", + "typescript": "5.6.1-rc", + "validate-npm-package-name": "^5.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@arethetypeswrong/core/node_modules/lru-cache": { + "version": "11.5.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz", + "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@arethetypeswrong/core/node_modules/typescript": { + "version": "5.6.1-rc", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.1-rc.tgz", + "integrity": "sha512-E3b2+1zEFu84jB0YQi9BORDjz9+jGbwwy1Zi3G0LUNw7a7cePUrHMRNy8aPh53nXpkFGVHSxIZo5vKTfYaFiBQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, "node_modules/@babel/code-frame": { "version": "7.23.5", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", @@ -273,38 +357,6 @@ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true }, - "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.23.5.tgz", - "integrity": "sha512-QELlRWxSpgdwdJzSJn4WAhKC+hvw/AtHbbrIoncKHkhKKR/luAlKkgBDcri1EzWAo8f8VvYVryEHN4tax/V67A==", - "dev": true, - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-member-expression-to-functions": "^7.23.0", - "@babel/helper-optimise-call-expression": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.20", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/@babel/helper-environment-visitor": { "version": "7.22.20", "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", @@ -339,18 +391,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.23.0.tgz", - "integrity": "sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==", - "dev": true, - "dependencies": { - "@babel/types": "^7.23.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-module-imports": { "version": "7.22.15", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", @@ -382,18 +422,6 @@ "@babel/core": "^7.0.0" } }, - "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz", - "integrity": "sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-plugin-utils": { "version": "7.22.5", "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", @@ -403,23 +431,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-replace-supers": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.22.20.tgz", - "integrity": "sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw==", - "dev": true, - "dependencies": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-member-expression-to-functions": "^7.22.15", - "@babel/helper-optimise-call-expression": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, "node_modules/@babel/helper-simple-access": { "version": "7.22.5", "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", @@ -432,18 +443,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz", - "integrity": "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-split-export-declaration": { "version": "7.22.6", "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", @@ -457,19 +456,21 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", - "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -583,10 +584,14 @@ } }, "node_modules/@babel/parser": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.5.tgz", - "integrity": "sha512-hOOqoiNXrmGdFbhgCzu6GiURxUgM27Xwd/aPuu8RfHEZPBzL1Z54okAHAQjXfcQNwvrlkAmAp4SlRTZ45vlthQ==", + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.8.tgz", + "integrity": "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==", "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.8" + }, "bin": { "parser": "bin/babel-parser.js" }, @@ -609,38 +614,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.23.3.tgz", - "integrity": "sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.23.3.tgz", - "integrity": "sha512-aVS0F65LKsdNOtcz6FRCpE4OgsP2OFnW46qNxNIX9h3wuzaNcSQsJysuMwqSibC98HPrf2vCgtxKNwS0DAlgcA==", - "dev": true, - "dependencies": { - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-simple-access": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@babel/plugin-transform-react-jsx": { "version": "7.23.4", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.23.4.tgz", @@ -705,43 +678,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-typescript": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.23.5.tgz", - "integrity": "sha512-2fMkXEJkrmwgu2Bsv1Saxgj30IXZdJ+84lQcKKI7sm719oXs0BBw2ZENKdJdR1PjWndgLCEBNXJOri0fk7RYQA==", - "dev": true, - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-create-class-features-plugin": "^7.23.5", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-typescript": "^7.23.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/preset-typescript": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.23.3.tgz", - "integrity": "sha512-17oIGVlqz6CchO9RFYn5U6ZpWRZIngayYCtrPRSgANSwC2V1Jb+iP74nVxzzXJte8b8BYxrL1yY96xfhTBrNNQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-validator-option": "^7.22.15", - "@babel/plugin-syntax-jsx": "^7.23.3", - "@babel/plugin-transform-modules-commonjs": "^7.23.3", - "@babel/plugin-transform-typescript": "^7.23.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@babel/runtime": { "version": "7.23.5", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.5.tgz", @@ -799,14 +735,14 @@ } }, "node_modules/@babel/types": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.5.tgz", - "integrity": "sha512-ON5kSOJwVO6xXVRTvOI0eOnWe7VdUcIpsovGo9U/Br4Ie4UVFQTboO2cYnDhAGU6Fp+UxSiT+pMft0SMHfuq6w==", + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz", + "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.23.4", - "@babel/helper-validator-identifier": "^7.22.20", - "to-fast-properties": "^2.0.0" + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -818,6 +754,24 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, + "node_modules/@braidai/lang": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@braidai/lang/-/lang-1.1.2.tgz", + "integrity": "sha512-qBcknbBufNHlui137Hft8xauQMTZDKdophmLFv05r2eNmdIv/MlPuP4TdUknHG68UdWLgVZwgxVe735HzJNIwA==", + "dev": true, + "license": "ISC" + }, + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.1.90" + } + }, "node_modules/@commitlint/cli": { "version": "17.8.1", "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-17.8.1.tgz", @@ -1099,355 +1053,428 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", - "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz", + "integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/android-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz", - "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz", + "integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/android-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz", - "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz", + "integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", - "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz", + "integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", - "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz", + "integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz", - "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz", + "integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "freebsd" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz", - "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz", + "integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "freebsd" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-arm": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz", - "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz", + "integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz", - "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz", + "integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz", - "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz", + "integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==", "cpu": [ "ia32" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", - "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz", + "integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==", "cpu": [ "loong64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz", - "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz", + "integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==", "cpu": [ "mips64el" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz", - "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz", + "integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==", "cpu": [ "ppc64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz", - "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz", + "integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==", "cpu": [ "riscv64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz", - "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz", + "integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==", "cpu": [ "s390x" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", - "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz", + "integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz", + "integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", - "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz", + "integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "netbsd" ], "engines": { - "node": ">=12" + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz", + "integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", - "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz", + "integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "openbsd" ], "engines": { - "node": ">=12" + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz", + "integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", - "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz", + "integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "sunos" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz", - "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz", + "integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz", - "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz", + "integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==", "cpu": [ "ia32" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz", - "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz", + "integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@eslint-community/eslint-utils": { @@ -1565,18 +1592,6 @@ "@gridventures/eslint-config-typescript": "^1.3.3" } }, - "node_modules/@gridventures/eslint-config-solid-js": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@gridventures/eslint-config-solid-js/-/eslint-config-solid-js-1.4.1.tgz", - "integrity": "sha512-YehiEqBY9Wql6PtgMjSNqIrG4rUlKc7FtyjKmX1/MxahV19WoQJjRK82HquSpyvLPEcZaYFZLYxFoxM0eUBoIw==", - "dev": true, - "dependencies": { - "@gridventures/eslint-config-base": "^1.3.3" - }, - "optionalDependencies": { - "@gridventures/eslint-config-typescript": "^1.3.3" - } - }, "node_modules/@gridventures/eslint-config-typescript": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/@gridventures/eslint-config-typescript/-/eslint-config-typescript-1.3.3.tgz", @@ -1777,10 +1792,11 @@ } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.9", @@ -2081,64 +2097,79 @@ "node": ">=10" } }, + "node_modules/@loaderkit/resolve": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@loaderkit/resolve/-/resolve-1.0.6.tgz", + "integrity": "sha512-G8FdIoF5CypfwmD9rl8BXod5HDn8JqB0CCNBXDTaRZ+yRYhARrrSToX1zg1zy9jX3zLqigsELwhT4gNtkdQAUg==", + "dev": true, + "license": "ISC", + "dependencies": { + "@braidai/lang": "^1.0.0" + } + }, "node_modules/@microsoft/api-extractor": { - "version": "7.38.3", - "resolved": "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.38.3.tgz", - "integrity": "sha512-xt9iYyC5f39281j77JTA9C3ISJpW1XWkCcnw+2vM78CPnro6KhPfwQdPDfwS5JCPNuq0grm8cMdPUOPvrchDWw==", + "version": "7.43.0", + "resolved": "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.43.0.tgz", + "integrity": "sha512-GFhTcJpB+MI6FhvXEI9b2K0snulNLWHqC/BbcJtyNYcKUiw7l3Lgis5ApsYncJ0leALX7/of4XfmXk+maT111w==", "dev": true, + "license": "MIT", "dependencies": { - "@microsoft/api-extractor-model": "7.28.2", + "@microsoft/api-extractor-model": "7.28.13", "@microsoft/tsdoc": "0.14.2", "@microsoft/tsdoc-config": "~0.16.1", - "@rushstack/node-core-library": "3.61.0", - "@rushstack/rig-package": "0.5.1", - "@rushstack/ts-command-line": "4.17.1", - "colors": "~1.2.1", + "@rushstack/node-core-library": "4.0.2", + "@rushstack/rig-package": "0.5.2", + "@rushstack/terminal": "0.10.0", + "@rushstack/ts-command-line": "4.19.1", "lodash": "~4.17.15", + "minimatch": "~3.0.3", "resolve": "~1.22.1", "semver": "~7.5.4", "source-map": "~0.6.1", - "typescript": "~5.0.4" + "typescript": "5.4.2" }, "bin": { "api-extractor": "bin/api-extractor" } }, "node_modules/@microsoft/api-extractor-model": { - "version": "7.28.2", - "resolved": "https://registry.npmjs.org/@microsoft/api-extractor-model/-/api-extractor-model-7.28.2.tgz", - "integrity": "sha512-vkojrM2fo3q4n4oPh4uUZdjJ2DxQ2+RnDQL/xhTWSRUNPF6P4QyrvY357HBxbnltKcYu+nNNolVqc6TIGQ73Ig==", + "version": "7.28.13", + "resolved": "https://registry.npmjs.org/@microsoft/api-extractor-model/-/api-extractor-model-7.28.13.tgz", + "integrity": "sha512-39v/JyldX4MS9uzHcdfmjjfS6cYGAoXV+io8B5a338pkHiSt+gy2eXQ0Q7cGFJ7quSa1VqqlMdlPrB6sLR/cAw==", "dev": true, + "license": "MIT", "dependencies": { "@microsoft/tsdoc": "0.14.2", "@microsoft/tsdoc-config": "~0.16.1", - "@rushstack/node-core-library": "3.61.0" + "@rushstack/node-core-library": "4.0.2" } }, - "node_modules/@microsoft/api-extractor/node_modules/typescript": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz", - "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==", + "node_modules/@microsoft/api-extractor/node_modules/minimatch": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.8.tgz", + "integrity": "sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==", "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">=12.20" + "node": "*" } }, "node_modules/@microsoft/tsdoc": { "version": "0.14.2", "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.14.2.tgz", "integrity": "sha512-9b8mPpKrfeGRuhFH5iO1iwCLeIIsV6+H1sRfxbkoGXIyQE2BTsPd9zqSqQJ+pv5sJ/hT5M1zvOFL02MnEezFug==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@microsoft/tsdoc-config": { "version": "0.16.2", "resolved": "https://registry.npmjs.org/@microsoft/tsdoc-config/-/tsdoc-config-0.16.2.tgz", "integrity": "sha512-OGiIzzoBLgWWR0UdRJX98oYO+XKGf7tiK4Zk6tQ/E4IJqGCe7dvkTvgDZV5cFJUzLGDOjeAXrnZoA6QkVySuxw==", "dev": true, + "license": "MIT", "dependencies": { "@microsoft/tsdoc": "0.14.2", "ajv": "~6.12.6", @@ -2151,6 +2182,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, + "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -2166,13 +2198,15 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@microsoft/tsdoc-config/node_modules/resolve": { "version": "1.19.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", "dev": true, + "license": "MIT", "dependencies": { "is-core-module": "^2.1.0", "path-parse": "^1.0.6" @@ -2868,6 +2902,22 @@ "vite": ">=2.0.0" } }, + "node_modules/@publint/pack": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@publint/pack/-/pack-0.1.6.tgz", + "integrity": "sha512-3uVNyGcVplhPZSLVyeIpL7+cIRn1YCSNHLG/rUIlBQMVH8YuN9++YF+5+UDIIO9RW98dujiUoTltO7RDB5bFJA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyexec": "^1.2.4" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://bjornlu.com/sponsor" + } + }, "node_modules/@rollup/pluginutils": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.2.1.tgz", @@ -3051,12 +3101,12 @@ ] }, "node_modules/@rushstack/node-core-library": { - "version": "3.61.0", - "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-3.61.0.tgz", - "integrity": "sha512-tdOjdErme+/YOu4gPed3sFS72GhtWCgNV9oDsHDnoLY5oDfwjKUc9Z+JOZZ37uAxcm/OCahDHfuu2ugqrfWAVQ==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-4.0.2.tgz", + "integrity": "sha512-hyES82QVpkfQMeBMteQUnrhASL/KHPhd7iJ8euduwNJG4mu2GSOKybf0rOEjOm1Wz7CwJEUm9y0yD7jg2C1bfg==", "dev": true, + "license": "MIT", "dependencies": { - "colors": "~1.2.1", "fs-extra": "~7.0.1", "import-lazy": "~4.0.0", "jju": "~1.4.0", @@ -3078,6 +3128,7 @@ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", "dev": true, + "license": "MIT", "dependencies": { "graceful-fs": "^4.1.2", "jsonfile": "^4.0.0", @@ -3092,6 +3143,7 @@ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", "dev": true, + "license": "MIT", "optionalDependencies": { "graceful-fs": "^4.1.6" } @@ -3101,29 +3153,67 @@ "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 4.0.0" } }, "node_modules/@rushstack/rig-package": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/@rushstack/rig-package/-/rig-package-0.5.1.tgz", - "integrity": "sha512-pXRYSe29TjRw7rqxD4WS3HN/sRSbfr+tJs4a9uuaSIBAITbUggygdhuG0VrO0EO+QqH91GhYMN4S6KRtOEmGVA==", + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@rushstack/rig-package/-/rig-package-0.5.2.tgz", + "integrity": "sha512-mUDecIJeH3yYGZs2a48k+pbhM6JYwWlgjs2Ca5f2n1G2/kgdgP9D/07oglEGf6mRyXEnazhEENeYTSNDRCwdqA==", "dev": true, + "license": "MIT", "dependencies": { "resolve": "~1.22.1", "strip-json-comments": "~3.1.1" } }, + "node_modules/@rushstack/terminal": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@rushstack/terminal/-/terminal-0.10.0.tgz", + "integrity": "sha512-UbELbXnUdc7EKwfH2sb8ChqNgapUOdqcCIdQP4NGxBpTZV2sQyeekuK3zmfQSa/MN+/7b4kBogl2wq0vpkpYGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rushstack/node-core-library": "4.0.2", + "supports-color": "~8.1.1" + }, + "peerDependencies": { + "@types/node": "*" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@rushstack/terminal/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, "node_modules/@rushstack/ts-command-line": { - "version": "4.17.1", - "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.17.1.tgz", - "integrity": "sha512-2jweO1O57BYP5qdBGl6apJLB+aRIn5ccIRTPDyULh0KMwVzFqWtw6IZWt1qtUoZD/pD2RNkIOosH6Cq45rIYeg==", + "version": "4.19.1", + "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.19.1.tgz", + "integrity": "sha512-J7H768dgcpG60d7skZ5uSSwyCZs/S2HrWP1Ds8d1qYAyaaeJmpmmLr9BVw97RjFzmQPOYnoXcKA4GkqDCkduQg==", "dev": true, + "license": "MIT", "dependencies": { + "@rushstack/terminal": "0.10.0", "@types/argparse": "1.0.38", "argparse": "~1.0.9", - "colors": "~1.2.1", "string-argv": "~0.3.1" } }, @@ -3132,6 +3222,7 @@ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "dev": true, + "license": "MIT", "dependencies": { "sprintf-js": "~1.0.2" } @@ -3416,93 +3507,267 @@ "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", "dev": true }, - "node_modules/@tiny-intl/core": { - "resolved": "packages/core", - "link": true - }, - "node_modules/@tiny-intl/preact": { - "resolved": "packages/preact", - "link": true - }, - "node_modules/@tiny-intl/react": { - "resolved": "packages/react", - "link": true - }, - "node_modules/@tiny-intl/solid-js": { - "resolved": "packages/solid-js", - "link": true - }, - "node_modules/@tootallnate/once": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", - "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "node_modules/@sindresorhus/is": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", "dev": true, + "license": "MIT", "engines": { - "node": ">= 10" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" } }, - "node_modules/@tsconfig/node10": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", - "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", - "dev": true - }, - "node_modules/@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true - }, - "node_modules/@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true - }, - "node_modules/@tsconfig/node16": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", - "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "dev": true - }, - "node_modules/@tufjs/canonical-json": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@tufjs/canonical-json/-/canonical-json-1.0.0.tgz", - "integrity": "sha512-QTnf++uxunWvG2z3UFNzAoQPHxnSXOwtaI3iJ+AohhV+5vONuArPjJE7aPXPVXfXJsqrVbZBu9b81AJoSd09IQ==", + "node_modules/@testing-library/dom": { + "version": "9.3.4", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.4.tgz", + "integrity": "sha512-FlS4ZWlp97iiNWig0Muq8p+3rVDjRiYE+YKGbAqXOu9nwJFFOdL00kFpz42M+4huzYi86vAK1sOOfyOG45muIQ==", "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.1.3", + "chalk": "^4.1.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "pretty-format": "^27.0.2" + }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=14" } }, - "node_modules/@tufjs/models": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@tufjs/models/-/models-1.0.4.tgz", - "integrity": "sha512-qaGV9ltJP0EO25YfFUPhxRVK0evXFIAGicsVXuRim4Ed9cjPxYhNnNJ49SFmbeLgtxpslIkX317IgpfcHPVj/A==", + "node_modules/@testing-library/dom/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, - "dependencies": { - "@tufjs/canonical-json": "1.0.0", - "minimatch": "^9.0.0" - }, + "license": "MIT", "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@tufjs/models/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "node_modules/@testing-library/dom/node_modules/aria-query": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz", + "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "balanced-match": "^1.0.0" + "deep-equal": "^2.0.5" } }, - "node_modules/@tufjs/models/node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "node_modules/@testing-library/dom/node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", "dev": true, + "license": "MIT", "dependencies": { - "brace-expansion": "^2.0.1" + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@testing-library/dom/node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@testing-library/preact": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@testing-library/preact/-/preact-3.2.4.tgz", + "integrity": "sha512-F+kJ243LP6VmEK1M809unzTE/ijg+bsMNuiRN0JEDIJBELKKDNhdgC/WrUSZ7klwJvtlO3wQZ9ix+jhObG07Fg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@testing-library/dom": "^8.11.1" + }, + "engines": { + "node": ">= 12" + }, + "peerDependencies": { + "preact": ">=10 || ^10.0.0-alpha.0 || ^10.0.0-beta.0" + } + }, + "node_modules/@testing-library/preact/node_modules/@testing-library/dom": { + "version": "8.20.1", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.20.1.tgz", + "integrity": "sha512-/DiOQ5xBxgdYRC8LNk7U+RWat0S3qRLeIw3ZIkMQ9kkVlRmwD/Eg8k8CqIpD6GW7u20JIUOfMKbxtiLutpjQ4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.1.3", + "chalk": "^4.1.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@testing-library/preact/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@testing-library/preact/node_modules/aria-query": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz", + "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "deep-equal": "^2.0.5" + } + }, + "node_modules/@testing-library/preact/node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@testing-library/preact/node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@testing-library/react": { + "version": "14.3.1", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-14.3.1.tgz", + "integrity": "sha512-H99XjUhWQw0lTgyMN05W3xQG1Nh4lq574D8keFf1dDoNTJgp66VbJozRaczoF+wsiaPJNt/TcnfpLGufGxSrZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.5", + "@testing-library/dom": "^9.0.0", + "@types/react-dom": "^18.0.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, + "node_modules/@tiny-intl/core": { + "resolved": "packages/core", + "link": true + }, + "node_modules/@tiny-intl/preact": { + "resolved": "packages/preact", + "link": true + }, + "node_modules/@tiny-intl/react": { + "resolved": "packages/react", + "link": true + }, + "node_modules/@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "dev": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true + }, + "node_modules/@tufjs/canonical-json": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@tufjs/canonical-json/-/canonical-json-1.0.0.tgz", + "integrity": "sha512-QTnf++uxunWvG2z3UFNzAoQPHxnSXOwtaI3iJ+AohhV+5vONuArPjJE7aPXPVXfXJsqrVbZBu9b81AJoSd09IQ==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@tufjs/models": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tufjs/models/-/models-1.0.4.tgz", + "integrity": "sha512-qaGV9ltJP0EO25YfFUPhxRVK0evXFIAGicsVXuRim4Ed9cjPxYhNnNJ49SFmbeLgtxpslIkX317IgpfcHPVj/A==", + "dev": true, + "dependencies": { + "@tufjs/canonical-json": "1.0.0", + "minimatch": "^9.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@tufjs/models/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@tufjs/models/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -3515,7 +3780,15 @@ "version": "1.0.38", "resolved": "https://registry.npmjs.org/@types/argparse/-/argparse-1.0.38.tgz", "integrity": "sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==", - "dev": true + "dev": true, + "license": "MIT" + }, + "node_modules/@types/aria-query": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", + "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", + "dev": true, + "license": "MIT" }, "node_modules/@types/babel__core": { "version": "7.20.5", @@ -3641,6 +3914,16 @@ "csstype": "^3.0.2" } }, + "node_modules/@types/react-dom": { + "version": "18.3.7", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz", + "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^18.0.0" + } + }, "node_modules/@types/scheduler": { "version": "0.16.8", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.8.tgz", @@ -3871,6 +4154,7 @@ "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-0.34.6.tgz", "integrity": "sha512-fivy/OK2d/EsJFoEoxHFEnNGTg+MmdZBAVK9Ka4qhXR2K3J0DS08vcGVwzDtXSuUMabLv4KtPcpSKkcMXFDViw==", "dev": true, + "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.1", "@bcoe/v8-coverage": "^0.2.3", @@ -3991,6 +4275,7 @@ "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-1.11.1.tgz", "integrity": "sha512-dOcNn3i9GgZAcJt43wuaEykSluAuOkQgzni1cuxLxTV0nJKanQztp7FxyswdRILaKH+P2XZMPRp2S4MV/pElCw==", "dev": true, + "license": "MIT", "dependencies": { "@volar/source-map": "1.11.1" } @@ -4000,6 +4285,7 @@ "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-1.11.1.tgz", "integrity": "sha512-hJnOnwZ4+WT5iupLRnuzbULZ42L7BWWPMmruzwtLhJfpDVoZLjNBxHDi2sY2bgZXCKlpU5XcsMFoYrsQmPhfZg==", "dev": true, + "license": "MIT", "dependencies": { "muggle-string": "^0.3.1" } @@ -4009,38 +4295,56 @@ "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-1.11.1.tgz", "integrity": "sha512-iU+t2mas/4lYierSnoFOeRFQUhAEMgsFuQxoxvwn5EdQopw43j+J27a4lt9LMInx1gLJBC6qL14WYGlgymaSMQ==", "dev": true, + "license": "MIT", "dependencies": { "@volar/language-core": "1.11.1", "path-browserify": "^1.0.1" } }, "node_modules/@vue/compiler-core": { - "version": "3.3.9", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.3.9.tgz", - "integrity": "sha512-+/Lf68Vr/nFBA6ol4xOtJrW+BQWv3QWKfRwGSm70jtXwfhZNF4R/eRgyVJYoxFRhdCTk/F6g99BP0ffPgZihfQ==", + "version": "3.5.41", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.41.tgz", + "integrity": "sha512-q0Xtv/F9w2YO/7htQhtiL+Ev2WCJbe5N2hc+XfgyKkEKqWpSxknmT8QOuGdEKNdjPq0c3F7rNpFkTo3Kfrm7pg==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/parser": "^7.23.3", - "@vue/shared": "3.3.9", + "@babel/parser": "^7.29.8", + "@vue/shared": "3.5.41", + "entities": "^7.0.1", "estree-walker": "^2.0.2", - "source-map-js": "^1.0.2" + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-core/node_modules/entities": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" } }, "node_modules/@vue/compiler-dom": { - "version": "3.3.9", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.3.9.tgz", - "integrity": "sha512-nfWubTtLXuT4iBeDSZ5J3m218MjOy42Vp2pmKVuBKo2/BLcrFUX8nCSr/bKRFiJ32R8qbdnnnBgRn9AdU5v0Sg==", + "version": "3.5.41", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.41.tgz", + "integrity": "sha512-oKacVfNglLvGjnS6BXOlGL7EyG2h8X03pqXCjzotRZUaXGjbrTJUnVAQjrCqUnS+lyu31nwQjZY/d817GmCnfw==", "dev": true, + "license": "MIT", "dependencies": { - "@vue/compiler-core": "3.3.9", - "@vue/shared": "3.3.9" + "@vue/compiler-core": "3.5.41", + "@vue/shared": "3.5.41" } }, "node_modules/@vue/language-core": { - "version": "1.8.24", - "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-1.8.24.tgz", - "integrity": "sha512-2ClHvij0WlsDWryPzXJCSpPc6rusZFNoVtRZGgGGkKCmKuIREDDKmH8j+1tYyxPYyH0qL6pZ6+IHD8KIm5nWAw==", + "version": "1.8.27", + "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-1.8.27.tgz", + "integrity": "sha512-L8Kc27VdQserNaCUNiSFdDl9LWT24ly8Hpwf1ECy3aFb9m6bDhBGQYOujDm21N7EW3moKIOKEanQwe1q5BK+mA==", "dev": true, + "license": "MIT", "dependencies": { "@volar/language-core": "~1.11.1", "@volar/source-map": "~1.11.1", @@ -4062,21 +4366,23 @@ } }, "node_modules/@vue/language-core/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.4.tgz", + "integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } }, "node_modules/@vue/language-core/node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "dev": true, + "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^2.0.2" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -4086,10 +4392,11 @@ } }, "node_modules/@vue/shared": { - "version": "3.3.9", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.9.tgz", - "integrity": "sha512-ZE0VTIR0LmYgeyhurPTpy4KzKsuDyQbMSdM49eKkMnT5X4VfFBLysMzjIZhLEFQYjjOVVfbvUDHckwjDFiO2eA==", - "dev": true + "version": "3.5.41", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.41.tgz", + "integrity": "sha512-IOnwSCma8j+9xJT6b8H0dEYidC80NsYmNMlZxRsukYcSoGaDBohog5hDxzeUXdFeGWFA++vWvxqOmrr96VlqMA==", + "dev": true, + "license": "MIT" }, "node_modules/@yarnpkg/lockfile": { "version": "1.1.0", @@ -4144,6 +4451,14 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/abab": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", + "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", + "deprecated": "Use your platform's native atob() and btoa() methods instead", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -4598,34 +4913,6 @@ "dequal": "^2.0.3" } }, - "node_modules/babel-plugin-jsx-dom-expressions": { - "version": "0.37.9", - "resolved": "https://registry.npmjs.org/babel-plugin-jsx-dom-expressions/-/babel-plugin-jsx-dom-expressions-0.37.9.tgz", - "integrity": "sha512-6w+zs2i14fVanj4e1hXCU5cp+x0U0LJ5jScknpMZZUteHhwFRGJflHMVJ+xAcW7ku41FYjr7DgtK9mnc2SXlJg==", - "dev": true, - "dependencies": { - "@babel/helper-module-imports": "7.18.6", - "@babel/plugin-syntax-jsx": "^7.18.6", - "@babel/types": "^7.20.7", - "html-entities": "2.3.3", - "validate-html-nesting": "^1.2.1" - }, - "peerDependencies": { - "@babel/core": "^7.20.12" - } - }, - "node_modules/babel-plugin-jsx-dom-expressions/node_modules/@babel/helper-module-imports": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", - "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", - "dev": true, - "dependencies": { - "@babel/types": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/babel-plugin-transform-hook-names": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/babel-plugin-transform-hook-names/-/babel-plugin-transform-hook-names-1.0.2.tgz", @@ -4635,18 +4922,6 @@ "@babel/core": "^7.12.10" } }, - "node_modules/babel-preset-solid": { - "version": "1.8.6", - "resolved": "https://registry.npmjs.org/babel-preset-solid/-/babel-preset-solid-1.8.6.tgz", - "integrity": "sha512-Ened42CHjU4EFkvNeS042/3Pm21yvMWn8p4G4ddzQTlKaMwSGGD1VciA/e7EshBVHJCcBj9vHiUd/r3A4qLPZA==", - "dev": true, - "dependencies": { - "babel-plugin-jsx-dom-expressions": "^0.37.9" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -4904,14 +5179,50 @@ } }, "node_modules/call-bind": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", - "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.9.tgz", + "integrity": "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "get-intrinsic": "^1.3.0", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", "dev": true, + "license": "MIT", "dependencies": { - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.1", - "set-function-length": "^1.1.1" + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -5006,6 +5317,16 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/chardet": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", @@ -5087,6 +5408,13 @@ "node": ">=8" } }, + "node_modules/cjs-module-lexer": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", + "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", + "dev": true, + "license": "MIT" + }, "node_modules/clean-stack": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", @@ -5108,18 +5436,112 @@ "node": ">=8" } }, - "node_modules/cli-spinners": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.1.tgz", - "integrity": "sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==", + "node_modules/cli-highlight": { + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/cli-highlight/-/cli-highlight-2.1.11.tgz", + "integrity": "sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==", "dev": true, - "engines": { - "node": ">=6" + "license": "ISC", + "dependencies": { + "chalk": "^4.0.0", + "highlight.js": "^10.7.1", + "mz": "^2.4.0", + "parse5": "^5.1.1", + "parse5-htmlparser2-tree-adapter": "^6.0.0", + "yargs": "^16.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, + "bin": { + "highlight": "bin/highlight" + }, + "engines": { + "node": ">=8.0.0", + "npm": ">=5.0.0" + } + }, + "node_modules/cli-highlight/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/cli-highlight/node_modules/parse5": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", + "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", + "dev": true, + "license": "MIT" + }, + "node_modules/cli-highlight/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/cli-highlight/node_modules/yargs": { + "version": "16.2.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.2.tgz", + "integrity": "sha512-Nt9ZJjXTv5R8MHbqby/wXQ6Gi0Bb3TcYZkR1bzuL4yB2OxWPkXknz513gEF0GoA6tn00UpbPvERW8rzCuWCA6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cli-spinners": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.1.tgz", + "integrity": "sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-table3": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz", + "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "string-width": "^4.2.0" + }, + "engines": { + "node": "10.* || >= 12.*" + }, + "optionalDependencies": { + "@colors/colors": "1.5.0" + } + }, "node_modules/cli-truncate": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz", @@ -5303,15 +5725,6 @@ "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", "dev": true }, - "node_modules/colors": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.2.5.tgz", - "integrity": "sha512-erNRLao/Y3Fv54qUa0LBB+//Uf3YwMUmdJinN20yMXm9zdKKqH9wt7R9IIVZ+K7ShzfpLV/Zg8+VyrBJYB4lpg==", - "dev": true, - "engines": { - "node": ">=0.1.90" - } - }, "node_modules/columnify": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/columnify/-/columnify-1.6.0.tgz", @@ -5360,7 +5773,8 @@ "version": "0.0.1", "resolved": "https://registry.npmjs.org/computeds/-/computeds-0.0.1.tgz", "integrity": "sha512-7CEBgcMjVmitjYo5q8JTJVra6X5mQ20uTThdK+0kR7UEaDrAWEQcRiBtWJzga4eRpP6afNwwLsX2SET2JhVB1Q==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/concat-map": { "version": "0.0.1", @@ -5630,6 +6044,19 @@ "node": ">= 8" } }, + "node_modules/cssstyle": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-3.0.0.tgz", + "integrity": "sha512-N4u2ABATi3Qplzf0hWbVCdjenim8F3ojEXpBDF5hBpjzW182MjNGLqfmQ0SkSPeQ+V86ZXgeH8aXj6kayd4jgg==", + "dev": true, + "license": "MIT", + "dependencies": { + "rrweb-cssom": "^0.6.0" + }, + "engines": { + "node": ">=14" + } + }, "node_modules/csstype": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", @@ -5651,6 +6078,58 @@ "node": ">=8" } }, + "node_modules/data-urls": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-4.0.0.tgz", + "integrity": "sha512-/mMTei/JXPqvFqQtfyTowxmJVwr2PVAeCcDxyFf6LhoOu/09TX2OX3kb2wzi4DMXcfj4OItwDOnhl5oziPnT6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "abab": "^2.0.6", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^12.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/data-urls/node_modules/tr46": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-4.1.1.tgz", + "integrity": "sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.3.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/data-urls/node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/data-urls/node_modules/whatwg-url": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-12.0.1.tgz", + "integrity": "sha512-Ed/LrqB8EPlGxjS+TrsXcpUond1mhccS3pchLhzSgPCnTimUCKj3IZE75pAs5m6heB2U2TMerKFUXheyHY+VDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "^4.1.1", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=14" + } + }, "node_modules/dateformat": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", @@ -5664,7 +6143,8 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/debug": { "version": "4.3.4", @@ -5717,6 +6197,13 @@ "node": ">=0.10.0" } }, + "node_modules/decimal.js": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", + "dev": true, + "license": "MIT" + }, "node_modules/dedent": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", @@ -5735,6 +6222,39 @@ "node": ">=6" } }, + "node_modules/deep-equal": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.3.tgz", + "integrity": "sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "call-bind": "^1.0.5", + "es-get-iterator": "^1.1.3", + "get-intrinsic": "^1.2.2", + "is-arguments": "^1.1.1", + "is-array-buffer": "^3.0.2", + "is-date-object": "^1.0.5", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "isarray": "^2.0.5", + "object-is": "^1.1.5", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.5.1", + "side-channel": "^1.0.4", + "which-boxed-primitive": "^1.0.2", + "which-collection": "^1.0.1", + "which-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -5754,17 +6274,21 @@ } }, "node_modules/define-data-property": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", - "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", "dev": true, + "license": "MIT", "dependencies": { - "get-intrinsic": "^1.2.1", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/define-lazy-prop": { @@ -5874,6 +6398,37 @@ "node": ">=6.0.0" } }, + "node_modules/dom-accessibility-api": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", + "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", + "dev": true, + "license": "MIT" + }, + "node_modules/domexception": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", + "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==", + "deprecated": "Use your platform's native DOMException instead", + "dev": true, + "license": "MIT", + "dependencies": { + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/domexception/node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, "node_modules/dot-prop": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", @@ -5907,6 +6462,21 @@ "node": ">=12" } }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/duplexer": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", @@ -5946,6 +6516,13 @@ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "dev": true }, + "node_modules/emojilib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/emojilib/-/emojilib-2.4.0.tgz", + "integrity": "sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw==", + "dev": true, + "license": "MIT" + }, "node_modules/encoding": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", @@ -5990,6 +6567,19 @@ "node": ">=8.6" } }, + "node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/env-paths": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", @@ -6011,6 +6601,19 @@ "node": ">=4" } }, + "node_modules/environment": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", + "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/err-code": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", @@ -6079,6 +6682,47 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-get-iterator": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", + "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "has-symbols": "^1.0.3", + "is-arguments": "^1.1.1", + "is-map": "^2.0.2", + "is-set": "^2.0.2", + "is-string": "^1.0.7", + "isarray": "^2.0.5", + "stop-iteration-iterator": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/es-iterator-helpers": { "version": "1.0.15", "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.15.tgz", @@ -6101,6 +6745,19 @@ "safe-array-concat": "^1.0.1" } }, + "node_modules/es-object-atoms": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/es-set-tostringtag": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz", @@ -6142,40 +6799,62 @@ } }, "node_modules/esbuild": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", - "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz", + "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==", "dev": true, "hasInstallScript": true, + "license": "MIT", "bin": { "esbuild": "bin/esbuild" }, "engines": { - "node": ">=12" + "node": ">=18" }, "optionalDependencies": { - "@esbuild/android-arm": "0.18.20", - "@esbuild/android-arm64": "0.18.20", - "@esbuild/android-x64": "0.18.20", - "@esbuild/darwin-arm64": "0.18.20", - "@esbuild/darwin-x64": "0.18.20", - "@esbuild/freebsd-arm64": "0.18.20", - "@esbuild/freebsd-x64": "0.18.20", - "@esbuild/linux-arm": "0.18.20", - "@esbuild/linux-arm64": "0.18.20", - "@esbuild/linux-ia32": "0.18.20", - "@esbuild/linux-loong64": "0.18.20", - "@esbuild/linux-mips64el": "0.18.20", - "@esbuild/linux-ppc64": "0.18.20", - "@esbuild/linux-riscv64": "0.18.20", - "@esbuild/linux-s390x": "0.18.20", - "@esbuild/linux-x64": "0.18.20", - "@esbuild/netbsd-x64": "0.18.20", - "@esbuild/openbsd-x64": "0.18.20", - "@esbuild/sunos-x64": "0.18.20", - "@esbuild/win32-arm64": "0.18.20", - "@esbuild/win32-ia32": "0.18.20", - "@esbuild/win32-x64": "0.18.20" + "@esbuild/aix-ppc64": "0.28.2", + "@esbuild/android-arm": "0.28.2", + "@esbuild/android-arm64": "0.28.2", + "@esbuild/android-x64": "0.28.2", + "@esbuild/darwin-arm64": "0.28.2", + "@esbuild/darwin-x64": "0.28.2", + "@esbuild/freebsd-arm64": "0.28.2", + "@esbuild/freebsd-x64": "0.28.2", + "@esbuild/linux-arm": "0.28.2", + "@esbuild/linux-arm64": "0.28.2", + "@esbuild/linux-ia32": "0.28.2", + "@esbuild/linux-loong64": "0.28.2", + "@esbuild/linux-mips64el": "0.28.2", + "@esbuild/linux-ppc64": "0.28.2", + "@esbuild/linux-riscv64": "0.28.2", + "@esbuild/linux-s390x": "0.28.2", + "@esbuild/linux-x64": "0.28.2", + "@esbuild/netbsd-arm64": "0.28.2", + "@esbuild/netbsd-x64": "0.28.2", + "@esbuild/openbsd-arm64": "0.28.2", + "@esbuild/openbsd-x64": "0.28.2", + "@esbuild/openharmony-arm64": "0.28.2", + "@esbuild/sunos-x64": "0.28.2", + "@esbuild/win32-arm64": "0.28.2", + "@esbuild/win32-ia32": "0.28.2", + "@esbuild/win32-x64": "0.28.2" + } + }, + "node_modules/esbuild/node_modules/@esbuild/aix-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz", + "integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" } }, "node_modules/escalade": { @@ -6532,125 +7211,6 @@ "semver": "bin/semver.js" } }, - "node_modules/eslint-plugin-solid": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-solid/-/eslint-plugin-solid-0.13.0.tgz", - "integrity": "sha512-Sutd+DxEGu9+Z9ITtHKXRAClxVe1a6C1XQZSuN8iBsMy0IAVEc6Tca1UYgc7tD2ZrRRjZKB9mohBOaZl5NJLgg==", - "dev": true, - "dependencies": { - "@typescript-eslint/utils": "^6.4.0", - "is-html": "^2.0.0", - "jsx-ast-utils": "^3.3.3", - "kebab-case": "^1.0.2", - "known-css-properties": "^0.24.0", - "style-to-object": "^0.3.0" - }, - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/eslint-plugin-solid/node_modules/@typescript-eslint/scope-manager": { - "version": "6.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.13.1.tgz", - "integrity": "sha512-BW0kJ7ceiKi56GbT2KKzZzN+nDxzQK2DS6x0PiSMPjciPgd/JRQGMibyaN2cPt2cAvuoH0oNvn2fwonHI+4QUQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.13.1", - "@typescript-eslint/visitor-keys": "6.13.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/eslint-plugin-solid/node_modules/@typescript-eslint/types": { - "version": "6.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.13.1.tgz", - "integrity": "sha512-gjeEskSmiEKKFIbnhDXUyiqVma1gRCQNbVZ1C8q7Zjcxh3WZMbzWVfGE9rHfWd1msQtPS0BVD9Jz9jded44eKg==", - "dev": true, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/eslint-plugin-solid/node_modules/@typescript-eslint/typescript-estree": { - "version": "6.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.13.1.tgz", - "integrity": "sha512-sBLQsvOC0Q7LGcUHO5qpG1HxRgePbT6wwqOiGLpR8uOJvPJbfs0mW3jPA3ujsDvfiVwVlWUDESNXv44KtINkUQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.13.1", - "@typescript-eslint/visitor-keys": "6.13.1", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/eslint-plugin-solid/node_modules/@typescript-eslint/utils": { - "version": "6.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.13.1.tgz", - "integrity": "sha512-ouPn/zVoan92JgAegesTXDB/oUp6BP1v8WpfYcqh649ejNc9Qv+B4FF2Ff626kO1xg0wWwwG48lAJ4JuesgdOw==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@types/json-schema": "^7.0.12", - "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.13.1", - "@typescript-eslint/types": "6.13.1", - "@typescript-eslint/typescript-estree": "6.13.1", - "semver": "^7.5.4" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - } - }, - "node_modules/eslint-plugin-solid/node_modules/@typescript-eslint/visitor-keys": { - "version": "6.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.13.1.tgz", - "integrity": "sha512-NDhQUy2tg6XGNBGDRm1XybOHSia8mcXmlbKWoQP+nm1BIIMxa55shyJfZkHpEBN62KNPLrocSM2PdPcaLgDKMQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.13.1", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, "node_modules/eslint-scope": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", @@ -6923,6 +7483,13 @@ "reusify": "^1.0.4" } }, + "node_modules/fflate": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.3.tgz", + "integrity": "sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==", + "dev": true, + "license": "MIT" + }, "node_modules/figures": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", @@ -7273,15 +7840,25 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", - "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "dev": true, + "license": "MIT", "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -7409,6 +7986,20 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/get-stream": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", @@ -7617,12 +8208,13 @@ } }, "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", "dev": true, - "dependencies": { - "get-intrinsic": "^1.1.3" + "license": "MIT", + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -7689,12 +8281,13 @@ } }, "node_modules/has-property-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", - "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "dev": true, + "license": "MIT", "dependencies": { - "get-intrinsic": "^1.2.2" + "es-define-property": "^1.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -7713,10 +8306,11 @@ } }, "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -7725,12 +8319,13 @@ } }, "node_modules/has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "dev": true, + "license": "MIT", "dependencies": { - "has-symbols": "^1.0.2" + "has-symbols": "^1.0.3" }, "engines": { "node": ">= 0.4" @@ -7746,10 +8341,11 @@ "dev": true }, "node_modules/hasown": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", - "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", "dev": true, + "license": "MIT", "dependencies": { "function-bind": "^1.1.2" }, @@ -7762,10 +8358,21 @@ "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true, + "license": "MIT", "bin": { "he": "bin/he" } }, + "node_modules/highlight.js": { + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", + "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": "*" + } + }, "node_modules/hosted-git-info": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", @@ -7778,11 +8385,18 @@ "node": ">=10" } }, - "node_modules/html-entities": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz", - "integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==", - "dev": true + "node_modules/html-encoding-sniffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", + "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-encoding": "^2.0.0" + }, + "engines": { + "node": ">=12" + } }, "node_modules/html-escaper": { "version": "2.0.2", @@ -7790,18 +8404,6 @@ "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "dev": true }, - "node_modules/html-tags": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.3.1.tgz", - "integrity": "sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/http-cache-semantics": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", @@ -7972,6 +8574,7 @@ "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz", "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -8095,12 +8698,6 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/inline-style-parser": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.1.1.tgz", - "integrity": "sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==", - "dev": true - }, "node_modules/inquirer": { "version": "8.2.6", "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.6.tgz", @@ -8128,14 +8725,15 @@ } }, "node_modules/internal-slot": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.6.tgz", - "integrity": "sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", "dev": true, + "license": "MIT", "dependencies": { - "get-intrinsic": "^1.2.2", - "hasown": "^2.0.0", - "side-channel": "^1.0.4" + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -8160,6 +8758,23 @@ "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", "dev": true }, + "node_modules/is-arguments": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz", + "integrity": "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-array-buffer": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", @@ -8361,18 +8976,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-html": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-html/-/is-html-2.0.0.tgz", - "integrity": "sha512-S+OpgB5i7wzIue/YSE5hg0e5ZYfG3hhpNh9KGl6ayJ38p7ED6wxQLd1TV91xHpcTvw90KMJ9EwN3F/iNflHBVg==", - "dev": true, - "dependencies": { - "html-tags": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/is-interactive": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", @@ -8469,6 +9072,13 @@ "node": ">=0.10.0" } }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true, + "license": "MIT" + }, "node_modules/is-regex": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", @@ -8630,18 +9240,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-what": { - "version": "4.1.16", - "resolved": "https://registry.npmjs.org/is-what/-/is-what-4.1.16.tgz", - "integrity": "sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==", - "dev": true, - "engines": { - "node": ">=12.13" - }, - "funding": { - "url": "https://github.com/sponsors/mesqueeb" - } - }, "node_modules/is-wsl": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", @@ -8802,7 +9400,8 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz", "integrity": "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/joycon": { "version": "3.1.1", @@ -8837,6 +9436,86 @@ "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", "dev": true }, + "node_modules/jsdom": { + "version": "22.1.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-22.1.0.tgz", + "integrity": "sha512-/9AVW7xNbsBv6GfWho4TTNjEo9fe6Zhf9O7s0Fhhr3u+awPwAJMKwAMXnkk5vBxflqLW9hTHX/0cs+P3gW+cQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "abab": "^2.0.6", + "cssstyle": "^3.0.0", + "data-urls": "^4.0.0", + "decimal.js": "^10.4.3", + "domexception": "^4.0.0", + "form-data": "^4.0.0", + "html-encoding-sniffer": "^3.0.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.1", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.4", + "parse5": "^7.1.2", + "rrweb-cssom": "^0.6.0", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.1.2", + "w3c-xmlserializer": "^4.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^2.0.0", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^12.0.1", + "ws": "^8.13.0", + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "canvas": "^2.5.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jsdom/node_modules/tr46": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-4.1.1.tgz", + "integrity": "sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.3.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/jsdom/node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/jsdom/node_modules/whatwg-url": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-12.0.1.tgz", + "integrity": "sha512-Ed/LrqB8EPlGxjS+TrsXcpUond1mhccS3pchLhzSgPCnTimUCKj3IZE75pAs5m6heB2U2TMerKFUXheyHY+VDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "^4.1.1", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=14" + } + }, "node_modules/jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", @@ -8982,12 +9661,6 @@ "node": ">=4.0" } }, - "node_modules/kebab-case": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/kebab-case/-/kebab-case-1.0.2.tgz", - "integrity": "sha512-7n6wXq4gNgBELfDCpzKc+mRrZFs7D+wgfF5WRFLNAr4DA/qtr9Js8uOAVAfHhuLMfAcQ0pRKqbpjx+TcJVdE1Q==", - "dev": true - }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -9015,12 +9688,6 @@ "graceful-fs": "^4.1.11" } }, - "node_modules/known-css-properties": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.24.0.tgz", - "integrity": "sha512-RTSoaUAfLvpR357vWzAz/50Q/BmHfmE6ETSWfutT0AJiw10e6CmcdYRQJlLRd95B53D0Y2aD1jSxD3V3ySF+PA==", - "dev": true - }, "node_modules/kolorist": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/kolorist/-/kolorist-1.8.0.tgz", @@ -9861,13 +10528,17 @@ "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", - "dev": true + "deprecated": "This package is deprecated. Use the optional chaining (?.) operator instead.", + "dev": true, + "license": "MIT" }, "node_modules/lodash.isequal": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", - "dev": true + "deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead.", + "dev": true, + "license": "MIT" }, "node_modules/lodash.isfunction": { "version": "3.0.9", @@ -10171,16 +10842,24 @@ "node": ">=10" } }, + "node_modules/lz-string": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", + "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", + "dev": true, + "license": "MIT", + "bin": { + "lz-string": "bin/bin.js" + } + }, "node_modules/magic-string": { - "version": "0.30.5", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.5.tgz", - "integrity": "sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==", + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", "dev": true, + "license": "MIT", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.15" - }, - "engines": { - "node": ">=12" + "@jridgewell/sourcemap-codec": "^1.5.5" } }, "node_modules/make-dir": { @@ -10252,6 +10931,93 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/marked": { + "version": "9.1.6", + "resolved": "https://registry.npmjs.org/marked/-/marked-9.1.6.tgz", + "integrity": "sha512-jcByLnIFkd5gSXZmjNvS1TlmRhCXZjIzHYlaGkPlLIekG55JDR2Z4va9tZwCiP+/RDERiNhMOFu01xd6O5ct1Q==", + "dev": true, + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 16" + } + }, + "node_modules/marked-terminal": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/marked-terminal/-/marked-terminal-7.3.0.tgz", + "integrity": "sha512-t4rBvPsHc57uE/2nJOLmMbZCQ4tgAccAED3ngXQqW6g+TxA488JzJ+FK3lQkzBQOI1mRV/r/Kq+1ZlJ4D0owQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^7.0.0", + "ansi-regex": "^6.1.0", + "chalk": "^5.4.1", + "cli-highlight": "^2.1.11", + "cli-table3": "^0.6.5", + "node-emoji": "^2.2.0", + "supports-hyperlinks": "^3.1.0" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "marked": ">=1 <16" + } + }, + "node_modules/marked-terminal/node_modules/ansi-escapes": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.3.0.tgz", + "integrity": "sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "environment": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/marked-terminal/node_modules/ansi-regex": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.3.0.tgz", + "integrity": "sha512-WpDfL7NO6j7tH88IDBNVdUJxDh9nmCteAVW9dsep846XdwF4naCBK+/tGLX3KJgcpgMRXCFlTM2hKGoK9FsdrQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/marked-terminal/node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/meow": { "version": "8.1.2", "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz", @@ -10418,21 +11184,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/merge-anything": { - "version": "5.1.7", - "resolved": "https://registry.npmjs.org/merge-anything/-/merge-anything-5.1.7.tgz", - "integrity": "sha512-eRtbOb1N5iyH0tkQDAoQ4Ipsp/5qSR79Dzrz8hEPxRX10RWWR/iQXdoKmBSRCThY1Fh5EhISDtpSc93fpxUniQ==", - "dev": true, - "dependencies": { - "is-what": "^4.1.8" - }, - "engines": { - "node": ">=12.13" - }, - "funding": { - "url": "https://github.com/sponsors/mesqueeb" - } - }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -10668,6 +11419,16 @@ "node": ">=0.10.0" } }, + "node_modules/mri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -10678,7 +11439,8 @@ "version": "0.3.1", "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.3.1.tgz", "integrity": "sha512-ckmWDJjphvd/FvZawgygcUeQCxzvohjFO5RxTjj4eq8kw359gFF3E1brjfI+viLMxss5JrHTDRHZvu2/tuy0Qg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/multimatch": { "version": "5.0.0", @@ -10776,6 +11538,22 @@ "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==", "dev": true }, + "node_modules/node-emoji": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-2.2.0.tgz", + "integrity": "sha512-Z3lTE9pLaJF47NyMhd4ww1yFTAP8YhYI8SleJiHzM46Fgpm5cnNzSl9XfzFNqbaz+VlJrIj3fXQ4DeN1Rjm6cw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/is": "^4.6.0", + "char-regex": "^1.0.2", + "emojilib": "^2.4.0", + "skin-tone": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/node-fetch": { "version": "2.6.7", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", @@ -11367,6 +12145,13 @@ "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, + "node_modules/nwsapi": { + "version": "2.2.24", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.24.tgz", + "integrity": "sha512-7YRhZ3jS45LwmSCT4b2sVFHt/WuovaktDU07QrtOBY2PXskss5a9jfmR9jptyumwXST+rFjrmppMY1KT/yn35A==", + "dev": true, + "license": "MIT" + }, "node_modules/nx": { "version": "16.10.0", "resolved": "https://registry.npmjs.org/nx/-/nx-16.10.0.tgz", @@ -11537,10 +12322,31 @@ } }, "node_modules/object-inspect": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", - "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-is": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", + "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -11877,6 +12683,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/package-manager-detector": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.8.0.tgz", + "integrity": "sha512-yQA4H19AmPEoMUeavPMDIe1higySl/gH/yaQrkT/s07Qp+7pp2hYz30N3z2l5BkjVkF9Ow6o0wjJamm2y7Sn0A==", + "dev": true, + "license": "MIT" + }, "node_modules/pacote": { "version": "15.2.0", "resolved": "https://registry.npmjs.org/pacote/-/pacote-15.2.0.tgz", @@ -12188,6 +13001,36 @@ "parse-path": "^7.0.0" } }, + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", + "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "parse5": "^6.0.1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter/node_modules/parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "dev": true, + "license": "MIT" + }, "node_modules/patch-package": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/patch-package/-/patch-package-8.0.0.tgz", @@ -12296,7 +13139,8 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/path-exists": { "version": "4.0.0", @@ -12390,10 +13234,11 @@ } }, "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" }, "node_modules/picomatch": { "version": "2.3.1", @@ -12734,6 +13579,41 @@ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", "dev": true }, + "node_modules/psl": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz", + "integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "funding": { + "url": "https://github.com/sponsors/lupomontero" + } + }, + "node_modules/publint": { + "version": "0.3.23", + "resolved": "https://registry.npmjs.org/publint/-/publint-0.3.23.tgz", + "integrity": "sha512-5MQipUPcB7MWw84zLUkHrg/H/UBtk3LL+A0GngTTBSsiNJLQurMUaSIRG3edlOrRz4UFe0AOKK9TZdIWviV+jQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@publint/pack": "^0.1.6", + "package-manager-detector": "^1.7.0", + "picocolors": "^1.1.1", + "sade": "^1.8.1" + }, + "bin": { + "publint": "src/cli.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://bjornlu.com/sponsor" + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -12743,6 +13623,13 @@ "node": ">=6" } }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "dev": true, + "license": "MIT" + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -13276,6 +14163,13 @@ "node": ">=0.10.0" } }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dev": true, + "license": "MIT" + }, "node_modules/resolve": { "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", @@ -13430,6 +14324,13 @@ "node": ">= 8" } }, + "node_modules/rrweb-cssom": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz", + "integrity": "sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==", + "dev": true, + "license": "MIT" + }, "node_modules/run-async": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", @@ -13471,6 +14372,19 @@ "tslib": "^2.1.0" } }, + "node_modules/sade": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", + "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "mri": "^1.1.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/safe-array-concat": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.1.tgz", @@ -13529,6 +14443,19 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, + "license": "ISC", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, "node_modules/scheduler": { "version": "0.23.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", @@ -13553,15 +14480,6 @@ "node": ">=10" } }, - "node_modules/seroval": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/seroval/-/seroval-0.15.1.tgz", - "integrity": "sha512-OPVtf0qmeC7RW+ScVX+7aOS+xoIM7pWcZ0jOWg2aTZigCydgRB04adfteBRbecZnnrO1WuGQ+C3tLeBBzX2zSQ==", - "dev": true, - "engines": { - "node": ">=10" - } - }, "node_modules/set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", @@ -13569,15 +14487,18 @@ "dev": true }, "node_modules/set-function-length": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", - "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", "dev": true, + "license": "MIT", "dependencies": { - "define-data-property": "^1.1.1", - "get-intrinsic": "^1.2.1", + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" + "has-property-descriptors": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -13631,14 +14552,76 @@ } }, "node_modules/side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", + "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4", + "side-channel-list": "^1.0.1", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -13901,6 +14884,19 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, + "node_modules/skin-tone": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/skin-tone/-/skin-tone-2.0.0.tgz", + "integrity": "sha512-kUMbT1oBJCpgrnKoSr0o6wPtvRWT9W9UKvGLwfJYO2WuahZRHOpEyL1ckyMGgMWh0UdpmaoFqKKD29WTomNEGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "unicode-emoji-modifier-base": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -13976,30 +14972,6 @@ "node": ">= 10" } }, - "node_modules/solid-js": { - "version": "1.8.7", - "resolved": "https://registry.npmjs.org/solid-js/-/solid-js-1.8.7.tgz", - "integrity": "sha512-9dzrSVieh2zj3SnJ02II6xZkonR6c+j/91b7XZUNcC6xSaldlqjjGh98F1fk5cRJ8ZTkzqF5fPIWDxEOs6QZXA==", - "dev": true, - "dependencies": { - "csstype": "^3.1.0", - "seroval": "^0.15.1" - } - }, - "node_modules/solid-refresh": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/solid-refresh/-/solid-refresh-0.5.3.tgz", - "integrity": "sha512-Otg5it5sjOdZbQZJnvo99TEBAr6J7PQ5AubZLNU6szZzg3RQQ5MX04oteBIIGDs0y2Qv8aXKm9e44V8z+UnFdw==", - "dev": true, - "dependencies": { - "@babel/generator": "^7.21.1", - "@babel/helper-module-imports": "^7.18.6", - "@babel/types": "^7.21.2" - }, - "peerDependencies": { - "solid-js": "^1.3" - } - }, "node_modules/sort-keys": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-2.0.0.tgz", @@ -14022,10 +14994,11 @@ } }, "node_modules/source-map-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } @@ -14113,6 +15086,20 @@ "integrity": "sha512-aFZ19IgVmhdB2uX599ve2kE6BIE3YMnQ6Gp6BURhW/oIzpXGKr878TQfAQZn1+i0Flcc/UKUy1gOlcfaUBCryg==", "dev": true }, + "node_modules/stop-iteration-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -14351,15 +15338,6 @@ "node": ">=4" } }, - "node_modules/style-to-object": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-0.3.0.tgz", - "integrity": "sha512-CzFnRRXhzWIdItT3OmF8SQfWyahHhjq3HwcMNCNLn+N7klOOqPjMeG/4JSu77D7ypZdGvSzvkrbyeTMizz2VrA==", - "dev": true, - "dependencies": { - "inline-style-parser": "0.1.1" - } - }, "node_modules/sucrase": { "version": "3.35.0", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", @@ -14464,6 +15442,23 @@ "node": ">=8" } }, + "node_modules/supports-hyperlinks": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.2.0.tgz", + "integrity": "sha512-zFObLMyZeEwzAoKCyu1B91U79K2t7ApXuQfo8OuxwXLDgcKxuwM+YvcbIhm6QWqz7mHUH1TVytR1PwVVjEuMig==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">=14.18" + }, + "funding": { + "url": "https://github.com/chalk/supports-hyperlinks?sponsor=1" + } + }, "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", @@ -14476,6 +15471,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true, + "license": "MIT" + }, "node_modules/tar": { "version": "6.1.11", "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", @@ -14589,6 +15591,16 @@ "integrity": "sha512-65NKvSuAVDP/n4CqH+a9w2kTlLReS9vhsAP06MWx+/89nMinJyB2icyl58RIcqCmIggpojIGeuJGhjU1aGMBSg==", "dev": true }, + "node_modules/tinyexec": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.0.tgz", + "integrity": "sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/tinypool": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.7.0.tgz", @@ -14619,15 +15631,6 @@ "node": ">=8.17.0" } }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -14640,6 +15643,32 @@ "node": ">=8.0" } }, + "node_modules/tough-cookie": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", + "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tough-cookie/node_modules/universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, "node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", @@ -14664,18 +15693,6 @@ "node": ">=8" } }, - "node_modules/ts-api-utils": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz", - "integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==", - "dev": true, - "engines": { - "node": ">=16.13.0" - }, - "peerDependencies": { - "typescript": ">=4.2.0" - } - }, "node_modules/ts-interface-checker": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", @@ -15627,10 +16644,11 @@ "dev": true }, "node_modules/typescript": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.2.tgz", - "integrity": "sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ==", + "version": "5.4.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.2.tgz", + "integrity": "sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==", "dev": true, + "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -15679,6 +16697,16 @@ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", "dev": true }, + "node_modules/unicode-emoji-modifier-base": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unicode-emoji-modifier-base/-/unicode-emoji-modifier-base-1.0.0.tgz", + "integrity": "sha512-yLSH4py7oFH3oG/9K+XWrz1pSi3dfUrWEnInbxMfArOfc1+33BlGPQtLsOYwvdMy11AwUBetYuaRxSPqgkq+8g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/unique-filename": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-2.0.1.tgz", @@ -15767,6 +16795,17 @@ "punycode": "^2.1.0" } }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, "node_modules/util": { "version": "0.10.4", "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", @@ -15837,12 +16876,6 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/validate-html-nesting": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/validate-html-nesting/-/validate-html-nesting-1.2.2.tgz", - "integrity": "sha512-hGdgQozCsQJMyfK5urgFcWEqsSSrK63Awe0t/IMR0bZ0QMtnuaiHzThW81guu3qx9abLi99NEuiaN6P9gVYsNg==", - "dev": true - }, "node_modules/validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", @@ -15866,10 +16899,11 @@ } }, "node_modules/validator": { - "version": "13.11.0", - "resolved": "https://registry.npmjs.org/validator/-/validator-13.11.0.tgz", - "integrity": "sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ==", + "version": "13.15.35", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.35.tgz", + "integrity": "sha512-TQ5pAGhd5whStmqWvYF4OjQROlmv9SMFVt37qoCBdqRffuuklWYQlCNnEs2ZaIBD1kZRNnikiZOS1eqgkar0iw==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.10" } @@ -15953,17 +16987,19 @@ } }, "node_modules/vite-plugin-dts": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/vite-plugin-dts/-/vite-plugin-dts-3.6.0.tgz", - "integrity": "sha512-doxhDRFJCZD2sGjIp4V800nm8Y19GvmwckjG5vYPuiqJ7OBjc9NlW1Vp9Gkyh2aXlUs1jTDRH/lxWfcsPLOQHg==", + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/vite-plugin-dts/-/vite-plugin-dts-3.9.1.tgz", + "integrity": "sha512-rVp2KM9Ue22NGWB8dNtWEr+KekN3rIgz1tWD050QnRGlriUCmaDwa7qA5zDEjbXg5lAXhYMSBJtx3q3hQIJZSg==", "dev": true, + "license": "MIT", "dependencies": { - "@microsoft/api-extractor": "^7.36.4", - "@rollup/pluginutils": "^5.0.2", - "@vue/language-core": "^1.8.8", + "@microsoft/api-extractor": "7.43.0", + "@rollup/pluginutils": "^5.1.0", + "@vue/language-core": "^1.8.27", "debug": "^4.3.4", "kolorist": "^1.8.0", - "vue-tsc": "^1.8.8" + "magic-string": "^0.30.8", + "vue-tsc": "^1.8.27" }, "engines": { "node": "^14.18.0 || >=16.0.0" @@ -16000,159 +17036,591 @@ } } }, - "node_modules/vite-plugin-solid": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/vite-plugin-solid/-/vite-plugin-solid-2.7.2.tgz", - "integrity": "sha512-GV2SMLAibOoXe76i02AsjAg7sbm/0lngBlERvJKVN67HOrJsHcWgkt0R6sfGLDJuFkv2aBe14Zm4vJcNME+7zw==", + "node_modules/vite/node_modules/@esbuild/android-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", + "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", + "cpu": [ + "arm" + ], "dev": true, - "dependencies": { - "@babel/core": "^7.23.0", - "@babel/preset-typescript": "^7.23.0", - "@types/babel__core": "^7.20.2", - "babel-preset-solid": "^1.8.0", - "merge-anything": "^5.1.7", - "solid-refresh": "^0.5.3", - "vitefu": "^0.2.4" - }, - "peerDependencies": { - "solid-js": "^1.7.2", - "vite": "^3.0.0 || ^4.0.0" + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" } }, - "node_modules/vitefu": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-0.2.5.tgz", - "integrity": "sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q==", + "node_modules/vite/node_modules/@esbuild/android-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz", + "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", + "cpu": [ + "arm64" + ], "dev": true, - "peerDependencies": { - "vite": "^3.0.0 || ^4.0.0 || ^5.0.0" - }, - "peerDependenciesMeta": { - "vite": { - "optional": true - } + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" } }, - "node_modules/vitest": { - "version": "0.34.6", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-0.34.6.tgz", - "integrity": "sha512-+5CALsOvbNKnS+ZHMXtuUC7nL8/7F1F2DnHGjSsszX8zCjWSSviphCb/NuS9Nzf4Q03KyyDRBAXhF/8lffME4Q==", + "node_modules/vite/node_modules/@esbuild/android-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz", + "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "@types/chai": "^4.3.5", - "@types/chai-subset": "^1.3.3", - "@types/node": "*", - "@vitest/expect": "0.34.6", - "@vitest/runner": "0.34.6", - "@vitest/snapshot": "0.34.6", - "@vitest/spy": "0.34.6", - "@vitest/utils": "0.34.6", - "acorn": "^8.9.0", - "acorn-walk": "^8.2.0", - "cac": "^6.7.14", - "chai": "^4.3.10", - "debug": "^4.3.4", - "local-pkg": "^0.4.3", - "magic-string": "^0.30.1", - "pathe": "^1.1.1", - "picocolors": "^1.0.0", - "std-env": "^3.3.3", - "strip-literal": "^1.0.1", - "tinybench": "^2.5.0", - "tinypool": "^0.7.0", - "vite": "^3.1.0 || ^4.0.0 || ^5.0.0-0", - "vite-node": "0.34.6", - "why-is-node-running": "^2.2.2" - }, - "bin": { - "vitest": "vitest.mjs" - }, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">=v14.18.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "@edge-runtime/vm": "*", - "@vitest/browser": "*", - "@vitest/ui": "*", - "happy-dom": "*", - "jsdom": "*", - "playwright": "*", - "safaridriver": "*", - "webdriverio": "*" - }, - "peerDependenciesMeta": { - "@edge-runtime/vm": { - "optional": true - }, - "@vitest/browser": { - "optional": true - }, - "@vitest/ui": { - "optional": true - }, - "happy-dom": { - "optional": true - }, - "jsdom": { - "optional": true - }, - "playwright": { - "optional": true - }, - "safaridriver": { - "optional": true - }, - "webdriverio": { - "optional": true - } + "node": ">=12" } }, - "node_modules/vue-template-compiler": { - "version": "2.7.15", - "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.15.tgz", - "integrity": "sha512-yQxjxMptBL7UAog00O8sANud99C6wJF+7kgbcwqkvA38vCGF7HWE66w0ZFnS/kX5gSoJr/PQ4/oS3Ne2pW37Og==", + "node_modules/vite/node_modules/@esbuild/darwin-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", + "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "de-indent": "^1.0.2", - "he": "^1.2.0" + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" } }, - "node_modules/vue-tsc": { - "version": "1.8.24", - "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-1.8.24.tgz", - "integrity": "sha512-eH1CSj231OzVEY5Hi7wS6ubzyOEwgr5jCptR0Ddf2SitGcaXIsPVDvrprm3eolCdyhDt3WS1Eb2F4fGX9BsUUw==", + "node_modules/vite/node_modules/@esbuild/darwin-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", + "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "@volar/typescript": "~1.11.1", - "@vue/language-core": "1.8.24", - "semver": "^7.5.4" - }, - "bin": { - "vue-tsc": "bin/vue-tsc.js" - }, - "peerDependencies": { - "typescript": "*" + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" } }, - "node_modules/wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz", + "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "defaults": "^1.0.3" + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" } }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "dev": true - }, - "node_modules/whatwg-url": { + "node_modules/vite/node_modules/@esbuild/freebsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz", + "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz", + "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz", + "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz", + "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-loong64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", + "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-mips64el": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz", + "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ppc64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz", + "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-riscv64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz", + "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-s390x": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz", + "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", + "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/netbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", + "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/openbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", + "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/sunos-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", + "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz", + "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz", + "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz", + "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/esbuild": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", + "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/android-arm": "0.18.20", + "@esbuild/android-arm64": "0.18.20", + "@esbuild/android-x64": "0.18.20", + "@esbuild/darwin-arm64": "0.18.20", + "@esbuild/darwin-x64": "0.18.20", + "@esbuild/freebsd-arm64": "0.18.20", + "@esbuild/freebsd-x64": "0.18.20", + "@esbuild/linux-arm": "0.18.20", + "@esbuild/linux-arm64": "0.18.20", + "@esbuild/linux-ia32": "0.18.20", + "@esbuild/linux-loong64": "0.18.20", + "@esbuild/linux-mips64el": "0.18.20", + "@esbuild/linux-ppc64": "0.18.20", + "@esbuild/linux-riscv64": "0.18.20", + "@esbuild/linux-s390x": "0.18.20", + "@esbuild/linux-x64": "0.18.20", + "@esbuild/netbsd-x64": "0.18.20", + "@esbuild/openbsd-x64": "0.18.20", + "@esbuild/sunos-x64": "0.18.20", + "@esbuild/win32-arm64": "0.18.20", + "@esbuild/win32-ia32": "0.18.20", + "@esbuild/win32-x64": "0.18.20" + } + }, + "node_modules/vitest": { + "version": "0.34.6", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-0.34.6.tgz", + "integrity": "sha512-+5CALsOvbNKnS+ZHMXtuUC7nL8/7F1F2DnHGjSsszX8zCjWSSviphCb/NuS9Nzf4Q03KyyDRBAXhF/8lffME4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/chai": "^4.3.5", + "@types/chai-subset": "^1.3.3", + "@types/node": "*", + "@vitest/expect": "0.34.6", + "@vitest/runner": "0.34.6", + "@vitest/snapshot": "0.34.6", + "@vitest/spy": "0.34.6", + "@vitest/utils": "0.34.6", + "acorn": "^8.9.0", + "acorn-walk": "^8.2.0", + "cac": "^6.7.14", + "chai": "^4.3.10", + "debug": "^4.3.4", + "local-pkg": "^0.4.3", + "magic-string": "^0.30.1", + "pathe": "^1.1.1", + "picocolors": "^1.0.0", + "std-env": "^3.3.3", + "strip-literal": "^1.0.1", + "tinybench": "^2.5.0", + "tinypool": "^0.7.0", + "vite": "^3.1.0 || ^4.0.0 || ^5.0.0-0", + "vite-node": "0.34.6", + "why-is-node-running": "^2.2.2" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": ">=v14.18.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@vitest/browser": "*", + "@vitest/ui": "*", + "happy-dom": "*", + "jsdom": "*", + "playwright": "*", + "safaridriver": "*", + "webdriverio": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + }, + "playwright": { + "optional": true + }, + "safaridriver": { + "optional": true + }, + "webdriverio": { + "optional": true + } + } + }, + "node_modules/vue-template-compiler": { + "version": "2.7.16", + "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.16.tgz", + "integrity": "sha512-AYbUWAJHLGGQM7+cNTELw+KsOG9nl2CnSv467WobS5Cv9uk3wFcnr1Etsz2sEIHEZvw1U+o9mRlEO6QbZvUPGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "de-indent": "^1.0.2", + "he": "^1.2.0" + } + }, + "node_modules/vue-tsc": { + "version": "1.8.27", + "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-1.8.27.tgz", + "integrity": "sha512-WesKCAZCRAbmmhuGl3+VrdWItEvfoFIPXOvUJkjULi+x+6G/Dy69yO3TBRJDr9eUlmsNAwVmxsNZxvHKzbkKdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/typescript": "~1.11.1", + "@vue/language-core": "1.8.27", + "semver": "^7.5.4" + }, + "bin": { + "vue-tsc": "bin/vue-tsc.js" + }, + "peerDependencies": { + "typescript": "*" + } + }, + "node_modules/w3c-xmlserializer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz", + "integrity": "sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dev": true, + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true + }, + "node_modules/whatwg-encoding": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", + "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", + "deprecated": "Use @exodus/bytes instead for a more spec-conformant and faster implementation", + "dev": true, + "license": "MIT", + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/whatwg-mimetype": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", + "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "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==", @@ -16429,6 +17897,45 @@ "node": ">=6" } }, + "node_modules/ws": { + "version": "8.21.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.3.tgz", + "integrity": "sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xml-name-validator": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", + "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true, + "license": "MIT" + }, "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", @@ -16524,6 +18031,7 @@ "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-5.0.5.tgz", "integrity": "sha512-D7eujBWkLa3p2sIpJA0d1pr7es+a7m0vFAnZLlCEKq/Ij2k0MLi9Br2UPxoxdYystm5K1yeBGzub0FlYUEWj2Q==", "dev": true, + "license": "MIT", "dependencies": { "lodash.get": "^4.4.2", "lodash.isequal": "^4.5.0", @@ -16544,6 +18052,7 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", "dev": true, + "license": "MIT", "optional": true, "engines": { "node": "^12.20.0 || >=14" @@ -16555,11 +18064,9 @@ "license": "MIT", "devDependencies": { "@vitest/coverage-v8": "^0.34.6", - "rollup-plugin-visualizer": "^5.9.2", "tsup": "^8.0.1", "typescript": "^5.2.2", "vite": "^4.5.2", - "vite-plugin-dts": "3.6.0", "vitest": "^0.34.6" } }, @@ -16572,64 +18079,20 @@ }, "devDependencies": { "@preact/preset-vite": "^2.6.0", + "@testing-library/preact": "^3.2.4", + "@vitest/coverage-v8": "^0.34.6", + "jsdom": "^22.1.0", "preact": "^10.19.1", "rollup-plugin-visualizer": "^5.10.0", "typescript": "^5.2.2", "vite": "^4.5.2", - "vite-plugin-dts": "^3.6.4" + "vite-plugin-dts": "^3.6.4", + "vitest": "^0.34.6" }, "peerDependencies": { "preact": "10.x" } }, - "packages/preact/node_modules/@rollup/pluginutils": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.0.tgz", - "integrity": "sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==", - "dev": true, - "dependencies": { - "@types/estree": "^1.0.0", - "estree-walker": "^2.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } - }, - "packages/preact/node_modules/vite-plugin-dts": { - "version": "3.6.4", - "resolved": "https://registry.npmjs.org/vite-plugin-dts/-/vite-plugin-dts-3.6.4.tgz", - "integrity": "sha512-yOVhUI/kQhtS6lCXRYYLv2UUf9bftcwQK9ROxCX2ul17poLQs02ctWX7+vXB8GPRzH8VCK3jebEFtPqqijXx6w==", - "dev": true, - "dependencies": { - "@microsoft/api-extractor": "^7.38.0", - "@rollup/pluginutils": "^5.0.5", - "@vue/language-core": "^1.8.20", - "debug": "^4.3.4", - "kolorist": "^1.8.0", - "vue-tsc": "^1.8.20" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "peerDependencies": { - "typescript": "*", - "vite": "*" - }, - "peerDependenciesMeta": { - "vite": { - "optional": true - } - } - }, "packages/react": { "name": "@tiny-intl/react", "version": "1.2.1", @@ -16638,38 +18101,23 @@ "@tiny-intl/core": "^1.2.0" }, "devDependencies": { + "@testing-library/react": "^14.3.1", "@types/react": "^18.0.28", "@vitejs/plugin-react": "^4.1.0", + "@vitest/coverage-v8": "^0.34.6", + "jsdom": "^22.1.0", "react": "^18.2.0", "react-dom": "^18.2.0", "rollup-plugin-visualizer": "^5.9.0", "typescript": "^5.2.2", "vite": "^4.5.2", - "vite-plugin-dts": "3.6.0" + "vite-plugin-dts": "^3.6.4", + "vitest": "^0.34.6" }, "peerDependencies": { "react": ">=18.0.0", "react-dom": ">=18.0.0" } - }, - "packages/solid-js": { - "name": "@tiny-intl/solid-js", - "version": "1.2.0", - "license": "MIT", - "dependencies": { - "@tiny-intl/core": "^1.2.0" - }, - "devDependencies": { - "rollup-plugin-visualizer": "^5.9.0", - "solid-js": "^1.8.3", - "typescript": "^5.2.2", - "vite": "^4.5.2", - "vite-plugin-dts": "3.6.0", - "vite-plugin-solid": "^2.6.1" - }, - "peerDependencies": { - "solid-js": ">=1.5.0" - } } } } diff --git a/package.json b/package.json index 6ab0ca7..699e6e5 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,13 @@ "main": "index.js", "scripts": { "lint": "eslint --config ./.eslintrc.cjs --ignore-path ./.eslintignore --cache", + "lint:all": "npm run lint -- .", + "build": "npm run build --workspace @tiny-intl/core && npm run build --workspace @tiny-intl/react && npm run build --workspace @tiny-intl/preact", + "size": "node ./scripts/bundle-size.mjs", + "publint": "publint packages/core && publint packages/react && publint packages/preact", + "attw": "attw --pack packages/core --ignore-rules no-resolution && attw --pack packages/react && attw --pack packages/preact", + "test": "lerna run test -- --run", + "coverage": "lerna run coverage", "release": "lerna publish --no-private", "git-hooks:commit-msg": "commitlint --edit", "git-hooks:pre-commit": "lint-staged", @@ -15,21 +22,22 @@ "author": "Phil Schaffarzyk", "license": "MIT", "devDependencies": { + "@arethetypeswrong/cli": "^0.18.5", "@commitlint/cli": "^17.4.4", "@commitlint/config-conventional": "^17.4.4", "@gridventures/eslint-config-base": "^1.3.3", "@gridventures/eslint-config-react": "^1.4.0", - "@gridventures/eslint-config-solid-js": "^1.4.1", "@gridventures/eslint-config-typescript": "^1.3.3", "@types/node": "^20.10.3", + "esbuild": "^0.28.2", "eslint": "^8.35.0", - "eslint-plugin-solid": "^0.13.0", "husky": "^8.0.3", "lerna": "^7.4.1", "lint-staged": "^15.0.1", "patch-package": "^8.0.0", "path": "^0.12.7", "prettier": "^2.8.4", + "publint": "^0.3.23", "typescript": "^5.2.2" }, "workspaces": [ diff --git a/packages/core/README.md b/packages/core/README.md index aa6a33c..c43ddf5 100644 --- a/packages/core/README.md +++ b/packages/core/README.md @@ -60,6 +60,12 @@ intl.tc('plusXDocumentsSelected', 3, { title: 'My-Doc' }); // My-Doc and 3 other intl.tc('plusXDocumentsSelected', 1, { title: 'My-Doc' }); // My-Doc and one other document selected ``` +> [!NOTE] +> `zero` is honoured whenever `count` is `0` and the key defines a `zero` entry, +> even in locales whose CLDR plural rules have no `zero` category (English and +> German among them, where `0` otherwise selects `other`). Without a `zero` +> entry, `0` falls back to the locale's own category. + ### Formatting dates and date-times Look at [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat) for more options. diff --git a/packages/core/package.json b/packages/core/package.json index 5074875..e774711 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -3,7 +3,7 @@ "version": "1.2.0", "repository": { "type": "git", - "url": "https://github.com/gridventures/tiny-intl", + "url": "git+https://github.com/ps73/tiny-intl.git", "directory": "packages/core" }, "author": "Phil Schaffarzyk ", @@ -16,33 +16,44 @@ "main": "./lib/cjs/index.js", "module": "./lib/esm/index.js", "types": "./lib/esm/index.d.ts", + "sideEffects": false, "exports": { ".": { - "import": "./lib/esm/index.js", - "require": "./lib/cjs/index.js", - "types": "./lib/esm/types/index.d.ts" + "import": { + "types": "./lib/esm/index.d.ts", + "default": "./lib/esm/index.js" + }, + "require": { + "types": "./lib/cjs/index.d.cts", + "default": "./lib/cjs/index.js" + } }, "./utils": { - "import": "./lib/esm/utils/index.js", - "require": "./lib/cjs/utils/index.js", - "types": "./lib/esm/utils/index.d.ts" - } + "import": { + "types": "./lib/esm/utils/index.d.ts", + "default": "./lib/esm/utils/index.js" + }, + "require": { + "types": "./lib/cjs/utils/index.d.cts", + "default": "./lib/cjs/utils/index.js" + } + }, + "./package.json": "./package.json" }, "scripts": { "dev": "vite", - "build": "vite build && npm run build2", - "build2": "FORMAT=esm tsup && FORMAT=cjs tsup", + "build": "npm run build:esm && npm run build:cjs", + "build:esm": "FORMAT=esm tsup", + "build:cjs": "FORMAT=cjs tsup", "preview": "vite preview", "test": "vitest", "coverage": "vitest run --coverage" }, "devDependencies": { "@vitest/coverage-v8": "^0.34.6", - "rollup-plugin-visualizer": "^5.9.2", "tsup": "^8.0.1", "typescript": "^5.2.2", "vite": "^4.5.2", - "vite-plugin-dts": "3.6.0", "vitest": "^0.34.6" } } diff --git a/packages/core/src/createTinyIntl.ts b/packages/core/src/createTinyIntl.ts index a6314d7..acaf8ac 100644 --- a/packages/core/src/createTinyIntl.ts +++ b/packages/core/src/createTinyIntl.ts @@ -45,7 +45,11 @@ export type CreateTinyIntlOptions = { export type TinyIntl = { locale: Locales; getLocale: () => Locales; - change: (locale: Locales, staticDict?: TinyIntlDict) => Promise; + change: ( + locale: Locales, + staticDict?: TinyIntlDict, + forceLoad?: boolean, + ) => Promise; t: (key: string, templateParams?: TinyIntlTranslateTemplate) => string; tc: (key: string, count: number, templateParams?: TinyIntlTranslateTemplate) => string; n: (number: number, options?: Intl.NumberFormatOptions) => string; @@ -72,7 +76,8 @@ export function createTinyIntl( const { fallbackLocale, loadDict, fallbackDict } = i18nOptions; const templateRegex = i18nOptions.templateRegex || /{{(.*?)}}/g; - let mounted = false; + let mountPromise: Promise | undefined; + let generation = 0; let locale: Locales = fallbackLocale; let pluralRules: Intl.PluralRules = new Intl.PluralRules(fallbackLocale); const numberFormatCache = new Map(); @@ -84,21 +89,25 @@ export function createTinyIntl( const subscriptions = new Set>(); async function change(nextLocale: Locales, staticDict?: TinyIntlDict, forceLoad = false) { - if (locale === nextLocale && !forceLoad) { + if (locale === nextLocale && !forceLoad && !staticDict) { return dict; } + generation += 1; + const gen = generation; locale = nextLocale; pluralRules = new Intl.PluralRules(locale); numberFormatCache.clear(); dateTimeFormatCache.clear(); relativeTimeFormatCache.clear(); - numberFormatCache.clear(); listFormatCache.clear(); collatorCache.clear(); if (staticDict) { dict = flattie(staticDict); } else if (loadDict) { - const nextDict = await loadDict(locale); + const nextDict = await loadDict(nextLocale); + if (gen !== generation) { + return dict; + } dict = flattie(nextDict); } subscriptions.forEach((cb) => cb(locale)); @@ -108,18 +117,18 @@ export function createTinyIntl( function template(str: string, templateParams: TinyIntlTranslateTemplate) { return str.replace( templateRegex, - (_, key: string) => templateParams[key]?.toString() || `[${key}]`, + (_, key: string) => templateParams[key]?.toString() ?? `[${key}]`, ); } function t(key: string, templateParams?: TinyIntlTranslateTemplate): string { - const value = dict[key] || dict[`${key}.one`] || `[${key}]`; + const value = dict[key] ?? dict[`${key}.one`] ?? `[${key}]`; return template(value, templateParams || {}); } function tc(key: string, count: number, templateParams?: TinyIntlTranslateTemplate): string { - const pluralKey = pluralRules.select(count); - const tKey = `${key}.${pluralKey}`; + const zeroKey = `${key}.zero`; + const tKey = count === 0 && zeroKey in dict ? zeroKey : `${key}.${pluralRules.select(count)}`; return t(tKey, { count, ...templateParams, @@ -128,7 +137,7 @@ export function createTinyIntl( function n(number: number, options?: Intl.NumberFormatOptions): string { const cacheKey = newCacheKey(options); - let formatter = numberFormatCache.get(locale); + let formatter = numberFormatCache.get(cacheKey); if (!formatter) { formatter = new Intl.NumberFormat(locale, options); numberFormatCache.set(cacheKey, formatter); @@ -139,7 +148,7 @@ export function createTinyIntl( function dt(date: Date | string | number, options?: Intl.DateTimeFormatOptions): string { const dateValue = new Date(date); const cacheKey = newCacheKey(options); - let formatter = dateTimeFormatCache.get(locale); + let formatter = dateTimeFormatCache.get(cacheKey); if (!formatter) { formatter = new Intl.DateTimeFormat(locale, options); dateTimeFormatCache.set(cacheKey, formatter); @@ -159,7 +168,7 @@ export function createTinyIntl( return ''; } const cacheKey = newCacheKey(rtOptions); - let formatter = relativeTimeFormatCache.get(locale); + let formatter = relativeTimeFormatCache.get(cacheKey); if (!formatter) { formatter = new Intl.RelativeTimeFormat(locale, rtOptions); relativeTimeFormatCache.set(cacheKey, formatter); @@ -172,8 +181,8 @@ export function createTinyIntl( console.warn('Intl.Collator is not supported in this browser'); return (x: string, y: string) => x.localeCompare(y); } - const cacheKey = newCacheKey(options || {}); - let formatter = collatorCache.get(locale); + const cacheKey = newCacheKey(options); + let formatter = collatorCache.get(cacheKey); if (!formatter) { formatter = new Intl.Collator(locale, options); collatorCache.set(cacheKey, formatter); @@ -197,7 +206,7 @@ export function createTinyIntl( } const intlOptions = typeof options === 'string' ? ({ type, style: 'long' } as const) : options; const cacheKey = newCacheKey(intlOptions); - let formatter = listFormatCache.get(locale); + let formatter = listFormatCache.get(cacheKey); if (!formatter) { formatter = new Intl.ListFormat(locale, intlOptions); listFormatCache.set(cacheKey, formatter); @@ -224,9 +233,16 @@ export function createTinyIntl( } async function mount() { - if (mounted) return; - mounted = true; - await change(detectDefaultLocale(), undefined, true); + if (!mountPromise) { + mountPromise = change(detectDefaultLocale(), undefined, true).then( + () => undefined, + (err) => { + mountPromise = undefined; + throw err; + }, + ); + } + return mountPromise; } return { diff --git a/packages/core/tests/index.test.ts b/packages/core/tests/index.test.ts index f9dad1d..1796d1f 100644 --- a/packages/core/tests/index.test.ts +++ b/packages/core/tests/index.test.ts @@ -20,6 +20,15 @@ const loadDict = (locale: string): TinyIntlDict => { one: '{{title}} and {{count}} more document selected', other: '{{title}} and {{count}} more documents selected', }, + trash: { + zero: 'Trash is empty', + one: '1 item in trash', + other: '{{count}} items in trash', + }, + folder: { + one: '1 folder', + other: '{{count}} folders', + }, }; } if (locale === 'de-DE') { @@ -36,6 +45,15 @@ const loadDict = (locale: string): TinyIntlDict => { one: '{{title}} und ein weiteres Dokument ausgewählt', other: '{{title}} und {{count}} weitere Dokumente ausgewählt', }, + trash: { + zero: 'Papierkorb ist leer', + one: '1 Element im Papierkorb', + other: '{{count}} Elemente im Papierkorb', + }, + folder: { + one: '1 Ordner', + other: '{{count}} Ordner', + }, }; } return {}; @@ -109,6 +127,22 @@ describe('@tiny-intl/core', () => { ); }); + it('uses the zero entry when count is 0 and one is defined', async ({ expect }) => { + await intl.change('en-US'); + expect(intl.tc('trash', 0)).toBe('Trash is empty'); + expect(intl.tc('trash', 1)).toBe('1 item in trash'); + expect(intl.tc('trash', 5)).toBe('5 items in trash'); + + await intl.change('de-DE'); + expect(intl.tc('trash', 0)).toBe('Papierkorb ist leer'); + expect(intl.tc('trash', 5)).toBe('5 Elemente im Papierkorb'); + }); + + it('falls back to the CLDR category when no zero entry is defined', async ({ expect }) => { + await intl.change('de-DE'); + expect(intl.tc('folder', 0)).toBe('0 Ordner'); + }); + it('date formatting', async ({ expect }) => { await intl.change('de-DE'); expect(intl.dt('2021-01-01', { dateStyle: 'full' })).toBe('Freitag, 1. Januar 2021'); @@ -173,6 +207,142 @@ describe('@tiny-intl/core', () => { expect(intl.list(['a', 'b', 'c'], { type: 'disjunction' })).toBe('a, b, or c'); }); + // Forces a genuine locale change (twice, landing back on en-US) so every + // formatter cache is guaranteed empty before we start counting constructor + // calls. A plain `await intl.change('en-US')` is not reliable here because + // if the shared `intl` instance already sits on 'en-US' (the common case, + // since afterEach resets to it) that call hits the `change()` early return + // and clears nothing, leaving caches polluted by whichever earlier test + // ran last. + async function resetFormatterCaches() { + await intl.change('de-DE'); + await intl.change('en-US'); + } + + // `vi.spyOn(Intl, 'RelativeTimeFormat' | 'ListFormat')` blows up with + // "Constructor ... requires 'new'" in this environment (unlike NumberFormat, + // DateTimeFormat and Collator, which spy fine) — vitest's spy wrapper loses + // the internal slot these two constructors check for. As a fallback, swap + // the constructor for a subclass that counts `super()` calls, then restore + // it — the same direct-reassignment technique already used below in + // 'fallbacks if newer Intl features are not supported'. + function countingConstructor unknown>(original: T) { + const state = { calls: 0 }; + class Counting extends (original as new (...args: never[]) => unknown) { + constructor(...args: never[]) { + super(...args); + state.calls += 1; + } + } + return { ctor: Counting as unknown as T, state }; + } + + it('reuses cached Intl.NumberFormat instances', async ({ expect }) => { + await resetFormatterCaches(); + + const numberFormatSpy = vi.spyOn(Intl, 'NumberFormat'); + intl.n(1); + intl.n(2); + intl.n(3); + expect(numberFormatSpy).toHaveBeenCalledTimes(1); + + intl.n(4, { style: 'percent' }); + intl.n(5, { style: 'percent' }); + expect(numberFormatSpy).toHaveBeenCalledTimes(2); + numberFormatSpy.mockRestore(); + }); + + it('reuses cached Intl.DateTimeFormat instances', async ({ expect }) => { + await resetFormatterCaches(); + + const dateTimeFormatSpy = vi.spyOn(Intl, 'DateTimeFormat'); + intl.dt('2021-01-01'); + intl.dt('2021-06-15'); + intl.dt('2022-12-31'); + expect(dateTimeFormatSpy).toHaveBeenCalledTimes(1); + + intl.dt('2021-01-01', { dateStyle: 'full' }); + intl.dt('2021-06-15', { dateStyle: 'full' }); + expect(dateTimeFormatSpy).toHaveBeenCalledTimes(2); + dateTimeFormatSpy.mockRestore(); + }); + + it('reuses cached Intl.RelativeTimeFormat instances', async ({ expect }) => { + await resetFormatterCaches(); + + const oldDateNow = Date.now; + Date.now = () => new Date('2020-01-01').getTime(); + + /* eslint-disable @typescript-eslint/ban-ts-comment */ + const originalRelativeTimeFormat = Intl.RelativeTimeFormat; + const { ctor, state } = countingConstructor(originalRelativeTimeFormat); + // @ts-ignore + Intl.RelativeTimeFormat = ctor; + + intl.rt('2021-01-01'); + intl.rt('2021-02-01'); + intl.rt('2021-03-01'); + expect(state.calls).toBe(1); + + intl.rt('2021-01-01', { style: 'long' }); + intl.rt('2021-02-01', { style: 'long' }); + expect(state.calls).toBe(2); + + // @ts-ignore + Intl.RelativeTimeFormat = originalRelativeTimeFormat; + /* eslint-enable @typescript-eslint/ban-ts-comment */ + Date.now = oldDateNow; + }); + + it('reuses cached Intl.ListFormat instances', async ({ expect }) => { + await resetFormatterCaches(); + + /* eslint-disable @typescript-eslint/ban-ts-comment */ + const originalListFormat = Intl.ListFormat; + const { ctor, state } = countingConstructor(originalListFormat); + // @ts-ignore + Intl.ListFormat = ctor; + + intl.list(['a', 'b', 'c']); + intl.list(['d', 'e']); + intl.list(['f']); + expect(state.calls).toBe(1); + + intl.list(['a', 'b', 'c'], { type: 'disjunction' }); + intl.list(['d', 'e'], { type: 'disjunction' }); + expect(state.calls).toBe(2); + + // @ts-ignore + Intl.ListFormat = originalListFormat; + /* eslint-enable @typescript-eslint/ban-ts-comment */ + }); + + it('reuses cached Intl.Collator instances', async ({ expect }) => { + await resetFormatterCaches(); + + const collatorSpy = vi.spyOn(Intl, 'Collator'); + intl.collator(); + intl.collator(); + intl.collator(); + expect(collatorSpy).toHaveBeenCalledTimes(1); + + intl.collator({ caseFirst: 'upper' }); + intl.collator({ caseFirst: 'upper' }); + expect(collatorSpy).toHaveBeenCalledTimes(2); + collatorSpy.mockRestore(); + }); + + it('rebuilds Intl formatters after a locale change', async ({ expect }) => { + await resetFormatterCaches(); + + const spy = vi.spyOn(Intl, 'NumberFormat'); + intl.n(1000); + await intl.change('de-DE'); + intl.n(1000); + expect(spy).toHaveBeenCalledTimes(2); + spy.mockRestore(); + }); + it('fallbacks if newer Intl features are not supported', async ({ expect }) => { await intl.change('de-DE'); /* eslint-disable @typescript-eslint/ban-ts-comment */ @@ -321,4 +491,141 @@ describe('@tiny-intl/core', () => { expect(unit).toBe(u); }); }); + + it('applies a staticDict for the current locale without a real change', async ({ expect }) => { + await intl.change('de-DE'); + await intl.change('de-DE', { foo: 'bar' }); + expect(intl.t('foo')).toBe('bar'); + }); + + it('discards a superseded change when loads resolve out of order', async ({ expect }) => { + const resolvers: Record void> = {}; + const racy = createTinyIntl<'en-US' | 'de-DE'>({ + fallbackLocale: 'en-US', + supportedLocales: ['en-US', 'de-DE'], + loadDict: (loc) => + new Promise((resolve) => { + resolvers[loc] = resolve; + }), + }); + + const first = racy.change('de-DE'); + const second = racy.change('en-US'); + + // resolve the *newer* request first, then the stale one + resolvers['en-US']({ greeting: 'Hello' }); + resolvers['de-DE']({ greeting: 'Hallo' }); + await Promise.all([first, second]); + + expect(racy.getLocale()).toBe('en-US'); + expect(racy.t('greeting')).toBe('Hello'); // NOT 'Hallo' + }); + + it('notifies subscribers only for the winning change, not the superseded one', async ({ + expect, + }) => { + const resolvers: Record void> = {}; + const racy = createTinyIntl<'en-US' | 'de-DE'>({ + fallbackLocale: 'en-US', + supportedLocales: ['en-US', 'de-DE'], + loadDict: (loc) => + new Promise((resolve) => { + resolvers[loc] = resolve; + }), + }); + + const seenLocales: string[] = []; + racy.subscribe((nextLocale) => { + seenLocales.push(nextLocale); + }); + + const first = racy.change('de-DE'); + const second = racy.change('en-US'); + + resolvers['en-US']({ greeting: 'Hello' }); + resolvers['de-DE']({ greeting: 'Hallo' }); + await Promise.all([first, second]); + + expect(seenLocales).toEqual(['en-US']); + }); + + it('mount() is re-entrant and shares one load between concurrent callers', async ({ expect }) => { + let calls = 0; + let resolveLoad: (d: TinyIntlDict) => void = () => {}; + const intl3 = createTinyIntl<'de-DE'>({ + fallbackLocale: 'de-DE', + supportedLocales: ['de-DE'], + loadDict: () => { + calls += 1; + return new Promise((resolve) => { + resolveLoad = resolve; + }); + }, + }); + + const mount1 = intl3.mount(); + const mount2 = intl3.mount(); + + let mount2Resolved = false; + mount2.then( + () => { + mount2Resolved = true; + }, + () => {}, + ); + + // Flush a few microtask ticks without resolving the load yet. A + // re-entrant mount2 must NOT resolve here — only a buggy mount() that + // returns an already-"mounted" promise while the first load is still in + // flight would resolve this early. + await Promise.resolve(); + await Promise.resolve(); + await Promise.resolve(); + + expect(mount2Resolved).toBe(false); + expect(calls).toBe(1); + + resolveLoad({ inbox: 'Posteingang' }); + await Promise.all([mount1, mount2]); + + expect(mount2Resolved).toBe(true); + expect(calls).toBe(1); + expect(intl3.t('inbox')).toBe('Posteingang'); + }); + + it('treats an empty string as a present translation', async ({ expect }) => { + const empty = createTinyIntl<'en-US'>({ + fallbackLocale: 'en-US', + supportedLocales: ['en-US'], + loadDict: () => ({ + blank: '', + trash: { zero: '', one: '1 item', other: '{{count}} items' }, + greet: 'Hi{{suffix}}', + }), + }); + await empty.mount(); + expect(empty.t('blank')).toBe(''); + expect(empty.tc('trash', 0)).toBe(''); + expect(empty.tc('trash', 2)).toBe('2 items'); + expect(empty.t('greet', { suffix: '' })).toBe('Hi'); + expect(empty.t('missing')).toBe('[missing]'); // genuinely absent keys unchanged + }); + + it('allows mount() to be retried after a failed load', async ({ expect }) => { + let attempt = 0; + const flaky = createTinyIntl<'en-US'>({ + fallbackLocale: 'en-US', + supportedLocales: ['en-US'], + loadDict: () => { + attempt += 1; + if (attempt === 1) return Promise.reject(new Error('network')); + return Promise.resolve({ hello: 'Hello' }); + }, + }); + + await expect(flaky.mount()).rejects.toThrow('network'); + await flaky.mount(); + expect(flaky.t('hello')).toBe('Hello'); + expect(attempt).toBe(2); + }); }); diff --git a/packages/core/tsup.config.ts b/packages/core/tsup.config.ts index ba1f068..ec37612 100644 --- a/packages/core/tsup.config.ts +++ b/packages/core/tsup.config.ts @@ -20,7 +20,7 @@ const addPackageJson = () => { }; export default defineConfig({ - entry: ['src'], + entry: ['src/**/*.ts', '!src/**/*.d.ts'], target: 'es2022', format, minifyIdentifiers: false, diff --git a/packages/core/vite.config.ts b/packages/core/vite.config.ts index 6b264b8..ef23c32 100644 --- a/packages/core/vite.config.ts +++ b/packages/core/vite.config.ts @@ -1,38 +1,5 @@ -import { resolve } from 'path'; - -import { visualizer } from 'rollup-plugin-visualizer'; -import dts from 'vite-plugin-dts'; import { defineConfig } from 'vitest/config'; // eslint-disable-line import/no-unresolved export default defineConfig({ - plugins: [ - dts({ - outDir: resolve(__dirname, 'dist', 'types'), - insertTypesEntry: true, - }), - visualizer({ - filename: 'dist/stats.html', - gzipSize: true, - brotliSize: true, - }), - ], - build: { - outDir: resolve(__dirname, 'dist'), - minify: false, - lib: { - // Could also be a dictionary or array of multiple entry points - entry: resolve(__dirname, 'src', 'index.ts'), - name: 'tinyIntl', - // the proper extensions will be added - fileName: 'index', - formats: ['es', 'cjs', 'umd'], - }, - rollupOptions: { - external: [], - output: { - globals: {}, - }, - }, - }, test: {}, }); diff --git a/packages/preact/package.json b/packages/preact/package.json index 2f5e1c5..17207c1 100644 --- a/packages/preact/package.json +++ b/packages/preact/package.json @@ -3,7 +3,7 @@ "version": "1.2.1", "repository": { "type": "git", - "url": "https://github.com/gridventures/tiny-intl", + "url": "git+https://github.com/ps73/tiny-intl.git", "directory": "packages/preact" }, "author": "Phil Schaffarzyk ", @@ -12,31 +12,44 @@ "files": [ "lib" ], - "main": "./lib/index.umd.cjs", + "main": "./lib/index.cjs", "module": "./lib/index.js", "types": "./lib/types/index.d.ts", + "sideEffects": false, "exports": { ".": { - "import": "./lib/index.js", - "require": "./lib/index.umd.cjs", - "types": "./lib/types/index.d.ts" - } + "import": { + "types": "./lib/types/index.d.ts", + "default": "./lib/index.js" + }, + "require": { + "types": "./lib/types/index.d.cts", + "default": "./lib/index.cjs" + } + }, + "./package.json": "./package.json" }, "scripts": { "dev": "vite", - "build": "tsc && vite build", - "preview": "vite preview" + "build": "tsc && vite build && node -e \"const f=require('fs');f.copyFileSync('lib/types/index.d.ts','lib/types/index.d.cts')\"", + "preview": "vite preview", + "test": "vitest", + "coverage": "vitest run --coverage" }, "peerDependencies": { "preact": "10.x" }, "devDependencies": { "@preact/preset-vite": "^2.6.0", + "@testing-library/preact": "^3.2.4", + "@vitest/coverage-v8": "^0.34.6", + "jsdom": "^22.1.0", "preact": "^10.19.1", "rollup-plugin-visualizer": "^5.10.0", "typescript": "^5.2.2", "vite": "^4.5.2", - "vite-plugin-dts": "^3.6.4" + "vite-plugin-dts": "^3.6.4", + "vitest": "^0.34.6" }, "dependencies": { "@tiny-intl/core": "^1.2.0" diff --git a/packages/preact/src/Translate.tsx b/packages/preact/src/Translate.tsx index edc58b9..bf3766b 100644 --- a/packages/preact/src/Translate.tsx +++ b/packages/preact/src/Translate.tsx @@ -63,19 +63,19 @@ export function Translate(props: TranslateProps) { const [changed, setChanged] = useState(0); const translateFn = useCallback(() => { - if (count) { + if (count != null) { return tc(name, count, options); } - if (date && !relative) { + if (date != null && !relative) { return dt(date, options); } - if (date && relative) { + if (date != null && relative) { return rt(date, options); } - if (number) { + if (number != null) { return n(number, options); } @@ -84,7 +84,7 @@ export function Translate(props: TranslateProps) { } return null; - }, [changed, count, date, name, number, options]); + }, [changed, count, date, name, number, options, relative]); useEffect(() => { const dispose = subscribe(() => { diff --git a/packages/preact/src/index.ts b/packages/preact/src/index.ts index a63b186..daaa3ac 100644 --- a/packages/preact/src/index.ts +++ b/packages/preact/src/index.ts @@ -1,2 +1,2 @@ -export * from './Translate'; -export * from './useIntl'; +export * from './Translate.js'; +export * from './useIntl.js'; diff --git a/packages/preact/src/useIntl.tsx b/packages/preact/src/useIntl.tsx index 43e9afa..df0f982 100644 --- a/packages/preact/src/useIntl.tsx +++ b/packages/preact/src/useIntl.tsx @@ -5,7 +5,7 @@ import { createContext, useCallback, useContext, useEffect, useState } from 'pre export const TinyIntlContext = createContext | undefined>(undefined); -export function useIntl() { +export function useIntl(): TinyIntl { const intl = useContext(TinyIntlContext); if (!intl) { diff --git a/packages/preact/tests/Translate.test.tsx b/packages/preact/tests/Translate.test.tsx new file mode 100644 index 0000000..eca5657 --- /dev/null +++ b/packages/preact/tests/Translate.test.tsx @@ -0,0 +1,165 @@ +import type { TinyIntl, TinyIntlDict } from '@tiny-intl/core'; + +import { cleanup, render, screen } from '@testing-library/preact'; +import { createTinyIntl, detectLocale } from '@tiny-intl/core'; +import { afterEach, describe, it } from 'vitest'; + +import { Translate } from '../src/Translate'; +import { TinyIntlContext } from '../src/useIntl'; + +const loadDict = (locale: string): TinyIntlDict => { + if (locale === 'en-US' || locale === 'sv-SE') { + return { + hello: 'Hello, {{name}}!', + inbox: 'Inbox', + document: { + zero: 'Documents', + one: 'Document', + other: 'Documents', + }, + plusXDocumentsSelected: { + zero: '{{title}} and {{count}} more documents selected', + one: '{{title}} and {{count}} more document selected', + other: '{{title}} and {{count}} more documents selected', + }, + }; + } + if (locale === 'de-DE') { + return { + hello: 'Hallo, {{name}}!', + inbox: 'Posteingang', + document: { + zero: 'Dokumente', + one: 'Dokument', + other: 'Dokumente', + }, + plusXDocumentsSelected: { + zero: '{{title}} ausgewählt', + one: '{{title}} und ein weiteres Dokument ausgewählt', + other: '{{title}} und {{count}} weitere Dokumente ausgewählt', + }, + }; + } + return {}; +}; + +async function createIntl() { + const intl = createTinyIntl<'en-US' | 'de-DE' | 'sv-SE'>({ + loadDict: (locale) => loadDict(locale), + fallbackLocale: 'en-US', + supportedLocales: ['en-US', 'de-DE', 'sv-SE'], + detectLocale: (params) => detectLocale('en-US', params), + }); + await intl.mount(); + return intl; +} + +describe('@tiny-intl/preact', () => { + let intl: TinyIntl<'en-US' | 'sv-SE' | 'de-DE'>; + + afterEach(cleanup); + + it('renders a plain string translation', async ({ expect }) => { + intl = await createIntl(); + render( + + + , + ); + expect(screen.getByText('Inbox')).toBeTruthy(); + }); + + it('renders the plural form when count is set', async ({ expect }) => { + intl = await createIntl(); + render( + + + , + ); + expect(screen.getByText('Documents')).toBeTruthy(); + }); + + it('renders a formatted number', async ({ expect }) => { + intl = await createIntl(); + render( + + + , + ); + expect(screen.getByText('1,000')).toBeTruthy(); + }); + + it('renders a formatted date', async ({ expect }) => { + intl = await createIntl(); + render( + + + , + ); + expect(screen.getByText('Friday, January 1, 2021')).toBeTruthy(); + }); + + it('passes the value to a function-as-children', async ({ expect }) => { + intl = await createIntl(); + render( + + {(value) => {value}} + , + ); + const el = screen.getByText('Inbox'); + expect(el.tagName).toBe('STRONG'); + }); + + it('renders the zero plural form for count={0}', async ({ expect }) => { + intl = await createIntl(); + await intl.change('de-DE'); + render( + + + , + ); + expect(screen.getByText('Dokumente')).toBeTruthy(); + }); + + it('renders the singular plural form for count={1}', async ({ expect }) => { + intl = await createIntl(); + await intl.change('de-DE'); + render( + + + , + ); + expect(screen.getByText('Dokument')).toBeTruthy(); + }); + + it('renders the plural form for count={5}', async ({ expect }) => { + intl = await createIntl(); + await intl.change('de-DE'); + render( + + + , + ); + expect(screen.getByText('Dokumente')).toBeTruthy(); + }); + + it('renders a formatted zero for number={0}', async ({ expect }) => { + intl = await createIntl(); + render( + + + , + ); + expect(screen.getByText('0')).toBeTruthy(); + }); + + it('renders a formatted date for the epoch date={0}', async ({ expect }) => { + intl = await createIntl(); + render( + + + , + ); + expect(screen.getByText('Thursday, January 1, 1970')).toBeTruthy(); + }); +}); diff --git a/packages/preact/tests/useIntl.test.tsx b/packages/preact/tests/useIntl.test.tsx new file mode 100644 index 0000000..ca1716e --- /dev/null +++ b/packages/preact/tests/useIntl.test.tsx @@ -0,0 +1,115 @@ +import type { TinyIntl, TinyIntlDict } from '@tiny-intl/core'; + +import { act, cleanup, render, renderHook, screen } from '@testing-library/preact'; +import { createTinyIntl, detectLocale } from '@tiny-intl/core'; +import { afterEach, describe, it } from 'vitest'; + +import { TinyIntlContext, useIntl } from '../src/useIntl'; + +const loadDict = (locale: string): TinyIntlDict => { + if (locale === 'en-US' || locale === 'sv-SE') { + return { + hello: 'Hello, {{name}}!', + inbox: 'Inbox', + document: { + zero: 'Documents', + one: 'Document', + other: 'Documents', + }, + plusXDocumentsSelected: { + zero: '{{title}} and {{count}} more documents selected', + one: '{{title}} and {{count}} more document selected', + other: '{{title}} and {{count}} more documents selected', + }, + }; + } + if (locale === 'de-DE') { + return { + hello: 'Hallo, {{name}}!', + inbox: 'Posteingang', + document: { + zero: 'Dokumente', + one: 'Dokument', + other: 'Dokumente', + }, + plusXDocumentsSelected: { + zero: '{{title}} ausgewählt', + one: '{{title}} und ein weiteres Dokument ausgewählt', + other: '{{title}} und {{count}} weitere Dokumente ausgewählt', + }, + }; + } + return {}; +}; + +async function createIntl() { + const intl = createTinyIntl<'en-US' | 'de-DE' | 'sv-SE'>({ + loadDict: (locale) => loadDict(locale), + fallbackLocale: 'en-US', + supportedLocales: ['en-US', 'de-DE', 'sv-SE'], + detectLocale: (params) => detectLocale('en-US', params), + }); + await intl.mount(); + return intl; +} + +describe('@tiny-intl/preact', () => { + let intl: TinyIntl<'en-US' | 'sv-SE' | 'de-DE'>; + + afterEach(cleanup); + + it('throws outside a provider', ({ expect }) => { + expect(() => renderHook(() => useIntl())).toThrow( + 'useIntl must be used within a TinyIntlContext.Provider', + ); + }); + + it('t() returns the translated string for the mounted locale', async ({ expect }) => { + intl = await createIntl(); + const { result } = renderHook(() => useIntl(), { + wrapper: ({ children }) => ( + {children} + ), + }); + expect(result.current.t('inbox')).toBe('Inbox'); + }); + + it('re-renders with the German string after intl.change', async ({ expect }) => { + intl = await createIntl(); + + function Inbox() { + const { t } = useIntl(); + return {t('inbox')}; + } + + render( + + + , + ); + + expect(screen.getByText('Inbox')).toBeTruthy(); + + await act(async () => { + await intl.change('de-DE'); + }); + + expect(screen.getByText('Posteingang')).toBeTruthy(); + }); + + it('n() and dt() format per locale', async ({ expect }) => { + intl = await createIntl(); + await act(async () => { + await intl.change('de-DE'); + }); + + const { result } = renderHook(() => useIntl(), { + wrapper: ({ children }) => ( + {children} + ), + }); + + expect(result.current.n(1000)).toBe('1.000'); + expect(result.current.dt('2021-01-01', { dateStyle: 'full' })).toBe('Freitag, 1. Januar 2021'); + }); +}); diff --git a/packages/preact/vite.config.ts b/packages/preact/vite.config.ts index ccf9064..0d2f4b4 100644 --- a/packages/preact/vite.config.ts +++ b/packages/preact/vite.config.ts @@ -2,8 +2,8 @@ import { resolve } from 'path'; import preact from '@preact/preset-vite'; import { visualizer } from 'rollup-plugin-visualizer'; -import { defineConfig } from 'vite'; import dts from 'vite-plugin-dts'; +import { defineConfig } from 'vitest/config'; // eslint-disable-line import/no-unresolved // https://vitejs.dev/config/ export default defineConfig({ @@ -27,7 +27,7 @@ export default defineConfig({ name: 'tinyIntl', // the proper extensions will be added fileName: 'index', - formats: ['es', 'cjs', 'umd'], + formats: ['es', 'cjs'], }, rollupOptions: { external: ['preact', 'preact/compat'], @@ -39,4 +39,8 @@ export default defineConfig({ }, }, }, + test: { + environment: 'jsdom', + globals: false, + }, }); diff --git a/packages/react/package.json b/packages/react/package.json index 8dc662e..e89c60c 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -3,7 +3,7 @@ "version": "1.2.1", "repository": { "type": "git", - "url": "https://github.com/gridventures/tiny-intl", + "url": "git+https://github.com/ps73/tiny-intl.git", "directory": "packages/react" }, "author": "Phil Schaffarzyk ", @@ -12,20 +12,29 @@ "files": [ "lib" ], - "main": "./lib/index.umd.cjs", + "main": "./lib/index.cjs", "module": "./lib/index.js", "types": "./lib/types/index.d.ts", + "sideEffects": false, "exports": { ".": { - "import": "./lib/index.js", - "require": "./lib/index.umd.cjs", - "types": "./lib/types/index.d.ts" - } + "import": { + "types": "./lib/types/index.d.ts", + "default": "./lib/index.js" + }, + "require": { + "types": "./lib/types/index.d.cts", + "default": "./lib/index.cjs" + } + }, + "./package.json": "./package.json" }, "scripts": { "dev": "vite", - "build": "tsc && vite build", - "preview": "vite preview" + "build": "tsc && vite build && node -e \"const f=require('fs');f.copyFileSync('lib/types/index.d.ts','lib/types/index.d.cts')\"", + "preview": "vite preview", + "test": "vitest", + "coverage": "vitest run --coverage" }, "peerDependencies": { "react": ">=18.0.0", @@ -35,13 +44,17 @@ "@tiny-intl/core": "^1.2.0" }, "devDependencies": { + "@testing-library/react": "^14.3.1", "@types/react": "^18.0.28", "@vitejs/plugin-react": "^4.1.0", + "@vitest/coverage-v8": "^0.34.6", + "jsdom": "^22.1.0", "react": "^18.2.0", "react-dom": "^18.2.0", "rollup-plugin-visualizer": "^5.9.0", "typescript": "^5.2.2", "vite": "^4.5.2", - "vite-plugin-dts": "3.6.0" + "vite-plugin-dts": "^3.6.4", + "vitest": "^0.34.6" } } diff --git a/packages/react/src/Translate.tsx b/packages/react/src/Translate.tsx index 2312d08..65c60e5 100644 --- a/packages/react/src/Translate.tsx +++ b/packages/react/src/Translate.tsx @@ -63,19 +63,19 @@ export function Translate(props: TranslateProps) { const [changed, setChanged] = useState(0); const translateFn = useCallback(() => { - if (count) { + if (count != null) { return tc(name, count, options); } - if (date && !relative) { + if (date != null && !relative) { return dt(date, options); } - if (date && relative) { + if (date != null && relative) { return rt(date, options); } - if (number) { + if (number != null) { return n(number, options); } @@ -84,7 +84,7 @@ export function Translate(props: TranslateProps) { } return null; - }, [changed, count, date, name, number, options]); + }, [changed, count, date, name, number, options, relative]); useEffect(() => { const dispose = subscribe(() => { diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts index a63b186..daaa3ac 100644 --- a/packages/react/src/index.ts +++ b/packages/react/src/index.ts @@ -1,2 +1,2 @@ -export * from './Translate'; -export * from './useIntl'; +export * from './Translate.js'; +export * from './useIntl.js'; diff --git a/packages/react/src/useIntl.tsx b/packages/react/src/useIntl.tsx index 12eadf4..35dedd6 100644 --- a/packages/react/src/useIntl.tsx +++ b/packages/react/src/useIntl.tsx @@ -5,7 +5,7 @@ import { createContext, useCallback, useContext, useEffect, useState } from 'rea export const TinyIntlContext = createContext | undefined>(undefined); -export function useIntl() { +export function useIntl(): TinyIntl { const intl = useContext(TinyIntlContext); if (!intl) { diff --git a/packages/react/tests/Translate.test.tsx b/packages/react/tests/Translate.test.tsx new file mode 100644 index 0000000..866e14b --- /dev/null +++ b/packages/react/tests/Translate.test.tsx @@ -0,0 +1,165 @@ +import type { TinyIntl, TinyIntlDict } from '@tiny-intl/core'; + +import { cleanup, render, screen } from '@testing-library/react'; +import { createTinyIntl, detectLocale } from '@tiny-intl/core'; +import { afterEach, describe, it } from 'vitest'; + +import { Translate } from '../src/Translate'; +import { TinyIntlContext } from '../src/useIntl'; + +const loadDict = (locale: string): TinyIntlDict => { + if (locale === 'en-US' || locale === 'sv-SE') { + return { + hello: 'Hello, {{name}}!', + inbox: 'Inbox', + document: { + zero: 'Documents', + one: 'Document', + other: 'Documents', + }, + plusXDocumentsSelected: { + zero: '{{title}} and {{count}} more documents selected', + one: '{{title}} and {{count}} more document selected', + other: '{{title}} and {{count}} more documents selected', + }, + }; + } + if (locale === 'de-DE') { + return { + hello: 'Hallo, {{name}}!', + inbox: 'Posteingang', + document: { + zero: 'Dokumente', + one: 'Dokument', + other: 'Dokumente', + }, + plusXDocumentsSelected: { + zero: '{{title}} ausgewählt', + one: '{{title}} und ein weiteres Dokument ausgewählt', + other: '{{title}} und {{count}} weitere Dokumente ausgewählt', + }, + }; + } + return {}; +}; + +async function createIntl() { + const intl = createTinyIntl<'en-US' | 'de-DE' | 'sv-SE'>({ + loadDict: (locale) => loadDict(locale), + fallbackLocale: 'en-US', + supportedLocales: ['en-US', 'de-DE', 'sv-SE'], + detectLocale: (params) => detectLocale('en-US', params), + }); + await intl.mount(); + return intl; +} + +describe('@tiny-intl/react', () => { + let intl: TinyIntl<'en-US' | 'sv-SE' | 'de-DE'>; + + afterEach(cleanup); + + it('renders a plain string translation', async ({ expect }) => { + intl = await createIntl(); + render( + + + , + ); + expect(screen.getByText('Inbox')).toBeTruthy(); + }); + + it('renders the plural form when count is set', async ({ expect }) => { + intl = await createIntl(); + render( + + + , + ); + expect(screen.getByText('Documents')).toBeTruthy(); + }); + + it('renders a formatted number', async ({ expect }) => { + intl = await createIntl(); + render( + + + , + ); + expect(screen.getByText('1,000')).toBeTruthy(); + }); + + it('renders a formatted date', async ({ expect }) => { + intl = await createIntl(); + render( + + + , + ); + expect(screen.getByText('Friday, January 1, 2021')).toBeTruthy(); + }); + + it('passes the value to a function-as-children', async ({ expect }) => { + intl = await createIntl(); + render( + + {(value) => {value}} + , + ); + const el = screen.getByText('Inbox'); + expect(el.tagName).toBe('STRONG'); + }); + + it('renders the zero plural form for count={0}', async ({ expect }) => { + intl = await createIntl(); + await intl.change('de-DE'); + render( + + + , + ); + expect(screen.getByText('Dokumente')).toBeTruthy(); + }); + + it('renders the singular plural form for count={1}', async ({ expect }) => { + intl = await createIntl(); + await intl.change('de-DE'); + render( + + + , + ); + expect(screen.getByText('Dokument')).toBeTruthy(); + }); + + it('renders the plural form for count={5}', async ({ expect }) => { + intl = await createIntl(); + await intl.change('de-DE'); + render( + + + , + ); + expect(screen.getByText('Dokumente')).toBeTruthy(); + }); + + it('renders a formatted zero for number={0}', async ({ expect }) => { + intl = await createIntl(); + render( + + + , + ); + expect(screen.getByText('0')).toBeTruthy(); + }); + + it('renders a formatted date for the epoch date={0}', async ({ expect }) => { + intl = await createIntl(); + render( + + + , + ); + expect(screen.getByText('Thursday, January 1, 1970')).toBeTruthy(); + }); +}); diff --git a/packages/react/tests/useIntl.test.tsx b/packages/react/tests/useIntl.test.tsx new file mode 100644 index 0000000..5979b73 --- /dev/null +++ b/packages/react/tests/useIntl.test.tsx @@ -0,0 +1,115 @@ +import type { TinyIntl, TinyIntlDict } from '@tiny-intl/core'; + +import { act, cleanup, render, renderHook, screen } from '@testing-library/react'; +import { createTinyIntl, detectLocale } from '@tiny-intl/core'; +import { afterEach, describe, it } from 'vitest'; + +import { TinyIntlContext, useIntl } from '../src/useIntl'; + +const loadDict = (locale: string): TinyIntlDict => { + if (locale === 'en-US' || locale === 'sv-SE') { + return { + hello: 'Hello, {{name}}!', + inbox: 'Inbox', + document: { + zero: 'Documents', + one: 'Document', + other: 'Documents', + }, + plusXDocumentsSelected: { + zero: '{{title}} and {{count}} more documents selected', + one: '{{title}} and {{count}} more document selected', + other: '{{title}} and {{count}} more documents selected', + }, + }; + } + if (locale === 'de-DE') { + return { + hello: 'Hallo, {{name}}!', + inbox: 'Posteingang', + document: { + zero: 'Dokumente', + one: 'Dokument', + other: 'Dokumente', + }, + plusXDocumentsSelected: { + zero: '{{title}} ausgewählt', + one: '{{title}} und ein weiteres Dokument ausgewählt', + other: '{{title}} und {{count}} weitere Dokumente ausgewählt', + }, + }; + } + return {}; +}; + +async function createIntl() { + const intl = createTinyIntl<'en-US' | 'de-DE' | 'sv-SE'>({ + loadDict: (locale) => loadDict(locale), + fallbackLocale: 'en-US', + supportedLocales: ['en-US', 'de-DE', 'sv-SE'], + detectLocale: (params) => detectLocale('en-US', params), + }); + await intl.mount(); + return intl; +} + +describe('@tiny-intl/react', () => { + let intl: TinyIntl<'en-US' | 'sv-SE' | 'de-DE'>; + + afterEach(cleanup); + + it('throws outside a provider', ({ expect }) => { + expect(() => renderHook(() => useIntl())).toThrow( + 'useIntl must be used within a TinyIntlContext.Provider', + ); + }); + + it('t() returns the translated string for the mounted locale', async ({ expect }) => { + intl = await createIntl(); + const { result } = renderHook(() => useIntl(), { + wrapper: ({ children }) => ( + {children} + ), + }); + expect(result.current.t('inbox')).toBe('Inbox'); + }); + + it('re-renders with the German string after intl.change', async ({ expect }) => { + intl = await createIntl(); + + function Inbox() { + const { t } = useIntl(); + return {t('inbox')}; + } + + render( + + + , + ); + + expect(screen.getByText('Inbox')).toBeTruthy(); + + await act(async () => { + await intl.change('de-DE'); + }); + + expect(screen.getByText('Posteingang')).toBeTruthy(); + }); + + it('n() and dt() format per locale', async ({ expect }) => { + intl = await createIntl(); + await act(async () => { + await intl.change('de-DE'); + }); + + const { result } = renderHook(() => useIntl(), { + wrapper: ({ children }) => ( + {children} + ), + }); + + expect(result.current.n(1000)).toBe('1.000'); + expect(result.current.dt('2021-01-01', { dateStyle: 'full' })).toBe('Freitag, 1. Januar 2021'); + }); +}); diff --git a/packages/react/vite.config.ts b/packages/react/vite.config.ts index ba1588c..04b7c7b 100644 --- a/packages/react/vite.config.ts +++ b/packages/react/vite.config.ts @@ -2,8 +2,8 @@ import { resolve } from 'path'; import react from '@vitejs/plugin-react'; import { visualizer } from 'rollup-plugin-visualizer'; -import { defineConfig } from 'vite'; import dts from 'vite-plugin-dts'; +import { defineConfig } from 'vitest/config'; // eslint-disable-line import/no-unresolved export default defineConfig({ plugins: [ @@ -30,7 +30,7 @@ export default defineConfig({ name: 'tinyIntl', // the proper extensions will be added fileName: 'index', - formats: ['es', 'cjs', 'umd'], + formats: ['es', 'cjs'], }, rollupOptions: { external: ['react', 'react-dom', 'react-jsx-runtime'], @@ -46,4 +46,8 @@ export default defineConfig({ esbuild: { jsxInject: `import * as React from 'react'`, }, + test: { + environment: 'jsdom', + globals: false, + }, }); diff --git a/packages/solid-js/.gitignore b/packages/solid-js/.gitignore deleted file mode 100644 index 0eae589..0000000 --- a/packages/solid-js/.gitignore +++ /dev/null @@ -1,25 +0,0 @@ -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -pnpm-debug.log* -lerna-debug.log* - -node_modules -dist -lib -dist-ssr -*.local - -# Editor directories and files -.vscode/* -!.vscode/extensions.json -.idea -.DS_Store -*.suo -*.ntvs* -*.njsproj -*.sln -*.sw? diff --git a/packages/solid-js/README.md b/packages/solid-js/README.md deleted file mode 100644 index 39f55fc..0000000 --- a/packages/solid-js/README.md +++ /dev/null @@ -1,255 +0,0 @@ -# @tiny-intl/solid-js - -[@tiny-intl/solid](./packages/solid) ![Solid Adater Size](https://deno.bundlejs.com/badge?q=@tiny-intl/solid-js&treeshake=%5B*%5D&config=%7B%22esbuild%22:%7B%22external%22:%5B%22solid-js%22%5D%7D%7D) - -A tiny library to translate or transform strings, dates and numbers based on native Intl. - -## Installation - -```bash -npm install @tiny-intl/core @tiny-intl/solid-js -``` - -## Usage - -```json -// en-US.json -{ - "messages": { - "hello": "Hello {{name}}!", - "document": { - "one": "Document", - "other": "Documents" - // supports also zero, two, few, many - } - } -} -``` - -```jsx -import { createTinyIntl, detectBrowserLocale } from '@tiny-intl/core'; -import { TinyIntlContext } from '@tiny-intl/solid-js'; - -const intl = createTinyIntl({ - fallbackLocale: 'en-US', - supportedLocales: ['en-US', 'de-DE'], - loadDict: async (nextLoc) => { - const dict = (await import(`./locales/${nextLoc}.json`)).default; - return dict.messages; - }, - detectLocale: ({ supportedLocales, fallbackLocale }) => { - // or any custom logic - return detectBrowserLocale({ supportedLocales, fallbackLocale }); - }, -}); - -const App = () => { - const [i18nMounted, setI18nMounted] = createSignal(false); - - intl.mount().then(() => { - setI18nMounted(true); - }); - - return ( - - - - - - ); -}; -``` - -### Translating strings - -This package uses native plural rules from Intl [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/PluralRules). - -```jsx -import { useIntl, Translate } from '@tiny-intl/solid-js'; - -// via primitive -const MyComponent = () => { - const { t, tc } = useIntl(); - - return ( -
- {/* Hello John! */} - {t('hello', { name: 'John' })()} - {/* Documents */} - {tc('document', 2)()} -
- ); -}; - -// via component -const MyComponent = () => { - return ( -
- {/* Hello John! */} - - {/* Documents */} - -
- ); -}; -``` - -### Formatting dates - -Look at [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat) for more options. - -```jsx -import { useIntl } from '@tiny-intl/solid-js'; - -// via primitive -const MyComponent = () => { - const { d } = useIntl(); - - return ( -
- {/* Tuesday, April 6, 2021 */} - {d(new Date(), { dateStyle: 'full' })()} -
- ); -}; - -// via component -const MyComponent = () => { - return ( -
- {/* Tuesday, April 6, 2021 */} - -
- ); -}; -``` - -### Relative Time Formatting - -Look at [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/RelativeTimeFormat) for more options. - -```jsx -import { useIntl } from '@tiny-intl/solid-js'; - -// via primitive -const MyComponent = () => { - const { rt } = useIntl(); - const date = new Date('2023-10-18T21:44:00.000z'); - - return ( -
- {/* 1 day ago */} - {rt(date)} -
- ); -}; - -// via component -const MyComponent = () => { - return ( -
- {/* 1 day ago */} - -
- ); -}; -``` - -### Formatting numbers - -Look at [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat) for more options. - -```jsx -import { useIntl } from '@tiny-intl/solid-js'; - -// via primitive -const MyComponent = () => { - const { n } = useIntl(); - - return ( -
- {/* €123,456.79 */} - {n(123456.789, { style: 'currency', currency: 'EUR' })()} -
- ); -}; - -// via component -const MyComponent = () => { - return ( -
- {/* €123,456.79 */} - -
- ); -}; -``` - -### List Formatting - -Look at [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/ListFormat) for more options. - -> [!NOTE] -> Only available via primitive. - -```jsx -import { useIntl } from '@tiny-intl/solid-js'; - -// via primitive -const MyComponent = () => { - const { list } = useIntl(); - - return ( -
- {/* a, b, and c */} - {list(['a', 'b', 'c'])} - {/* a, b, or c */} - {list(['a', 'b', 'c'], 'OR')} - {/* a b c */} - {list(['a', 'b', 'c'], { type: 'unit', style: 'narrow' })} -
- ); -}; -``` - -### Sorting - -Look at [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Collator) for more options. - -> [!NOTE] -> Only available via primitive. - -```jsx -import { useIntl } from '@tiny-intl/solid-js'; - -// via primitive -const MyComponent = () => { - const { sort, collator } = useIntl(); - - return ( -
- {/* en-US: ['a', 'ä', 'Z', 'z'], swedish: ['a', 'Z', 'z', 'ä'] */} - {sort(['Z', 'a', 'z', 'ä'], { caseFirst: 'upper' })} - {/* [{ name: 'a' }, { name: 'Z' }] */} - {[{ name: 'Z' }, { name: 'a' }].sort((a, b) => collator(a.name, b.name))} -
- ); -}; -``` - -### Change locale - -```jsx -import { useIntl } from '@tiny-intl/solid-js'; - -const MyComponent = () => { - const { change, getLocale } = useIntl(); - - return ( -
- {getLocale()} - -
- ); -}; -``` diff --git a/packages/solid-js/package.json b/packages/solid-js/package.json deleted file mode 100644 index e8bbde0..0000000 --- a/packages/solid-js/package.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "name": "@tiny-intl/solid-js", - "version": "1.2.0", - "repository": { - "type": "git", - "url": "https://github.com/gridventures/tiny-intl", - "directory": "packages/solid-js" - }, - "author": "Phil Schaffarzyk ", - "license": "MIT", - "type": "module", - "files": [ - "lib" - ], - "main": "./lib/index.umd.cjs", - "module": "./lib/index.js", - "types": "./lib/types/index.d.ts", - "exports": { - ".": { - "import": "./lib/index.js", - "require": "./lib/index.umd.cjs", - "types": "./lib/types/index.d.ts" - } - }, - "scripts": { - "dev": "vite", - "build": "tsc && vite build", - "preview": "vite preview" - }, - "dependencies": { - "@tiny-intl/core": "^1.2.0" - }, - "peerDependencies": { - "solid-js": ">=1.5.0" - }, - "devDependencies": { - "rollup-plugin-visualizer": "^5.9.0", - "solid-js": "^1.8.3", - "typescript": "^5.2.2", - "vite": "^4.5.2", - "vite-plugin-dts": "3.6.0", - "vite-plugin-solid": "^2.6.1" - } -} diff --git a/packages/solid-js/src/Translate.tsx b/packages/solid-js/src/Translate.tsx deleted file mode 100644 index e7f1c2b..0000000 --- a/packages/solid-js/src/Translate.tsx +++ /dev/null @@ -1,114 +0,0 @@ -import type { TinyIntlTranslateTemplate } from '@tiny-intl/core'; -import type { JSX } from 'solid-js'; - -import { Show, createMemo, createSignal, onCleanup, onMount, useContext } from 'solid-js'; - -import { TinyIntlContext } from './useIntl'; - -export type TranslateProps = { - children?: (value: string | null) => JSX.Element; -} & ( - | { - // Case: string translation - name: string; - count?: number; - date?: undefined; - number?: undefined; - options?: TinyIntlTranslateTemplate; - relative?: undefined; - unit?: undefined; - } - | { - // Case: dateFormat - name?: undefined; - count?: undefined; - data?: undefined; - date?: Date | string | number; - number?: undefined; - options?: Intl.DateTimeFormatOptions; - relative?: undefined; - } - | { - // Case: relativeTimeFormat - name?: undefined; - count?: undefined; - data?: undefined; - date?: Date | string | number; - number?: undefined; - options?: Intl.RelativeTimeFormatOptions; - relative?: true; - } - | { - // Case: numberFormat - name?: undefined; - count?: undefined; - data?: undefined; - date?: undefined; - number?: number; - options?: Intl.NumberFormatOptions; - relative?: undefined; - } -); - -export function Translate(props: TranslateProps) { - const intl = useContext(TinyIntlContext); - - if (!intl) { - throw new Error('useIntl must be used within a TinyIntlContext.Provider'); - } - - const [changed, setChanged] = createSignal(0); - - const translateFn = createMemo( - () => { - changed(); - - if (props.count) { - return intl.tc(props.name, props.count, props.options); - } - - if (props.date && !props.relative) { - return intl.dt(props.date, props.options); - } - - if (props.date && props.relative) { - return intl.rt(props.date, props.options); - } - - if (props.number) { - return intl.n(props.number, props.options); - } - - if (props.name) { - return intl.t(props.name, props.options); - } - - return null; - }, - null, - { - equals: (a, b) => a === b, - }, - ); - - onMount(() => { - const dispose = intl.subscribe(() => { - setChanged((prev) => prev + 1); - }); - onCleanup(() => { - dispose(); - }); - }); - - return ( - - {(childFn) => childFn(translateFn())} - - ); -} - -export const Trans = Translate; diff --git a/packages/solid-js/src/index.ts b/packages/solid-js/src/index.ts deleted file mode 100644 index a63b186..0000000 --- a/packages/solid-js/src/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './Translate'; -export * from './useIntl'; diff --git a/packages/solid-js/src/useIntl.tsx b/packages/solid-js/src/useIntl.tsx deleted file mode 100644 index b82c673..0000000 --- a/packages/solid-js/src/useIntl.tsx +++ /dev/null @@ -1,86 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import type { TinyIntl } from '@tiny-intl/core'; -import type { Accessor } from 'solid-js'; - -import { - createContext, - useContext, - createEffect, - createSignal, - createMemo, - onCleanup, -} from 'solid-js'; - -export const TinyIntlContext = createContext | undefined>(undefined); - -const memoizeCallback = - any>(fn: T, trackDep: () => Accessor) => - (...args: Parameters): Accessor> => { - const result = createMemo( - () => { - trackDep()(); - return fn.call(args); - }, - null, - { - equals: (a, b) => a === b, - }, - ); - return result; - }; - -export function useIntl() { - const intl = useContext(TinyIntlContext); - - if (!intl) { - throw new Error('useIntl must be used within a TinyIntlContext.Provider'); - } - - const [changed, setChanged] = createSignal(0); - - const getLocale = createMemo( - () => { - changed(); - return intl.locale; - }, - '', - { - equals: (a, b) => a === b, - }, - ); - const t = memoizeCallback(intl.t, () => changed); - const tc = memoizeCallback(intl.tc, () => changed); - const n = memoizeCallback(intl.n, () => changed); - const dt = memoizeCallback(intl.dt, () => changed); - const rt = memoizeCallback(intl.rt, () => changed); - const sort = memoizeCallback(intl.sort, () => changed); - const collator = memoizeCallback(intl.collator, () => changed); - const list = memoizeCallback(intl.list, () => changed); - - createEffect(() => { - const dispose = intl.subscribe(() => { - setChanged((prev) => prev + 1); - }); - onCleanup(() => { - dispose(); - }); - }); - - return { - ...intl, - t, - tc, - n, - dt, - /** - * @deprecated use dt instead. Will be removed in stable release. - */ - d: dt, - rt, - sort, - collator, - list, - change: intl.change, - getLocale, - }; -} diff --git a/packages/solid-js/src/vite-env.d.ts b/packages/solid-js/src/vite-env.d.ts deleted file mode 100644 index 11f02fe..0000000 --- a/packages/solid-js/src/vite-env.d.ts +++ /dev/null @@ -1 +0,0 @@ -/// diff --git a/packages/solid-js/tsconfig.json b/packages/solid-js/tsconfig.json deleted file mode 100644 index 3795de4..0000000 --- a/packages/solid-js/tsconfig.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "compilerOptions": { - "target": "ESNext", - "useDefineForClassFields": true, - "lib": ["DOM", "DOM.Iterable", "ESNext"], - "allowJs": false, - "skipLibCheck": true, - "esModuleInterop": false, - "allowSyntheticDefaultImports": true, - "strict": true, - "forceConsistentCasingInFileNames": true, - "module": "ESNext", - "moduleResolution": "Node", - "resolveJsonModule": true, - "isolatedModules": true, - "noEmit": true, - "jsx": "preserve", - "jsxImportSource": "solid-js", - }, - "include": ["src"], - "references": [{ "path": "./tsconfig.node.json" }] -} diff --git a/packages/solid-js/tsconfig.node.json b/packages/solid-js/tsconfig.node.json deleted file mode 100644 index 9d31e2a..0000000 --- a/packages/solid-js/tsconfig.node.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "compilerOptions": { - "composite": true, - "module": "ESNext", - "moduleResolution": "Node", - "allowSyntheticDefaultImports": true - }, - "include": ["vite.config.ts"] -} diff --git a/packages/solid-js/vite.config.ts b/packages/solid-js/vite.config.ts deleted file mode 100644 index af518b9..0000000 --- a/packages/solid-js/vite.config.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { resolve } from 'path'; - -import { visualizer } from 'rollup-plugin-visualizer'; -import { defineConfig } from 'vite'; -import dts from 'vite-plugin-dts'; -import solid from 'vite-plugin-solid'; - -export default defineConfig({ - plugins: [ - dts({ - outDir: resolve(__dirname, 'lib', 'types'), - insertTypesEntry: true, - }), - solid(), - visualizer({ - filename: 'dist/stats.html', - gzipSize: true, - brotliSize: true, - }), - ], - build: { - outDir: resolve(__dirname, 'lib'), - lib: { - // Could also be a dictionary or array of multiple entry points - entry: resolve(__dirname, 'src', 'index.ts'), - name: 'tinyIntl', - // the proper extensions will be added - fileName: 'index', - formats: ['es', 'cjs', 'umd'], - }, - rollupOptions: { - external: ['solid-js'], - output: { - globals: { - 'solid-js': 'solid', - }, - }, - }, - }, -}); diff --git a/plans/001-adapter-test-and-ci-baseline.md b/plans/001-adapter-test-and-ci-baseline.md new file mode 100644 index 0000000..c18ddae --- /dev/null +++ b/plans/001-adapter-test-and-ci-baseline.md @@ -0,0 +1,563 @@ +# Plan 001: Establish a working verification baseline — tests for the react and preact adapters, and a CI pipeline that runs them + +> **Executor instructions**: Follow this plan step by step. Run every +> verification command and confirm the expected result before moving to the +> next step. If anything in the "STOP conditions" section occurs, stop and +> report — do not improvise. When done, update the status row for this plan +> in `plans/README.md`. +> +> **Drift check (run first)**: +> `git diff --stat 794eb48..HEAD -- packages/react packages/preact .github package.json` +> If any in-scope file changed since this plan was written, compare the +> "Current state" excerpts against the live code before proceeding; on a +> mismatch, treat it as a STOP condition. + +## Status + +- **Priority**: P1 +- **Effort**: M +- **Risk**: LOW (adds test/CI infrastructure only; no runtime source changes) +- **Depends on**: none. Assumes `plans/002-remove-solid-js-support.md` lands — + see "A note on `packages/solid-js`" below. +- **Category**: tests +- **Planned at**: commit `794eb48`, 2026-08-12 + +## Why this matters + +This repo publishes framework adapters to npm, and not one of them has a single +automated test. Only `@tiny-intl/core` has a suite, and CI runs only that one +package — it never lints, never typechecks, and never builds the adapters. + +That gap has already shipped a broken package. The Solid adapter's `useIntl()` +returned `'[undefined]'` from every primitive it exposed +(`packages/solid-js/src/useIntl.tsx:22` called `fn.call(args)`, which passes the +argument array as `this` and forwards no arguments) — for its entire published +life. One render test would have caught it. That package is being deleted rather +than fixed (plan 002), but react and preact are built the same way, by the same +copy-paste, and have the same amount of coverage: none. + +After this plan lands, `npm test` at the repo root runs a real suite for all +three remaining packages, CI runs lint + tests on every push, and plan 003 has a +verification gate to prove its fix works and stays fixed. + +### A note on `packages/solid-js` + +`plans/002-remove-solid-js-support.md` deletes that package. This plan therefore +covers **react and preact only** and never touches `packages/solid-js`, whether +or not 002 has run yet. There is no ordering constraint between the two plans. + +If plan 002 is later rejected, `packages/solid-js` will be left with no test +harness and this plan must be re-scoped to add one — budget extra time for it, +because getting `vite-plugin-solid` to cooperate with vitest 0.34 is +substantially harder than the react/preact setup below. + +## Current state + +### Files and their roles + +- `package.json` — root workspace manifest (npm workspaces + lerna). Has a `lint` + script with **no target argument**, and no `test` script at all. +- `.github/workflows/unit-tests.yml` — the only CI workflow. Runs core's coverage + script and nothing else, using deprecated action versions. +- `packages/core/vite.config.ts` — the only package with a vitest config + (`test: {}` at the bottom, via `defineConfig` from `vitest/config`). +- `packages/core/tests/index.test.ts` — the only test file in the repo; **use it + as the structural pattern** for all new tests. +- `packages/react/package.json`, `packages/preact/package.json` — no `test` + script, no test runner in `devDependencies`. +- `packages/react/vite.config.ts`, `packages/preact/vite.config.ts` — build-only + configs; `defineConfig` is imported from `vite`, not `vitest/config`, and there + is no `test` key. + +### Excerpt — root `package.json:5-13` (the lint script has no target) + +```json + "scripts": { + "lint": "eslint --config ./.eslintrc.cjs --ignore-path ./.eslintignore --cache", + "release": "lerna publish --no-private", + "git-hooks:commit-msg": "commitlint --edit", + "git-hooks:pre-commit": "lint-staged", + "preversion": "lerna run coverage && lerna run build", + "prepare": "husky install" + }, +``` + +`npm run lint` with no file arguments lints nothing. It only ever does work via +`.lintstagedrc`, which appends staged filenames: + +```json +{ + "packages/**/*.{ts,tsx,js,jsx}": "npm run lint" +} +``` + +### Excerpt — `.github/workflows/unit-tests.yml` (entire file) + +```yaml +name: Run unit tests + +on: + push: + branches: ['*'] + pull_request: + branches: ['*'] +jobs: + build: + name: 'Run tests' + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [16, 18, 20, 21] + + steps: + - name: checkout + uses: actions/checkout@v4 + - name: restore caches + uses: actions/cache@v2 + with: + path: | + ./node_modules + ./.coverage + ./.eslintcache + key: ${{ runner.os }}-${{ matrix.node-version }}-${{ hashFiles('package-lock.json') }} + - name: Run tests + uses: actions/setup-node@v2 + with: + node-version: ${{ matrix.node-version }} + cache: 'npm' + - run: npm install && cd packages/core && npm run coverage +``` + +Problems, all of which this plan fixes: `actions/cache@v2` and +`actions/setup-node@v2` run on the retired Node 16 action runtime; the cache step +runs *before* `setup-node` and duplicates `setup-node`'s own `cache: 'npm'`; +`npm install` is used despite a committed `package-lock.json`; the matrix +includes Node 16 and 21, both long past end-of-life; the step labelled +`Run tests` is actually the Node setup step; and only `packages/core` is tested. + +### Excerpt — `packages/core/vite.config.ts` (the vitest config pattern to copy) + +```ts +import { defineConfig } from 'vitest/config'; // eslint-disable-line import/no-unresolved + +export default defineConfig({ + plugins: [ /* dts, visualizer */ ], + build: { /* ... */ }, + test: {}, +}); +``` + +### Excerpt — `packages/core/tests/index.test.ts:1-6, 44-62` (the test style to match) + +```ts +import type { TinyIntl, TinyIntlDict } from '../src'; + +import { vi, afterEach, describe, it } from 'vitest'; + +import { createTinyIntl, detectBrowserLocale, detectLocale } from '../src'; +import { relativeTimeFormatForDiff } from '../src/utils'; + +describe('@tiny-intl/core', () => { + let intl: TinyIntl<'en-US' | 'sv-SE' | 'de-DE'>; + + afterEach(async () => { + await intl.change('en-US'); + }); + + it('mount instance', async ({ expect }) => { + intl = createTinyIntl<'en-US' | 'de-DE' | 'sv-SE'>({ + loadDict: (locale) => loadDict(locale), + fallbackLocale: 'en-US', + supportedLocales: ['en-US', 'de-DE', 'sv-SE'], + detectLocale: (params) => detectLocale('en-US', params), + }); + await intl.mount(); + expect(intl.locale).toBe('en-US'); + }); +``` + +**Conventions you must match**, all visible above: + +- Tests live in a `tests/` directory beside `src/`, **not** co-located with + source files. Import from `'../src'`, not from `'../lib'` or the package name. +- `expect` is taken from the **per-test fixture argument** + (`it('name', async ({ expect }) => {...})`), not imported from `vitest`. + Keep doing that. +- Only `vi`, `afterEach`, `describe`, `it` are imported from `vitest`. +- Single top-level `describe` named after the package (`'@tiny-intl/core'` → + use `'@tiny-intl/react'` etc.). +- Formatting is Prettier with `singleQuote: true`, `printWidth: 100`, + `trailingComma: "all"` (see `.prettierrc`). 2-space indent (see `.editorconfig`). + +### Excerpt — `packages/react/package.json:25-46` (no test script, no runner) + +```json + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "preview": "vite preview" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + }, + "dependencies": { + "@tiny-intl/core": "^1.2.0" + }, + "devDependencies": { + "@types/react": "^18.0.28", + "@vitejs/plugin-react": "^4.1.0", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "rollup-plugin-visualizer": "^5.9.0", + "typescript": "^5.2.2", + "vite": "^4.5.2", + "vite-plugin-dts": "3.6.0" + } +``` + +`packages/preact/package.json` has the same shape with `preact` in place of react. + +### Version constraint you must respect + +The repo is on **vitest `^0.34.6`** and **vite `^4.5.2`** (see +`packages/core/package.json:37-46`). Do **not** upgrade either in this plan — +a vitest 0→3 and vite 4→7 migration is a separate, larger piece of work +(recorded in `plans/README.md` under deferred findings). Install testing +libraries whose versions are compatible with vitest 0.34 / vite 4. + +## Commands you will need + +> These were read from `package.json` / CI config during planning. `node_modules` +> was **not** installed at planning time, so none of them were executed — expect +> to discover the real behaviour of `npm run build` in Step 1. + +| Purpose | Command | Expected on success | +|---|---|---| +| Install (root, workspaces) | `npm install` | exit 0 | +| Core tests | `npm run test --workspace @tiny-intl/core -- --run` | all pass | +| Core coverage | `npm run coverage --workspace @tiny-intl/core` | exit 0 | +| Typecheck+build one adapter | `npm run build --workspace @tiny-intl/react` | exit 0 | +| Lint (after Step 4) | `npm run lint:all` | exit 0 | +| All tests (after Step 4) | `npm test` | all pass | + +## Scope + +**In scope** (the only files you should modify or create): + +- `package.json` (root) — add `test` / `lint:all` scripts +- `.github/workflows/unit-tests.yml` — rewrite +- `packages/react/package.json`, `packages/react/vite.config.ts`, + `packages/react/tests/useIntl.test.tsx` (create), + `packages/react/tests/Translate.test.tsx` (create) +- `packages/preact/package.json`, `packages/preact/vite.config.ts`, + `packages/preact/tests/useIntl.test.tsx` (create), + `packages/preact/tests/Translate.test.tsx` (create) +- `package-lock.json` — will change as a result of installing devDependencies; + commit the result. +- `plans/README.md` — status row only + +**Out of scope** (do NOT touch, even though they look related): + +- **`packages/solid-js/**` — do not add tests, a `test` script, or a vitest + config there.** That package is being deleted by + `plans/002-remove-solid-js-support.md`. If the directory still exists when you + run, simply leave it alone. +- **Any file under `packages/react/src/` or `packages/preact/src/`.** This plan + adds tests that document *current* behaviour. Fixing behaviour is plan 003. +- `packages/core/**` — core already has a working suite; do not restructure it. +- Upgrading vitest, vite, eslint, prettier, lerna or typescript major versions. +- `.eslintrc.cjs` — unless a new test file trips a lint rule, in which case see + Step 4. + +## Git workflow + +- Branch: `advisor/001-adapter-test-and-ci-baseline` +- The repo enforces **conventional commits** via commitlint + (`commitlint.config.js` + `.husky/commit-msg`), with scopes restricted to + `core`, `react`, `solid-js`, `preact` (plan 002 removes `solid-js` from that + list). Recent history for reference: + `test(core): use stubs for mocking navigator variable`, + `ci: implement unit testing inside github action`, + `chore: updated outdated dev dependencies`. + Use `test(react): ...`, `test(preact): ...`, `ci: ...`, `chore: ...`. +- Commit per step or per logical unit. Do NOT push or open a PR unless the + operator instructed it. + +## Steps + +### Step 1: Record the current build/typecheck baseline before changing anything + +Install and find out what actually works today. Each adapter's `build` script is +`tsc && vite build`, so it doubles as a typecheck. + +```bash +npm install +npm run build --workspace @tiny-intl/react +npm run build --workspace @tiny-intl/preact +npm run test --workspace @tiny-intl/core -- --run +``` + +Write the exit status and, on failure, the **first 30 lines of error output** for +each of these four commands into your final report. + +**Known baseline, measured 2026-08-12 — do not act on any of it, just confirm +it still holds:** + +- All four commands exited 0. `npx tsc --noEmit` also exits 0 in both + `packages/react` and `packages/preact`. There is no pre-existing type error. +- The **preact** build prints 6 non-fatal `TS2742` diagnostics from + `vite-plugin-dts` ("The inferred type of 'useIntl' cannot be named without a + reference to …/createTinyIntl. This is likely not portable."). The build still + exits 0. As a result `packages/preact/lib/types/useIntl.d.ts` is **never + emitted**, even though `lib/types/index.d.ts` re-exports from it. React emits + all three declaration files correctly. + +That preact declaration gap is a real shipped bug, but it is **out of scope +here** — it has its own plan (006). If you observe it, note it and move on. If +you observe something *different* from the baseline above, record the exact +output; the repo has drifted. + +**Verify**: all four commands have been run and their results recorded. This step +cannot "fail" — it is a measurement. + +### Step 2: Add the test harness to `@tiny-intl/react` + +2a. Add devDependencies and a `test` script to `packages/react/package.json`: + +```json + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "preview": "vite preview", + "test": "vitest", + "coverage": "vitest run --coverage" + }, +``` + +Install (from the repo root, so npm workspaces hoists correctly): + +```bash +npm install -D --workspace @tiny-intl/react \ + vitest@^0.34.6 @vitest/coverage-v8@^0.34.6 jsdom@^22 \ + @testing-library/react@^14 +``` + +2b. Give the package a vitest config. `packages/react/vite.config.ts` currently +imports `defineConfig` from `'vite'`. Change that import to `'vitest/config'` and +add a `test` block — matching how `packages/core/vite.config.ts` does it. Leave +every existing `plugins` and `build` entry exactly as it is: + +```ts +import { defineConfig } from 'vitest/config'; // eslint-disable-line import/no-unresolved + +export default defineConfig({ + plugins: [ /* unchanged */ ], + build: { /* unchanged */ }, + test: { + environment: 'jsdom', + globals: false, + }, +}); +``` + +2c. Create `packages/react/tests/useIntl.test.tsx`. It must cover, at minimum: + +1. `useIntl()` outside a provider throws + `'useIntl must be used within a TinyIntlContext.Provider'`. +2. `t('inbox')` returns the translated string for the mounted locale. +3. After `await intl.change('de-DE')`, a component using `useIntl()` **re-renders + with the German string** (this is the subscription path, + `packages/react/src/useIntl.tsx:26-31`). +4. `n(1000)` and `dt('2021-01-01', { dateStyle: 'full' })` return locale-correct + output. + +Build the `intl` instance with `createTinyIntl` from `@tiny-intl/core` and an +inline `loadDict`, exactly as `packages/core/tests/index.test.ts:8-42` does — +copy that fixture dictionary rather than inventing a new one, so all three +packages' tests share vocabulary. Wrap components in +``. + +Locale change happens outside React, so wrap it: +`await act(async () => { await intl.change('de-DE'); });` + +**Verify**: `npm run test --workspace @tiny-intl/react -- --run` → all tests pass, +0 failures. + +### Step 3: Repeat Step 2 for `@tiny-intl/preact` + +Same structure. `packages/preact/src/useIntl.tsx` is a byte-for-byte copy of the +React one except it imports from `'preact/compat'` — so the tests are the same +tests with a different testing library. + +```bash +npm install -D --workspace @tiny-intl/preact \ + vitest@^0.34.6 @vitest/coverage-v8@^0.34.6 jsdom@^22 \ + @testing-library/preact@^3 +``` + +`@preact/preset-vite` is already in the package's devDependencies and already in +`packages/preact/vite.config.ts` `plugins`, so JSX transformation is handled. Add +the same `test: { environment: 'jsdom' }` block and switch the `defineConfig` +import to `'vitest/config'`. + +**Verify**: `npm run test --workspace @tiny-intl/preact -- --run` → all pass. + +### Step 4: Wire up root-level scripts + +In root `package.json`, add a `test` script and a lintable `lint:all`, leaving +the existing `lint` entry untouched (it is invoked by `.lintstagedrc`, which +appends filenames to it — adding a target there would break that): + +```json + "scripts": { + "lint": "eslint --config ./.eslintrc.cjs --ignore-path ./.eslintignore --cache", + "lint:all": "npm run lint -- .", + "test": "lerna run test -- --run", + "coverage": "lerna run coverage", + "release": "lerna publish --no-private", + "git-hooks:commit-msg": "commitlint --edit", + "git-hooks:pre-commit": "lint-staged", + "preversion": "lerna run coverage && lerna run build", + "prepare": "husky install" + }, +``` + +Run `npm run lint:all`. If it reports errors **in the test files you just +wrote**, fix the test files. If it reports errors in pre-existing `src/` files, +do NOT fix them — record them in your report and move on (`src/` is out of +scope). If a lint rule is fundamentally incompatible with test files (e.g. +`import/no-extraneous-dependencies` firing on devDependency imports), add a +narrowly-scoped override to `.eslintrc.cjs` for `packages/*/tests/**` only, in +the same style as the existing `'**/*.config.*'` override at the bottom of that +file. + +**Verify**: `npm test` → core, react and preact each run their suites, exit 0. +`npm run lint:all` → exit 0, or exits non-zero **only** on pre-existing `src/` +findings which you have listed in your report. + +### Step 5: Replace the CI workflow + +Rewrite `.github/workflows/unit-tests.yml` entirely: + +```yaml +name: CI + +on: + push: + branches: ['*'] + pull_request: + branches: ['*'] + +jobs: + test: + name: Lint & test (node ${{ matrix.node-version }}) + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + node-version: [20, 22, 24] + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + cache: 'npm' + + - name: Install + run: npm ci + + - name: Lint + run: npm run lint:all + + - name: Test + run: npm test +``` + +Rationale for each change, so you can defend it in review: `setup-node@v4` and +`checkout@v4` run on a supported action runtime (v2 does not); `setup-node`'s +built-in `cache: 'npm'` replaces the hand-rolled `actions/cache` step, which was +both redundant and ordered before the Node install it depended on; `npm ci` is +the correct command when `package-lock.json` is committed; Node 16, 18 and 21 are +all past end-of-life and are dropped. + +**Verify**: `npx --yes yaml-lint .github/workflows/unit-tests.yml` → valid, or if +that tool is unavailable, `node -e "require('fs').readFileSync('.github/workflows/unit-tests.yml','utf8')"` +plus a careful re-read. If `act` or a push to a branch is available, confirm the +workflow goes green. + +## Test plan + +New files (all modelled structurally on `packages/core/tests/index.test.ts` — +`tests/` directory, `describe('@tiny-intl/')`, per-test `{ expect }` +fixture, shared fixture dictionary): + +| File | Cases | +|---|---| +| `packages/react/tests/useIntl.test.tsx` | throws outside provider; `t` returns translation; re-renders on `intl.change`; `n` and `dt` format per locale | +| `packages/react/tests/Translate.test.tsx` | ``; `count={2}` renders the plural form; `number={1000}` renders `1,000`; `date=` renders a formatted date; function-as-children receives the value | +| `packages/preact/tests/useIntl.test.tsx` | same as react | +| `packages/preact/tests/Translate.test.tsx` | same as react | + +Do **not** add `count={0}` or `number={0}` cases here. Those are the regression +tests for plan 003 and belong in that plan, so that plan 003 has a test that goes +red-then-green. + +Verification: `npm test` → all suites pass; 4 new test files exist. + +## Done criteria + +ALL must hold: + +- [ ] `npm ci` exits 0 +- [ ] `npm test` exits 0 and runs suites for core, react and preact +- [ ] `npm run lint:all` exits 0, or fails only on pre-existing `src/` findings listed in the report +- [ ] `ls packages/react/tests packages/preact/tests` — each contains at least 2 test files +- [ ] `git diff --name-only 794eb48..HEAD -- 'packages/*/src'` returns **no output** (no source file was modified) +- [ ] `git status --porcelain packages/solid-js` returns no output (that package was not touched) +- [ ] `.github/workflows/unit-tests.yml` contains `actions/setup-node@v4` and `npm ci`, and contains no occurrence of `@v2` +- [ ] The report records the Step 1 baseline results for all four commands +- [ ] `plans/README.md` status row for 001 updated + +## STOP conditions + +Stop and report back (do not improvise) if: + +- Installing the testing libraries produces peer-dependency errors that only + resolve by upgrading vitest or vite to a new major — that is explicitly out of + scope. Report the conflict. +- You find yourself needing to edit a file under `packages/react/src/` or + `packages/preact/src/` to make a test pass. That is the signal that you have + crossed into plan 003. +- `npm test` (via `lerna run test`) fails because `packages/solid-js` still + exists and has no `test` script. `lerna run` skips packages without the script, + so this should not happen — if it does, report rather than adding a script + there. +- A test you write to document *current* behaviour fails in a way that looks like + a real bug in react or preact that is not the `count={0}` / `number={0}` issue + plan 003 covers. Record the finding; do not fix it. + +## Maintenance notes + +- **Not covered by this plan, deliberately**: CI does not run `lerna run build`, + so a broken adapter build still won't be caught. Nothing blocks adding it — + all three builds are green as of 2026-08-12 — it was simply left out to keep + this plan's diff small. Worth adding as a follow-up, ideally together with a + check that fails on `TS2742` (see plan 006: those diagnostics currently pass + CI silently while dropping a declaration file from the published package). +- Also worth adding to CI once plan 005 lands: `npx publint` on each package, so + a broken `exports` map cannot ship again. +- The `preversion` script (`lerna run coverage && lerna run build`) will now do + more work, because two more packages gained a `coverage` script. That is + intended. +- Reviewers should check that no test asserts against built output (`lib/`) — + every import must come from `../src` or from the `@tiny-intl/core` workspace + package, matching the core suite. +- If a fourth adapter is ever added (Vue, Svelte), the react harness in Step 2 is + the template to copy — it is the simplest of the two. diff --git a/plans/002-remove-solid-js-support.md b/plans/002-remove-solid-js-support.md new file mode 100644 index 0000000..dd75e8e --- /dev/null +++ b/plans/002-remove-solid-js-support.md @@ -0,0 +1,499 @@ +# Plan 002: Remove `@tiny-intl/solid-js` from the monorepo and deprecate it on npm + +> **Executor instructions**: Follow this plan step by step. Run every +> verification command and confirm the expected result before moving to the +> next step. If anything in the "STOP conditions" section occurs, stop and +> report — do not improvise. When done, update the status row for this plan +> in `plans/README.md`. +> +> **Drift check (run first)**: +> `git diff --stat 794eb48..HEAD -- packages/solid-js .eslintrc.cjs package.json commitlint.config.js README.md` +> If any in-scope file changed since this plan was written, compare the +> "Current state" excerpts against the live code before proceeding; on a +> mismatch, treat it as a STOP condition. +> +> **This plan deletes a published package.** Step 7 is human-gated and must NOT +> be executed by an agent. + +## Why this matters + +The maintainer has decided to drop Solid support in the next release. This plan +carries that decision out. + +The decision is well-supported by what the audit found. `@tiny-intl/solid-js` is +the only adapter whose primary API does not work at all: `memoizeCallback` at +`packages/solid-js/src/useIntl.tsx:22` calls `fn.call(args)`, which passes the +argument *array* as the `this` value and forwards **no arguments**, so every +primitive returned by `useIntl()` — `t`, `tc`, `n`, `dt`, `rt`, `sort`, +`collator`, `list` — returns `'[undefined]'` or its numeric equivalent rather +than a translation. That has been the published behaviour since the adapter +shipped, which is also a strong signal about how many users it has. + +Removing it deletes a third copy of the triplicated `Translate.tsx`, drops two +lint dependencies plus the whole Solid toolchain from the dev install, and +removes the single hardest piece of work from plan 001 (Solid + vitest 0.34 +configuration). Plans 001 and 003 have already been scoped to exclude +`packages/solid-js` on the assumption this plan lands. + +What removal does **not** do: it does not un-publish anything. `@tiny-intl/solid-js` +versions up to `1.2.0` stay installable from npm forever. Step 7 covers the +deprecation notice that tells existing installers what happened — that is the +part that actually reaches users, and it needs a human with publish rights. + +## Status + +- **Priority**: P1 +- **Effort**: S +- **Risk**: LOW-MED — the code deletion is mechanical and fully verifiable; the + risk lives in Step 7 (npm deprecation), which is human-gated and reversible. +- **Depends on**: none. Can run first, or in parallel with 001, 004 and 005. +- **Category**: tech-debt +- **Planned at**: commit `794eb48`, 2026-08-12 + +## Current state + +Every reference to Solid in the repository, found with +`grep -rni solid . --exclude-dir={node_modules,.git,lib,dist,coverage,plans}`. +This list is exhaustive as of `794eb48` — if you find a reference not on it, +that is drift and a STOP condition. + +### 1. The package itself — `packages/solid-js/` (13 tracked files) + +``` +packages/solid-js/.gitignore +packages/solid-js/README.md +packages/solid-js/package.json +packages/solid-js/src/Translate.tsx +packages/solid-js/src/index.ts +packages/solid-js/src/useIntl.tsx +packages/solid-js/src/vite-env.d.ts +packages/solid-js/tsconfig.json +packages/solid-js/tsconfig.node.json +packages/solid-js/vite.config.ts +``` + +(plus git-ignored `lib/` and `dist/` build output in the working tree) + +`packages/solid-js/package.json` declares `"name": "@tiny-intl/solid-js"`, +`"version": "1.2.0"`, a `solid-js: >=1.5.0` peer dependency and a +`@tiny-intl/core: ^1.2.0` dependency. **No other package in this repo depends on +it** — `packages/react/package.json` and `packages/preact/package.json` depend +only on `@tiny-intl/core`, and nothing imports from `@tiny-intl/solid-js`. + +### 2. `.eslintrc.cjs:72-79` — the Solid lint override + +```js + { + files: ['packages/solid-js/**/*.ts', 'packages/solid-js/**/*.tsx'], + + extends: [ + '@gridventures/eslint-config-solid-js/typescript', + '@gridventures/eslint-config-base/prettier', + ], + }, +``` + +This is the third entry in the `overrides` array, between the React/Preact +override (ends line 70) and the `'**/*.config.*'` override (starts line 81). + +### 3. `package.json:22,26` — two Solid-only devDependencies + +```json + "@gridventures/eslint-config-base": "^1.3.3", + "@gridventures/eslint-config-react": "^1.4.0", + "@gridventures/eslint-config-solid-js": "^1.4.1", + "@gridventures/eslint-config-typescript": "^1.3.3", + "@types/node": "^20.10.3", + "eslint": "^8.35.0", + "eslint-plugin-solid": "^0.13.0", + "husky": "^8.0.3", +``` + +Both exist only to lint `packages/solid-js`. Note that `eslint-plugin-solid` is +declared at the root but never referenced by name in `.eslintrc.cjs` — it is +pulled in transitively by `@gridventures/eslint-config-solid-js`. + +### 4. `commitlint.config.js:8` — the commit-scope allow-list + +```js + 'scope-enum': async () => { + return [2, 'always', ['core', 'react', 'solid-js', 'preact']]; + }, +``` + +This is enforced on every commit via `.husky/commit-msg`. Leaving `'solid-js'` +in place after the package is gone means the tooling still advertises a scope +that has no code behind it. + +### 5. `README.md:10` — the package list entry + +```markdown +- [@tiny-intl/solid](./packages/solid) ![Solid Adater Size](https://deno.bundlejs.com/badge?q=@tiny-intl/solid-js&treeshake=%5B*%5D&config=%7B%22esbuild%22:%7B%22external%22:%5B%22solid-js%22%5D%7D%7D) +``` + +(The link target `./packages/solid` was already wrong — the directory is +`packages/solid-js`. The whole line goes away here, which resolves that.) + +### 6. `package-lock.json` — 13 Solid-related entries + +`node_modules/@gridventures/eslint-config-solid-js`, +`node_modules/@tiny-intl/solid-js`, `node_modules/babel-preset-solid`, +`node_modules/eslint-plugin-solid` (+ 5 nested `@typescript-eslint/*` entries), +`node_modules/solid-js`, `node_modules/solid-refresh`, +`node_modules/vite-plugin-solid`, and the `packages/solid-js` workspace entry. +These are regenerated, not hand-edited (Step 5). + +### Not a reference, but nearby + +`.vscode.tmpl/settings.json` sets `eslint.workingDirectories` to +`packages/store`, `packages/persist`, `packages/feathers` — none of which exist +in this repo. That file is stale boilerplate from another project. **Out of +scope**; recorded in `plans/README.md` as a deferred finding. + +### Conventions + +- Prettier: `singleQuote: true`, `printWidth: 100`, `trailingComma: "all"`; + 2-space indent (`.editorconfig`). +- `workspaces: ["packages/*"]` in the root `package.json` is a glob — removing + the directory is sufficient, no manifest edit is needed there. +- lerna runs in fixed mode (`lerna.json` has a single `version: "1.2.1"`) and + publishes with `--no-private`. A package that no longer exists is simply never + published again; no lerna configuration change is required. + +## Commands you will need + +> Read from `package.json` during planning; `node_modules` was not installed at +> planning time, so none of these were executed. + +| Purpose | Command | Expected on success | +|---|---|---| +| Install / regenerate lock | `npm install` | exit 0 | +| Core tests | `npm run test --workspace @tiny-intl/core -- --run` | all pass | +| Build an adapter | `npm run build --workspace @tiny-intl/react` | exit 0 | +| Lint | `npm run lint -- packages` (or `npm run lint:all` once plan 001 has landed) | exit 0 | +| List workspaces | `npm query ".workspace"` | three packages, no solid-js | + +## Scope + +**In scope** (the only files you should modify or delete): + +- `packages/solid-js/**` — delete the entire directory +- `.eslintrc.cjs` — remove the Solid override block +- `package.json` (root) — remove two devDependencies +- `commitlint.config.js` — remove the `solid-js` scope +- `README.md` (root) — remove the package-list line +- `package-lock.json` — regenerated by `npm install` +- `plans/README.md` — status row only + +**Out of scope** (do NOT touch, even though they look related): + +- **`packages/react/**` and `packages/preact/**`.** They share no code with the + Solid adapter — `Translate.tsx` is copy-pasted, not imported. Nothing about + removing Solid requires touching them. +- **`packages/core/**`.** Core has no knowledge of any adapter. +- `lerna.json`, package `version` fields, and anything about the release + process. Cutting the release is a human action (Step 7). +- `.vscode.tmpl/settings.json` — stale, but unrelated (see above). +- **Do not run `npm deprecate`, `npm unpublish`, `npm publish`, or + `lerna publish`.** Step 7 is documentation for a human. +- Do not add a replacement, a shim, or a migration guide for Solid users beyond + the deprecation message text in Step 7. If a migration path is wanted, that is + a separate decision. + +## Git workflow + +- Branch: `advisor/002-remove-solid-js-support` +- Conventional commits, enforced by commitlint via `.husky/commit-msg`. + **Sequencing matters here**: commit the `commitlint.config.js` change + (Step 4) *last*, or use a non-`solid-js` scope throughout — once `'solid-js'` + is removed from the `scope-enum`, a commit message scoped `solid-js` will be + rejected by the hook. + Suggested messages, all using allowed scopes: + - `feat: remove solid-js adapter package` (no scope — allowed) + - `chore: drop solid-js lint dependencies` + - `docs: remove solid-js from the package list` + + Prior art in `git log`: `feat: removed defaultProps for compatibility reasons`, + `chore: updated outdated dev dependencies`. +- This is a **breaking change for consumers of `@tiny-intl/solid-js`**. Say so in + the commit body so whoever cuts the release picks the right version bump. +- Do NOT push, publish, or open a PR unless the operator instructed it. + +## Steps + +### Step 1: Confirm nothing depends on the package + +Before deleting anything, prove the removal is safe: + +```bash +grep -rn "@tiny-intl/solid-js" --exclude-dir=node_modules --exclude-dir=.git \ + --exclude-dir=lib --exclude-dir=dist --exclude-dir=plans \ + --include=package.json --include=*.ts --include=*.tsx . +``` + +**Verify**: the only matches are inside `packages/solid-js/` itself and in +`package-lock.json`. If any file under `packages/core`, `packages/react` or +`packages/preact` references `@tiny-intl/solid-js`, STOP and report — the +dependency graph is not what this plan assumes. + +Also record the current published state for the Step 7 handoff: + +```bash +npm view @tiny-intl/solid-js versions --json +npm view @tiny-intl/solid-js dist-tags --json +``` + +(If the registry is unreachable, note it and continue — this is information for +the human doing Step 7, not a gate.) + +### Step 2: Delete the package directory + +```bash +git rm -r packages/solid-js +rm -rf packages/solid-js +``` + +(The second command clears the git-ignored `lib/`, `dist/` and `node_modules` +that `git rm` leaves behind.) + +**Verify**: + +```bash +test -d packages/solid-js && echo "STILL PRESENT" || echo "removed" +``` +→ `removed`. + +### Step 3: Remove the ESLint override + +In `.eslintrc.cjs`, delete the entire override object at lines 72-79 — the one +whose `files` array is `['packages/solid-js/**/*.ts', 'packages/solid-js/**/*.tsx']`. +Leave the React/Preact override above it and the `'**/*.config.*'` override +below it exactly as they are, and make sure the surrounding array commas stay +valid. + +Then remove the two Solid-only devDependencies from the root `package.json`: + +```bash +npm uninstall @gridventures/eslint-config-solid-js eslint-plugin-solid +``` + +**Verify**: + +```bash +grep -n "solid" .eslintrc.cjs package.json +``` +→ no output. + +```bash +node -e "require('./.eslintrc.cjs'); console.log('eslintrc parses')" +``` +→ prints `eslintrc parses`. + +### Step 4: Remove the `solid-js` commit scope + +In `commitlint.config.js`, change line 8 from: + +```js + return [2, 'always', ['core', 'react', 'solid-js', 'preact']]; +``` + +to: + +```js + return [2, 'always', ['core', 'react', 'preact']]; +``` + +**Verify**: + +```bash +echo "fix(solid-js): should be rejected" | npx commitlint +``` +→ exits **non-zero** with a `scope must be one of [core, react, preact]` error. + +```bash +echo "fix(react): should be accepted" | npx commitlint +``` +→ exits 0. + +Remember the Git-workflow note: from this point on, no commit message may use +the `solid-js` scope. + +### Step 5: Update the README and regenerate the lockfile + +Delete line 10 of the root `README.md` — the whole +`- [@tiny-intl/solid](./packages/solid) ...` bullet. The list should end with +the preact entry. + +Then add a short note below the list so anyone landing on the repo (or arriving +from the npm page) understands what happened: + +```markdown +> [!NOTE] +> `@tiny-intl/solid-js` was removed in the next release. Published versions up +> to 1.2.0 remain installable from npm but are no longer maintained. +``` + +Regenerate the lockfile: + +```bash +npm install +``` + +**Verify**: + +```bash +grep -c "solid" package-lock.json +``` +→ `0`. + +```bash +npm query ".workspace" | grep -c '"name"' +``` +→ `3`. (If `npm query` is unavailable on the installed npm version, use +`ls packages/` → `core preact react`.) + +### Step 6: Full verification + +```bash +npm run lint -- packages +npm run test --workspace @tiny-intl/core -- --run +npm run build --workspace @tiny-intl/core +npm run build --workspace @tiny-intl/react +npm run build --workspace @tiny-intl/preact +``` + +**Verify**: lint exits 0; core's 18 tests pass; all three builds exit 0. + +If a react or preact build fails inside `Translate.tsx` with a `TS2345` +union-destructuring error, that is the **pre-existing** failure owned by plan +003 — confirm the error text matches what plan 003 describes, note it, and +continue. It is not caused by this plan. Any *other* failure is a STOP +condition. + +Also confirm you have not touched anything you shouldn't have: + +```bash +git status --porcelain +``` +→ lists only the files in the In-scope list (plus deletions under +`packages/solid-js/`). + +### Step 7: Hand off the npm deprecation — DO NOT RUN THIS + +**This step is for a human with npm publish rights. An executor agent must not +perform it.** Deleting the directory stops future publishes; it does nothing to +the versions already on the registry. Anyone running +`npm install @tiny-intl/solid-js` today still gets `1.2.0` and no indication it +is dead. + +Include the following in your final report, verbatim, as the handoff: + +> **Manual follow-up required before/with the next release.** +> +> Mark the published package deprecated so existing and new installers are +> warned: +> +> ``` +> npm deprecate @tiny-intl/solid-js "No longer maintained. Solid support was removed from tiny-intl; use @tiny-intl/core directly." +> ``` +> +> Notes for whoever runs it: +> - `npm deprecate` is **reversible** — passing an empty string as the message +> clears the notice. +> - It does **not** unpublish. Existing installs keep working; new installs print +> the warning. Do not use `npm unpublish` — it breaks anyone pinned to that +> version. +> - Requires publish rights on the `@tiny-intl` scope. +> - The next release is a **breaking change** for Solid consumers even though +> the remaining packages are unaffected; pick the version bump accordingly and +> mention the removal in the release notes. +> - Published versions at time of removal: (paste the `npm view` output from +> Step 1 here). + +**Verify**: the handoff text above appears in your report. Do not run any npm +publish or deprecate command. + +## Test plan + +There are no new tests. This plan deletes code; the verification is that +everything else still builds, lints and passes: + +- `npm run test --workspace @tiny-intl/core -- --run` → the existing 18 core + tests pass unchanged. Core is untouched, so any failure means something went + wrong outside the intended scope. +- `npm run build` for core, react and preact → all exit 0, proving no build + depended on the Solid package or its toolchain. +- `npm run lint -- packages` → exits 0, proving the ESLint config is still valid + after the override block was removed. +- `npx commitlint` on a `solid-js`-scoped message → rejected (Step 4). +- Every `grep` in the Done criteria returning empty is the machine-checkable + proof that no reference survived. + +Do **not** add tests, and do not port any of the Solid package's would-be tests +elsewhere. + +## Done criteria + +ALL must hold: + +- [ ] `test -d packages/solid-js` → false +- [ ] `grep -rni solid . --exclude-dir={node_modules,.git,lib,dist,coverage,plans} --exclude=package-lock.json` + → **exactly one line**: the `@tiny-intl/solid-js` mention inside the + README removal note that Step 5 adds. No code, config or dependency + reference may remain. + *(Corrected 2026-08-12: this criterion originally said "no output", which + contradicted Step 5's own instruction to add that note. The executor + caught the contradiction and followed Step 5 — the right call.)* +- [ ] `grep -c solid package-lock.json` → `0` +- [ ] `ls packages/` → exactly `core`, `preact`, `react` +- [ ] `node -e "require('./.eslintrc.cjs')"` exits 0 +- [ ] `echo "fix(solid-js): x" | npx commitlint` exits non-zero +- [ ] `npm run lint -- packages` exits 0 +- [ ] `npm run test --workspace @tiny-intl/core -- --run` exits 0 (18 tests) +- [ ] `npm run build` exits 0 for core, react and preact +- [ ] `README.md` contains no `packages/solid` link and does contain the removal note +- [ ] `git status --porcelain` lists only in-scope paths +- [ ] The Step 7 npm-deprecation handoff text is in the final report, and no + npm publish/deprecate command was executed +- [ ] `plans/README.md` status row for 002 updated + +## STOP conditions + +Stop and report back (do not improvise) if: + +- Step 1 finds any file under `packages/core`, `packages/react` or + `packages/preact` importing from or depending on `@tiny-intl/solid-js`. +- `grep -rni solid` turns up a reference not listed in "Current state" — the + repo has drifted since this plan was written, and the removal may be + incomplete. +- Removing `@gridventures/eslint-config-solid-js` or `eslint-plugin-solid` + breaks linting of the react, preact or core packages. That would mean another + config depends on them transitively; report before re-adding anything. +- A react or preact build fails with an error **other than** the pre-existing + `TS2345` in `Translate.tsx` described in plan 003. +- You find yourself needing to edit `packages/react`, `packages/preact` or + `packages/core` to make anything pass. +- Anyone or anything suggests running `npm unpublish`. Do not. + +## Maintenance notes + +- **The finding this supersedes**: the `fn.call(args)` bug at + `packages/solid-js/src/useIntl.tsx:22` is now closed by deletion rather than + by a fix. It is recorded with its evidence in `plans/README.md` under + "Findings closed by plan 002" in case the decision is ever revisited — the + fix was a one-line change to `fn(...args)` plus tests. +- **Plans 001 and 003 assume this lands.** Both have been scoped to react and + preact only. If this plan is rejected or reverted, `packages/solid-js` will be + left with no test coverage and two unfixed bugs (the `useIntl` breakage above, + and the falsy `count`/`number` props in its `Translate.tsx`), and those two + plans need re-scoping to cover it again. +- **`Translate.tsx` is still duplicated** between react and preact — byte-identical + below the imports. Removing the third copy reduces the drift surface but does + not eliminate it; plan 003 keeps a `diff` check between the two. +- **What a reviewer should scrutinise**: that the diff contains no changes under + `packages/core`, `packages/react` or `packages/preact`; that the `overrides` + array in `.eslintrc.cjs` is still valid JavaScript with correct commas; and + that the commitlint scope change was committed with an allowed scope. +- If Solid support is ever reinstated, note that the hardest part was never the + adapter code — it was getting `vite-plugin-solid` to cooperate with vitest + 0.34 in a test environment. Budget for that before budgeting for the adapter. diff --git a/plans/003-fix-translate-falsy-props.md b/plans/003-fix-translate-falsy-props.md new file mode 100644 index 0000000..c5395c0 --- /dev/null +++ b/plans/003-fix-translate-falsy-props.md @@ -0,0 +1,452 @@ +# Plan 003: Fix `` — `count={0}` and `number={0}` render the wrong thing (or nothing) in the react and preact adapters + +> **Executor instructions**: Follow this plan step by step. Run every +> verification command and confirm the expected result before moving to the +> next step. If anything in the "STOP conditions" section occurs, stop and +> report — do not improvise. When done, update the status row for this plan +> in `plans/README.md`. +> +> **Drift check (run first)**: +> `git diff --stat 794eb48..HEAD -- packages/react/src/Translate.tsx packages/preact/src/Translate.tsx` +> If any in-scope file changed since this plan was written, compare the +> "Current state" excerpts against the live code before proceeding; on a +> mismatch, treat it as a STOP condition. + +## Status + +- **Priority**: P1 +- **Effort**: S +- **Risk**: LOW — the branch conditions change, so every `` prop + combination needs a test, but the typecheck baseline is verified green and the + change is three guards. +- **Depends on**: `plans/001-adapter-test-and-ci-baseline.md` +- **Category**: bug +- **Planned at**: commit `794eb48`, 2026-08-12 + +## Why this matters + +The `` component dispatches on which prop was passed, using +truthiness checks: `if (count)`, `if (date && ...)`, `if (number)`. Zero is +falsy, so the two most common zero-valued cases fall through to the wrong +branch: + +- `` skips the plural branch and lands on + `if (name)`, calling `t('document')`. Core's `t` falls back to the `.one` key + (`packages/core/src/createTinyIntl.ts:116`), so the user sees the **singular** + form — "Document" — where their dictionary explicitly defines a `zero` entry + saying "Documents". Empty-state counts are exactly where correct plural + handling matters most, and this is a library whose headline feature is native + `Intl.PluralRules` support. +- `` matches no branch at all and returns `null`. The + component **renders nothing** for the number zero. Any "0 items", "€0.00" or + "0%" in a consuming app silently disappears. +- `` (Unix epoch) has the same shape of problem, though + it is a far rarer input. + +Both published adapters carry the identical bug — `Translate.tsx` is byte-identical +between react and preact below the import block. + +### A note on `packages/solid-js` + +The Solid adapter had the same bug. It is not fixed here: that package is being +deleted by `plans/002-remove-solid-js-support.md`. This plan covers **react and +preact only** and never touches `packages/solid-js`, whether or not 002 has run +yet — there is no ordering constraint between the two plans. If plan 002 is +later rejected, this plan must be re-scoped to include the Solid copy (which +needs the same three condition changes, expressed as `props.count` / +`props.number` / `props.date` inside its `createMemo`). + +## Current state + +### Files and their roles + +- `packages/react/src/Translate.tsx` — React adapter; bug at lines 66 and 78. +- `packages/preact/src/Translate.tsx` — byte-identical to the React file below + the imports; same lines. + +### Excerpt — `packages/react/src/Translate.tsx:53-101` (the bug, and its surroundings) + +```tsx +export function Translate(props: TranslateProps) { + const intl = useContext(TinyIntlContext); + + if (!intl) { + throw new Error('useIntl must be used within a TinyIntlContext.Provider'); + } + + const { subscribe, t, tc, dt, n, rt } = intl; + const { name, count, date, number, options, children, relative } = props; // eslint-disable-line no-shadow + + const [changed, setChanged] = useState(0); + + const translateFn = useCallback(() => { + if (count) { // <-- BUG: 0 is falsy + return tc(name, count, options); + } + + if (date && !relative) { // <-- BUG: epoch 0 is falsy + return dt(date, options); + } + + if (date && relative) { + return rt(date, options); + } + + if (number) { // <-- BUG: 0 is falsy + return n(number, options); + } + + if (name) { + return t(name, options); + } + + return null; + }, [changed, count, date, name, number, options]); +``` + +`packages/preact/src/Translate.tsx:53-101` is the same code with +`from 'preact/compat'` on line 4. + +### Excerpt — `packages/react/src/Translate.tsx:8-51` (the props union — read this before Step 2) + +```tsx +export type TranslateProps = { + children?: (value: string | null) => React.ReactNode; +} & ( + | { + // Case: string translation + name: string; + count?: number; + date?: undefined; + number?: undefined; + options?: TinyIntlTranslateTemplate; + relative?: undefined; + unit?: undefined; + } + | { + // Case: dateFormat + name?: undefined; + count?: undefined; + data?: undefined; + date?: Date | string | number; + number?: undefined; + options?: Intl.DateTimeFormatOptions; + relative?: undefined; + } + | { + // Case: relativeTimeFormat + name?: undefined; + count?: undefined; + data?: undefined; + date?: Date | string | number; + number?: undefined; + options?: Intl.RelativeTimeFormatOptions; + relative?: true; + } + | { + // Case: numberFormat + name?: undefined; + count?: undefined; + data?: undefined; + date?: undefined; + number?: number; + options?: Intl.NumberFormatOptions; + relative?: undefined; + } +); +``` + +**TypeScript narrows this union correctly today — verified, 2026-08-12.** An +earlier draft of this plan claimed `tc(name, count, options)` was a latent +`TS2345` error. That was wrong, and it was measured: `npx tsc --noEmit` exits +**0** in both `packages/react` and `packages/preact` (with `src/Translate.tsx` +confirmed present in the compilation via `--listFiles`). + +The reason matters for Step 2. Each union member gives `count`, `date` and +`number` a *unit* type — `count?: number` in the first member and +`count?: undefined` in the other three — which is exactly the shape TypeScript's +control-flow analysis for destructured discriminated unions (TS 4.6+) can +discriminate on. So inside `if (count) { … }`, narrowing `count` to a truthy +`number` eliminates the three members where it is `undefined`, which in turn +narrows `name` to `string` and `options` to `TinyIntlTranslateTemplate | undefined`. +The call typechecks. + +**Consequence for the fix**: replacing `if (count)` with +`typeof count === 'number'` should narrow *just as well* — it also excludes the +`undefined` members. So the casts an earlier draft prescribed are most likely +unnecessary, and adding them would weaken type safety for no reason. Step 2 +therefore tells you to write the fix **without** casts and let the compiler +decide. + +Note the typo carried through three of the four union members in both packages: +`data?: undefined` where `date?: undefined` was clearly meant. Fixing that would +change which prop combinations typecheck for consumers — **out of scope here**, +recorded in `plans/README.md` as a deferred finding. + +### Conventions + +- Prettier: `singleQuote: true`, `printWidth: 100`, `trailingComma: "all"`; + 2-space indent. +- React/Preact files are linted with `@gridventures/eslint-config-react` + + `/hooks` (see the `overrides` block in `.eslintrc.cjs`). +- The existing `/* eslint-disable react-hooks/exhaustive-deps */` at the top of + both files stays — do not remove it. + +## Commands you will need + +> Read from `package.json` during planning; `node_modules` was not installed at +> planning time, so these were not executed. + +| Purpose | Command | Expected on success | +|---|---|---| +| Install | `npm install` | exit 0 | +| Typecheck + build (per package) | `npm run build --workspace @tiny-intl/react` | exit 0 | +| Tests (per package) | `npm run test --workspace @tiny-intl/react -- --run` | all pass | +| Full suite | `npm test` | all pass | +| Lint | `npm run lint:all` | exit 0 | + +## Scope + +**In scope** (the only files you should modify): + +- `packages/react/src/Translate.tsx` +- `packages/preact/src/Translate.tsx` +- `packages/react/tests/Translate.test.tsx` +- `packages/preact/tests/Translate.test.tsx` +- `plans/README.md` — status row only + +**Out of scope** (do NOT touch, even though they look related): + +- **`packages/solid-js/**`** — being deleted by plan 002. If the directory still + exists when you run, leave it alone. +- **The `TranslateProps` union itself.** Do not add a discriminant, do not fix + the `data?: undefined` typo, do not split it into separate components. Any of + those changes what compiles for existing consumers — a breaking change that + needs its own release decision. This plan changes runtime behaviour only, plus + the minimum type assertions needed to keep `tsc` quiet. +- `packages/*/src/useIntl.tsx` — the react/preact ones have a separate deferred + finding (stale `useCallback` identity). +- `packages/core/**` — core's `t()` fallback to `.one` is intended behaviour for + a missing key. +- The falsy-`options` question. `options={0}` is not representable in the type; + ignore it. + +## Git workflow + +- Branch: `advisor/003-fix-translate-falsy-props` +- Conventional commits, enforced by commitlint, with scopes limited to + `core`, `react`, `preact` (and `solid-js` until plan 002 removes it). One + commit per package reads well here: + `fix(react): render Translate correctly for zero count and number`, then + `fix(preact): ...`. Prior art in `git log`: + `fix(core): return fallback if navigator is undefined`. +- Do NOT push or open a PR unless the operator instructed it. + +## Steps + +### Step 1: Reproduce the bug, and confirm the typecheck baseline is green + +```bash +npm install +npm run build --workspace @tiny-intl/react +cd packages/react && npx tsc --noEmit; echo "tsc exit=$?"; cd ../.. +``` + +**Expected**: build exits 0 and `tsc exit=0`. This was verified on 2026-08-12 — +there is **no** pre-existing type error in `Translate.tsx`. If you see type +errors here, STOP: the repo has drifted and the analysis in "Current state" no +longer holds. + +(Capture `tsc`'s exit code without a pipe — `npx tsc --noEmit | head` reports +`head`'s status, not the compiler's.) + +Then add failing tests **before** touching the source. In +`packages/react/tests/Translate.test.tsx` (created by plan 001), add: + +```tsx +it('renders the zero plural form for count={0}', async ({ expect }) => { + // de-DE dictionary defines document.zero = 'Dokumente', document.one = 'Dokument' + // expected: 'Dokumente' +}); + +it('renders a formatted zero for number={0}', async ({ expect }) => { + // expected: '0', not '' and not null +}); +``` + +**Verify**: `npm run test --workspace @tiny-intl/react -- --run` → these two +tests **fail**, and the failure messages show `'Dokument'` (singular) and an +empty render respectively. If they pass, STOP — the bug does not reproduce and +this plan's premise is wrong. + +### Step 2: Fix `packages/react/src/Translate.tsx` + +Replace the body of `translateFn` (lines 65-87) with explicit presence checks. +**Write it exactly as below — with no type assertions.** See "Current state": +TypeScript discriminates this union on the unit types of `count`/`date`/`number`, +and `typeof` guards preserve that narrowing. + +```tsx + const translateFn = useCallback(() => { + if (typeof count === 'number') { + return tc(name, count, options); + } + + if (date !== undefined && !relative) { + return dt(date, options); + } + + if (date !== undefined && relative) { + return rt(date, options); + } + + if (typeof number === 'number') { + return n(number, options); + } + + if (name) { + return t(name, options); + } + + return null; + }, [changed, count, date, name, number, options, relative]); +``` + +Three things changed and each is load-bearing: + +1. `if (count)` → `typeof count === 'number'`, so `0` takes the plural branch. +2. `if (number)` → `typeof number === 'number'`, so `0` is formatted. +3. `date &&` → `date !== undefined`, so the epoch `0` formats as a date. + +Plus `relative` is added to the `useCallback` dependency array, where it was +missing. Everything else — including the final `if (name)` — is unchanged. + +**If and only if `tsc` then reports an error**, add the narrowest fix that +silences it, in this order of preference: (a) an extra `typeof` guard that helps +the compiler discriminate, e.g. `typeof count === 'number' && typeof name === 'string'`; +(b) failing that, an `as` assertion on the specific argument that errors, with a +comment naming the TS error code. Do **not** pre-emptively add assertions. +**Report every assertion you had to add, with the exact compiler error that +forced it** — that outcome contradicts the verified baseline and your reviewer +needs to know. + +`TinyIntlTranslateTemplate` is already imported at the top of the file +(line 2) — no new import is needed unless you add an assertion that uses it. + +**Verify**: + +```bash +npm run build --workspace @tiny-intl/react +cd packages/react && npx tsc --noEmit; echo "tsc exit=$?"; cd ../.. +npm run test --workspace @tiny-intl/react -- --run +``` +→ build exits 0, `tsc exit=0`, and the two tests from Step 1 now pass. + +### Step 3: Apply the identical fix to `packages/preact/src/Translate.tsx` + +The Preact file is character-identical to the React one from line 8 onward. +Apply exactly the same replacement, then confirm the two files have not drifted +apart in any way other than their imports: + +```bash +diff <(tail -n +8 packages/react/src/Translate.tsx) <(tail -n +8 packages/preact/src/Translate.tsx) +``` +→ no output (both files are identical after their import blocks). + +Add the same two tests to `packages/preact/tests/Translate.test.tsx`. + +**Verify**: `npm run build --workspace @tiny-intl/preact` exits 0; +`npm run test --workspace @tiny-intl/preact -- --run` → all pass. + +### Step 4: Prove no truthiness checks on numeric props remain + +```bash +grep -rn "if (count)\|if (number)\|date &&" packages/react/src/Translate.tsx packages/preact/src/Translate.tsx +``` +→ no output. + +```bash +npm test +npm run lint:all +``` + +**Verify**: full suite passes; lint exits 0. + +## Test plan + +Add to each of `packages/react/tests/Translate.test.tsx` and +`packages/preact/tests/Translate.test.tsx` — modelled structurally on +`packages/core/tests/index.test.ts` (per-test `{ expect }` fixture, shared +fixture dictionary, `describe` named after the package): + +| Case | Props | Expected | +|---|---|---| +| zero plural (regression) | `name="document" count={0}`, locale de-DE | `Dokumente` (the `zero` entry), **not** `Dokument` | +| one plural | `name="document" count={1}`, de-DE | `Dokument` | +| many plural | `name="document" count={5}`, de-DE | `Dokumente` | +| zero number (regression) | `number={0}`, en-US | `0` — non-empty render | +| non-zero number | `number={1000}`, en-US | `1,000` | +| epoch date (regression) | `date={0}` | a formatted date string, non-empty | +| plain name | `name="inbox"`, de-DE | `Posteingang` | +| template params | `name="hello" options={{ name: 'John' }}` | `Hello, John!` | +| function children | `name="inbox"` with `children={(v) => ...}` | the callback receives the translated value | +| no matching prop | `{}` (cast as needed) | renders nothing | + +The first, fourth and sixth rows are the regression tests for this plan — they +must be observed failing in Step 1 before the fix. + +Verification: `npm test` → all pass, with ≥ 3 new tests per adapter package. + +## Done criteria + +ALL must hold: + +- [ ] `grep -rn "if (count)\|if (number)" packages/react/src/Translate.tsx packages/preact/src/Translate.tsx` → no output +- [ ] `grep -rn "date &&" packages/react/src/Translate.tsx packages/preact/src/Translate.tsx` → no output +- [ ] `npm run build --workspace @tiny-intl/react` exits 0 +- [ ] `npm run build --workspace @tiny-intl/preact` exits 0 +- [ ] `npm test` exits 0; each adapter has a passing `count={0}` and `number={0}` test +- [ ] `npm run lint:all` exits 0 +- [ ] `diff <(tail -n +8 packages/react/src/Translate.tsx) <(tail -n +8 packages/preact/src/Translate.tsx)` → no output +- [ ] `git status --porcelain packages/solid-js` returns no output (that package was not touched) +- [ ] `git status --porcelain` lists only files from the In-scope list +- [ ] `plans/README.md` status row for 003 updated + +## STOP conditions + +Stop and report back (do not improvise) if: + +- The Step 1 regression tests **pass** before the fix — the bug does not + reproduce as described and the diagnosis is wrong. +- `packages/react/src/Translate.tsx:66` does not read `if (count) {` — the code + has drifted since this plan was written. +- **`tsc --noEmit` reports errors in `Translate.tsx` in Step 1, before you have + changed anything.** The verified baseline is clean; errors there mean the repo + drifted and this plan's type analysis is stale. +- Making `tsc` pass after Step 2 requires changing `TranslateProps` itself, or + more than one `as` assertion per file. Report what you found; changing the + exported prop types is a breaking change and needs a human decision. +- Fixing the epoch-date case (`date !== undefined`) breaks an existing test — + it may reveal that some consumer path relies on `date={null}` or + `date={''}` falling through. Report before adapting. + +## Maintenance notes + +- **An explicit discriminant would still be an improvement.** TypeScript + narrows the current union only because every member happens to give + `count`/`date`/`number` a unit type; that is a property the union could lose + accidentally in any future edit, silently taking type safety with it. An + explicit `kind` prop, or splitting `` into ``, ``, + ``, would make the discrimination intentional and let TypeScript + reject invalid prop *combinations* at the call site (which it does not do + today). Breaking API change — next major. +- **Deferred, recorded in `plans/README.md`**: the `data?: undefined` typo in + three of the four union members (both packages) — clearly meant to be `date`, + and fixing it changes which prop combinations typecheck. +- `Translate.tsx` is duplicated between react and preact, byte-identical below + the imports (it used to be triplicated; plan 002 removes the third copy). + Every future change to one must be mirrored; the `diff` check in Step 3's + verification is worth promoting to a CI guard if this pattern persists. +- **What a reviewer should scrutinise**: that the branch *order* is unchanged + (count → date → relative date → number → name), since it defines precedence + when a consumer passes prop combinations the types don't forbid. diff --git a/plans/004-fix-dead-intl-formatter-caches.md b/plans/004-fix-dead-intl-formatter-caches.md new file mode 100644 index 0000000..5c0bd59 --- /dev/null +++ b/plans/004-fix-dead-intl-formatter-caches.md @@ -0,0 +1,448 @@ +# Plan 004: Fix the five `Intl` formatter caches in core — every one is written with one key and read with another, so none of them ever hits + +> **Executor instructions**: Follow this plan step by step. Run every +> verification command and confirm the expected result before moving to the +> next step. If anything in the "STOP conditions" section occurs, stop and +> report — do not improvise. When done, update the status row for this plan +> in `plans/README.md`. +> +> **Drift check (run first)**: +> `git diff --stat 794eb48..HEAD -- packages/core/src/createTinyIntl.ts packages/core/tests` +> If any in-scope file changed since this plan was written, compare the +> "Current state" excerpts against the live code before proceeding; on a +> mismatch, treat it as a STOP condition. + +## Status + +- **Priority**: P1 +- **Effort**: S +- **Risk**: LOW — output values are unchanged; only the number of `Intl` + constructions changes. The caches are already cleared on every locale change, + so keying purely by options is correct. +- **Depends on**: none (core already has a working vitest suite) +- **Category**: perf +- **Planned at**: commit `794eb48`, 2026-08-12 + +## Why this matters + +`createTinyIntl` builds five `Map` caches so that repeated calls to `n()`, +`dt()`, `rt()`, `list()` and `collator()` reuse their `Intl.*Format` instances. +Constructing an `Intl` formatter is the expensive part of the operation — it +resolves locale data and builds the format pattern — while `.format()` on an +existing instance is comparatively cheap. That is why the cache exists. + +The cache does not work. Each function computes a `cacheKey` from its options, +**writes** the formatter under `cacheKey`, and then **reads** with +`cache.get(locale)`. `locale` is a value like `'de-DE'`; the stored keys are +`'_'` or a JSON string like `'{"dateStyle":"full"}'`. They can never be equal, +so every lookup misses and a brand-new `Intl` formatter is constructed on +**every single call** to any of those five functions. The `Map`s fill up and are +never read. + +For the library's core use case — a list view formatting a few hundred numbers +or dates per render — this is the difference between five formatter +constructions and several hundred. It is a one-character-per-site fix in a +package that advertises itself on being small and fast. + +There is no correctness bug today: because the lookup always misses, the +formatter is always built with the caller's actual options. That also means the +naive "fix" of keying by `locale` would be actively wrong — it would return a +formatter built with someone else's options. The fix below keys by `cacheKey` +alone, which is safe precisely because `change()` clears all five caches. + +## Current state + +### Files and their roles + +- `packages/core/src/createTinyIntl.ts` — the whole of `createTinyIntl`, + including the five caches, the `newCacheKey` helper, and the `change()` + function that clears them. +- `packages/core/tests/index.test.ts` — the existing suite; already covers the + *output* of all five functions, so it protects this refactor. New tests here + will assert the cache actually hits. + +### Excerpt — `packages/core/src/createTinyIntl.ts:65-67` (the key helper) + +```ts +function newCacheKey(v?: object | undefined) { + return v ? JSON.stringify(v) : '_'; +} +``` + +### Excerpt — `packages/core/src/createTinyIntl.ts:78-97` (the caches, and where they are cleared) + +```ts + const numberFormatCache = new Map(); + const dateTimeFormatCache = new Map(); + const relativeTimeFormatCache = new Map(); + const listFormatCache = new Map(); + const collatorCache = new Map(); +``` + +```ts + async function change(nextLocale: Locales, staticDict?: TinyIntlDict, forceLoad = false) { + if (locale === nextLocale && !forceLoad) { + return dict; + } + locale = nextLocale; + pluralRules = new Intl.PluralRules(locale); + numberFormatCache.clear(); + dateTimeFormatCache.clear(); + relativeTimeFormatCache.clear(); + numberFormatCache.clear(); // <-- line 95: duplicate of line 92 + listFormatCache.clear(); + collatorCache.clear(); +``` + +Every cache is cleared whenever the locale changes — which is what makes +"key by options only" correct. Line 95 clears `numberFormatCache` a second time; +line 92 already did. Harmless, but it is the reason no one noticed that no cache +was clearing wrongly: all five *are* covered. + +### Excerpt — `packages/core/src/createTinyIntl.ts:129-206` (the five broken sites) + +```ts + function n(number: number, options?: Intl.NumberFormatOptions): string { + const cacheKey = newCacheKey(options); + let formatter = numberFormatCache.get(locale); // <-- reads `locale` + if (!formatter) { + formatter = new Intl.NumberFormat(locale, options); + numberFormatCache.set(cacheKey, formatter); // <-- writes `cacheKey` + } + return formatter.format(number); + } + + function dt(date: Date | string | number, options?: Intl.DateTimeFormatOptions): string { + const dateValue = new Date(date); + const cacheKey = newCacheKey(options); + let formatter = dateTimeFormatCache.get(locale); // <-- same + if (!formatter) { + formatter = new Intl.DateTimeFormat(locale, options); + dateTimeFormatCache.set(cacheKey, formatter); + } + return formatter.format(dateValue); + } + + // Intl.RelativeTimeFormat is not supported in Safari < 14 or on MacOS < 11 + function rt(date: Date | string | number, options?: TinyIntlRelativeTimeFormatOptions): string { + const { fallback, ...rtOptions } = options || {}; + const [value, unit] = automaticRelativeTimeFormat(date); + if (!Intl.RelativeTimeFormat) { + console.warn('Intl.RelativeTimeFormat is not supported in this browser'); + if (fallback) { + return fallback(value, unit); + } + return ''; + } + const cacheKey = newCacheKey(rtOptions); + let formatter = relativeTimeFormatCache.get(locale); // <-- same + if (!formatter) { + formatter = new Intl.RelativeTimeFormat(locale, rtOptions); + relativeTimeFormatCache.set(cacheKey, formatter); + } + return formatter.format(value, unit); + } + + function collator(options?: Intl.CollatorOptions) { + if (!Intl.Collator) { + console.warn('Intl.Collator is not supported in this browser'); + return (x: string, y: string) => x.localeCompare(y); + } + const cacheKey = newCacheKey(options || {}); // <-- note: `|| {}` + let formatter = collatorCache.get(locale); // <-- same + if (!formatter) { + formatter = new Intl.Collator(locale, options); + collatorCache.set(cacheKey, formatter); + } + return formatter.compare; + } + + function list(items: string[], options?: Intl.ListFormatOptions | 'AND' | 'OR') { + if (!Intl.ListFormat) { + console.warn('Intl.ListFormat is not supported in this browser'); + return items.join(', '); + } + let type: 'conjunction' | 'disjunction' = 'conjunction'; + if (options === 'OR') { + type = 'disjunction'; + } + const intlOptions = typeof options === 'string' ? ({ type, style: 'long' } as const) : options; + const cacheKey = newCacheKey(intlOptions); + let formatter = listFormatCache.get(locale); // <-- same + if (!formatter) { + formatter = new Intl.ListFormat(locale, intlOptions); + listFormatCache.set(cacheKey, formatter); + } + return formatter.format(items); + } +``` + +Note `collator` uses `newCacheKey(options || {})` while the other four use +`newCacheKey(options)`. That produces the key `'{}'` for a no-options call where +the others produce `'_'` — a cosmetic inconsistency worth normalising while you +are here. + +### Conventions + +- The file starts with `/* eslint-disable @typescript-eslint/naming-convention */` + — keep it. +- Prettier: `singleQuote: true`, `printWidth: 100`, `trailingComma: "all"`; + 2-space indent. +- Test style: see `packages/core/tests/index.test.ts` — `describe('@tiny-intl/core')`, + per-test `{ expect }` fixture argument (not an import), `vi` imported from + `vitest` for stubbing, `afterEach` resetting the locale to `'en-US'`. + +## Commands you will need + +> Read from `package.json` during planning; `node_modules` was not installed at +> planning time, so these were not executed. + +| Purpose | Command | Expected on success | +|---|---|---| +| Install | `npm install` | exit 0 | +| Core tests | `npm run test --workspace @tiny-intl/core -- --run` | all pass | +| Core coverage | `npm run coverage --workspace @tiny-intl/core` | exit 0 | +| Core build (also typechecks) | `npm run build --workspace @tiny-intl/core` | exit 0 | +| Lint | `npm run lint:all` (or `npm run lint -- packages/core`) | exit 0 | + +## Scope + +**In scope** (the only files you should modify): + +- `packages/core/src/createTinyIntl.ts` +- `packages/core/tests/index.test.ts` +- `plans/README.md` — status row only + +**Out of scope** (do NOT touch, even though they look related): + +- **`change()`'s early return and its `forceLoad` parameter.** There is a + separate known issue there (a lost-update race, and `staticDict` being + silently dropped when the locale is unchanged) recorded in `plans/README.md`. + Touching `change()` beyond deleting the one duplicate `.clear()` line will + collide with that work. +- Cache eviction / size bounds. The maps are unbounded by design; see + Maintenance notes. +- The `console.warn` calls in `rt`, `collator` and `list` — noisy, but changing + them changes observable behaviour that the existing test at + `packages/core/tests/index.test.ts:176-208` depends on. +- Anything under `packages/react` or `packages/preact` (or `packages/solid-js`, + if it is still present — plan 002 deletes it). + +## Git workflow + +- Branch: `advisor/004-fix-dead-intl-formatter-caches` +- Conventional commits (commitlint enforced). Prior art in `git log`: + `fix(core): improved performance by using param for initial dict loading`. + Suggested: `fix(core): read Intl formatter caches with the key they are written with`. +- Do NOT push or open a PR unless the operator instructed it. + +## Steps + +### Step 1: Write the failing cache-hit tests first + +Add a new block to `packages/core/tests/index.test.ts`, inside the existing +`describe('@tiny-intl/core')`. It must prove the cache hits by counting +constructor calls: + +```ts + it('reuses cached Intl formatters', async ({ expect }) => { + await intl.change('en-US'); + + const numberFormatSpy = vi.spyOn(Intl, 'NumberFormat'); + intl.n(1); + intl.n(2); + intl.n(3); + expect(numberFormatSpy).toHaveBeenCalledTimes(1); + + intl.n(4, { style: 'percent' }); + intl.n(5, { style: 'percent' }); + expect(numberFormatSpy).toHaveBeenCalledTimes(2); + numberFormatSpy.mockRestore(); + }); +``` + +Repeat the same shape for `Intl.DateTimeFormat` (via `intl.dt`), +`Intl.RelativeTimeFormat` (via `intl.rt` — stub `Date.now` as at +`packages/core/tests/index.test.ts:119-129`), `Intl.ListFormat` (via +`intl.list`) and `Intl.Collator` (via `intl.collator`). Five tests, or one test +with five sections. + +Add one more test proving the cache is invalidated on locale change: + +```ts + it('rebuilds Intl formatters after a locale change', async ({ expect }) => { + await intl.change('en-US'); + const spy = vi.spyOn(Intl, 'NumberFormat'); + intl.n(1000); + await intl.change('de-DE'); + intl.n(1000); + expect(spy).toHaveBeenCalledTimes(2); + spy.mockRestore(); + }); +``` + +If `vi.spyOn(Intl, 'NumberFormat')` does not work with `new` in vitest 0.34, +fall back to `vi.stubGlobal` with a hand-rolled counting wrapper that delegates +to the original constructor — the same `vi.stubGlobal` technique the suite +already uses for `navigator` at `packages/core/tests/index.test.ts:258`. Note +which approach you used in your report. + +**Verify**: `npm run test --workspace @tiny-intl/core -- --run` → the new +cache-hit tests **fail**, each reporting 3 (or 2) constructor calls where 1 was +expected. The locale-change test should already pass. If the cache-hit tests +pass before the fix, STOP — the bug does not reproduce. + +### Step 2: Read each cache with the key it is written with + +In `packages/core/src/createTinyIntl.ts`, change five lines. In each function, +the `.get(locale)` becomes `.get(cacheKey)`: + +| Line | Function | From | To | +|---|---|---|---| +| 131 | `n` | `numberFormatCache.get(locale)` | `numberFormatCache.get(cacheKey)` | +| 142 | `dt` | `dateTimeFormatCache.get(locale)` | `dateTimeFormatCache.get(cacheKey)` | +| 162 | `rt` | `relativeTimeFormatCache.get(locale)` | `relativeTimeFormatCache.get(cacheKey)` | +| 176 | `collator` | `collatorCache.get(locale)` | `collatorCache.get(cacheKey)` | +| 200 | `list` | `listFormatCache.get(locale)` | `listFormatCache.get(cacheKey)` | + +Do not change anything else in those functions — in particular, the `new Intl.*` +constructions must keep passing `locale` as their first argument. `locale` is +still the right thing to *construct* with; it is only wrong as a *lookup key*. + +While in `collator` (line 175), normalise the key computation for consistency +with the other four: + +```ts + const cacheKey = newCacheKey(options); +``` + +(dropping the `|| {}`, so a no-options call keys as `'_'` like everywhere else). + +**Verify**: + +```bash +grep -n "Cache.get(locale)" packages/core/src/createTinyIntl.ts +``` +→ no output. + +```bash +grep -c "Cache.get(cacheKey)" packages/core/src/createTinyIntl.ts +``` +→ `5`. + +```bash +npm run test --workspace @tiny-intl/core -- --run +``` +→ all tests pass, including the new cache-hit tests and all 18 pre-existing +tests. **The pre-existing tests are the real safety net here** — they assert +concrete formatted output for de-DE, en-US and sv-SE across numbers, dates, +relative times, sorting and lists. If any of them now fails, the caching change +has altered output and something is wrong. + +### Step 3: Remove the duplicate cache clear + +At `packages/core/src/createTinyIntl.ts:95`, delete the second +`numberFormatCache.clear();` (line 92 already does it). The block should end up +as exactly five `.clear()` calls, one per cache: + +```ts + numberFormatCache.clear(); + dateTimeFormatCache.clear(); + relativeTimeFormatCache.clear(); + listFormatCache.clear(); + collatorCache.clear(); +``` + +Do not otherwise modify `change()`. + +**Verify**: + +```bash +grep -c "numberFormatCache.clear()" packages/core/src/createTinyIntl.ts +``` +→ `1`. + +### Step 4: Full verification + +```bash +npm run build --workspace @tiny-intl/core +npm run coverage --workspace @tiny-intl/core +npm run lint:all +``` + +**Verify**: build exits 0; coverage run exits 0 with all tests passing; lint +exits 0. + +## Test plan + +- **File**: `packages/core/tests/index.test.ts` (existing). +- **Structural pattern**: the file itself — `describe('@tiny-intl/core')`, + per-test `{ expect }` fixture, `vi` for stubbing, `afterEach` resetting to + `'en-US'`. Place the new tests after the existing `'list formatting'` test so + related concerns stay together. +- **New cases**: + 1. `n` constructs one `Intl.NumberFormat` across repeated identical calls + 2. …and a second for a different options object + 3. `dt` — same two properties + 4. `rt` — same two properties (with `Date.now` stubbed) + 5. `list` — same two properties + 6. `collator` — same two properties + 7. a locale change forces reconstruction +- **Regression protection**: the 18 pre-existing tests already pin the formatted + output of every affected function in three locales. They must all still pass + unmodified — do not adjust an existing assertion to accommodate this change. +- **Verification**: `npm run test --workspace @tiny-intl/core -- --run` → all + pass, with ≥ 6 new tests. + +## Done criteria + +ALL must hold: + +- [ ] `grep -n "Cache.get(locale)" packages/core/src/createTinyIntl.ts` → no output +- [ ] `grep -c "Cache.get(cacheKey)" packages/core/src/createTinyIntl.ts` → `5` +- [ ] `grep -c "numberFormatCache.clear()" packages/core/src/createTinyIntl.ts` → `1` +- [ ] `npm run test --workspace @tiny-intl/core -- --run` exits 0, with ≥ 6 new tests +- [ ] All 18 pre-existing core tests pass **without any assertion being modified** + (`git diff packages/core/tests/index.test.ts` shows additions only) +- [ ] `npm run build --workspace @tiny-intl/core` exits 0 +- [ ] `npm run lint:all` exits 0 +- [ ] `git status --porcelain` lists only files from the In-scope list +- [ ] `plans/README.md` status row for 004 updated + +## STOP conditions + +Stop and report back (do not improvise) if: + +- The Step 1 cache-hit tests pass **before** the fix — the bug does not + reproduce and the diagnosis is wrong. +- `packages/core/src/createTinyIntl.ts:131` does not read + `let formatter = numberFormatCache.get(locale);` — the code has drifted. +- Any pre-existing test in `packages/core/tests/index.test.ts` fails after Step + 2. Do **not** edit that test to make it pass; the change has altered output, + which it must not, and that needs investigating. +- You conclude the cache should be keyed by `` `${locale}:${cacheKey}` ``. + It should not — `change()` clears every cache on locale change, so the locale + is already implied. If you believe `change()` can be reached without clearing, + report that finding instead of working around it. +- `vi.spyOn` on an `Intl` constructor cannot be made to work after two attempts + with both the `spyOn` and `stubGlobal` approaches. + +## Maintenance notes + +- **Unbounded growth, accepted**: each cache now grows one entry per distinct + options object per locale, cleared on every locale change. For normal usage + (a handful of format configurations) that is a few entries. A caller + generating options dynamically — e.g. `{ currency: order.currency }` across + many currencies — would grow the map. If that ever becomes a real + problem, an LRU bound is the fix; do not pre-emptively add one. +- **Key stability**: `newCacheKey` uses `JSON.stringify`, whose output depends + on property insertion order. `{ style: 'currency', currency: 'EUR' }` and + `{ currency: 'EUR', style: 'currency' }` produce different keys and therefore + two formatters. Correct, just slightly wasteful. Sorting keys before + stringifying would fix it, at the cost of doing more work on the hot path. +- **What a reviewer should scrutinise**: that every `new Intl.*(...)` call still + receives `locale` as its first argument, and that the diff to the existing + test file is additions only. +- If `change()` is ever refactored to *not* clear the caches (for example, to + cache across locales), these keys must immediately become locale-qualified. + That is the one change that would silently break this fix. diff --git a/plans/005-fix-core-package-exports.md b/plans/005-fix-core-package-exports.md new file mode 100644 index 0000000..7640364 --- /dev/null +++ b/plans/005-fix-core-package-exports.md @@ -0,0 +1,552 @@ +# Plan 005: Fix `@tiny-intl/core`'s `exports` map — the declared `types` path does not exist — and delete the dead vite build + +> **Executor instructions**: Follow this plan step by step. Run every +> verification command and confirm the expected result before moving to the +> next step. If anything in the "STOP conditions" section occurs, stop and +> report — do not improvise. When done, update the status row for this plan +> in `plans/README.md`. +> +> **Drift check (run first)**: +> `git diff --stat 794eb48..HEAD -- packages/core/package.json packages/core/vite.config.ts packages/core/tsup.config.ts packages/react/package.json packages/preact/package.json` +> If any in-scope file changed since this plan was written, compare the +> "Current state" excerpts against the live code before proceeding; on a +> mismatch, treat it as a STOP condition. + +## Status + +- **Priority**: P2 +- **Effort**: S +- **Risk**: MED — this changes how the published packages resolve for *every* + consumer. It cannot be verified by the unit tests; it needs the packaging + checks in Step 2. Get those green before merging. +- **Depends on**: none +- **Category**: tech-debt / dx +- **Planned at**: commit `794eb48`, 2026-08-12 + +## Why this matters + +`@tiny-intl/core` declares a `types` entry in its `exports` map that points at a +file that has never existed: + +```json +"types": "./lib/esm/types/index.d.ts" +``` + +The build (tsup) emits declarations to `lib/esm/index.d.ts` — there is no +`lib/esm/types/` directory at all. The `types` condition is also listed **last** +in the condition object, where the Node/TypeScript resolution algorithm — which +picks the first matching condition — will have already matched `import` or +`require` before reaching it. And the `require` branch points at +`./lib/cjs/index.js`, whose declaration file is `index.d.cts`, which is not the +name TypeScript looks for next to a `.js` file. + +For a TypeScript library, type resolution *is* the product. Consumers on +`moduleResolution: "node16" | "nodenext" | "bundler"` — the default for anything +modern — are relying on a fallback path rather than the declared one, and CJS +consumers are the most likely to get "Could not find a declaration file for +module '@tiny-intl/core'". The exact symptom varies by resolver and TypeScript +version, which is precisely why Step 2 measures it with a tool instead of +guessing. + +The same file also carries a dead build: `npm run build` runs `vite build` +(emitting to `dist/`) and then tsup (emitting to `lib/`), but `files` only ships +`lib` and `src`. Everything vite produces is thrown away on every build, along +with two devDependencies that exist solely to feed it. And tsup's +`entry: ['src']` glob sweeps in `src/vite-env.d.ts`, publishing junk +`lib/esm/vite-env.d.js` and `lib/cjs/vite-env.d.d.cts` files to npm. + +## Current state + +### Files and their roles + +- `packages/core/package.json` — the broken `exports` map; the `build` script + that runs the dead vite step. +- `packages/core/vite.config.ts` — a library build config emitting to `dist/`, + **and** the vitest `test: {}` config. The test config must survive; the build + config should not. +- `packages/core/tsup.config.ts` — the build that actually produces what ships. +- `packages/react/package.json`, `packages/preact/package.json` — same + `types`-last condition ordering; their paths do at least exist. + +### Excerpt — `packages/core/package.json:8-30` + +```json + "type": "module", + "files": [ + "lib", + "src" + ], + "main": "./lib/cjs/index.js", + "module": "./lib/esm/index.js", + "types": "./lib/esm/index.d.ts", + "exports": { + ".": { + "import": "./lib/esm/index.js", + "require": "./lib/cjs/index.js", + "types": "./lib/esm/types/index.d.ts" + }, + "./utils": { + "import": "./lib/esm/utils/index.js", + "require": "./lib/cjs/utils/index.js", + "types": "./lib/esm/utils/index.d.ts" + } + }, + "scripts": { + "dev": "vite", + "build": "vite build && npm run build2", + "build2": "FORMAT=esm tsup && FORMAT=cjs tsup", +``` + +Note the top-level `"types": "./lib/esm/index.d.ts"` is **correct** — it is only +the one inside `exports` that points at the non-existent +`lib/esm/types/index.d.ts`. + +### What the build actually emits + +`packages/core/lib/esm/` contains: + +``` +createTinyIntl.d.ts createTinyIntl.js createTinyIntl.js.map +detectBrowserLocale.d.ts detectBrowserLocale.js detectBrowserLocale.js.map +detectLocale.d.ts detectLocale.js detectLocale.js.map +index.d.ts index.js index.js.map +package.json utils/ +vite-env.d.d.ts vite-env.d.js vite-env.d.js.map +``` + +`packages/core/lib/cjs/` is the same with `.d.cts` in place of `.d.ts` +(`index.d.cts`, `createTinyIntl.d.cts`, …) and a `package.json` containing +`{"type":"commonjs"}`. + +There is **no `lib/esm/types/` directory**. The three `vite-env.d.*` files are +the junk from the entry glob. + +### Excerpt — `packages/core/tsup.config.ts` (what really builds the package) + +```ts +const baseOutDir = 'lib'; +const format: Format = (process.env.FORMAT as 'cjs' | 'esm') || 'cjs'; + +const addPackageJson = () => { + const dir = path.resolve(baseOutDir); + writeFileSync( + path.resolve(dir, format, 'package.json'), + JSON.stringify({ type: format === 'cjs' ? 'commonjs' : 'module' }), + ); +}; + +export default defineConfig({ + entry: ['src'], + target: 'es2022', + format, + bundle: false, + sourcemap: true, + clean: true, + dts: true, + outDir: format === 'cjs' ? `${baseOutDir}/cjs` : `${baseOutDir}/esm`, + config: format === 'cjs' ? 'tsconfig.cjs.json' : 'tsconfig.json', + onSuccess: () => { addPackageJson(); /* ... */ }, + outExtension: () => ({ js: '.js' }), +}); +``` + +`entry: ['src']` is the glob that pulls in `src/vite-env.d.ts`. + +### Excerpt — `packages/core/vite.config.ts` (the dead build, plus the live test config) + +```ts +import { defineConfig } from 'vitest/config'; // eslint-disable-line import/no-unresolved + +export default defineConfig({ + plugins: [ + dts({ outDir: resolve(__dirname, 'dist', 'types'), insertTypesEntry: true }), + visualizer({ filename: 'dist/stats.html', gzipSize: true, brotliSize: true }), + ], + build: { + outDir: resolve(__dirname, 'dist'), + minify: false, + lib: { entry: resolve(__dirname, 'src', 'index.ts'), name: 'tinyIntl', fileName: 'index', + formats: ['es', 'cjs', 'umd'] }, + rollupOptions: { external: [], output: { globals: {} } }, + }, + test: {}, +}); +``` + +Everything above `test: {}` produces `dist/`, which `files: ["lib", "src"]` does +not publish. `test: {}` is what vitest reads and **must survive**. + +### One thing that looks wrong but is not + +`files` includes `"src"`. That is **deliberate and must stay**: tsup runs with +`sourcemap: true`, and the emitted `.js.map` files reference `../../src/*.ts`. +Shipping `src` is what makes those source maps resolve for consumers. Do not +"optimise" it away. + +### Conventions + +- Prettier: `singleQuote: true`, `printWidth: 100`, `trailingComma: "all"`. +- `package.json` files in this repo are 2-space indented with keys in the order + shown above; preserve that ordering rather than reformatting. + +## Commands you will need + +> Read from `package.json` during planning; `node_modules` was not installed at +> planning time, so none of these were executed — including the packaging +> checks in Step 2, which need network access to fetch the tools via `npx`. + +| Purpose | Command | Expected on success | +|---|---|---| +| Install | `npm install` | exit 0 | +| Build core | `npm run build --workspace @tiny-intl/core` | exit 0 | +| Publint | `npx --yes publint packages/core` | no errors | +| Types check | `npx --yes @arethetypeswrong/cli --pack packages/core` | no ✗ rows | +| Core tests | `npm run test --workspace @tiny-intl/core -- --run` | all pass | +| Lint | `npm run lint:all` | exit 0 | + +## Scope + +**In scope** (the only files you should modify): + +- `packages/core/package.json` +- `packages/core/vite.config.ts` +- `packages/core/tsup.config.ts` +- `packages/react/package.json`, `packages/preact/package.json` — condition + ordering + `sideEffects` only +- `package-lock.json` — changes when devDependencies are removed +- `plans/README.md` — status row only + +**Out of scope** (do NOT touch, even though they look related): + +- **The adapter packages' `vite.config.ts` files.** Their vite build is the + *real* build for those packages (`build: "tsc && vite build"` → `lib/`), + unlike core's. Only core's vite build is dead. +- `files: ["lib", "src"]` in core — `src` is required for source maps, see above. +- Package versions and anything under `lerna.json`. Releasing is a separate, + human-gated action (`npm run release` → `lerna publish`). +- Publishing. This plan changes packaging metadata; it does not publish. +- **`packages/solid-js/**` and the root `README.md`** — both are owned by + `plans/002-remove-solid-js-support.md`, which deletes that package and its + README entry. Do not add `sideEffects` or fix `exports` there; if the + directory still exists when you run, leave it alone. +- Adding `exports` subpaths that do not exist today. + +## Git workflow + +- Branch: `advisor/005-fix-core-package-exports` +- Conventional commits (commitlint enforced). Prior art in `git log`: + `feat: better esm exports`, `fix: built config`. + Suggested: `fix(core): point exports types at the emitted declaration files` + and `chore(core): drop the unused vite library build`. +- Do NOT push, publish, or open a PR unless the operator instructed it. + +## Steps + +### Step 1: Build, and confirm the declared types path really is missing + +```bash +npm install +npm run build --workspace @tiny-intl/core +ls packages/core/lib/esm/ +ls packages/core/lib/cjs/ +ls packages/core/lib/esm/types 2>&1 +``` + +**Verify**: `lib/esm/index.d.ts` and `lib/cjs/index.d.cts` exist; +`ls packages/core/lib/esm/types` reports "No such file or directory". If a +`types/` directory *does* exist, STOP — the build differs from what this plan +assumes. + +### Step 2: Measure the current packaging state with real tools + +```bash +npx --yes publint packages/core +npx --yes @arethetypeswrong/cli --pack packages/core +``` + +Record the full output of both. `@arethetypeswrong/cli` reports per-resolution +verdicts (node10, node16-cjs, node16-esm, bundler); expect failures on at least +the CJS rows. This output is your before/after evidence — you will re-run both +at the end. + +If both tools report a clean bill of health, STOP and report: the resolution +problem may be masked by fallbacks in a way this plan mis-diagnoses, and the +`exports` fix should then be judged on correctness-of-metadata alone rather than +on a user-visible failure. + +**Verify**: both commands ran and their output is recorded. (If `npx` cannot +reach the network, note that and continue — the `exports` map is still +demonstrably wrong per Step 1 — but say clearly in your report that the +packaging verification could not be performed.) + +### Step 3: Fix core's `exports` map + +Replace the `exports` block in `packages/core/package.json` with nested +conditions, `types` first in each: + +```json + "main": "./lib/cjs/index.js", + "module": "./lib/esm/index.js", + "types": "./lib/esm/index.d.ts", + "sideEffects": false, + "exports": { + ".": { + "import": { + "types": "./lib/esm/index.d.ts", + "default": "./lib/esm/index.js" + }, + "require": { + "types": "./lib/cjs/index.d.cts", + "default": "./lib/cjs/index.js" + } + }, + "./utils": { + "import": { + "types": "./lib/esm/utils/index.d.ts", + "default": "./lib/esm/utils/index.js" + }, + "require": { + "types": "./lib/cjs/utils/index.d.cts", + "default": "./lib/cjs/utils/index.js" + } + }, + "./package.json": "./package.json" + }, +``` + +Why each part: + +- `types` must be the **first** key inside each condition object — resolvers take + the first match, so a `types` entry listed after `import`/`require` is + unreachable. +- The ESM branch gets `.d.ts`, the CJS branch gets `.d.cts`, matching what tsup + actually emits (confirmed in Step 1). +- `"./package.json"` is exported because tooling routinely reads it, and an + `exports` map without it blocks that. +- `sideEffects: false` tells bundlers every module is safe to drop when unused. + This package is pure functions; the claim is true. It is what makes the bundle + size badge in the README honest for partial imports. + +Confirm each of the six declared paths exists: + +```bash +for f in lib/esm/index.d.ts lib/esm/index.js lib/cjs/index.d.cts lib/cjs/index.js \ + lib/esm/utils/index.d.ts lib/esm/utils/index.js \ + lib/cjs/utils/index.d.cts lib/cjs/utils/index.js; do + test -f "packages/core/$f" && echo "OK $f" || echo "MISSING $f" +done +``` + +**Verify**: every line prints `OK`. Any `MISSING` line is a STOP condition — the +map must describe files that exist. + +### Step 4: Stop emitting `vite-env.d.*` into the published package + +In `packages/core/tsup.config.ts`, narrow the entry glob so declaration files +are not treated as entry points: + +```ts + entry: ['src/**/*.ts', '!src/**/*.d.ts'], +``` + +Rebuild and confirm the junk is gone: + +```bash +npm run build --workspace @tiny-intl/core +ls packages/core/lib/esm/ packages/core/lib/cjs/ | grep vite-env +``` + +**Verify**: the `grep` produces no output, and `lib/esm/index.js`, +`lib/esm/utils/index.js` and their `.d.ts` counterparts all still exist (re-run +the Step 3 file-existence loop). + +If the negation glob is not supported by the installed tsup version, fall back +to listing entries explicitly: +`entry: ['src/index.ts', 'src/createTinyIntl.ts', 'src/detectLocale.ts', 'src/detectBrowserLocale.ts', 'src/utils/index.ts', 'src/utils/flattie.ts', 'src/utils/relativeTimeFormat.ts']` +— and say in your report which form you used. + +### Step 5: Delete the dead vite build from core + +5a. In `packages/core/package.json`, collapse the two build scripts: + +```json + "build": "npm run build:esm && npm run build:cjs", + "build:esm": "FORMAT=esm tsup", + "build:cjs": "FORMAT=cjs tsup", +``` + +(removing `"build2"` and the leading `vite build`). Leave `dev`, `preview`, +`test` and `coverage` alone. + +5b. In `packages/core/vite.config.ts`, remove the `plugins` and `build` keys and +their now-unused imports, keeping **only** the vitest configuration: + +```ts +import { defineConfig } from 'vitest/config'; // eslint-disable-line import/no-unresolved + +export default defineConfig({ + test: {}, +}); +``` + +5c. Remove the two devDependencies that existed only for that build: + +```bash +npm uninstall --workspace @tiny-intl/core rollup-plugin-visualizer vite-plugin-dts +``` + +Keep `vite` itself — vitest 0.34 depends on it and `dev`/`preview` still use it. + +5d. Delete the stale output directory (it is git-ignored, so this only affects +your working tree): `rm -rf packages/core/dist`. + +**Verify**: + +```bash +npm run build --workspace @tiny-intl/core +test -d packages/core/dist && echo "dist recreated — unexpected" || echo "no dist — correct" +npm run test --workspace @tiny-intl/core -- --run +``` +→ build exits 0, `no dist — correct`, all core tests pass (this proves the +trimmed `vite.config.ts` still configures vitest). + +### Step 6: Fix the condition ordering in the adapter packages + +Both `packages/react/package.json` and `packages/preact/package.json` have the +same `types`-last problem. Their paths do exist +(`./lib/types/index.d.ts` is produced by `vite-plugin-dts`), so this is metadata +correctness rather than a live breakage. For each, replace the `exports` block +and add `sideEffects`: + +```json + "sideEffects": false, + "exports": { + ".": { + "types": "./lib/types/index.d.ts", + "import": "./lib/index.js", + "require": "./lib/index.umd.cjs" + }, + "./package.json": "./package.json" + }, +``` + +Leave `main`, `module`, `types`, `files` and everything else untouched. These +packages emit a single `.d.ts` set for both formats, so the flat form with +`types` first is correct and no nesting is needed. + +**Verify**: for each adapter, + +```bash +npm run build --workspace @tiny-intl/react +npx --yes publint packages/react +``` +→ build exits 0; publint reports no errors. Repeat for preact. +(If an adapter build fails inside `Translate.tsx`, that is the pre-existing +failure owned by plan 003 — note it and move on; it is not caused by this plan.) + +Do **not** apply this to `packages/solid-js` even if the directory is still +present — plan 002 deletes it. + +### Step 7: Re-run the packaging checks + +```bash +npm run build --workspace @tiny-intl/core +npx --yes publint packages/core +npx --yes @arethetypeswrong/cli --pack packages/core +npm test +npm run lint:all +``` + +**Verify**: both packaging tools report strictly better results than the Step 2 +baseline — specifically, no error about a missing or unresolvable types entry. +All tests pass. Lint exits 0. Include the before/after tool output in your +report. + +(If `npm test` fails inside a react or preact `Translate` test, check whether +plan 003 has landed — that is its territory, not this plan's.) + +## Test plan + +This plan is not covered by unit tests — it changes packaging metadata, which +the vitest suite (importing from `../src`) cannot observe. Verification is +therefore tool-based, and that is the point of Steps 2 and 8: + +- `publint` — validates `exports`, `main`, `module`, `types`, file existence and + format/extension agreement. +- `@arethetypeswrong/cli --pack` — resolves the packed tarball as node10, + node16-cjs, node16-esm and bundler consumers would, and reports which + resolutions find types. +- The existing core suite must still pass, which proves the trimmed + `vite.config.ts` still supplies vitest's configuration (Step 5). +- The per-path `test -f` loop in Step 3 is the machine-checkable guard that the + `exports` map only names files the build actually produces. + +Do not add unit tests for this plan. If you want a lasting guard, the right one +is adding `npx publint` to the CI workflow — note that as a follow-up rather +than doing it here (CI is plan 001's file). + +## Done criteria + +ALL must hold: + +- [ ] `grep -n "lib/esm/types" packages/core/package.json` → no output +- [ ] `node -e "const e=require('./packages/core/package.json').exports; for (const k of ['.','./utils']) for (const c of ['import','require']) { const p=e[k][c].types; require('fs').accessSync('packages/core/'+p); } console.log('all exports types paths exist')"` → prints the message, exits 0 +- [ ] `npm run build --workspace @tiny-intl/core` exits 0 +- [ ] `ls packages/core/lib/esm packages/core/lib/cjs | grep vite-env` → no output +- [ ] `test -d packages/core/dist` → false after a build +- [ ] `grep -n "vite build" packages/core/package.json` → no output +- [ ] `grep -l '"sideEffects": false' packages/core/package.json packages/react/package.json packages/preact/package.json` → three files +- [ ] `npx --yes publint packages/core` → no errors +- [ ] `npx --yes @arethetypeswrong/cli --pack packages/core` → strictly better than the Step 2 baseline, with no missing-types errors +- [ ] `npm test` exits 0 +- [ ] `npm run lint:all` exits 0 +- [ ] `git status --porcelain packages/solid-js README.md` returns no output (both are plan 002's) +- [ ] `git status --porcelain` lists only files from the In-scope list +- [ ] `plans/README.md` status row for 005 updated + +## STOP conditions + +Stop and report back (do not improvise) if: + +- `packages/core/lib/esm/types/` **does** exist after a clean build — the build + differs from what this plan measured, and the `exports` map may be correct. +- Any path in the new `exports` map does not exist after a build (the Step 3 + file-existence loop prints `MISSING`). +- `npm uninstall` of `vite-plugin-dts` or `rollup-plugin-visualizer` breaks a + build in *any* package — the adapters use both, and if the monorepo relied on + hoisting from core's devDependencies, they must be added to the adapters' + own devDependencies instead. Report before doing that. +- Trimming `packages/core/vite.config.ts` stops vitest from running. Restore the + file and report; the fix is to move the config to `vitest.config.ts` instead, + which is a different change than this plan describes. +- You conclude the fix requires changing package `version` fields, or running + `npm publish` / `lerna publish`. It does not — publishing is a separate, + human-gated action. +- `@arethetypeswrong/cli` reports *worse* results after the change than before. + +## Maintenance notes + +- **Follow-up worth doing**: add `npx publint` (and optionally + `attw --pack`) as a CI step so a future edit to `exports` cannot silently + point at a non-existent file again. That belongs in + `.github/workflows/unit-tests.yml`, which plan 001 rewrites — sequence this + after 001. +- **This change ships on the next release, not immediately.** `exports` changes + affect consumers only once published. Whoever runs `npm run release` should + treat this as a patch that fixes type resolution and mention it in the + release notes. +- **`files: ["lib", "src"]` must keep `src`** — source maps reference it. If + someone later removes `sourcemap: true` from `tsup.config.ts`, `src` can go + too, and the npm tarball roughly halves. +- **What a reviewer should scrutinise**: that `types` is the first key in every + condition object; that the CJS branch points at `.d.cts` and the ESM branch at + `.d.ts`; and that `sideEffects: false` is actually true for these packages + (it is — every module is pure function/type declarations, with no top-level + side effects). +- The adapter packages still build with vite and emit a `dist/stats.html` from + `rollup-plugin-visualizer` into a directory that is not published. That is + harmless there (unlike core, whose *entire* vite output was dead) and is left + alone deliberately. +- If plan 002 is rejected and `packages/solid-js` survives, it needs the same + Step 6 treatment (`types` first, `sideEffects: false`) — it has the identical + `exports` shape as the other two adapters. diff --git a/plans/006-fix-preact-missing-useintl-types.md b/plans/006-fix-preact-missing-useintl-types.md new file mode 100644 index 0000000..bf80701 --- /dev/null +++ b/plans/006-fix-preact-missing-useintl-types.md @@ -0,0 +1,342 @@ +# Plan 006: `@tiny-intl/preact` ships a type entry point that re-exports a file it never emits + +> **Executor instructions**: Follow this plan step by step. Run every +> verification command and confirm the expected result before moving on. Touch +> only the files listed as in scope. If any STOP condition occurs, stop and +> report — do not improvise. +> +> **This plan may turn out to be a no-op.** Step 1 decides. Read it first. +> +> **Drift check (run first)**: +> `git diff --stat 794eb48..HEAD -- packages/preact packages/core/package.json` + +## Status + +- **Priority**: P1 (published, user-facing, silent) +- **Effort**: S +- **Risk**: LOW +- **Depends on**: `plans/005-fix-core-package-exports.md` — see Step 1 +- **Category**: bug / packaging +- **Planned at**: commit `794eb48`, observed 2026-08-12 while reviewing plan 002 + +## Why this matters + +`packages/preact/lib/types/index.d.ts` — the file `@tiny-intl/preact` names in +both its `types` field and its `exports` map — contains: + +```ts +export * from './Translate'; +export * from './useIntl'; +``` + +But `useIntl.d.ts` **is not emitted**. The directory after a clean build holds +only `Translate.d.ts` and `index.d.ts`. So the package's public type entry point +re-exports a module that does not exist in the tarball. + +Every TypeScript consumer of `@tiny-intl/preact` is affected: `useIntl` — the +package's primary API — has no type declaration, and depending on the consumer's +`moduleResolution` and `skipLibCheck` settings they get either an implicit +`any`, an unresolved-module error on the re-export, or a broken entry point. +`@tiny-intl/react` emits all three files correctly, so this is preact-only and +easy to miss. + +The build does not fail. `vite-plugin-dts` prints six `TS2742` diagnostics, +declines to emit the file, and exits 0. Nothing in CI looks at that output. + +### Measured evidence (2026-08-12) + +``` +$ npm run build --workspace @tiny-intl/preact # exit 0 +src/useIntl.tsx:8:17 - error TS2742: The inferred type of 'useIntl' cannot be +named without a reference to '../../../node_modules/@tiny-intl/core/lib/esm/createTinyIntl'. +This is likely not portable. A type annotation is necessary. + (×6) + +$ ls packages/react/lib/types/ → Translate.d.ts index.d.ts useIntl.d.ts +$ ls packages/preact/lib/types/ → Translate.d.ts index.d.ts +``` + +### Root cause, and why it is probably plan 005's to fix + +`packages/react/tsconfig.json` uses `"moduleResolution": "Node"`; +`packages/preact/tsconfig.json` uses `"moduleResolution": "bundler"`. That is the +only material difference between two otherwise byte-identical `useIntl.tsx` +files. + +`"Node"` (node10) ignores a package's `exports` map entirely and reads the +top-level `"types": "./lib/esm/index.d.ts"` from `@tiny-intl/core` — which is +correct, so react resolves `TinyIntl` through the package entry and can name it. + +`"bundler"` **honours** `exports`. And `@tiny-intl/core`'s exports map declares: + +```json +"types": "./lib/esm/types/index.d.ts" +``` + +That path does not exist — it is exactly the defect plan 005 fixes. With the +declared types path unresolvable, TypeScript falls back to reaching the symbol +through the deep internal path `…/lib/esm/createTinyIntl`, which it cannot write +into a portable `.d.ts` — hence `TS2742`, hence no emit. + +**That hypothesis was tested on 2026-08-12 and DISPROVEN.** Plan 005 landed — +core's `exports` map now declares `types` first in every condition, pointing at +files that exist, and `@arethetypeswrong/cli` reports 🟢 for node16-CJS, +node16-ESM and bundler. The preact build was then re-run from scratch: + +``` +$ npm run build --workspace @tiny-intl/preact # exit 0 +TS2742 count: 6 # unchanged +$ ls packages/preact/lib/types/ +Translate.d.ts index.d.ts # useIntl.d.ts still missing +$ ls packages/react/lib/types/ +Translate.d.ts index.d.ts useIntl.d.ts # react still fine +``` + +So the broken `exports` map was **not** the cause. Do not spend time re-testing +it — go straight to the fix in Step 2, which addresses `TS2742` directly and +independently of root cause (the diagnostic itself says "A type annotation is +necessary"). + +Two candidate causes remain, neither yet confirmed, both only worth chasing if +Step 2 fails: + +1. **`moduleResolution` split** — preact `"bundler"`, react `"Node"`. Still the + most plausible: the two files are otherwise byte-identical below their import + lines. Step 3 covers this as a fallback. +2. **`vite-plugin-dts` version skew** — preact resolves its own `^3.6.4` (3.6.4 + installed) while react uses the hoisted pinned `3.6.0`. Worth testing by + temporarily aligning them if Steps 2 and 3 both fail. + +Why the annotation fix works regardless: `TS2742` fires when TypeScript must +*write down* an inferred type but cannot construct a portable path to the +symbol's declaring module. `useIntl`'s return type is inferred from a spread of +`TinyIntl` plus eight wrapped callbacks. Annotate the return explicitly +with a type that is already imported by name, and there is nothing left to +infer. + +## Current state + +- `packages/preact/tsconfig.json` — `"moduleResolution": "bundler"`, + `"target": "ES2020"`, `paths` aliasing `react`/`react-dom` to `preact/compat`. +- `packages/preact/src/useIntl.tsx:8` — `export function useIntl() {` with an + **inferred** return type. The inferred type spreads `intl` + (`TinyIntl`, imported as a type on line 2) plus eight locally wrapped + callbacks. +- `packages/preact/vite.config.ts` — `dts({ outDir: resolve(__dirname, 'lib', 'types'), insertTypesEntry: true })`. +- `packages/react/src/useIntl.tsx` — identical source, different import + specifier (`'react'` vs `'preact/compat'`), emits fine. + +### Conventions + +- Prettier: `singleQuote: true`, `printWidth: 100`, `trailingComma: "all"`; + 2-space indent. +- Commit scopes allowed by commitlint: `core`, `react`, `preact`. + +## Commands you will need + +| Purpose | Command | Expected on success | +|---|---|---| +| Install | `npm install` | exit 0 | +| Build preact | `npm run build --workspace @tiny-intl/preact` | exit 0 | +| Inspect emitted types | `ls packages/preact/lib/types/` | 3 files incl. `useIntl.d.ts` | +| Count TS2742 | `npm run build --workspace @tiny-intl/preact 2>&1 \| grep -c TS2742` | `0` | +| Tests | `npm test` | all pass | +| Lint | `npm run lint:all` | exit 0 | + +## Scope + +**In scope**: + +- `packages/preact/src/useIntl.tsx` — an explicit return type annotation (Step 2) +- `packages/preact/tsconfig.json` — only as the documented fallback in Step 3 +- `packages/preact/package.json` + `package-lock.json` — only as the second + fallback in Step 3b + +**Out of scope**: + +- `packages/core/**` — the `exports` map is plan 005's; do not edit it here. +- `packages/react/**` — emits correctly; leave it alone. +- Changing what `useIntl` returns at runtime. This is a types-only change; the + emitted JavaScript must be byte-identical. +- `packages/preact/src/Translate.tsx` and all test files. + +## Git workflow + +- Conventional commits. Suggested: `fix(preact): emit type declarations for useIntl`. +- Do NOT push or open a PR. + +## Steps + +### Step 1: Confirm the bug still reproduces on a clean build + +Plan 005 must have landed in your branch. Confirm, then reproduce: + +```bash +grep -n "lib/esm/types" packages/core/package.json # expect: no output +rm -rf packages/preact/lib packages/core/lib +npm run build --workspace @tiny-intl/core +npm run build --workspace @tiny-intl/preact 2>&1 | tee /tmp/preact-build.txt +grep -c TS2742 /tmp/preact-build.txt +ls packages/preact/lib/types/ +``` + +If the first grep still finds `lib/esm/types`, plan 005 has NOT landed — +**STOP and report**. + +**Expected** (measured 2026-08-12): `TS2742` count `6`, and +`packages/preact/lib/types/` contains only `Translate.d.ts` and `index.d.ts`. +Record both. This is the "before" evidence. + +If instead the count is 0 and `useIntl.d.ts` is present, the bug has been fixed +by something else since — **STOP and report**; do not make changes the repo no +longer needs. + +**Verify**: you have the `TS2742` count and the directory listing recorded. + +### Step 2: Annotate `useIntl`'s return type explicitly + +`TS2742` means TypeScript cannot *write down* the inferred type without +referencing a module it has no portable path to. The direct fix is to stop +relying on inference: declare the return type using only types that are already +imported by name. + +In `packages/preact/src/useIntl.tsx`, add an exported interface describing the +return value and annotate the function with it. The shape must match what the +function actually returns today — spread of `TinyIntl`, with `t`, `tc`, +`n`, `d`, `dt`, `rt`, `sort`, `collator`, `list` and `change` overridden. Derive +it from the already-imported `TinyIntl` type rather than restating each +signature, e.g.: + +```tsx +export type UseIntlResult = TinyIntl; + +export function useIntl(): UseIntlResult { +``` + +Then check that against reality: the function returns `{ ...intl, t, tc, n, +d: dt, dt, rt, sort, collator, list, change: intl.change }`. If every one of +those wrapped values has the same type as the corresponding member of +`TinyIntl` (they are `useCallback` wrappers around exactly those +members, so they should), `TinyIntl` is the honest annotation and no +new interface is needed. + +**If the annotation does not typecheck** — i.e. the returned object is not +assignable to `TinyIntl` — do not force it with a cast. That would mean +the wrappers genuinely change the type, which is information your reviewer +needs. STOP and report the compiler error. + +**Verify**: + +```bash +cd packages/preact && npx tsc --noEmit; echo "tsc exit=$?"; cd ../.. +npm run build --workspace @tiny-intl/preact 2>&1 | grep -c TS2742 # expect 0 +ls packages/preact/lib/types/ # expect 3 files +``` + +### Step 3: Fallback only — align `moduleResolution` with react + +**Only if Step 2 fails to eliminate `TS2742`.** Change +`packages/preact/tsconfig.json`'s `"moduleResolution": "bundler"` to `"Node"`, +matching `packages/react/tsconfig.json`, and remove +`"allowImportingTsExtensions": true` if TypeScript then rejects it (that option +requires `bundler`/`node16`+ resolution and `noEmit`). + +This is a fallback, not the preferred fix: `bundler` is the more modern and more +correct setting, and moving away from it trades a newer resolution mode for a +declaration file. Take it only if Step 2 has failed, revert it if it does not +help, and say so prominently in your report either way. + +**Verify**: same three commands as Step 2. + +### Step 3b: Second fallback — test the plugin version skew + +**Only if Steps 2 and 3 both fail.** `packages/preact` resolves its own +`vite-plugin-dts@3.6.4`; `packages/react` uses the hoisted pinned `3.6.0`. +Temporarily align preact to `3.6.0`: + +```bash +npm install -D --workspace @tiny-intl/preact vite-plugin-dts@3.6.0 +npm run build --workspace @tiny-intl/preact 2>&1 | grep -c TS2742 +ls packages/preact/lib/types/ +``` + +If that emits `useIntl.d.ts`, the plugin version was the cause — keep the change +(pinning both adapters to the same version is desirable anyway, see Maintenance +notes) and revert whatever Step 3 changed. If it does not help, revert this too +and **STOP**: three hypotheses exhausted means the diagnosis needs a human. + +**Verify**: report which of Steps 2 / 3 / 3b actually fixed it, and confirm you +reverted the ones that did not. + +### Step 4: Confirm the runtime output did not change + +This is a types-only plan. Prove the emitted JavaScript is unaffected: + +```bash +git diff --stat -- packages/preact/src/useIntl.tsx +npm test +npm run lint:all +``` + +**Verify**: the source diff touches only type annotations (no change to any +expression or statement); all tests pass; lint exits 0. + +## Test plan + +The regression guard here is a build artifact, not a unit test — the failure +mode is "a file is missing from the package", which no runtime test can see. + +- **Primary check**: `ls packages/preact/lib/types/` contains `useIntl.d.ts`. +- **Primary check**: the preact build emits zero `TS2742` diagnostics. +- **Consumer-level check** (do this if plan 005's tooling is available): + `npx --yes @arethetypeswrong/cli --pack packages/preact` — it resolves the + packed tarball the way a consumer would and will flag an unresolvable + re-export. +- The existing preact test suite (added by plan 001) must still pass unchanged; + it proves the runtime behaviour is untouched. + +Do not add a unit test for this. If you want a lasting guard, the right one is a +CI assertion that the build emits no `TS2742` — noted as a follow-up in plan 001. + +## Done criteria + +ALL must hold (or Step 1 concluded no-op, in which case only the first two): + +- [ ] `ls packages/preact/lib/types/` lists `useIntl.d.ts`, `Translate.d.ts`, `index.d.ts` +- [ ] `npm run build --workspace @tiny-intl/preact 2>&1 | grep -c TS2742` → `0` +- [ ] `cd packages/preact && npx tsc --noEmit` exits 0 +- [ ] `npm test` exits 0 +- [ ] `npm run lint:all` exits 0 +- [ ] `git status --porcelain` lists only in-scope files +- [ ] The report states which Step 1 outcome occurred + +## STOP conditions + +Stop and report back (do not improvise) if: + +- `grep -n "lib/esm/types" packages/core/package.json` still matches — plan 005 + has not landed and this plan's Step 1 cannot run. +- The returned object is not assignable to `TinyIntl` (Step 2). Report + the compiler error; do not cast around it. +- Eliminating `TS2742` appears to require editing `packages/core/**`. That is + plan 005's territory — report what you found. +- The preact build starts *failing* (non-zero exit) at any point. It exits 0 + today; a regression to failure is worse than the bug. + +## Maintenance notes + +- **How this stayed hidden**: `vite-plugin-dts` prints `TS2742` as an "error" + but does not fail the build, and CI never built the adapters at all. The + durable fix is a CI step that fails on `TS2742` — cheap, and it would have + caught this the day it appeared. +- **Version skew worth resolving separately**: `packages/react` pins + `vite-plugin-dts` to `3.6.0` while `packages/preact` floats on `^3.6.4`, so + the two adapters generate declarations with different plugin versions. Whether + or not that contributed here, having two adapters on two versions of the same + build plugin is a latent source of exactly this kind of asymmetry. Align them. +- **What a reviewer should scrutinise**: that the diff is annotations only — the + compiled JS must be identical — and that `UseIntlResult` (if introduced) is + actually exported, or consumers hit `TS4023` on the re-export instead. +- If Step 1 reported no-op, keep this plan file: it documents why the preact + declaration output is worth watching, and the CI guard in the first bullet is + still worth adding. diff --git a/plans/007-recover-bundle-size-regression.md b/plans/007-recover-bundle-size-regression.md new file mode 100644 index 0000000..fc89602 --- /dev/null +++ b/plans/007-recover-bundle-size-regression.md @@ -0,0 +1,390 @@ +# Plan 007: Recover the adapter bundle-size regression, and stop shipping two dead bundles + +> **Executor instructions**: Follow this plan step by step. Run every +> verification command and confirm the expected result before moving on. Touch +> only the files listed as in scope. If any STOP condition occurs, stop and +> report — do not improvise. +> +> **Drift check (run first)**: +> `git diff --stat 49bd556..HEAD -- packages/react packages/preact` + +## Status + +- **Priority**: P1 (this project's entire value proposition is size) +- **Effort**: S +- **Risk**: LOW-MED — Step 2 changes runtime guards that a regression test suite + already pins; Step 3 changes what the `require` entry resolves to. +- **Depends on**: plans 001–006 (all merged into `main` at `49bd556`) +- **Category**: perf / packaging +- **Planned at**: commit `49bd556`, 2026-08-12 + +## Why this matters + +`tiny-intl` is sold on bundle size — every README leads with a bundlejs badge. +Plan 003 fixed a real bug (`` rendered the singular, +`number={0}` rendered nothing) but paid for it in bytes, because +`typeof count === 'number'` is far longer than `count`. + +Measured with esbuild (`bundle: true, minify: true, format: 'esm'`, framework +externalised) then gzip level 9 — a methodology that **reproduces the published +badge numbers exactly** for both adapters: + +| package | before (`794eb48`) | after (`49bd556`) | delta | +|---|---|---|---| +| `@tiny-intl/core` | 1418 B | 1422 B | +4 B | +| `@tiny-intl/react` | **541 B** | 558 B | **+17 B** | +| `@tiny-intl/preact` | **727 B** | 741 B | **+14 B** | + +Core's +4 B is gzip noise (its minified size actually *fell*, 3116 → 3102 B). +The adapter regressions are real and are the target here. + +There is also dead weight in the published tarballs. Both adapters build +`formats: ['es', 'cjs', 'umd']`, but `package.json` references only +`./lib/index.js` (import) and `./lib/index.umd.cjs` (require). So: + +- `lib/index.cjs` — **3039 B in react, 1571 B in preact — is referenced by + nothing** and ships in every install. +- The `require` entry points at the **larger** of the two CommonJS-capable + files (react: UMD 3588 B vs CJS 3039 B). +- Pointing `require` at a UMD bundle is also why + `attw --pack packages/preact` reports 👺 "Masquerading as ESM" on the node16 + rows — a known outstanding defect. **Fixing the size issue fixes that too.** + +## Current state + +### The guards to shrink — `packages/react/src/Translate.tsx:65-87` + +```tsx + const translateFn = useCallback(() => { + if (typeof count === 'number') { + return tc(name, count, options); + } + + if (date !== undefined && !relative) { + return dt(date, options); + } + + if (date !== undefined && relative) { + return rt(date, options); + } + + if (typeof number === 'number') { + return n(number, options); + } + + if (name) { + return t(name, options); + } + + return null; + }, [changed, count, date, name, number, options, relative]); +``` + +`packages/preact/src/Translate.tsx` is byte-identical below line 8. + +The prop types (`TranslateProps`, same file) declare `count?: number`, +`number?: number`, `date?: Date | string | number` — and, in the union members +where each is absent, the unit type `undefined`. So the only non-value these can +hold is `undefined`, which is what makes the shorter form below equivalent. + +### The build config — `packages/react/vite.config.ts` / `packages/preact/vite.config.ts` + +```ts + build: { + outDir: resolve(__dirname, 'lib'), + lib: { + entry: resolve(__dirname, 'src', 'index.ts'), + name: 'tinyIntl', + fileName: 'index', + formats: ['es', 'cjs', 'umd'], + }, +``` + +Both files also carry a vitest `test` block added by plan 001 — **leave it +alone**. + +### The manifests — `packages/react/package.json` / `packages/preact/package.json` + +```json + "main": "./lib/index.umd.cjs", + "module": "./lib/index.js", + "types": "./lib/types/index.d.ts", + "sideEffects": false, + "exports": { + ".": { + "types": "./lib/types/index.d.ts", + "import": "./lib/index.js", + "require": "./lib/index.umd.cjs" + }, + "./package.json": "./package.json" + }, +``` + +### Conventions + +- Prettier: `singleQuote: true`, `printWidth: 100`, `trailingComma: "all"`. +- Commit scopes allowed by commitlint: `core`, `react`, `preact`. +- The suite is 52 tests (core 24, react 14, preact 14). `npm test` must stay green. + +## Commands you will need + +| Purpose | Command | Expected | +|---|---|---| +| Install | `npm ci` | exit 0 | +| Build one package | `npm run build --workspace @tiny-intl/react` | exit 0 | +| Typecheck | `cd packages/react && npx tsc --noEmit` | exit 0 | +| Tests | `npm test` | 52 pass | +| Lint | `npm run lint:all` | exit 0 | +| Measure | `node measure.mjs` (Step 1) | prints 3 rows | + +## Scope + +**In scope**: + +- `packages/react/src/Translate.tsx`, `packages/preact/src/Translate.tsx` +- `packages/react/vite.config.ts`, `packages/preact/vite.config.ts` — the + `formats` array only +- `packages/react/package.json`, `packages/preact/package.json` — `main` and the + `require` condition only + +**Out of scope**: + +- `packages/core/**` — its +4 B is gzip noise, not a regression worth chasing. +- The `test` blocks in the adapter `vite.config.ts` files. +- Any test file. The existing regression tests are the safety net; **do not edit + them to accommodate a change.** +- `TranslateProps` itself. +- Core's `files: ["lib", "src"]` and its sourcemaps. That is a separate, + larger tarball-size decision the maintainer has not yet made. +- Publishing, versions, `lerna.json`. + +## Git workflow + +- Conventional commits. Suggested: `perf(react): shrink Translate guards`, + `perf(preact): shrink Translate guards`, `fix(react): point require at the CJS build`. +- Do NOT push or open a PR. + +## Steps + +### Step 1: Set up the measurement harness and record the baseline + +Create `measure.mjs` in the repo root (delete it before you commit — it must not +end up in the diff): + +```js +import { createRequire } from 'node:module'; +import { gzipSync } from 'node:zlib'; +const require = createRequire(import.meta.url); +const { build } = require('esbuild'); +const targets = [ + { name: '@tiny-intl/core', entry: 'packages/core/lib/esm/index.js', external: [] }, + { name: '@tiny-intl/react', entry: 'packages/react/lib/index.js', external: ['react','react-dom','react/jsx-runtime'] }, + { name: '@tiny-intl/preact', entry: 'packages/preact/lib/index.js', external: ['preact','preact/compat','preact/jsx-runtime'] }, +]; +for (const t of targets) { + const r = await build({ entryPoints: [t.entry], bundle: true, minify: true, format: 'esm', + treeShaking: true, external: t.external, write: false, legalComments: 'none' }); + const out = r.outputFiles[0].contents; + console.log(t.name.padEnd(20) + ' min=' + String(out.length).padStart(5) + + 'B min+gzip=' + String(gzipSync(out, { level: 9 }).length).padStart(4) + 'B'); +} +``` + +```bash +npm ci +for p in core react preact; do npm run build --workspace @tiny-intl/$p; done +node measure.mjs +``` + +**Expected baseline** (measured 2026-08-12 at `49bd556`): core `1422B`, +react `558B`, preact `741B`. Record what you actually get. + +If your numbers differ by more than ~5 B from those, **STOP and report** — your +esbuild version differs and the before/after comparison below will not be +meaningful. + +### Step 2: Shrink the guards + +In **both** `packages/react/src/Translate.tsx` and +`packages/preact/src/Translate.tsx`, replace the four guards: + +| from | to | +|---|---| +| `typeof count === 'number'` | `count != null` | +| `date !== undefined && !relative` | `date != null && !relative` | +| `date !== undefined && relative` | `date != null && relative` | +| `typeof number === 'number'` | `number != null` | + +Why this is equivalent and not a regression of plan 003's fix: each of these +props is typed as `T | undefined`, so `!= null` excludes exactly the same values +`typeof`/`!== undefined` did — while still being **true for `0`**, which is the +entire point of plan 003. It is also marginally more robust: a plain-JS consumer +passing `null` now falls through instead of being formatted as a number. + +`!= null` (rather than `!== undefined`) is deliberate — it is the shortest form +that survives minification, and it is why this recovers bytes. + +**If ESLint's `eqeqeq` rule rejects `!= null`**, do not add a disable comment. +Fall back to `!== undefined` for all four (still shorter than `typeof`), record +that you did, and continue. + +Change nothing else — not the branch order, not the `if (name)` guard, not the +dependency array. + +**Verify**: + +```bash +cd packages/react && npx tsc --noEmit; echo "react tsc=$?"; cd ../.. +cd packages/preact && npx tsc --noEmit; echo "preact tsc=$?"; cd ../.. +diff <(tail -n +8 packages/react/src/Translate.tsx) <(tail -n +8 packages/preact/src/Translate.tsx) +npm test +``` + +→ both `tsc=0`; the `diff` produces no output (adapters stay identical); +**52 tests pass**. The `count={0}` / `number={0}` / `date={0}` regression tests +from plan 003 passing is the proof this did not undo that fix. + +If `tsc` now errors, `!= null` failed to narrow the props union — **STOP and +report the exact error**; do not paper over it with an assertion. + +### Step 3: Stop building and shipping the UMD bundle + +In both `packages/react/vite.config.ts` and `packages/preact/vite.config.ts`, +change the formats array: + +```ts + formats: ['es', 'cjs'], +``` + +Then in both `package.json` files, point the CommonJS entry at the real CJS +build instead of the UMD one: + +```json + "main": "./lib/index.cjs", +``` + +```json + "require": "./lib/index.cjs" +``` + +Leave `module`, `types`, `sideEffects`, `files` and the `./package.json` export +untouched. + +Rationale to defend in review: `index.cjs` was already being built and shipped +while nothing referenced it; `index.umd.cjs` was the larger file *and* the cause +of `attw`'s "Masquerading as ESM" finding. A UMD build exists to be loaded from a +`