a post-component library for building user-interfaces on the web.

switch to node:test (#48)

should massively reduce deps

tombl.dev 594a6820 cb6b34e1

verified
+1107 -3853
+9 -11
.github/workflows/test.yml
··· 7 7 test: 8 8 runs-on: ubuntu-latest 9 9 10 - permissions: 11 - contents: read 12 - pull-requests: write 13 - 14 - container: 15 - image: mcr.microsoft.com/playwright:v1.50.1-noble 16 - options: --user 1001 17 - 18 10 steps: 19 11 - uses: actions/checkout@v4 12 + - uses: actions/setup-node@v4 13 + with: 14 + node-version: 23.x 20 15 - run: npm install 21 - - run: npm run test 22 - - if: always() && github.event_name == 'pull_request' 23 - uses: davelosert/vitest-coverage-report-action@v2 16 + - run: npm run test:coverage 17 + - if: always() 18 + uses: coverallsapp/github-action@v2 19 + with: 20 + file: lcov.info 21 + format: lcov
+1 -1
.gitignore
··· 1 1 node_modules 2 2 dist 3 - coverage 3 + lcov.info
+97 -91
build.js
··· 6 6 import { build } from 'rolldown' 7 7 import { walk } from 'zimmerframe' 8 8 9 - try { 10 - await rm('dist', { recursive: true }) 11 - } catch {} 9 + await rm('dist', { recursive: true, force: true }) 12 10 13 - /** @type {import('rolldown').Plugin} */ 14 - const strip_asserts_plugin = { 15 - name: 'strip-asserts', 16 - transform(code, id, { moduleType }) { 17 - if (id.includes('node_modules')) return 11 + await Promise.all([bundle_code(), generate_declarations(), write_package_json()]) 18 12 19 - const ast = this.parse(code, { lang: moduleType }) 20 - const source = new MagicString(code, { filename: id }) 13 + async function bundle_code() { 14 + /** @type {import('rolldown').Plugin} */ 15 + const strip_asserts_plugin = { 16 + name: 'strip-asserts', 17 + transform(code, id, { moduleType }) { 18 + if (id.includes('node_modules')) return 21 19 22 - walk(/** @type {import('@oxc-project/types').Node} */ (ast), null, { 23 - CallExpression(node, { next }) { 24 - if (node.callee.type === 'Identifier' && node.callee.name === 'assert') { 25 - source.update(node.start, node.end, ';') 26 - return 27 - } 20 + const ast = this.parse(code, { lang: moduleType }) 21 + const source = new MagicString(code, { filename: id }) 28 22 29 - next() 30 - }, 31 - }) 23 + walk(/** @type {import('@oxc-project/types').Node} */ (ast), null, { 24 + CallExpression(node, { next }) { 25 + if (node.callee.type === 'Identifier' && node.callee.name === 'assert') { 26 + source.update(node.start, node.end, ';') 27 + return 28 + } 32 29 33 - return { code: source.toString(), map: source.generateMap() } 34 - }, 35 - } 30 + next() 31 + }, 32 + }) 36 33 37 - /** @returns {import('rolldown').BuildOptions} */ 38 - function define_bundle(env) { 39 - const is_dev = env === 'dev' 40 - 41 - return { 42 - input: { 43 - client: './src/client.ts', 44 - server: './src/server.ts', 45 - index: './src/index.ts', 46 - }, 47 - plugins: [!is_dev && strip_asserts_plugin], 48 - output: { 49 - dir: 'dist', 50 - entryFileNames: is_dev ? '[name].js' : '[name].min.js', 51 - chunkFileNames: is_dev ? '[name].js' : '[name].min.js', 52 - banner: is_dev ? '// @ts-nocheck' : undefined, 53 - minify: !is_dev, 54 - plugins: [ 55 - !is_dev && 56 - terser({ 57 - mangle: { properties: { regex: /^_/ } }, 58 - }), 59 - ], 60 - }, 61 - define: { 62 - __DEV__: JSON.stringify(is_dev), 34 + return { code: source.toString(), map: source.generateMap() } 63 35 }, 64 36 } 65 - } 66 37 67 - const bundles = await build([define_bundle('dev'), define_bundle('prod')]) 68 - 69 - await createBundle({ 70 - project: 'tsconfig.json', 71 - output: 'dist/types.d.ts', 72 - modules: { 73 - dhtml: './src/index.ts', 74 - 'dhtml/client': './src/client.ts', 75 - 'dhtml/server': './src/server.ts', 76 - }, 77 - }) 38 + /** @returns {import('rolldown').BuildOptions} */ 39 + function define_bundle(env) { 40 + const is_dev = env === 'dev' 78 41 79 - const pkg = JSON.parse(await readFile('package.json', 'utf8')) 80 - 81 - delete pkg.scripts 82 - delete pkg.devDependencies 83 - delete pkg.prettier 84 - ;(function walk(exports) { 85 - if (typeof exports === 'string') { 86 - if (exports.startsWith('./src/')) exports = exports.slice('./src/'.length) 87 - exports = exports.replace(/\.ts$/, '') 88 42 return { 89 - types: './types.d.ts', 90 - production: `./${exports}.min.js`, 91 - default: `./${exports}.js`, 43 + input: { 44 + client: './src/client.ts', 45 + server: './src/server.ts', 46 + index: './src/index.ts', 47 + }, 48 + plugins: [!is_dev && strip_asserts_plugin], 49 + output: { 50 + dir: 'dist', 51 + entryFileNames: is_dev ? '[name].js' : '[name].min.js', 52 + chunkFileNames: is_dev ? '[name].js' : '[name].min.js', 53 + banner: is_dev ? '// @ts-nocheck' : undefined, 54 + minify: !is_dev, 55 + plugins: [ 56 + !is_dev && 57 + terser({ 58 + mangle: { properties: { regex: /^_/ } }, 59 + }), 60 + ], 61 + }, 62 + define: { 63 + __DEV__: JSON.stringify(is_dev), 64 + }, 92 65 } 93 66 } 94 - for (const key in exports) { 95 - exports[key] = walk(exports[key]) 96 - } 97 - return exports 98 - })(pkg.exports) 99 67 100 - await writeFile('dist/package.json', JSON.stringify(pkg, null, 2)) 68 + const bundles = await build([define_bundle('dev'), define_bundle('prod')]) 101 69 102 - console.table( 103 - Object.fromEntries( 104 - bundles.flatMap(bundle => 105 - bundle.output.map(file => [ 106 - file.fileName, 107 - { 108 - normal: file.code.length, 109 - gzip: gzipSync(file.code).length, 110 - brotli: brotliCompressSync(file.code).length, 111 - }, 112 - ]), 70 + console.table( 71 + Object.fromEntries( 72 + bundles.flatMap(bundle => 73 + bundle.output.map(file => [ 74 + file.fileName, 75 + { 76 + normal: file.code.length, 77 + gzip: gzipSync(file.code).length, 78 + brotli: brotliCompressSync(file.code).length, 79 + }, 80 + ]), 81 + ), 113 82 ), 114 - ), 115 - ) 83 + ) 84 + } 85 + 86 + async function generate_declarations() { 87 + await createBundle({ 88 + project: 'tsconfig.json', 89 + output: 'dist/types.d.ts', 90 + modules: { 91 + dhtml: './src/index.ts', 92 + 'dhtml/client': './src/client.ts', 93 + 'dhtml/server': './src/server.ts', 94 + }, 95 + }) 96 + } 97 + 98 + async function write_package_json() { 99 + const pkg = JSON.parse(await readFile('package.json', 'utf8')) 100 + 101 + delete pkg.scripts 102 + delete pkg.devDependencies 103 + delete pkg.prettier 104 + ;(function walk(exports) { 105 + if (typeof exports === 'string') { 106 + if (exports.startsWith('./src/')) exports = exports.slice('./src/'.length) 107 + exports = exports.replace(/\.ts$/, '') 108 + return { 109 + types: './types.d.ts', 110 + production: `./${exports}.min.js`, 111 + default: `./${exports}.js`, 112 + } 113 + } 114 + for (const key in exports) { 115 + exports[key] = walk(exports[key]) 116 + } 117 + return exports 118 + })(pkg.exports) 119 + 120 + await writeFile('dist/package.json', JSON.stringify(pkg, null, 2)) 121 + }
+1 -5
examples/kanban/package-lock.json
··· 19 19 "../..": { 20 20 "devDependencies": { 21 21 "@rollup/plugin-terser": "^0.4.4", 22 - "@vitest/browser": "^3.0.6", 23 - "@vitest/coverage-v8": "^3.0.6", 24 - "@vitest/ui": "^3.0.5", 25 22 "dhtml": ".", 26 23 "dts-buddy": "^0.5.5", 24 + "happy-dom": "^17.4.4", 27 25 "htmlparser2": "^10.0.0", 28 26 "magic-string": "^0.30.17", 29 - "playwright": "^1.50.1", 30 27 "prettier": "^3.4.2", 31 28 "rolldown": "^1.0.0-beta.6", 32 29 "typescript": "^5.7.2", 33 - "vitest": "^3.0.6", 34 30 "zimmerframe": "^1.1.2" 35 31 } 36 32 },
+68 -2682
package-lock.json
··· 7 7 "": { 8 8 "name": "dhtml", 9 9 "devDependencies": { 10 + "@happy-dom/global-registrator": "^17.4.4", 10 11 "@rollup/plugin-terser": "^0.4.4", 11 - "@vitest/browser": "^3.0.6", 12 - "@vitest/coverage-v8": "^3.0.6", 13 - "@vitest/ui": "^3.0.5", 12 + "@types/node": "^22.13.11", 14 13 "dhtml": ".", 15 14 "dts-buddy": "^0.5.5", 16 15 "htmlparser2": "^10.0.0", 17 16 "magic-string": "^0.30.17", 18 - "playwright": "^1.50.1", 19 17 "prettier": "^3.4.2", 20 18 "rolldown": "^1.0.0-beta.6", 21 19 "typescript": "^5.7.2", 22 - "vitest": "^3.0.6", 23 20 "zimmerframe": "^1.1.2" 24 21 } 25 22 }, 26 - "node_modules/@ampproject/remapping": { 27 - "version": "2.3.0", 28 - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", 29 - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", 30 - "dev": true, 31 - "license": "Apache-2.0", 32 - "dependencies": { 33 - "@jridgewell/gen-mapping": "^0.3.5", 34 - "@jridgewell/trace-mapping": "^0.3.24" 35 - }, 36 - "engines": { 37 - "node": ">=6.0.0" 38 - } 39 - }, 40 - "node_modules/@babel/code-frame": { 41 - "version": "7.26.2", 42 - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", 43 - "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", 44 - "dev": true, 45 - "license": "MIT", 46 - "dependencies": { 47 - "@babel/helper-validator-identifier": "^7.25.9", 48 - "js-tokens": "^4.0.0", 49 - "picocolors": "^1.0.0" 50 - }, 51 - "engines": { 52 - "node": ">=6.9.0" 53 - } 54 - }, 55 - "node_modules/@babel/helper-string-parser": { 56 - "version": "7.25.9", 57 - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", 58 - "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", 59 - "dev": true, 60 - "license": "MIT", 61 - "engines": { 62 - "node": ">=6.9.0" 63 - } 64 - }, 65 - "node_modules/@babel/helper-validator-identifier": { 66 - "version": "7.25.9", 67 - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", 68 - "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", 69 - "dev": true, 70 - "license": "MIT", 71 - "engines": { 72 - "node": ">=6.9.0" 73 - } 74 - }, 75 - "node_modules/@babel/parser": { 76 - "version": "7.26.9", 77 - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.9.tgz", 78 - "integrity": "sha512-81NWa1njQblgZbQHxWHpxxCzNsa3ZwvFqpUg7P+NNUU6f3UU2jBEg4OlF/J6rl8+PQGh1q6/zWScd001YwcA5A==", 79 - "dev": true, 80 - "license": "MIT", 81 - "dependencies": { 82 - "@babel/types": "^7.26.9" 83 - }, 84 - "bin": { 85 - "parser": "bin/babel-parser.js" 86 - }, 87 - "engines": { 88 - "node": ">=6.0.0" 89 - } 90 - }, 91 - "node_modules/@babel/runtime": { 92 - "version": "7.26.7", 93 - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.7.tgz", 94 - "integrity": "sha512-AOPI3D+a8dXnja+iwsUqGRjr1BbZIe771sXdapOtYI531gSqpi92vXivKcq2asu/DFpdl1ceFAKZyRzK2PCVcQ==", 95 - "dev": true, 96 - "license": "MIT", 97 - "dependencies": { 98 - "regenerator-runtime": "^0.14.0" 99 - }, 100 - "engines": { 101 - "node": ">=6.9.0" 102 - } 103 - }, 104 - "node_modules/@babel/types": { 105 - "version": "7.26.9", 106 - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.9.tgz", 107 - "integrity": "sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw==", 108 - "dev": true, 109 - "license": "MIT", 110 - "dependencies": { 111 - "@babel/helper-string-parser": "^7.25.9", 112 - "@babel/helper-validator-identifier": "^7.25.9" 113 - }, 114 - "engines": { 115 - "node": ">=6.9.0" 116 - } 117 - }, 118 - "node_modules/@bcoe/v8-coverage": { 119 - "version": "1.0.2", 120 - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz", 121 - "integrity": "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==", 122 - "dev": true, 123 - "license": "MIT", 124 - "engines": { 125 - "node": ">=18" 126 - } 127 - }, 128 - "node_modules/@bundled-es-modules/cookie": { 129 - "version": "2.0.1", 130 - "resolved": "https://registry.npmjs.org/@bundled-es-modules/cookie/-/cookie-2.0.1.tgz", 131 - "integrity": "sha512-8o+5fRPLNbjbdGRRmJj3h6Hh1AQJf2dk3qQ/5ZFb+PXkRNiSoMGGUKlsgLfrxneb72axVJyIYji64E2+nNfYyw==", 132 - "dev": true, 133 - "license": "ISC", 134 - "dependencies": { 135 - "cookie": "^0.7.2" 136 - } 137 - }, 138 - "node_modules/@bundled-es-modules/statuses": { 139 - "version": "1.0.1", 140 - "resolved": "https://registry.npmjs.org/@bundled-es-modules/statuses/-/statuses-1.0.1.tgz", 141 - "integrity": "sha512-yn7BklA5acgcBr+7w064fGV+SGIFySjCKpqjcWgBAIfrAkY+4GQTJJHQMeT3V/sgz23VTEVV8TtOmkvJAhFVfg==", 142 - "dev": true, 143 - "license": "ISC", 144 - "dependencies": { 145 - "statuses": "^2.0.1" 146 - } 147 - }, 148 - "node_modules/@bundled-es-modules/tough-cookie": { 149 - "version": "0.1.6", 150 - "resolved": "https://registry.npmjs.org/@bundled-es-modules/tough-cookie/-/tough-cookie-0.1.6.tgz", 151 - "integrity": "sha512-dvMHbL464C0zI+Yqxbz6kZ5TOEp7GLW+pry/RWndAR8MJQAXZ2rPmIs8tziTZjeIyhSNZgZbCePtfSbdWqStJw==", 152 - "dev": true, 153 - "license": "ISC", 154 - "dependencies": { 155 - "@types/tough-cookie": "^4.0.5", 156 - "tough-cookie": "^4.1.4" 157 - } 158 - }, 159 23 "node_modules/@emnapi/core": { 160 24 "version": "1.3.1", 161 25 "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.3.1.tgz", ··· 190 54 "tslib": "^2.4.0" 191 55 } 192 56 }, 193 - "node_modules/@esbuild/aix-ppc64": { 194 - "version": "0.24.2", 195 - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.2.tgz", 196 - "integrity": "sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==", 197 - "cpu": [ 198 - "ppc64" 199 - ], 200 - "dev": true, 201 - "license": "MIT", 202 - "optional": true, 203 - "os": [ 204 - "aix" 205 - ], 206 - "engines": { 207 - "node": ">=18" 208 - } 209 - }, 210 - "node_modules/@esbuild/android-arm": { 211 - "version": "0.24.2", 212 - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.2.tgz", 213 - "integrity": "sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==", 214 - "cpu": [ 215 - "arm" 216 - ], 217 - "dev": true, 218 - "license": "MIT", 219 - "optional": true, 220 - "os": [ 221 - "android" 222 - ], 223 - "engines": { 224 - "node": ">=18" 225 - } 226 - }, 227 - "node_modules/@esbuild/android-arm64": { 228 - "version": "0.24.2", 229 - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.2.tgz", 230 - "integrity": "sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==", 231 - "cpu": [ 232 - "arm64" 233 - ], 234 - "dev": true, 235 - "license": "MIT", 236 - "optional": true, 237 - "os": [ 238 - "android" 239 - ], 240 - "engines": { 241 - "node": ">=18" 242 - } 243 - }, 244 - "node_modules/@esbuild/android-x64": { 245 - "version": "0.24.2", 246 - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.2.tgz", 247 - "integrity": "sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==", 248 - "cpu": [ 249 - "x64" 250 - ], 251 - "dev": true, 252 - "license": "MIT", 253 - "optional": true, 254 - "os": [ 255 - "android" 256 - ], 257 - "engines": { 258 - "node": ">=18" 259 - } 260 - }, 261 - "node_modules/@esbuild/darwin-arm64": { 262 - "version": "0.24.2", 263 - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.2.tgz", 264 - "integrity": "sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==", 265 - "cpu": [ 266 - "arm64" 267 - ], 268 - "dev": true, 269 - "license": "MIT", 270 - "optional": true, 271 - "os": [ 272 - "darwin" 273 - ], 274 - "engines": { 275 - "node": ">=18" 276 - } 277 - }, 278 - "node_modules/@esbuild/darwin-x64": { 279 - "version": "0.24.2", 280 - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.2.tgz", 281 - "integrity": "sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==", 282 - "cpu": [ 283 - "x64" 284 - ], 285 - "dev": true, 286 - "license": "MIT", 287 - "optional": true, 288 - "os": [ 289 - "darwin" 290 - ], 291 - "engines": { 292 - "node": ">=18" 293 - } 294 - }, 295 - "node_modules/@esbuild/freebsd-arm64": { 296 - "version": "0.24.2", 297 - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.2.tgz", 298 - "integrity": "sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==", 299 - "cpu": [ 300 - "arm64" 301 - ], 302 - "dev": true, 303 - "license": "MIT", 304 - "optional": true, 305 - "os": [ 306 - "freebsd" 307 - ], 308 - "engines": { 309 - "node": ">=18" 310 - } 311 - }, 312 - "node_modules/@esbuild/freebsd-x64": { 313 - "version": "0.24.2", 314 - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.2.tgz", 315 - "integrity": "sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==", 316 - "cpu": [ 317 - "x64" 318 - ], 319 - "dev": true, 320 - "license": "MIT", 321 - "optional": true, 322 - "os": [ 323 - "freebsd" 324 - ], 325 - "engines": { 326 - "node": ">=18" 327 - } 328 - }, 329 - "node_modules/@esbuild/linux-arm": { 330 - "version": "0.24.2", 331 - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.2.tgz", 332 - "integrity": "sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==", 333 - "cpu": [ 334 - "arm" 335 - ], 336 - "dev": true, 337 - "license": "MIT", 338 - "optional": true, 339 - "os": [ 340 - "linux" 341 - ], 342 - "engines": { 343 - "node": ">=18" 344 - } 345 - }, 346 - "node_modules/@esbuild/linux-arm64": { 347 - "version": "0.24.2", 348 - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.2.tgz", 349 - "integrity": "sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==", 350 - "cpu": [ 351 - "arm64" 352 - ], 353 - "dev": true, 354 - "license": "MIT", 355 - "optional": true, 356 - "os": [ 357 - "linux" 358 - ], 359 - "engines": { 360 - "node": ">=18" 361 - } 362 - }, 363 - "node_modules/@esbuild/linux-ia32": { 364 - "version": "0.24.2", 365 - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.2.tgz", 366 - "integrity": "sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==", 367 - "cpu": [ 368 - "ia32" 369 - ], 370 - "dev": true, 371 - "license": "MIT", 372 - "optional": true, 373 - "os": [ 374 - "linux" 375 - ], 376 - "engines": { 377 - "node": ">=18" 378 - } 379 - }, 380 - "node_modules/@esbuild/linux-loong64": { 381 - "version": "0.24.2", 382 - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.2.tgz", 383 - "integrity": "sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==", 384 - "cpu": [ 385 - "loong64" 386 - ], 387 - "dev": true, 388 - "license": "MIT", 389 - "optional": true, 390 - "os": [ 391 - "linux" 392 - ], 393 - "engines": { 394 - "node": ">=18" 395 - } 396 - }, 397 - "node_modules/@esbuild/linux-mips64el": { 398 - "version": "0.24.2", 399 - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.2.tgz", 400 - "integrity": "sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==", 401 - "cpu": [ 402 - "mips64el" 403 - ], 404 - "dev": true, 405 - "license": "MIT", 406 - "optional": true, 407 - "os": [ 408 - "linux" 409 - ], 410 - "engines": { 411 - "node": ">=18" 412 - } 413 - }, 414 - "node_modules/@esbuild/linux-ppc64": { 415 - "version": "0.24.2", 416 - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.2.tgz", 417 - "integrity": "sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==", 418 - "cpu": [ 419 - "ppc64" 420 - ], 421 - "dev": true, 422 - "license": "MIT", 423 - "optional": true, 424 - "os": [ 425 - "linux" 426 - ], 427 - "engines": { 428 - "node": ">=18" 429 - } 430 - }, 431 - "node_modules/@esbuild/linux-riscv64": { 432 - "version": "0.24.2", 433 - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.2.tgz", 434 - "integrity": "sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==", 435 - "cpu": [ 436 - "riscv64" 437 - ], 438 - "dev": true, 439 - "license": "MIT", 440 - "optional": true, 441 - "os": [ 442 - "linux" 443 - ], 444 - "engines": { 445 - "node": ">=18" 446 - } 447 - }, 448 - "node_modules/@esbuild/linux-s390x": { 449 - "version": "0.24.2", 450 - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.2.tgz", 451 - "integrity": "sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==", 452 - "cpu": [ 453 - "s390x" 454 - ], 455 - "dev": true, 456 - "license": "MIT", 457 - "optional": true, 458 - "os": [ 459 - "linux" 460 - ], 461 - "engines": { 462 - "node": ">=18" 463 - } 464 - }, 465 - "node_modules/@esbuild/linux-x64": { 466 - "version": "0.24.2", 467 - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.2.tgz", 468 - "integrity": "sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==", 469 - "cpu": [ 470 - "x64" 471 - ], 472 - "dev": true, 473 - "license": "MIT", 474 - "optional": true, 475 - "os": [ 476 - "linux" 477 - ], 478 - "engines": { 479 - "node": ">=18" 480 - } 481 - }, 482 - "node_modules/@esbuild/netbsd-arm64": { 483 - "version": "0.24.2", 484 - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.24.2.tgz", 485 - "integrity": "sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==", 486 - "cpu": [ 487 - "arm64" 488 - ], 489 - "dev": true, 490 - "license": "MIT", 491 - "optional": true, 492 - "os": [ 493 - "netbsd" 494 - ], 495 - "engines": { 496 - "node": ">=18" 497 - } 498 - }, 499 - "node_modules/@esbuild/netbsd-x64": { 500 - "version": "0.24.2", 501 - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.2.tgz", 502 - "integrity": "sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==", 503 - "cpu": [ 504 - "x64" 505 - ], 506 - "dev": true, 507 - "license": "MIT", 508 - "optional": true, 509 - "os": [ 510 - "netbsd" 511 - ], 512 - "engines": { 513 - "node": ">=18" 514 - } 515 - }, 516 - "node_modules/@esbuild/openbsd-arm64": { 517 - "version": "0.24.2", 518 - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.2.tgz", 519 - "integrity": "sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==", 520 - "cpu": [ 521 - "arm64" 522 - ], 523 - "dev": true, 524 - "license": "MIT", 525 - "optional": true, 526 - "os": [ 527 - "openbsd" 528 - ], 529 - "engines": { 530 - "node": ">=18" 531 - } 532 - }, 533 - "node_modules/@esbuild/openbsd-x64": { 534 - "version": "0.24.2", 535 - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.2.tgz", 536 - "integrity": "sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==", 537 - "cpu": [ 538 - "x64" 539 - ], 540 - "dev": true, 541 - "license": "MIT", 542 - "optional": true, 543 - "os": [ 544 - "openbsd" 545 - ], 546 - "engines": { 547 - "node": ">=18" 548 - } 549 - }, 550 - "node_modules/@esbuild/sunos-x64": { 551 - "version": "0.24.2", 552 - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.2.tgz", 553 - "integrity": "sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==", 554 - "cpu": [ 555 - "x64" 556 - ], 557 - "dev": true, 558 - "license": "MIT", 559 - "optional": true, 560 - "os": [ 561 - "sunos" 562 - ], 563 - "engines": { 564 - "node": ">=18" 565 - } 566 - }, 567 - "node_modules/@esbuild/win32-arm64": { 568 - "version": "0.24.2", 569 - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.2.tgz", 570 - "integrity": "sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==", 571 - "cpu": [ 572 - "arm64" 573 - ], 574 - "dev": true, 575 - "license": "MIT", 576 - "optional": true, 577 - "os": [ 578 - "win32" 579 - ], 580 - "engines": { 581 - "node": ">=18" 582 - } 583 - }, 584 - "node_modules/@esbuild/win32-ia32": { 585 - "version": "0.24.2", 586 - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.2.tgz", 587 - "integrity": "sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==", 588 - "cpu": [ 589 - "ia32" 590 - ], 591 - "dev": true, 592 - "license": "MIT", 593 - "optional": true, 594 - "os": [ 595 - "win32" 596 - ], 597 - "engines": { 598 - "node": ">=18" 599 - } 600 - }, 601 - "node_modules/@esbuild/win32-x64": { 602 - "version": "0.24.2", 603 - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.2.tgz", 604 - "integrity": "sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==", 605 - "cpu": [ 606 - "x64" 607 - ], 608 - "dev": true, 609 - "license": "MIT", 610 - "optional": true, 611 - "os": [ 612 - "win32" 613 - ], 614 - "engines": { 615 - "node": ">=18" 616 - } 617 - }, 618 - "node_modules/@inquirer/confirm": { 619 - "version": "5.1.5", 620 - "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.5.tgz", 621 - "integrity": "sha512-ZB2Cz8KeMINUvoeDi7IrvghaVkYT2RB0Zb31EaLWOE87u276w4wnApv0SH2qWaJ3r0VSUa3BIuz7qAV2ZvsZlg==", 622 - "dev": true, 623 - "license": "MIT", 624 - "dependencies": { 625 - "@inquirer/core": "^10.1.6", 626 - "@inquirer/type": "^3.0.4" 627 - }, 628 - "engines": { 629 - "node": ">=18" 630 - }, 631 - "peerDependencies": { 632 - "@types/node": ">=18" 633 - }, 634 - "peerDependenciesMeta": { 635 - "@types/node": { 636 - "optional": true 637 - } 638 - } 639 - }, 640 - "node_modules/@inquirer/core": { 641 - "version": "10.1.6", 642 - "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.1.6.tgz", 643 - "integrity": "sha512-Bwh/Zk6URrHwZnSSzAZAKH7YgGYi0xICIBDFOqBQoXNNAzBHw/bgXgLmChfp+GyR3PnChcTbiCTZGC6YJNJkMA==", 644 - "dev": true, 645 - "license": "MIT", 646 - "dependencies": { 647 - "@inquirer/figures": "^1.0.10", 648 - "@inquirer/type": "^3.0.4", 649 - "ansi-escapes": "^4.3.2", 650 - "cli-width": "^4.1.0", 651 - "mute-stream": "^2.0.0", 652 - "signal-exit": "^4.1.0", 653 - "wrap-ansi": "^6.2.0", 654 - "yoctocolors-cjs": "^2.1.2" 655 - }, 656 - "engines": { 657 - "node": ">=18" 658 - }, 659 - "peerDependencies": { 660 - "@types/node": ">=18" 661 - }, 662 - "peerDependenciesMeta": { 663 - "@types/node": { 664 - "optional": true 665 - } 666 - } 667 - }, 668 - "node_modules/@inquirer/figures": { 669 - "version": "1.0.10", 670 - "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.10.tgz", 671 - "integrity": "sha512-Ey6176gZmeqZuY/W/nZiUyvmb1/qInjcpiZjXWi6nON+nxJpD1bxtSoBxNliGISae32n6OwbY+TSXPZ1CfS4bw==", 57 + "node_modules/@happy-dom/global-registrator": { 58 + "version": "17.4.4", 59 + "resolved": "https://registry.npmjs.org/@happy-dom/global-registrator/-/global-registrator-17.4.4.tgz", 60 + "integrity": "sha512-njrU74GrYVHO43upIJr96f7pEmUG7YLZbHCGiHALBECeVnDKpepzL9kVc7KIl8S2nQOkPA0rAA1EyC3xASb54w==", 672 61 "dev": true, 673 62 "license": "MIT", 674 - "engines": { 675 - "node": ">=18" 676 - } 677 - }, 678 - "node_modules/@inquirer/type": { 679 - "version": "3.0.4", 680 - "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.4.tgz", 681 - "integrity": "sha512-2MNFrDY8jkFYc9Il9DgLsHhMzuHnOYM1+CUYVWbzu9oT0hC7V7EcYvdCKeoll/Fcci04A+ERZ9wcc7cQ8lTkIA==", 682 - "dev": true, 683 - "license": "MIT", 684 - "engines": { 685 - "node": ">=18" 686 - }, 687 - "peerDependencies": { 688 - "@types/node": ">=18" 689 - }, 690 - "peerDependenciesMeta": { 691 - "@types/node": { 692 - "optional": true 693 - } 694 - } 695 - }, 696 - "node_modules/@isaacs/cliui": { 697 - "version": "8.0.2", 698 - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", 699 - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", 700 - "dev": true, 701 - "license": "ISC", 702 63 "dependencies": { 703 - "string-width": "^5.1.2", 704 - "string-width-cjs": "npm:string-width@^4.2.0", 705 - "strip-ansi": "^7.0.1", 706 - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", 707 - "wrap-ansi": "^8.1.0", 708 - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" 64 + "happy-dom": "^17.4.4" 709 65 }, 710 66 "engines": { 711 - "node": ">=12" 712 - } 713 - }, 714 - "node_modules/@isaacs/cliui/node_modules/ansi-regex": { 715 - "version": "6.1.0", 716 - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", 717 - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", 718 - "dev": true, 719 - "license": "MIT", 720 - "engines": { 721 - "node": ">=12" 722 - }, 723 - "funding": { 724 - "url": "https://github.com/chalk/ansi-regex?sponsor=1" 725 - } 726 - }, 727 - "node_modules/@isaacs/cliui/node_modules/ansi-styles": { 728 - "version": "6.2.1", 729 - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", 730 - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", 731 - "dev": true, 732 - "license": "MIT", 733 - "engines": { 734 - "node": ">=12" 735 - }, 736 - "funding": { 737 - "url": "https://github.com/chalk/ansi-styles?sponsor=1" 738 - } 739 - }, 740 - "node_modules/@isaacs/cliui/node_modules/emoji-regex": { 741 - "version": "9.2.2", 742 - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", 743 - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", 744 - "dev": true, 745 - "license": "MIT" 746 - }, 747 - "node_modules/@isaacs/cliui/node_modules/string-width": { 748 - "version": "5.1.2", 749 - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", 750 - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", 751 - "dev": true, 752 - "license": "MIT", 753 - "dependencies": { 754 - "eastasianwidth": "^0.2.0", 755 - "emoji-regex": "^9.2.2", 756 - "strip-ansi": "^7.0.1" 757 - }, 758 - "engines": { 759 - "node": ">=12" 760 - }, 761 - "funding": { 762 - "url": "https://github.com/sponsors/sindresorhus" 763 - } 764 - }, 765 - "node_modules/@isaacs/cliui/node_modules/strip-ansi": { 766 - "version": "7.1.0", 767 - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", 768 - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", 769 - "dev": true, 770 - "license": "MIT", 771 - "dependencies": { 772 - "ansi-regex": "^6.0.1" 773 - }, 774 - "engines": { 775 - "node": ">=12" 776 - }, 777 - "funding": { 778 - "url": "https://github.com/chalk/strip-ansi?sponsor=1" 779 - } 780 - }, 781 - "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { 782 - "version": "8.1.0", 783 - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", 784 - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", 785 - "dev": true, 786 - "license": "MIT", 787 - "dependencies": { 788 - "ansi-styles": "^6.1.0", 789 - "string-width": "^5.0.1", 790 - "strip-ansi": "^7.0.1" 791 - }, 792 - "engines": { 793 - "node": ">=12" 794 - }, 795 - "funding": { 796 - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" 797 - } 798 - }, 799 - "node_modules/@istanbuljs/schema": { 800 - "version": "0.1.3", 801 - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", 802 - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", 803 - "dev": true, 804 - "license": "MIT", 805 - "engines": { 806 - "node": ">=8" 67 + "node": ">=18.0.0" 807 68 } 808 69 }, 809 70 "node_modules/@jridgewell/gen-mapping": { ··· 858 119 "@jridgewell/sourcemap-codec": "^1.4.14" 859 120 } 860 121 }, 861 - "node_modules/@mswjs/interceptors": { 862 - "version": "0.37.6", 863 - "resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.37.6.tgz", 864 - "integrity": "sha512-wK+5pLK5XFmgtH3aQ2YVvA3HohS3xqV/OxuVOdNx9Wpnz7VE/fnC+e1A7ln6LFYeck7gOJ/dsZV6OLplOtAJ2w==", 865 - "dev": true, 866 - "license": "MIT", 867 - "dependencies": { 868 - "@open-draft/deferred-promise": "^2.2.0", 869 - "@open-draft/logger": "^0.3.0", 870 - "@open-draft/until": "^2.0.0", 871 - "is-node-process": "^1.2.0", 872 - "outvariant": "^1.4.3", 873 - "strict-event-emitter": "^0.5.1" 874 - }, 875 - "engines": { 876 - "node": ">=18" 877 - } 878 - }, 879 122 "node_modules/@napi-rs/wasm-runtime": { 880 123 "version": "0.2.7", 881 124 "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.7.tgz", ··· 889 132 "@tybys/wasm-util": "^0.9.0" 890 133 } 891 134 }, 892 - "node_modules/@open-draft/deferred-promise": { 893 - "version": "2.2.0", 894 - "resolved": "https://registry.npmjs.org/@open-draft/deferred-promise/-/deferred-promise-2.2.0.tgz", 895 - "integrity": "sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==", 896 - "dev": true, 897 - "license": "MIT" 898 - }, 899 - "node_modules/@open-draft/logger": { 900 - "version": "0.3.0", 901 - "resolved": "https://registry.npmjs.org/@open-draft/logger/-/logger-0.3.0.tgz", 902 - "integrity": "sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==", 903 - "dev": true, 904 - "license": "MIT", 905 - "dependencies": { 906 - "is-node-process": "^1.2.0", 907 - "outvariant": "^1.4.0" 908 - } 909 - }, 910 - "node_modules/@open-draft/until": { 911 - "version": "2.1.0", 912 - "resolved": "https://registry.npmjs.org/@open-draft/until/-/until-2.1.0.tgz", 913 - "integrity": "sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==", 914 - "dev": true, 915 - "license": "MIT" 916 - }, 917 135 "node_modules/@oxc-project/types": { 918 136 "version": "0.58.1", 919 137 "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.58.1.tgz", ··· 923 141 "funding": { 924 142 "url": "https://github.com/sponsors/Boshen" 925 143 } 926 - }, 927 - "node_modules/@pkgjs/parseargs": { 928 - "version": "0.11.0", 929 - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", 930 - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", 931 - "dev": true, 932 - "license": "MIT", 933 - "optional": true, 934 - "engines": { 935 - "node": ">=14" 936 - } 937 - }, 938 - "node_modules/@polka/url": { 939 - "version": "1.0.0-next.28", 940 - "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.28.tgz", 941 - "integrity": "sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw==", 942 - "dev": true, 943 - "license": "MIT" 944 144 }, 945 145 "node_modules/@rolldown/binding-darwin-arm64": { 946 146 "version": "1.0.0-beta.6", ··· 1148 348 "optional": true, 1149 349 "os": [ 1150 350 "android" 1151 - ] 351 + ], 352 + "peer": true 1152 353 }, 1153 354 "node_modules/@rollup/rollup-android-arm64": { 1154 355 "version": "4.34.8", ··· 1162 363 "optional": true, 1163 364 "os": [ 1164 365 "android" 1165 - ] 366 + ], 367 + "peer": true 1166 368 }, 1167 369 "node_modules/@rollup/rollup-darwin-arm64": { 1168 370 "version": "4.34.8", ··· 1176 378 "optional": true, 1177 379 "os": [ 1178 380 "darwin" 1179 - ] 381 + ], 382 + "peer": true 1180 383 }, 1181 384 "node_modules/@rollup/rollup-darwin-x64": { 1182 385 "version": "4.34.8", ··· 1190 393 "optional": true, 1191 394 "os": [ 1192 395 "darwin" 1193 - ] 396 + ], 397 + "peer": true 1194 398 }, 1195 399 "node_modules/@rollup/rollup-freebsd-arm64": { 1196 400 "version": "4.34.8", ··· 1204 408 "optional": true, 1205 409 "os": [ 1206 410 "freebsd" 1207 - ] 411 + ], 412 + "peer": true 1208 413 }, 1209 414 "node_modules/@rollup/rollup-freebsd-x64": { 1210 415 "version": "4.34.8", ··· 1218 423 "optional": true, 1219 424 "os": [ 1220 425 "freebsd" 1221 - ] 426 + ], 427 + "peer": true 1222 428 }, 1223 429 "node_modules/@rollup/rollup-linux-arm-gnueabihf": { 1224 430 "version": "4.34.8", ··· 1232 438 "optional": true, 1233 439 "os": [ 1234 440 "linux" 1235 - ] 441 + ], 442 + "peer": true 1236 443 }, 1237 444 "node_modules/@rollup/rollup-linux-arm-musleabihf": { 1238 445 "version": "4.34.8", ··· 1246 453 "optional": true, 1247 454 "os": [ 1248 455 "linux" 1249 - ] 456 + ], 457 + "peer": true 1250 458 }, 1251 459 "node_modules/@rollup/rollup-linux-arm64-gnu": { 1252 460 "version": "4.34.8", ··· 1260 468 "optional": true, 1261 469 "os": [ 1262 470 "linux" 1263 - ] 471 + ], 472 + "peer": true 1264 473 }, 1265 474 "node_modules/@rollup/rollup-linux-arm64-musl": { 1266 475 "version": "4.34.8", ··· 1274 483 "optional": true, 1275 484 "os": [ 1276 485 "linux" 1277 - ] 486 + ], 487 + "peer": true 1278 488 }, 1279 489 "node_modules/@rollup/rollup-linux-loongarch64-gnu": { 1280 490 "version": "4.34.8", ··· 1288 498 "optional": true, 1289 499 "os": [ 1290 500 "linux" 1291 - ] 501 + ], 502 + "peer": true 1292 503 }, 1293 504 "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { 1294 505 "version": "4.34.8", ··· 1302 513 "optional": true, 1303 514 "os": [ 1304 515 "linux" 1305 - ] 516 + ], 517 + "peer": true 1306 518 }, 1307 519 "node_modules/@rollup/rollup-linux-riscv64-gnu": { 1308 520 "version": "4.34.8", ··· 1316 528 "optional": true, 1317 529 "os": [ 1318 530 "linux" 1319 - ] 531 + ], 532 + "peer": true 1320 533 }, 1321 534 "node_modules/@rollup/rollup-linux-s390x-gnu": { 1322 535 "version": "4.34.8", ··· 1330 543 "optional": true, 1331 544 "os": [ 1332 545 "linux" 1333 - ] 546 + ], 547 + "peer": true 1334 548 }, 1335 549 "node_modules/@rollup/rollup-linux-x64-gnu": { 1336 550 "version": "4.34.8", ··· 1344 558 "optional": true, 1345 559 "os": [ 1346 560 "linux" 1347 - ] 561 + ], 562 + "peer": true 1348 563 }, 1349 564 "node_modules/@rollup/rollup-linux-x64-musl": { 1350 565 "version": "4.34.8", ··· 1358 573 "optional": true, 1359 574 "os": [ 1360 575 "linux" 1361 - ] 576 + ], 577 + "peer": true 1362 578 }, 1363 579 "node_modules/@rollup/rollup-win32-arm64-msvc": { 1364 580 "version": "4.34.8", ··· 1372 588 "optional": true, 1373 589 "os": [ 1374 590 "win32" 1375 - ] 591 + ], 592 + "peer": true 1376 593 }, 1377 594 "node_modules/@rollup/rollup-win32-ia32-msvc": { 1378 595 "version": "4.34.8", ··· 1386 603 "optional": true, 1387 604 "os": [ 1388 605 "win32" 1389 - ] 606 + ], 607 + "peer": true 1390 608 }, 1391 609 "node_modules/@rollup/rollup-win32-x64-msvc": { 1392 610 "version": "4.34.8", ··· 1400 618 "optional": true, 1401 619 "os": [ 1402 620 "win32" 1403 - ] 1404 - }, 1405 - "node_modules/@testing-library/dom": { 1406 - "version": "10.4.0", 1407 - "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz", 1408 - "integrity": "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==", 1409 - "dev": true, 1410 - "license": "MIT", 1411 - "dependencies": { 1412 - "@babel/code-frame": "^7.10.4", 1413 - "@babel/runtime": "^7.12.5", 1414 - "@types/aria-query": "^5.0.1", 1415 - "aria-query": "5.3.0", 1416 - "chalk": "^4.1.0", 1417 - "dom-accessibility-api": "^0.5.9", 1418 - "lz-string": "^1.5.0", 1419 - "pretty-format": "^27.0.2" 1420 - }, 1421 - "engines": { 1422 - "node": ">=18" 1423 - } 1424 - }, 1425 - "node_modules/@testing-library/user-event": { 1426 - "version": "14.6.1", 1427 - "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.6.1.tgz", 1428 - "integrity": "sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==", 1429 - "dev": true, 1430 - "license": "MIT", 1431 - "engines": { 1432 - "node": ">=12", 1433 - "npm": ">=6" 1434 - }, 1435 - "peerDependencies": { 1436 - "@testing-library/dom": ">=7.21.4" 1437 - } 621 + ], 622 + "peer": true 1438 623 }, 1439 624 "node_modules/@tybys/wasm-util": { 1440 625 "version": "0.9.0", ··· 1447 632 "tslib": "^2.4.0" 1448 633 } 1449 634 }, 1450 - "node_modules/@types/aria-query": { 1451 - "version": "5.0.4", 1452 - "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", 1453 - "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", 1454 - "dev": true, 1455 - "license": "MIT" 1456 - }, 1457 - "node_modules/@types/cookie": { 1458 - "version": "0.6.0", 1459 - "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", 1460 - "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==", 1461 - "dev": true, 1462 - "license": "MIT" 1463 - }, 1464 635 "node_modules/@types/estree": { 1465 636 "version": "1.0.6", 1466 637 "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", 1467 638 "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", 1468 639 "dev": true, 1469 - "license": "MIT" 640 + "license": "MIT", 641 + "optional": true, 642 + "peer": true 1470 643 }, 1471 - "node_modules/@types/statuses": { 1472 - "version": "2.0.5", 1473 - "resolved": "https://registry.npmjs.org/@types/statuses/-/statuses-2.0.5.tgz", 1474 - "integrity": "sha512-jmIUGWrAiwu3dZpxntxieC+1n/5c3mjrImkmOSQ2NC5uP6cYO4aAZDdSmRcI5C1oiTmqlZGHC+/NmJrKogbP5A==", 644 + "node_modules/@types/node": { 645 + "version": "22.13.11", 646 + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.11.tgz", 647 + "integrity": "sha512-iEUCUJoU0i3VnrCmgoWCXttklWcvoCIx4jzcP22fioIVSdTmjgoEvmAO/QPw6TcS9k5FrNgn4w7q5lGOd1CT5g==", 1475 648 "dev": true, 1476 - "license": "MIT" 1477 - }, 1478 - "node_modules/@types/tough-cookie": { 1479 - "version": "4.0.5", 1480 - "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", 1481 - "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", 1482 - "dev": true, 1483 - "license": "MIT" 649 + "license": "MIT", 650 + "dependencies": { 651 + "undici-types": "~6.20.0" 652 + } 1484 653 }, 1485 654 "node_modules/@valibot/to-json-schema": { 1486 655 "version": "1.0.0-rc.0", ··· 1492 661 "valibot": "^1.0.0 || ^1.0.0-beta.5 || ^1.0.0-rc" 1493 662 } 1494 663 }, 1495 - "node_modules/@vitest/browser": { 1496 - "version": "3.0.6", 1497 - "resolved": "https://registry.npmjs.org/@vitest/browser/-/browser-3.0.6.tgz", 1498 - "integrity": "sha512-FqKwCAkALZfNzGNx4YvRJa6HCWM2USWTjOdNO2egI/s6+3WkIl4xAlYISOARLJLDAI3yCXcpTtuUUF39K8TQgw==", 1499 - "dev": true, 1500 - "license": "MIT", 1501 - "dependencies": { 1502 - "@testing-library/dom": "^10.4.0", 1503 - "@testing-library/user-event": "^14.6.1", 1504 - "@vitest/mocker": "3.0.6", 1505 - "@vitest/utils": "3.0.6", 1506 - "magic-string": "^0.30.17", 1507 - "msw": "^2.7.0", 1508 - "sirv": "^3.0.1", 1509 - "tinyrainbow": "^2.0.0", 1510 - "ws": "^8.18.0" 1511 - }, 1512 - "funding": { 1513 - "url": "https://opencollective.com/vitest" 1514 - }, 1515 - "peerDependencies": { 1516 - "playwright": "*", 1517 - "vitest": "3.0.6", 1518 - "webdriverio": "*" 1519 - }, 1520 - "peerDependenciesMeta": { 1521 - "playwright": { 1522 - "optional": true 1523 - }, 1524 - "safaridriver": { 1525 - "optional": true 1526 - }, 1527 - "webdriverio": { 1528 - "optional": true 1529 - } 1530 - } 1531 - }, 1532 - "node_modules/@vitest/coverage-v8": { 1533 - "version": "3.0.6", 1534 - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.0.6.tgz", 1535 - "integrity": "sha512-JRTlR8Bw+4BcmVTICa7tJsxqphAktakiLsAmibVLAWbu1lauFddY/tXeM6sAyl1cgkPuXtpnUgaCPhTdz1Qapg==", 1536 - "dev": true, 1537 - "license": "MIT", 1538 - "dependencies": { 1539 - "@ampproject/remapping": "^2.3.0", 1540 - "@bcoe/v8-coverage": "^1.0.2", 1541 - "debug": "^4.4.0", 1542 - "istanbul-lib-coverage": "^3.2.2", 1543 - "istanbul-lib-report": "^3.0.1", 1544 - "istanbul-lib-source-maps": "^5.0.6", 1545 - "istanbul-reports": "^3.1.7", 1546 - "magic-string": "^0.30.17", 1547 - "magicast": "^0.3.5", 1548 - "std-env": "^3.8.0", 1549 - "test-exclude": "^7.0.1", 1550 - "tinyrainbow": "^2.0.0" 1551 - }, 1552 - "funding": { 1553 - "url": "https://opencollective.com/vitest" 1554 - }, 1555 - "peerDependencies": { 1556 - "@vitest/browser": "3.0.6", 1557 - "vitest": "3.0.6" 1558 - }, 1559 - "peerDependenciesMeta": { 1560 - "@vitest/browser": { 1561 - "optional": true 1562 - } 1563 - } 1564 - }, 1565 - "node_modules/@vitest/expect": { 1566 - "version": "3.0.6", 1567 - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.0.6.tgz", 1568 - "integrity": "sha512-zBduHf/ja7/QRX4HdP1DSq5XrPgdN+jzLOwaTq/0qZjYfgETNFCKf9nOAp2j3hmom3oTbczuUzrzg9Hafh7hNg==", 1569 - "dev": true, 1570 - "license": "MIT", 1571 - "dependencies": { 1572 - "@vitest/spy": "3.0.6", 1573 - "@vitest/utils": "3.0.6", 1574 - "chai": "^5.2.0", 1575 - "tinyrainbow": "^2.0.0" 1576 - }, 1577 - "funding": { 1578 - "url": "https://opencollective.com/vitest" 1579 - } 1580 - }, 1581 - "node_modules/@vitest/mocker": { 1582 - "version": "3.0.6", 1583 - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.0.6.tgz", 1584 - "integrity": "sha512-KPztr4/tn7qDGZfqlSPQoF2VgJcKxnDNhmfR3VgZ6Fy1bO8T9Fc1stUiTXtqz0yG24VpD00pZP5f8EOFknjNuQ==", 1585 - "dev": true, 1586 - "license": "MIT", 1587 - "dependencies": { 1588 - "@vitest/spy": "3.0.6", 1589 - "estree-walker": "^3.0.3", 1590 - "magic-string": "^0.30.17" 1591 - }, 1592 - "funding": { 1593 - "url": "https://opencollective.com/vitest" 1594 - }, 1595 - "peerDependencies": { 1596 - "msw": "^2.4.9", 1597 - "vite": "^5.0.0 || ^6.0.0" 1598 - }, 1599 - "peerDependenciesMeta": { 1600 - "msw": { 1601 - "optional": true 1602 - }, 1603 - "vite": { 1604 - "optional": true 1605 - } 1606 - } 1607 - }, 1608 - "node_modules/@vitest/pretty-format": { 1609 - "version": "3.0.6", 1610 - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.0.6.tgz", 1611 - "integrity": "sha512-Zyctv3dbNL+67qtHfRnUE/k8qxduOamRfAL1BurEIQSyOEFffoMvx2pnDSSbKAAVxY0Ej2J/GH2dQKI0W2JyVg==", 1612 - "dev": true, 1613 - "license": "MIT", 1614 - "dependencies": { 1615 - "tinyrainbow": "^2.0.0" 1616 - }, 1617 - "funding": { 1618 - "url": "https://opencollective.com/vitest" 1619 - } 1620 - }, 1621 - "node_modules/@vitest/runner": { 1622 - "version": "3.0.6", 1623 - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.0.6.tgz", 1624 - "integrity": "sha512-JopP4m/jGoaG1+CBqubV/5VMbi7L+NQCJTu1J1Pf6YaUbk7bZtaq5CX7p+8sY64Sjn1UQ1XJparHfcvTTdu9cA==", 1625 - "dev": true, 1626 - "license": "MIT", 1627 - "dependencies": { 1628 - "@vitest/utils": "3.0.6", 1629 - "pathe": "^2.0.3" 1630 - }, 1631 - "funding": { 1632 - "url": "https://opencollective.com/vitest" 1633 - } 1634 - }, 1635 - "node_modules/@vitest/snapshot": { 1636 - "version": "3.0.6", 1637 - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.0.6.tgz", 1638 - "integrity": "sha512-qKSmxNQwT60kNwwJHMVwavvZsMGXWmngD023OHSgn873pV0lylK7dwBTfYP7e4URy5NiBCHHiQGA9DHkYkqRqg==", 1639 - "dev": true, 1640 - "license": "MIT", 1641 - "dependencies": { 1642 - "@vitest/pretty-format": "3.0.6", 1643 - "magic-string": "^0.30.17", 1644 - "pathe": "^2.0.3" 1645 - }, 1646 - "funding": { 1647 - "url": "https://opencollective.com/vitest" 1648 - } 1649 - }, 1650 - "node_modules/@vitest/spy": { 1651 - "version": "3.0.6", 1652 - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.0.6.tgz", 1653 - "integrity": "sha512-HfOGx/bXtjy24fDlTOpgiAEJbRfFxoX3zIGagCqACkFKKZ/TTOE6gYMKXlqecvxEndKFuNHcHqP081ggZ2yM0Q==", 1654 - "dev": true, 1655 - "license": "MIT", 1656 - "dependencies": { 1657 - "tinyspy": "^3.0.2" 1658 - }, 1659 - "funding": { 1660 - "url": "https://opencollective.com/vitest" 1661 - } 1662 - }, 1663 - "node_modules/@vitest/ui": { 1664 - "version": "3.0.6", 1665 - "resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-3.0.6.tgz", 1666 - "integrity": "sha512-N4M2IUG2Q5LCeX4OWs48pQF4P3qsFejmDTc6QWGRFTLPrEe5EvM5HN0WSUnGAmuzQpSWv7ItfSsIJIWaEM2wpQ==", 1667 - "dev": true, 1668 - "license": "MIT", 1669 - "dependencies": { 1670 - "@vitest/utils": "3.0.6", 1671 - "fflate": "^0.8.2", 1672 - "flatted": "^3.3.2", 1673 - "pathe": "^2.0.3", 1674 - "sirv": "^3.0.1", 1675 - "tinyglobby": "^0.2.11", 1676 - "tinyrainbow": "^2.0.0" 1677 - }, 1678 - "funding": { 1679 - "url": "https://opencollective.com/vitest" 1680 - }, 1681 - "peerDependencies": { 1682 - "vitest": "3.0.6" 1683 - } 1684 - }, 1685 - "node_modules/@vitest/utils": { 1686 - "version": "3.0.6", 1687 - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.0.6.tgz", 1688 - "integrity": "sha512-18ktZpf4GQFTbf9jK543uspU03Q2qya7ZGya5yiZ0Gx0nnnalBvd5ZBislbl2EhLjM8A8rt4OilqKG7QwcGkvQ==", 1689 - "dev": true, 1690 - "license": "MIT", 1691 - "dependencies": { 1692 - "@vitest/pretty-format": "3.0.6", 1693 - "loupe": "^3.1.3", 1694 - "tinyrainbow": "^2.0.0" 1695 - }, 1696 - "funding": { 1697 - "url": "https://opencollective.com/vitest" 1698 - } 1699 - }, 1700 664 "node_modules/acorn": { 1701 665 "version": "8.14.0", 1702 666 "dev": true, ··· 1708 672 "node": ">=0.4.0" 1709 673 } 1710 674 }, 1711 - "node_modules/ansi-escapes": { 1712 - "version": "4.3.2", 1713 - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", 1714 - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", 1715 - "dev": true, 1716 - "license": "MIT", 1717 - "dependencies": { 1718 - "type-fest": "^0.21.3" 1719 - }, 1720 - "engines": { 1721 - "node": ">=8" 1722 - }, 1723 - "funding": { 1724 - "url": "https://github.com/sponsors/sindresorhus" 1725 - } 1726 - }, 1727 - "node_modules/ansi-escapes/node_modules/type-fest": { 1728 - "version": "0.21.3", 1729 - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", 1730 - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", 1731 - "dev": true, 1732 - "license": "(MIT OR CC0-1.0)", 1733 - "engines": { 1734 - "node": ">=10" 1735 - }, 1736 - "funding": { 1737 - "url": "https://github.com/sponsors/sindresorhus" 1738 - } 1739 - }, 1740 - "node_modules/ansi-regex": { 1741 - "version": "5.0.1", 1742 - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", 1743 - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", 1744 - "dev": true, 1745 - "license": "MIT", 1746 - "engines": { 1747 - "node": ">=8" 1748 - } 1749 - }, 1750 - "node_modules/ansi-styles": { 1751 - "version": "4.3.0", 1752 - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 1753 - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 1754 - "dev": true, 1755 - "license": "MIT", 1756 - "dependencies": { 1757 - "color-convert": "^2.0.1" 1758 - }, 1759 - "engines": { 1760 - "node": ">=8" 1761 - }, 1762 - "funding": { 1763 - "url": "https://github.com/chalk/ansi-styles?sponsor=1" 1764 - } 1765 - }, 1766 - "node_modules/aria-query": { 1767 - "version": "5.3.0", 1768 - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", 1769 - "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", 1770 - "dev": true, 1771 - "license": "Apache-2.0", 1772 - "dependencies": { 1773 - "dequal": "^2.0.3" 1774 - } 1775 - }, 1776 - "node_modules/assertion-error": { 1777 - "version": "2.0.1", 1778 - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", 1779 - "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", 1780 - "dev": true, 1781 - "license": "MIT", 1782 - "engines": { 1783 - "node": ">=12" 1784 - } 1785 - }, 1786 - "node_modules/balanced-match": { 1787 - "version": "1.0.2", 1788 - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 1789 - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", 1790 - "dev": true, 1791 - "license": "MIT" 1792 - }, 1793 - "node_modules/brace-expansion": { 1794 - "version": "2.0.1", 1795 - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", 1796 - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", 1797 - "dev": true, 1798 - "license": "MIT", 1799 - "dependencies": { 1800 - "balanced-match": "^1.0.0" 1801 - } 1802 - }, 1803 675 "node_modules/buffer-from": { 1804 676 "version": "1.1.2", 1805 677 "dev": true, 1806 678 "license": "MIT" 1807 679 }, 1808 - "node_modules/cac": { 1809 - "version": "6.7.14", 1810 - "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", 1811 - "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", 1812 - "dev": true, 1813 - "license": "MIT", 1814 - "engines": { 1815 - "node": ">=8" 1816 - } 1817 - }, 1818 - "node_modules/chai": { 1819 - "version": "5.2.0", 1820 - "resolved": "https://registry.npmjs.org/chai/-/chai-5.2.0.tgz", 1821 - "integrity": "sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==", 1822 - "dev": true, 1823 - "license": "MIT", 1824 - "dependencies": { 1825 - "assertion-error": "^2.0.1", 1826 - "check-error": "^2.1.1", 1827 - "deep-eql": "^5.0.1", 1828 - "loupe": "^3.1.0", 1829 - "pathval": "^2.0.0" 1830 - }, 1831 - "engines": { 1832 - "node": ">=12" 1833 - } 1834 - }, 1835 - "node_modules/chalk": { 1836 - "version": "4.1.2", 1837 - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", 1838 - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", 1839 - "dev": true, 1840 - "license": "MIT", 1841 - "dependencies": { 1842 - "ansi-styles": "^4.1.0", 1843 - "supports-color": "^7.1.0" 1844 - }, 1845 - "engines": { 1846 - "node": ">=10" 1847 - }, 1848 - "funding": { 1849 - "url": "https://github.com/chalk/chalk?sponsor=1" 1850 - } 1851 - }, 1852 - "node_modules/check-error": { 1853 - "version": "2.1.1", 1854 - "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", 1855 - "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==", 1856 - "dev": true, 1857 - "license": "MIT", 1858 - "engines": { 1859 - "node": ">= 16" 1860 - } 1861 - }, 1862 - "node_modules/cli-width": { 1863 - "version": "4.1.0", 1864 - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", 1865 - "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", 1866 - "dev": true, 1867 - "license": "ISC", 1868 - "engines": { 1869 - "node": ">= 12" 1870 - } 1871 - }, 1872 - "node_modules/cliui": { 1873 - "version": "8.0.1", 1874 - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", 1875 - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", 1876 - "dev": true, 1877 - "license": "ISC", 1878 - "dependencies": { 1879 - "string-width": "^4.2.0", 1880 - "strip-ansi": "^6.0.1", 1881 - "wrap-ansi": "^7.0.0" 1882 - }, 1883 - "engines": { 1884 - "node": ">=12" 1885 - } 1886 - }, 1887 - "node_modules/cliui/node_modules/wrap-ansi": { 1888 - "version": "7.0.0", 1889 - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", 1890 - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", 1891 - "dev": true, 1892 - "license": "MIT", 1893 - "dependencies": { 1894 - "ansi-styles": "^4.0.0", 1895 - "string-width": "^4.1.0", 1896 - "strip-ansi": "^6.0.0" 1897 - }, 1898 - "engines": { 1899 - "node": ">=10" 1900 - }, 1901 - "funding": { 1902 - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" 1903 - } 1904 - }, 1905 - "node_modules/color-convert": { 1906 - "version": "2.0.1", 1907 - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 1908 - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 1909 - "dev": true, 1910 - "license": "MIT", 1911 - "dependencies": { 1912 - "color-name": "~1.1.4" 1913 - }, 1914 - "engines": { 1915 - "node": ">=7.0.0" 1916 - } 1917 - }, 1918 - "node_modules/color-name": { 1919 - "version": "1.1.4", 1920 - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 1921 - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 1922 - "dev": true, 1923 - "license": "MIT" 1924 - }, 1925 680 "node_modules/commander": { 1926 681 "version": "2.20.3", 1927 682 "dev": true, 1928 683 "license": "MIT" 1929 684 }, 1930 - "node_modules/cookie": { 1931 - "version": "0.7.2", 1932 - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", 1933 - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", 1934 - "dev": true, 1935 - "license": "MIT", 1936 - "engines": { 1937 - "node": ">= 0.6" 1938 - } 1939 - }, 1940 - "node_modules/cross-spawn": { 1941 - "version": "7.0.6", 1942 - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", 1943 - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", 1944 - "dev": true, 1945 - "license": "MIT", 1946 - "dependencies": { 1947 - "path-key": "^3.1.0", 1948 - "shebang-command": "^2.0.0", 1949 - "which": "^2.0.1" 1950 - }, 1951 - "engines": { 1952 - "node": ">= 8" 1953 - } 1954 - }, 1955 - "node_modules/debug": { 1956 - "version": "4.4.0", 1957 - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", 1958 - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", 1959 - "dev": true, 1960 - "license": "MIT", 1961 - "dependencies": { 1962 - "ms": "^2.1.3" 1963 - }, 1964 - "engines": { 1965 - "node": ">=6.0" 1966 - }, 1967 - "peerDependenciesMeta": { 1968 - "supports-color": { 1969 - "optional": true 1970 - } 1971 - } 1972 - }, 1973 - "node_modules/deep-eql": { 1974 - "version": "5.0.2", 1975 - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", 1976 - "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", 1977 - "dev": true, 1978 - "license": "MIT", 1979 - "engines": { 1980 - "node": ">=6" 1981 - } 1982 - }, 1983 - "node_modules/dequal": { 1984 - "version": "2.0.3", 1985 - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", 1986 - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", 1987 - "dev": true, 1988 - "license": "MIT", 1989 - "engines": { 1990 - "node": ">=6" 1991 - } 1992 - }, 1993 685 "node_modules/dhtml": { 1994 686 "resolved": "", 1995 687 "link": true 1996 - }, 1997 - "node_modules/dom-accessibility-api": { 1998 - "version": "0.5.16", 1999 - "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", 2000 - "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", 2001 - "dev": true, 2002 - "license": "MIT" 2003 688 }, 2004 689 "node_modules/dom-serializer": { 2005 690 "version": "2.0.0", ··· 2096 781 "typescript": ">=5.0.4 <5.8" 2097 782 } 2098 783 }, 2099 - "node_modules/eastasianwidth": { 2100 - "version": "0.2.0", 2101 - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", 2102 - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", 2103 - "dev": true, 2104 - "license": "MIT" 2105 - }, 2106 - "node_modules/emoji-regex": { 2107 - "version": "8.0.0", 2108 - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", 2109 - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", 2110 - "dev": true, 2111 - "license": "MIT" 2112 - }, 2113 784 "node_modules/entities": { 2114 785 "version": "6.0.0", 2115 786 "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.0.tgz", ··· 2123 794 "url": "https://github.com/fb55/entities?sponsor=1" 2124 795 } 2125 796 }, 2126 - "node_modules/es-module-lexer": { 2127 - "version": "1.6.0", 2128 - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz", 2129 - "integrity": "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==", 2130 - "dev": true, 2131 - "license": "MIT" 2132 - }, 2133 - "node_modules/esbuild": { 2134 - "version": "0.24.2", 2135 - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.2.tgz", 2136 - "integrity": "sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==", 2137 - "dev": true, 2138 - "hasInstallScript": true, 2139 - "license": "MIT", 2140 - "bin": { 2141 - "esbuild": "bin/esbuild" 2142 - }, 2143 - "engines": { 2144 - "node": ">=18" 2145 - }, 2146 - "optionalDependencies": { 2147 - "@esbuild/aix-ppc64": "0.24.2", 2148 - "@esbuild/android-arm": "0.24.2", 2149 - "@esbuild/android-arm64": "0.24.2", 2150 - "@esbuild/android-x64": "0.24.2", 2151 - "@esbuild/darwin-arm64": "0.24.2", 2152 - "@esbuild/darwin-x64": "0.24.2", 2153 - "@esbuild/freebsd-arm64": "0.24.2", 2154 - "@esbuild/freebsd-x64": "0.24.2", 2155 - "@esbuild/linux-arm": "0.24.2", 2156 - "@esbuild/linux-arm64": "0.24.2", 2157 - "@esbuild/linux-ia32": "0.24.2", 2158 - "@esbuild/linux-loong64": "0.24.2", 2159 - "@esbuild/linux-mips64el": "0.24.2", 2160 - "@esbuild/linux-ppc64": "0.24.2", 2161 - "@esbuild/linux-riscv64": "0.24.2", 2162 - "@esbuild/linux-s390x": "0.24.2", 2163 - "@esbuild/linux-x64": "0.24.2", 2164 - "@esbuild/netbsd-arm64": "0.24.2", 2165 - "@esbuild/netbsd-x64": "0.24.2", 2166 - "@esbuild/openbsd-arm64": "0.24.2", 2167 - "@esbuild/openbsd-x64": "0.24.2", 2168 - "@esbuild/sunos-x64": "0.24.2", 2169 - "@esbuild/win32-arm64": "0.24.2", 2170 - "@esbuild/win32-ia32": "0.24.2", 2171 - "@esbuild/win32-x64": "0.24.2" 2172 - } 2173 - }, 2174 - "node_modules/escalade": { 2175 - "version": "3.2.0", 2176 - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", 2177 - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", 2178 - "dev": true, 2179 - "license": "MIT", 2180 - "engines": { 2181 - "node": ">=6" 2182 - } 2183 - }, 2184 - "node_modules/estree-walker": { 2185 - "version": "3.0.3", 2186 - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", 2187 - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", 2188 - "dev": true, 2189 - "license": "MIT", 2190 - "dependencies": { 2191 - "@types/estree": "^1.0.0" 2192 - } 2193 - }, 2194 - "node_modules/expect-type": { 2195 - "version": "1.1.0", 2196 - "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.1.0.tgz", 2197 - "integrity": "sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA==", 2198 - "dev": true, 2199 - "license": "Apache-2.0", 2200 - "engines": { 2201 - "node": ">=12.0.0" 2202 - } 2203 - }, 2204 797 "node_modules/fdir": { 2205 798 "version": "6.4.3", 2206 799 "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.3.tgz", ··· 2216 809 } 2217 810 } 2218 811 }, 2219 - "node_modules/fflate": { 2220 - "version": "0.8.2", 2221 - "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", 2222 - "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", 2223 - "dev": true, 2224 - "license": "MIT" 2225 - }, 2226 - "node_modules/flatted": { 2227 - "version": "3.3.2", 2228 - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz", 2229 - "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==", 2230 - "dev": true, 2231 - "license": "ISC" 2232 - }, 2233 - "node_modules/foreground-child": { 2234 - "version": "3.3.0", 2235 - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", 2236 - "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", 2237 - "dev": true, 2238 - "license": "ISC", 2239 - "dependencies": { 2240 - "cross-spawn": "^7.0.0", 2241 - "signal-exit": "^4.0.1" 2242 - }, 2243 - "engines": { 2244 - "node": ">=14" 2245 - }, 2246 - "funding": { 2247 - "url": "https://github.com/sponsors/isaacs" 2248 - } 2249 - }, 2250 812 "node_modules/fsevents": { 2251 813 "version": "2.3.3", 2252 814 "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", ··· 2258 820 "os": [ 2259 821 "darwin" 2260 822 ], 823 + "peer": true, 2261 824 "engines": { 2262 825 "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 2263 826 } 2264 827 }, 2265 - "node_modules/get-caller-file": { 2266 - "version": "2.0.5", 2267 - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", 2268 - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", 2269 - "dev": true, 2270 - "license": "ISC", 2271 - "engines": { 2272 - "node": "6.* || 8.* || >= 10.*" 2273 - } 2274 - }, 2275 - "node_modules/glob": { 2276 - "version": "10.4.5", 2277 - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", 2278 - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", 2279 - "dev": true, 2280 - "license": "ISC", 2281 - "dependencies": { 2282 - "foreground-child": "^3.1.0", 2283 - "jackspeak": "^3.1.2", 2284 - "minimatch": "^9.0.4", 2285 - "minipass": "^7.1.2", 2286 - "package-json-from-dist": "^1.0.0", 2287 - "path-scurry": "^1.11.1" 2288 - }, 2289 - "bin": { 2290 - "glob": "dist/esm/bin.mjs" 2291 - }, 2292 - "funding": { 2293 - "url": "https://github.com/sponsors/isaacs" 2294 - } 2295 - }, 2296 - "node_modules/graphql": { 2297 - "version": "16.10.0", 2298 - "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.10.0.tgz", 2299 - "integrity": "sha512-AjqGKbDGUFRKIRCP9tCKiIGHyriz2oHEbPIbEtcSLSs4YjReZOIPQQWek4+6hjw62H9QShXHyaGivGiYVLeYFQ==", 2300 - "dev": true, 2301 - "license": "MIT", 2302 - "engines": { 2303 - "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" 2304 - } 2305 - }, 2306 828 "node_modules/happy-dom": { 2307 - "version": "17.0.0", 2308 - "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-17.0.0.tgz", 2309 - "integrity": "sha512-jGuUr3UrgMzt1Mopyof3RzD49/GudAp1suP5KFU+EvNXmqUAMXpxux2zEJbabE1YXs0APrY61iRZ0BKMMWCGTg==", 829 + "version": "17.4.4", 830 + "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-17.4.4.tgz", 831 + "integrity": "sha512-/Pb0ctk3HTZ5xEL3BZ0hK1AqDSAUuRQitOmROPHhfUYEWpmTImwfD8vFDGADmMAX0JYgbcgxWoLFKtsWhcpuVA==", 2310 832 "dev": true, 2311 833 "license": "MIT", 2312 - "optional": true, 2313 - "peer": true, 2314 834 "dependencies": { 2315 835 "webidl-conversions": "^7.0.0", 2316 836 "whatwg-mimetype": "^3.0.0" ··· 2319 839 "node": ">=18.0.0" 2320 840 } 2321 841 }, 2322 - "node_modules/has-flag": { 2323 - "version": "4.0.0", 2324 - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 2325 - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 2326 - "dev": true, 2327 - "license": "MIT", 2328 - "engines": { 2329 - "node": ">=8" 2330 - } 2331 - }, 2332 - "node_modules/headers-polyfill": { 2333 - "version": "4.0.3", 2334 - "resolved": "https://registry.npmjs.org/headers-polyfill/-/headers-polyfill-4.0.3.tgz", 2335 - "integrity": "sha512-IScLbePpkvO846sIwOtOTDjutRMWdXdJmXdMvk6gCBHxFO8d+QKOQedyZSxFTTFYRSmlgSTDtXqqq4pcenBXLQ==", 2336 - "dev": true, 2337 - "license": "MIT" 2338 - }, 2339 - "node_modules/html-escaper": { 2340 - "version": "2.0.2", 2341 - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", 2342 - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", 2343 - "dev": true, 2344 - "license": "MIT" 2345 - }, 2346 842 "node_modules/htmlparser2": { 2347 843 "version": "10.0.0", 2348 844 "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.0.0.tgz", ··· 2363 859 "entities": "^6.0.0" 2364 860 } 2365 861 }, 2366 - "node_modules/is-fullwidth-code-point": { 2367 - "version": "3.0.0", 2368 - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", 2369 - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", 2370 - "dev": true, 2371 - "license": "MIT", 2372 - "engines": { 2373 - "node": ">=8" 2374 - } 2375 - }, 2376 - "node_modules/is-node-process": { 2377 - "version": "1.2.0", 2378 - "resolved": "https://registry.npmjs.org/is-node-process/-/is-node-process-1.2.0.tgz", 2379 - "integrity": "sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==", 2380 - "dev": true, 2381 - "license": "MIT" 2382 - }, 2383 - "node_modules/isexe": { 2384 - "version": "2.0.0", 2385 - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 2386 - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", 2387 - "dev": true, 2388 - "license": "ISC" 2389 - }, 2390 - "node_modules/istanbul-lib-coverage": { 2391 - "version": "3.2.2", 2392 - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", 2393 - "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", 2394 - "dev": true, 2395 - "license": "BSD-3-Clause", 2396 - "engines": { 2397 - "node": ">=8" 2398 - } 2399 - }, 2400 - "node_modules/istanbul-lib-report": { 2401 - "version": "3.0.1", 2402 - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", 2403 - "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", 2404 - "dev": true, 2405 - "license": "BSD-3-Clause", 2406 - "dependencies": { 2407 - "istanbul-lib-coverage": "^3.0.0", 2408 - "make-dir": "^4.0.0", 2409 - "supports-color": "^7.1.0" 2410 - }, 2411 - "engines": { 2412 - "node": ">=10" 2413 - } 2414 - }, 2415 - "node_modules/istanbul-lib-source-maps": { 2416 - "version": "5.0.6", 2417 - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz", 2418 - "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==", 2419 - "dev": true, 2420 - "license": "BSD-3-Clause", 2421 - "dependencies": { 2422 - "@jridgewell/trace-mapping": "^0.3.23", 2423 - "debug": "^4.1.1", 2424 - "istanbul-lib-coverage": "^3.0.0" 2425 - }, 2426 - "engines": { 2427 - "node": ">=10" 2428 - } 2429 - }, 2430 - "node_modules/istanbul-reports": { 2431 - "version": "3.1.7", 2432 - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", 2433 - "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", 2434 - "dev": true, 2435 - "license": "BSD-3-Clause", 2436 - "dependencies": { 2437 - "html-escaper": "^2.0.0", 2438 - "istanbul-lib-report": "^3.0.0" 2439 - }, 2440 - "engines": { 2441 - "node": ">=8" 2442 - } 2443 - }, 2444 - "node_modules/jackspeak": { 2445 - "version": "3.4.3", 2446 - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", 2447 - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", 2448 - "dev": true, 2449 - "license": "BlueOak-1.0.0", 2450 - "dependencies": { 2451 - "@isaacs/cliui": "^8.0.2" 2452 - }, 2453 - "funding": { 2454 - "url": "https://github.com/sponsors/isaacs" 2455 - }, 2456 - "optionalDependencies": { 2457 - "@pkgjs/parseargs": "^0.11.0" 2458 - } 2459 - }, 2460 - "node_modules/js-tokens": { 2461 - "version": "4.0.0", 2462 - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 2463 - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", 2464 - "dev": true, 2465 - "license": "MIT" 2466 - }, 2467 862 "node_modules/kleur": { 2468 863 "version": "4.1.5", 2469 864 "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", ··· 2481 876 "dev": true, 2482 877 "license": "MIT" 2483 878 }, 2484 - "node_modules/loupe": { 2485 - "version": "3.1.3", 2486 - "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.3.tgz", 2487 - "integrity": "sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==", 2488 - "dev": true, 2489 - "license": "MIT" 2490 - }, 2491 - "node_modules/lru-cache": { 2492 - "version": "10.4.3", 2493 - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", 2494 - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", 2495 - "dev": true, 2496 - "license": "ISC" 2497 - }, 2498 - "node_modules/lz-string": { 2499 - "version": "1.5.0", 2500 - "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", 2501 - "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", 2502 - "dev": true, 2503 - "license": "MIT", 2504 - "bin": { 2505 - "lz-string": "bin/bin.js" 2506 - } 2507 - }, 2508 879 "node_modules/magic-string": { 2509 880 "version": "0.30.17", 2510 881 "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", ··· 2513 884 "license": "MIT", 2514 885 "dependencies": { 2515 886 "@jridgewell/sourcemap-codec": "^1.5.0" 2516 - } 2517 - }, 2518 - "node_modules/magicast": { 2519 - "version": "0.3.5", 2520 - "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.5.tgz", 2521 - "integrity": "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==", 2522 - "dev": true, 2523 - "license": "MIT", 2524 - "dependencies": { 2525 - "@babel/parser": "^7.25.4", 2526 - "@babel/types": "^7.25.4", 2527 - "source-map-js": "^1.2.0" 2528 - } 2529 - }, 2530 - "node_modules/make-dir": { 2531 - "version": "4.0.0", 2532 - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", 2533 - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", 2534 - "dev": true, 2535 - "license": "MIT", 2536 - "dependencies": { 2537 - "semver": "^7.5.3" 2538 - }, 2539 - "engines": { 2540 - "node": ">=10" 2541 - }, 2542 - "funding": { 2543 - "url": "https://github.com/sponsors/sindresorhus" 2544 - } 2545 - }, 2546 - "node_modules/minimatch": { 2547 - "version": "9.0.5", 2548 - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", 2549 - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", 2550 - "dev": true, 2551 - "license": "ISC", 2552 - "dependencies": { 2553 - "brace-expansion": "^2.0.1" 2554 - }, 2555 - "engines": { 2556 - "node": ">=16 || 14 >=14.17" 2557 - }, 2558 - "funding": { 2559 - "url": "https://github.com/sponsors/isaacs" 2560 - } 2561 - }, 2562 - "node_modules/minipass": { 2563 - "version": "7.1.2", 2564 - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", 2565 - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", 2566 - "dev": true, 2567 - "license": "ISC", 2568 - "engines": { 2569 - "node": ">=16 || 14 >=14.17" 2570 887 } 2571 888 }, 2572 889 "node_modules/mri": { ··· 2579 896 "node": ">=4" 2580 897 } 2581 898 }, 2582 - "node_modules/mrmime": { 2583 - "version": "2.0.1", 2584 - "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", 2585 - "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", 2586 - "dev": true, 2587 - "license": "MIT", 2588 - "engines": { 2589 - "node": ">=10" 2590 - } 2591 - }, 2592 - "node_modules/ms": { 2593 - "version": "2.1.3", 2594 - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 2595 - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", 2596 - "dev": true, 2597 - "license": "MIT" 2598 - }, 2599 - "node_modules/msw": { 2600 - "version": "2.7.0", 2601 - "resolved": "https://registry.npmjs.org/msw/-/msw-2.7.0.tgz", 2602 - "integrity": "sha512-BIodwZ19RWfCbYTxWTUfTXc+sg4OwjCAgxU1ZsgmggX/7S3LdUifsbUPJs61j0rWb19CZRGY5if77duhc0uXzw==", 2603 - "dev": true, 2604 - "hasInstallScript": true, 2605 - "license": "MIT", 2606 - "dependencies": { 2607 - "@bundled-es-modules/cookie": "^2.0.1", 2608 - "@bundled-es-modules/statuses": "^1.0.1", 2609 - "@bundled-es-modules/tough-cookie": "^0.1.6", 2610 - "@inquirer/confirm": "^5.0.0", 2611 - "@mswjs/interceptors": "^0.37.0", 2612 - "@open-draft/deferred-promise": "^2.2.0", 2613 - "@open-draft/until": "^2.1.0", 2614 - "@types/cookie": "^0.6.0", 2615 - "@types/statuses": "^2.0.4", 2616 - "graphql": "^16.8.1", 2617 - "headers-polyfill": "^4.0.2", 2618 - "is-node-process": "^1.2.0", 2619 - "outvariant": "^1.4.3", 2620 - "path-to-regexp": "^6.3.0", 2621 - "picocolors": "^1.1.1", 2622 - "strict-event-emitter": "^0.5.1", 2623 - "type-fest": "^4.26.1", 2624 - "yargs": "^17.7.2" 2625 - }, 2626 - "bin": { 2627 - "msw": "cli/index.js" 2628 - }, 2629 - "engines": { 2630 - "node": ">=18" 2631 - }, 2632 - "funding": { 2633 - "url": "https://github.com/sponsors/mswjs" 2634 - }, 2635 - "peerDependencies": { 2636 - "typescript": ">= 4.8.x" 2637 - }, 2638 - "peerDependenciesMeta": { 2639 - "typescript": { 2640 - "optional": true 2641 - } 2642 - } 2643 - }, 2644 - "node_modules/mute-stream": { 2645 - "version": "2.0.0", 2646 - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz", 2647 - "integrity": "sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==", 2648 - "dev": true, 2649 - "license": "ISC", 2650 - "engines": { 2651 - "node": "^18.17.0 || >=20.5.0" 2652 - } 2653 - }, 2654 - "node_modules/nanoid": { 2655 - "version": "3.3.8", 2656 - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", 2657 - "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", 2658 - "dev": true, 2659 - "funding": [ 2660 - { 2661 - "type": "github", 2662 - "url": "https://github.com/sponsors/ai" 2663 - } 2664 - ], 2665 - "license": "MIT", 2666 - "bin": { 2667 - "nanoid": "bin/nanoid.cjs" 2668 - }, 2669 - "engines": { 2670 - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" 2671 - } 2672 - }, 2673 - "node_modules/outvariant": { 2674 - "version": "1.4.3", 2675 - "resolved": "https://registry.npmjs.org/outvariant/-/outvariant-1.4.3.tgz", 2676 - "integrity": "sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==", 2677 - "dev": true, 2678 - "license": "MIT" 2679 - }, 2680 - "node_modules/package-json-from-dist": { 2681 - "version": "1.0.1", 2682 - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", 2683 - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", 2684 - "dev": true, 2685 - "license": "BlueOak-1.0.0" 2686 - }, 2687 - "node_modules/path-key": { 2688 - "version": "3.1.1", 2689 - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", 2690 - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", 2691 - "dev": true, 2692 - "license": "MIT", 2693 - "engines": { 2694 - "node": ">=8" 2695 - } 2696 - }, 2697 - "node_modules/path-scurry": { 2698 - "version": "1.11.1", 2699 - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", 2700 - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", 2701 - "dev": true, 2702 - "license": "BlueOak-1.0.0", 2703 - "dependencies": { 2704 - "lru-cache": "^10.2.0", 2705 - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" 2706 - }, 2707 - "engines": { 2708 - "node": ">=16 || 14 >=14.18" 2709 - }, 2710 - "funding": { 2711 - "url": "https://github.com/sponsors/isaacs" 2712 - } 2713 - }, 2714 - "node_modules/path-to-regexp": { 2715 - "version": "6.3.0", 2716 - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", 2717 - "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==", 2718 - "dev": true, 2719 - "license": "MIT" 2720 - }, 2721 - "node_modules/pathe": { 2722 - "version": "2.0.3", 2723 - "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", 2724 - "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", 2725 - "dev": true, 2726 - "license": "MIT" 2727 - }, 2728 - "node_modules/pathval": { 2729 - "version": "2.0.0", 2730 - "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz", 2731 - "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==", 2732 - "dev": true, 2733 - "license": "MIT", 2734 - "engines": { 2735 - "node": ">= 14.16" 2736 - } 2737 - }, 2738 - "node_modules/picocolors": { 2739 - "version": "1.1.1", 2740 - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", 2741 - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", 2742 - "dev": true, 2743 - "license": "ISC" 2744 - }, 2745 899 "node_modules/picomatch": { 2746 900 "version": "4.0.2", 2747 901 "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", ··· 2755 909 "url": "https://github.com/sponsors/jonschlinkert" 2756 910 } 2757 911 }, 2758 - "node_modules/playwright": { 2759 - "version": "1.50.1", 2760 - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.50.1.tgz", 2761 - "integrity": "sha512-G8rwsOQJ63XG6BbKj2w5rHeavFjy5zynBA9zsJMMtBoe/Uf757oG12NXz6e6OirF7RCrTVAKFXbLmn1RbL7Qaw==", 2762 - "dev": true, 2763 - "license": "Apache-2.0", 2764 - "dependencies": { 2765 - "playwright-core": "1.50.1" 2766 - }, 2767 - "bin": { 2768 - "playwright": "cli.js" 2769 - }, 2770 - "engines": { 2771 - "node": ">=18" 2772 - }, 2773 - "optionalDependencies": { 2774 - "fsevents": "2.3.2" 2775 - } 2776 - }, 2777 - "node_modules/playwright-core": { 2778 - "version": "1.50.1", 2779 - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.50.1.tgz", 2780 - "integrity": "sha512-ra9fsNWayuYumt+NiM069M6OkcRb1FZSK8bgi66AtpFoWkg2+y0bJSNmkFrWhMbEBbVKC/EruAHH3g0zmtwGmQ==", 2781 - "dev": true, 2782 - "license": "Apache-2.0", 2783 - "bin": { 2784 - "playwright-core": "cli.js" 2785 - }, 2786 - "engines": { 2787 - "node": ">=18" 2788 - } 2789 - }, 2790 - "node_modules/playwright/node_modules/fsevents": { 2791 - "version": "2.3.2", 2792 - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", 2793 - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", 2794 - "dev": true, 2795 - "hasInstallScript": true, 2796 - "license": "MIT", 2797 - "optional": true, 2798 - "os": [ 2799 - "darwin" 2800 - ], 2801 - "engines": { 2802 - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 2803 - } 2804 - }, 2805 - "node_modules/postcss": { 2806 - "version": "8.5.3", 2807 - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", 2808 - "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", 2809 - "dev": true, 2810 - "funding": [ 2811 - { 2812 - "type": "opencollective", 2813 - "url": "https://opencollective.com/postcss/" 2814 - }, 2815 - { 2816 - "type": "tidelift", 2817 - "url": "https://tidelift.com/funding/github/npm/postcss" 2818 - }, 2819 - { 2820 - "type": "github", 2821 - "url": "https://github.com/sponsors/ai" 2822 - } 2823 - ], 2824 - "license": "MIT", 2825 - "dependencies": { 2826 - "nanoid": "^3.3.8", 2827 - "picocolors": "^1.1.1", 2828 - "source-map-js": "^1.2.1" 2829 - }, 2830 - "engines": { 2831 - "node": "^10 || ^12 || >=14" 2832 - } 2833 - }, 2834 912 "node_modules/prettier": { 2835 913 "version": "3.4.2", 2836 914 "dev": true, ··· 2845 923 "url": "https://github.com/prettier/prettier?sponsor=1" 2846 924 } 2847 925 }, 2848 - "node_modules/pretty-format": { 2849 - "version": "27.5.1", 2850 - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", 2851 - "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", 2852 - "dev": true, 2853 - "license": "MIT", 2854 - "dependencies": { 2855 - "ansi-regex": "^5.0.1", 2856 - "ansi-styles": "^5.0.0", 2857 - "react-is": "^17.0.1" 2858 - }, 2859 - "engines": { 2860 - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" 2861 - } 2862 - }, 2863 - "node_modules/pretty-format/node_modules/ansi-styles": { 2864 - "version": "5.2.0", 2865 - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", 2866 - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", 2867 - "dev": true, 2868 - "license": "MIT", 2869 - "engines": { 2870 - "node": ">=10" 2871 - }, 2872 - "funding": { 2873 - "url": "https://github.com/chalk/ansi-styles?sponsor=1" 2874 - } 2875 - }, 2876 - "node_modules/psl": { 2877 - "version": "1.15.0", 2878 - "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz", 2879 - "integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==", 2880 - "dev": true, 2881 - "license": "MIT", 2882 - "dependencies": { 2883 - "punycode": "^2.3.1" 2884 - }, 2885 - "funding": { 2886 - "url": "https://github.com/sponsors/lupomontero" 2887 - } 2888 - }, 2889 - "node_modules/punycode": { 2890 - "version": "2.3.1", 2891 - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", 2892 - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", 2893 - "dev": true, 2894 - "license": "MIT", 2895 - "engines": { 2896 - "node": ">=6" 2897 - } 2898 - }, 2899 - "node_modules/querystringify": { 2900 - "version": "2.2.0", 2901 - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", 2902 - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", 2903 - "dev": true, 2904 - "license": "MIT" 2905 - }, 2906 926 "node_modules/randombytes": { 2907 927 "version": "2.1.0", 2908 928 "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", ··· 2913 933 "safe-buffer": "^5.1.0" 2914 934 } 2915 935 }, 2916 - "node_modules/react-is": { 2917 - "version": "17.0.2", 2918 - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", 2919 - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", 2920 - "dev": true, 2921 - "license": "MIT" 2922 - }, 2923 - "node_modules/regenerator-runtime": { 2924 - "version": "0.14.1", 2925 - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", 2926 - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", 2927 - "dev": true, 2928 - "license": "MIT" 2929 - }, 2930 - "node_modules/require-directory": { 2931 - "version": "2.1.1", 2932 - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", 2933 - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", 2934 - "dev": true, 2935 - "license": "MIT", 2936 - "engines": { 2937 - "node": ">=0.10.0" 2938 - } 2939 - }, 2940 - "node_modules/requires-port": { 2941 - "version": "1.0.0", 2942 - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", 2943 - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", 2944 - "dev": true, 2945 - "license": "MIT" 2946 - }, 2947 936 "node_modules/rolldown": { 2948 937 "version": "1.0.0-beta.6", 2949 938 "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-beta.6.tgz", ··· 2987 976 "integrity": "sha512-489gTVMzAYdiZHFVA/ig/iYFllCcWFHMvUHI1rpFmkoUtRlQxqh6/yiNqnYibjMZ2b/+FUQwldG+aLsEt6bglQ==", 2988 977 "dev": true, 2989 978 "license": "MIT", 979 + "optional": true, 980 + "peer": true, 2990 981 "dependencies": { 2991 982 "@types/estree": "1.0.6" 2992 983 }, ··· 3054 1045 ], 3055 1046 "license": "MIT" 3056 1047 }, 3057 - "node_modules/semver": { 3058 - "version": "7.7.1", 3059 - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", 3060 - "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", 3061 - "dev": true, 3062 - "license": "ISC", 3063 - "bin": { 3064 - "semver": "bin/semver.js" 3065 - }, 3066 - "engines": { 3067 - "node": ">=10" 3068 - } 3069 - }, 3070 1048 "node_modules/serialize-javascript": { 3071 1049 "version": "6.0.2", 3072 1050 "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", ··· 3077 1055 "randombytes": "^2.1.0" 3078 1056 } 3079 1057 }, 3080 - "node_modules/shebang-command": { 3081 - "version": "2.0.0", 3082 - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", 3083 - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", 3084 - "dev": true, 3085 - "license": "MIT", 3086 - "dependencies": { 3087 - "shebang-regex": "^3.0.0" 3088 - }, 3089 - "engines": { 3090 - "node": ">=8" 3091 - } 3092 - }, 3093 - "node_modules/shebang-regex": { 3094 - "version": "3.0.0", 3095 - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", 3096 - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", 3097 - "dev": true, 3098 - "license": "MIT", 3099 - "engines": { 3100 - "node": ">=8" 3101 - } 3102 - }, 3103 - "node_modules/siginfo": { 3104 - "version": "2.0.0", 3105 - "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", 3106 - "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", 3107 - "dev": true, 3108 - "license": "ISC" 3109 - }, 3110 - "node_modules/signal-exit": { 3111 - "version": "4.1.0", 3112 - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", 3113 - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", 3114 - "dev": true, 3115 - "license": "ISC", 3116 - "engines": { 3117 - "node": ">=14" 3118 - }, 3119 - "funding": { 3120 - "url": "https://github.com/sponsors/isaacs" 3121 - } 3122 - }, 3123 - "node_modules/sirv": { 3124 - "version": "3.0.1", 3125 - "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.1.tgz", 3126 - "integrity": "sha512-FoqMu0NCGBLCcAkS1qA+XJIQTR6/JHfQXl+uGteNCQ76T91DMUjPa9xfmeqMY3z80nLSg9yQmNjK0Px6RWsH/A==", 3127 - "dev": true, 3128 - "license": "MIT", 3129 - "dependencies": { 3130 - "@polka/url": "^1.0.0-next.24", 3131 - "mrmime": "^2.0.0", 3132 - "totalist": "^3.0.0" 3133 - }, 3134 - "engines": { 3135 - "node": ">=18" 3136 - } 3137 - }, 3138 1058 "node_modules/smob": { 3139 1059 "version": "1.5.0", 3140 1060 "resolved": "https://registry.npmjs.org/smob/-/smob-1.5.0.tgz", ··· 3150 1070 "node": ">=0.10.0" 3151 1071 } 3152 1072 }, 3153 - "node_modules/source-map-js": { 3154 - "version": "1.2.1", 3155 - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", 3156 - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", 3157 - "dev": true, 3158 - "license": "BSD-3-Clause", 3159 - "engines": { 3160 - "node": ">=0.10.0" 3161 - } 3162 - }, 3163 1073 "node_modules/source-map-support": { 3164 1074 "version": "0.5.21", 3165 1075 "dev": true, ··· 3169 1079 "source-map": "^0.6.0" 3170 1080 } 3171 1081 }, 3172 - "node_modules/stackback": { 3173 - "version": "0.0.2", 3174 - "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", 3175 - "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", 3176 - "dev": true, 3177 - "license": "MIT" 3178 - }, 3179 - "node_modules/statuses": { 3180 - "version": "2.0.1", 3181 - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", 3182 - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", 3183 - "dev": true, 3184 - "license": "MIT", 3185 - "engines": { 3186 - "node": ">= 0.8" 3187 - } 3188 - }, 3189 - "node_modules/std-env": { 3190 - "version": "3.8.0", 3191 - "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.8.0.tgz", 3192 - "integrity": "sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w==", 3193 - "dev": true, 3194 - "license": "MIT" 3195 - }, 3196 - "node_modules/strict-event-emitter": { 3197 - "version": "0.5.1", 3198 - "resolved": "https://registry.npmjs.org/strict-event-emitter/-/strict-event-emitter-0.5.1.tgz", 3199 - "integrity": "sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==", 3200 - "dev": true, 3201 - "license": "MIT" 3202 - }, 3203 - "node_modules/string-width": { 3204 - "version": "4.2.3", 3205 - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", 3206 - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", 3207 - "dev": true, 3208 - "license": "MIT", 3209 - "dependencies": { 3210 - "emoji-regex": "^8.0.0", 3211 - "is-fullwidth-code-point": "^3.0.0", 3212 - "strip-ansi": "^6.0.1" 3213 - }, 3214 - "engines": { 3215 - "node": ">=8" 3216 - } 3217 - }, 3218 - "node_modules/string-width-cjs": { 3219 - "name": "string-width", 3220 - "version": "4.2.3", 3221 - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", 3222 - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", 3223 - "dev": true, 3224 - "license": "MIT", 3225 - "dependencies": { 3226 - "emoji-regex": "^8.0.0", 3227 - "is-fullwidth-code-point": "^3.0.0", 3228 - "strip-ansi": "^6.0.1" 3229 - }, 3230 - "engines": { 3231 - "node": ">=8" 3232 - } 3233 - }, 3234 - "node_modules/strip-ansi": { 3235 - "version": "6.0.1", 3236 - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", 3237 - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", 3238 - "dev": true, 3239 - "license": "MIT", 3240 - "dependencies": { 3241 - "ansi-regex": "^5.0.1" 3242 - }, 3243 - "engines": { 3244 - "node": ">=8" 3245 - } 3246 - }, 3247 - "node_modules/strip-ansi-cjs": { 3248 - "name": "strip-ansi", 3249 - "version": "6.0.1", 3250 - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", 3251 - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", 3252 - "dev": true, 3253 - "license": "MIT", 3254 - "dependencies": { 3255 - "ansi-regex": "^5.0.1" 3256 - }, 3257 - "engines": { 3258 - "node": ">=8" 3259 - } 3260 - }, 3261 - "node_modules/supports-color": { 3262 - "version": "7.2.0", 3263 - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", 3264 - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", 3265 - "dev": true, 3266 - "license": "MIT", 3267 - "dependencies": { 3268 - "has-flag": "^4.0.0" 3269 - }, 3270 - "engines": { 3271 - "node": ">=8" 3272 - } 3273 - }, 3274 1082 "node_modules/terser": { 3275 1083 "version": "5.37.0", 3276 1084 "dev": true, ··· 3288 1096 "node": ">=10" 3289 1097 } 3290 1098 }, 3291 - "node_modules/test-exclude": { 3292 - "version": "7.0.1", 3293 - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-7.0.1.tgz", 3294 - "integrity": "sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==", 3295 - "dev": true, 3296 - "license": "ISC", 3297 - "dependencies": { 3298 - "@istanbuljs/schema": "^0.1.2", 3299 - "glob": "^10.4.1", 3300 - "minimatch": "^9.0.4" 3301 - }, 3302 - "engines": { 3303 - "node": ">=18" 3304 - } 3305 - }, 3306 - "node_modules/tinybench": { 3307 - "version": "2.9.0", 3308 - "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", 3309 - "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", 3310 - "dev": true, 3311 - "license": "MIT" 3312 - }, 3313 - "node_modules/tinyexec": { 3314 - "version": "0.3.2", 3315 - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", 3316 - "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", 3317 - "dev": true, 3318 - "license": "MIT" 3319 - }, 3320 1099 "node_modules/tinyglobby": { 3321 1100 "version": "0.2.12", 3322 1101 "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.12.tgz", ··· 3334 1113 "url": "https://github.com/sponsors/SuperchupuDev" 3335 1114 } 3336 1115 }, 3337 - "node_modules/tinypool": { 3338 - "version": "1.0.2", 3339 - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.2.tgz", 3340 - "integrity": "sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==", 3341 - "dev": true, 3342 - "license": "MIT", 3343 - "engines": { 3344 - "node": "^18.0.0 || >=20.0.0" 3345 - } 3346 - }, 3347 - "node_modules/tinyrainbow": { 3348 - "version": "2.0.0", 3349 - "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz", 3350 - "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==", 3351 - "dev": true, 3352 - "license": "MIT", 3353 - "engines": { 3354 - "node": ">=14.0.0" 3355 - } 3356 - }, 3357 - "node_modules/tinyspy": { 3358 - "version": "3.0.2", 3359 - "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz", 3360 - "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==", 3361 - "dev": true, 3362 - "license": "MIT", 3363 - "engines": { 3364 - "node": ">=14.0.0" 3365 - } 3366 - }, 3367 - "node_modules/totalist": { 3368 - "version": "3.0.1", 3369 - "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", 3370 - "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", 3371 - "dev": true, 3372 - "license": "MIT", 3373 - "engines": { 3374 - "node": ">=6" 3375 - } 3376 - }, 3377 - "node_modules/tough-cookie": { 3378 - "version": "4.1.4", 3379 - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", 3380 - "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", 3381 - "dev": true, 3382 - "license": "BSD-3-Clause", 3383 - "dependencies": { 3384 - "psl": "^1.1.33", 3385 - "punycode": "^2.1.1", 3386 - "universalify": "^0.2.0", 3387 - "url-parse": "^1.5.3" 3388 - }, 3389 - "engines": { 3390 - "node": ">=6" 3391 - } 3392 - }, 3393 1116 "node_modules/ts-api-utils": { 3394 1117 "version": "1.4.3", 3395 1118 "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", ··· 3411 1134 "license": "0BSD", 3412 1135 "optional": true 3413 1136 }, 3414 - "node_modules/type-fest": { 3415 - "version": "4.33.0", 3416 - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.33.0.tgz", 3417 - "integrity": "sha512-s6zVrxuyKbbAsSAD5ZPTB77q4YIdRctkTbJ2/Dqlinwz+8ooH2gd+YA7VA6Pa93KML9GockVvoxjZ2vHP+mu8g==", 3418 - "dev": true, 3419 - "license": "(MIT OR CC0-1.0)", 3420 - "engines": { 3421 - "node": ">=16" 3422 - }, 3423 - "funding": { 3424 - "url": "https://github.com/sponsors/sindresorhus" 3425 - } 3426 - }, 3427 1137 "node_modules/typescript": { 3428 1138 "version": "5.7.2", 3429 1139 "dev": true, ··· 3436 1146 "node": ">=14.17" 3437 1147 } 3438 1148 }, 3439 - "node_modules/universalify": { 3440 - "version": "0.2.0", 3441 - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", 3442 - "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", 1149 + "node_modules/undici-types": { 1150 + "version": "6.20.0", 1151 + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", 1152 + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", 3443 1153 "dev": true, 3444 - "license": "MIT", 3445 - "engines": { 3446 - "node": ">= 4.0.0" 3447 - } 3448 - }, 3449 - "node_modules/url-parse": { 3450 - "version": "1.5.10", 3451 - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", 3452 - "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", 3453 - "dev": true, 3454 - "license": "MIT", 3455 - "dependencies": { 3456 - "querystringify": "^2.1.1", 3457 - "requires-port": "^1.0.0" 3458 - } 1154 + "license": "MIT" 3459 1155 }, 3460 1156 "node_modules/valibot": { 3461 1157 "version": "1.0.0-rc.4", ··· 3472 1168 } 3473 1169 } 3474 1170 }, 3475 - "node_modules/vite": { 3476 - "version": "6.1.1", 3477 - "resolved": "https://registry.npmjs.org/vite/-/vite-6.1.1.tgz", 3478 - "integrity": "sha512-4GgM54XrwRfrOp297aIYspIti66k56v16ZnqHvrIM7mG+HjDlAwS7p+Srr7J6fGvEdOJ5JcQ/D9T7HhtdXDTzA==", 3479 - "dev": true, 3480 - "license": "MIT", 3481 - "dependencies": { 3482 - "esbuild": "^0.24.2", 3483 - "postcss": "^8.5.2", 3484 - "rollup": "^4.30.1" 3485 - }, 3486 - "bin": { 3487 - "vite": "bin/vite.js" 3488 - }, 3489 - "engines": { 3490 - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" 3491 - }, 3492 - "funding": { 3493 - "url": "https://github.com/vitejs/vite?sponsor=1" 3494 - }, 3495 - "optionalDependencies": { 3496 - "fsevents": "~2.3.3" 3497 - }, 3498 - "peerDependencies": { 3499 - "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", 3500 - "jiti": ">=1.21.0", 3501 - "less": "*", 3502 - "lightningcss": "^1.21.0", 3503 - "sass": "*", 3504 - "sass-embedded": "*", 3505 - "stylus": "*", 3506 - "sugarss": "*", 3507 - "terser": "^5.16.0", 3508 - "tsx": "^4.8.1", 3509 - "yaml": "^2.4.2" 3510 - }, 3511 - "peerDependenciesMeta": { 3512 - "@types/node": { 3513 - "optional": true 3514 - }, 3515 - "jiti": { 3516 - "optional": true 3517 - }, 3518 - "less": { 3519 - "optional": true 3520 - }, 3521 - "lightningcss": { 3522 - "optional": true 3523 - }, 3524 - "sass": { 3525 - "optional": true 3526 - }, 3527 - "sass-embedded": { 3528 - "optional": true 3529 - }, 3530 - "stylus": { 3531 - "optional": true 3532 - }, 3533 - "sugarss": { 3534 - "optional": true 3535 - }, 3536 - "terser": { 3537 - "optional": true 3538 - }, 3539 - "tsx": { 3540 - "optional": true 3541 - }, 3542 - "yaml": { 3543 - "optional": true 3544 - } 3545 - } 3546 - }, 3547 - "node_modules/vite-node": { 3548 - "version": "3.0.6", 3549 - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.0.6.tgz", 3550 - "integrity": "sha512-s51RzrTkXKJrhNbUzQRsarjmAae7VmMPAsRT7lppVpIg6mK3zGthP9Hgz0YQQKuNcF+Ii7DfYk3Fxz40jRmePw==", 3551 - "dev": true, 3552 - "license": "MIT", 3553 - "dependencies": { 3554 - "cac": "^6.7.14", 3555 - "debug": "^4.4.0", 3556 - "es-module-lexer": "^1.6.0", 3557 - "pathe": "^2.0.3", 3558 - "vite": "^5.0.0 || ^6.0.0" 3559 - }, 3560 - "bin": { 3561 - "vite-node": "vite-node.mjs" 3562 - }, 3563 - "engines": { 3564 - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" 3565 - }, 3566 - "funding": { 3567 - "url": "https://opencollective.com/vitest" 3568 - } 3569 - }, 3570 - "node_modules/vitest": { 3571 - "version": "3.0.6", 3572 - "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.0.6.tgz", 3573 - "integrity": "sha512-/iL1Sc5VeDZKPDe58oGK4HUFLhw6b5XdY1MYawjuSaDA4sEfYlY9HnS6aCEG26fX+MgUi7MwlduTBHHAI/OvMA==", 3574 - "dev": true, 3575 - "license": "MIT", 3576 - "dependencies": { 3577 - "@vitest/expect": "3.0.6", 3578 - "@vitest/mocker": "3.0.6", 3579 - "@vitest/pretty-format": "^3.0.6", 3580 - "@vitest/runner": "3.0.6", 3581 - "@vitest/snapshot": "3.0.6", 3582 - "@vitest/spy": "3.0.6", 3583 - "@vitest/utils": "3.0.6", 3584 - "chai": "^5.2.0", 3585 - "debug": "^4.4.0", 3586 - "expect-type": "^1.1.0", 3587 - "magic-string": "^0.30.17", 3588 - "pathe": "^2.0.3", 3589 - "std-env": "^3.8.0", 3590 - "tinybench": "^2.9.0", 3591 - "tinyexec": "^0.3.2", 3592 - "tinypool": "^1.0.2", 3593 - "tinyrainbow": "^2.0.0", 3594 - "vite": "^5.0.0 || ^6.0.0", 3595 - "vite-node": "3.0.6", 3596 - "why-is-node-running": "^2.3.0" 3597 - }, 3598 - "bin": { 3599 - "vitest": "vitest.mjs" 3600 - }, 3601 - "engines": { 3602 - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" 3603 - }, 3604 - "funding": { 3605 - "url": "https://opencollective.com/vitest" 3606 - }, 3607 - "peerDependencies": { 3608 - "@edge-runtime/vm": "*", 3609 - "@types/debug": "^4.1.12", 3610 - "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", 3611 - "@vitest/browser": "3.0.6", 3612 - "@vitest/ui": "3.0.6", 3613 - "happy-dom": "*", 3614 - "jsdom": "*" 3615 - }, 3616 - "peerDependenciesMeta": { 3617 - "@edge-runtime/vm": { 3618 - "optional": true 3619 - }, 3620 - "@types/debug": { 3621 - "optional": true 3622 - }, 3623 - "@types/node": { 3624 - "optional": true 3625 - }, 3626 - "@vitest/browser": { 3627 - "optional": true 3628 - }, 3629 - "@vitest/ui": { 3630 - "optional": true 3631 - }, 3632 - "happy-dom": { 3633 - "optional": true 3634 - }, 3635 - "jsdom": { 3636 - "optional": true 3637 - } 3638 - } 3639 - }, 3640 1171 "node_modules/webidl-conversions": { 3641 1172 "version": "7.0.0", 3642 1173 "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", 3643 1174 "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", 3644 1175 "dev": true, 3645 1176 "license": "BSD-2-Clause", 3646 - "optional": true, 3647 - "peer": true, 3648 1177 "engines": { 3649 1178 "node": ">=12" 3650 1179 } ··· 3655 1184 "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", 3656 1185 "dev": true, 3657 1186 "license": "MIT", 3658 - "optional": true, 3659 - "peer": true, 3660 1187 "engines": { 3661 1188 "node": ">=12" 3662 - } 3663 - }, 3664 - "node_modules/which": { 3665 - "version": "2.0.2", 3666 - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", 3667 - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", 3668 - "dev": true, 3669 - "license": "ISC", 3670 - "dependencies": { 3671 - "isexe": "^2.0.0" 3672 - }, 3673 - "bin": { 3674 - "node-which": "bin/node-which" 3675 - }, 3676 - "engines": { 3677 - "node": ">= 8" 3678 - } 3679 - }, 3680 - "node_modules/why-is-node-running": { 3681 - "version": "2.3.0", 3682 - "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", 3683 - "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", 3684 - "dev": true, 3685 - "license": "MIT", 3686 - "dependencies": { 3687 - "siginfo": "^2.0.0", 3688 - "stackback": "0.0.2" 3689 - }, 3690 - "bin": { 3691 - "why-is-node-running": "cli.js" 3692 - }, 3693 - "engines": { 3694 - "node": ">=8" 3695 - } 3696 - }, 3697 - "node_modules/wrap-ansi": { 3698 - "version": "6.2.0", 3699 - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", 3700 - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", 3701 - "dev": true, 3702 - "license": "MIT", 3703 - "dependencies": { 3704 - "ansi-styles": "^4.0.0", 3705 - "string-width": "^4.1.0", 3706 - "strip-ansi": "^6.0.0" 3707 - }, 3708 - "engines": { 3709 - "node": ">=8" 3710 - } 3711 - }, 3712 - "node_modules/wrap-ansi-cjs": { 3713 - "name": "wrap-ansi", 3714 - "version": "7.0.0", 3715 - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", 3716 - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", 3717 - "dev": true, 3718 - "license": "MIT", 3719 - "dependencies": { 3720 - "ansi-styles": "^4.0.0", 3721 - "string-width": "^4.1.0", 3722 - "strip-ansi": "^6.0.0" 3723 - }, 3724 - "engines": { 3725 - "node": ">=10" 3726 - }, 3727 - "funding": { 3728 - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" 3729 - } 3730 - }, 3731 - "node_modules/ws": { 3732 - "version": "8.18.0", 3733 - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", 3734 - "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", 3735 - "dev": true, 3736 - "license": "MIT", 3737 - "engines": { 3738 - "node": ">=10.0.0" 3739 - }, 3740 - "peerDependencies": { 3741 - "bufferutil": "^4.0.1", 3742 - "utf-8-validate": ">=5.0.2" 3743 - }, 3744 - "peerDependenciesMeta": { 3745 - "bufferutil": { 3746 - "optional": true 3747 - }, 3748 - "utf-8-validate": { 3749 - "optional": true 3750 - } 3751 - } 3752 - }, 3753 - "node_modules/y18n": { 3754 - "version": "5.0.8", 3755 - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", 3756 - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", 3757 - "dev": true, 3758 - "license": "ISC", 3759 - "engines": { 3760 - "node": ">=10" 3761 - } 3762 - }, 3763 - "node_modules/yargs": { 3764 - "version": "17.7.2", 3765 - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", 3766 - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", 3767 - "dev": true, 3768 - "license": "MIT", 3769 - "dependencies": { 3770 - "cliui": "^8.0.1", 3771 - "escalade": "^3.1.1", 3772 - "get-caller-file": "^2.0.5", 3773 - "require-directory": "^2.1.1", 3774 - "string-width": "^4.2.3", 3775 - "y18n": "^5.0.5", 3776 - "yargs-parser": "^21.1.1" 3777 - }, 3778 - "engines": { 3779 - "node": ">=12" 3780 - } 3781 - }, 3782 - "node_modules/yargs-parser": { 3783 - "version": "21.1.1", 3784 - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", 3785 - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", 3786 - "dev": true, 3787 - "license": "ISC", 3788 - "engines": { 3789 - "node": ">=12" 3790 - } 3791 - }, 3792 - "node_modules/yoctocolors-cjs": { 3793 - "version": "2.1.2", 3794 - "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.2.tgz", 3795 - "integrity": "sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==", 3796 - "dev": true, 3797 - "license": "MIT", 3798 - "engines": { 3799 - "node": ">=18" 3800 - }, 3801 - "funding": { 3802 - "url": "https://github.com/sponsors/sindresorhus" 3803 1189 } 3804 1190 }, 3805 1191 "node_modules/zimmerframe": {
+6 -8
package.json
··· 10 10 "build": "node build.js", 11 11 "format": "prettier --write . --cache", 12 12 "check": "tsc", 13 - "test": "vitest run", 14 - "test:watch": "vitest dev", 15 - "test:prod": "npm run build && NODE_ENV=production vitest run" 13 + "test": "node --no-warnings --test --experimental-test-coverage src/*/tests/*", 14 + "test:coverage": "node --no-warnings --test --experimental-test-coverage --test-reporter=spec --test-reporter-destination=stderr --test-reporter=lcov --test-reporter-destination=lcov.info src/*/tests/*", 15 + "test:watch": "node --no-warnings --test --watch src/*/tests/*", 16 + "test:prod": "npm run build && NODE_ENV=production npm test" 16 17 }, 17 18 "devDependencies": { 19 + "@happy-dom/global-registrator": "^17.4.4", 18 20 "@rollup/plugin-terser": "^0.4.4", 19 - "@vitest/browser": "^3.0.6", 20 - "@vitest/coverage-v8": "^3.0.6", 21 - "@vitest/ui": "^3.0.5", 21 + "@types/node": "^22.13.11", 22 22 "dhtml": ".", 23 23 "dts-buddy": "^0.5.5", 24 24 "htmlparser2": "^10.0.0", 25 25 "magic-string": "^0.30.17", 26 - "playwright": "^1.50.1", 27 26 "prettier": "^3.4.2", 28 27 "rolldown": "^1.0.0-beta.6", 29 28 "typescript": "^5.7.2", 30 - "vitest": "^3.0.6", 31 29 "zimmerframe": "^1.1.2" 32 30 }, 33 31 "prettier": {
+16 -7
src/client/compiler.ts
··· 122 122 }) 123 123 } else { 124 124 if (!(name in node)) { 125 - for (const property in node) { 126 - if (property.toLowerCase() === name) { 127 - name = property 128 - break 129 - } 130 - } 125 + name = (correct_case_cache[node.tagName] ??= generate_case_map(node))[name] 131 126 } 132 127 patch(node, parseInt(match[1]), node => { 133 128 assert(node instanceof Node) 134 129 return createPropertyPart(node, name) 135 130 }) 136 131 } 137 - } else if (__DEV__) { 132 + } else { 138 133 assert(!DYNAMIC_GLOBAL.test(value), `expected a whole dynamic value for ${name}, got a partial one`) 139 134 } 140 135 } ··· 148 143 templates.set(statics, compiled) 149 144 return compiled 150 145 } 146 + 147 + const correct_case_cache: Record<string, Record<string, string>> = {} 148 + function generate_case_map(node: Node) { 149 + const cache: Record<string, string> = {} 150 + 151 + while (node !== null) { 152 + for (const prop of Object.getOwnPropertyNames(node)) { 153 + cache[prop.toLowerCase()] ??= prop 154 + } 155 + node = Object.getPrototypeOf(node) 156 + } 157 + 158 + return cache 159 + }
+78 -79
src/client/tests/attributes.test.ts
··· 1 1 import { html } from 'dhtml' 2 - import { describe, expect, it, vi } from 'vitest' 3 - import { setup } from './setup' 2 + import test, { type TestContext } from 'node:test' 3 + import { setup } from './setup.ts' 4 4 5 - describe('attributes', () => { 6 - it('supports regular attributes', () => { 7 - const { root, el } = setup() 5 + test('regular attributes', (t: TestContext) => { 6 + const { root, el } = setup() 8 7 9 - root.render(html`<h1 style=${'color: red'}>Hello, world!</h1>`) 10 - expect(el.querySelector('h1')).toHaveAttribute('style', 'color: red;') 11 - }) 8 + root.render(html`<h1 style=${'color: red'}>Hello, world!</h1>`) 9 + t.assert.strictEqual(el.querySelector('h1')!.getAttribute('style'), 'color: red;') 10 + }) 12 11 13 - it('can toggle attributes', () => { 14 - const { root, el } = setup() 12 + test('can toggle attributes', (t: TestContext) => { 13 + const { root, el } = setup() 15 14 16 - let hidden: unknown = false 17 - const template = () => html`<h1 hidden=${hidden}>Hello, world!</h1>` 15 + let hidden: unknown = false 16 + const template = () => html`<h1 hidden=${hidden}>Hello, world!</h1>` 18 17 19 - root.render(template()) 20 - expect(el.querySelector('h1')).not.toHaveAttribute('hidden') 18 + root.render(template()) 19 + t.assert.ok(!el.querySelector('h1')!.hasAttribute('hidden')) 21 20 22 - hidden = true 23 - root.render(template()) 24 - expect(el.querySelector('h1')).toHaveAttribute('hidden') 21 + hidden = true 22 + root.render(template()) 23 + t.assert.ok(el.querySelector('h1')!.hasAttribute('hidden')) 25 24 26 - hidden = null 27 - root.render(template()) 28 - expect(el.querySelector('h1')).not.toHaveAttribute('hidden') 29 - }) 25 + hidden = null 26 + root.render(template()) 27 + t.assert.ok(!el.querySelector('h1')!.hasAttribute('hidden')) 28 + }) 30 29 31 - it('supports property attributes', () => { 32 - const { root, el } = setup() 30 + test('supports property attributes', (t: TestContext) => { 31 + const { root, el } = setup() 33 32 34 - root.render(html`<details open=${true}></details>`) 35 - expect(el.querySelector('details')!.open).toBe(true) 33 + root.render(html`<details open=${true}></details>`) 34 + t.assert.ok(el.querySelector('details')!.open) 36 35 37 - root.render(html`<details open=${false}></details>`) 38 - expect(el.querySelector('details')!.open).toBe(false) 39 - }) 36 + root.render(html`<details open=${false}></details>`) 37 + t.assert.ok(!el.querySelector('details')!.open) 38 + }) 40 39 41 - it('guesses the case of properties', () => { 42 - const { root, el } = setup() 40 + test('infers the case of properties', (t: TestContext) => { 41 + const { root, el } = setup() 43 42 44 - const innerHTML = '<h1>Hello, world!</h1>' 43 + const innerHTML = '<h1>Hello, world!</h1>' 45 44 46 - root.render(html`<div innerhtml=${innerHTML}></div>`) 47 - expect(el.querySelector('div')!.innerHTML).toBe(innerHTML) 45 + root.render(html`<div innerhtml=${innerHTML}></div>`) 46 + t.assert.strictEqual(el.querySelector('div')!.innerHTML, innerHTML) 48 47 49 - root.render(html`<span innerHTML=${innerHTML}></span>`) 50 - expect(el.querySelector('span')!.innerHTML).toBe(innerHTML) 51 - }) 48 + root.render(html`<span innerHTML=${innerHTML}></span>`) 49 + t.assert.strictEqual(el.querySelector('span')!.innerHTML, innerHTML) 50 + }) 52 51 53 - it('treats class/for specially', () => { 54 - const { root, el } = setup() 52 + test('treats class/for specially', (t: TestContext) => { 53 + const { root, el } = setup() 55 54 56 - root.render(html`<h1 class=${'foo'}>Hello, world!</h1>`) 57 - expect(el.querySelector('h1')).toHaveClass('foo') 55 + root.render(html`<h1 class=${'foo'}>Hello, world!</h1>`) 56 + t.assert.strictEqual(el.querySelector('h1')!.className, 'foo') 58 57 59 - root.render(html`<label for=${'foo'}>Hello, world!</label>`) 60 - expect(el.querySelector('label')).toHaveAttribute('for', 'foo') 61 - }) 58 + root.render(html`<label for=${'foo'}>Hello, world!</label>`) 59 + t.assert.strictEqual(el.querySelector('label')!.htmlFor, 'foo') 60 + }) 62 61 63 - it('handles data attributes', () => { 64 - const { root, el } = setup() 62 + test('handles data attributes', (t: TestContext) => { 63 + const { root, el } = setup() 65 64 66 - root.render(html`<h1 data-foo=${'bar'}>Hello, world!</h1>`) 67 - expect(el.querySelector('h1')).toHaveAttribute('data-foo', 'bar') 68 - }) 65 + root.render(html`<h1 data-foo=${'bar'}>Hello, world!</h1>`) 66 + t.assert.strictEqual(el.querySelector('h1')!.dataset.foo, 'bar') 67 + }) 69 68 70 - it('supports events', () => { 71 - const { root, el } = setup() 69 + test('supports events', (t: TestContext) => { 70 + const { root, el } = setup() 72 71 73 - let clicks = 0 74 - root.render(html` 75 - <button 76 - onclick=${() => { 77 - clicks++ 78 - }} 79 - > 80 - Click me 81 - </button> 82 - `) 72 + let clicks = 0 73 + root.render(html` 74 + <button 75 + onclick=${() => { 76 + clicks++ 77 + }} 78 + > 79 + Click me 80 + </button> 81 + `) 83 82 84 - expect(clicks).toBe(0) 85 - el.querySelector('button')!.click() 86 - expect(clicks).toBe(1) 87 - el.querySelector('button')!.click() 88 - expect(clicks).toBe(2) 89 - }) 83 + t.assert.strictEqual(clicks, 0) 84 + el.querySelector('button')!.click() 85 + t.assert.strictEqual(clicks, 1) 86 + el.querySelector('button')!.click() 87 + t.assert.strictEqual(clicks, 2) 88 + }) 90 89 91 - it('supports event handlers that change', () => { 92 - const { root, el } = setup() 90 + test('supports event handlers that change', (t: TestContext) => { 91 + const { root, el } = setup() 93 92 94 - const template = (handler: (() => void) | null) => html`<input onblur=${handler}>Click me</input>` 93 + const template = (handler: (() => void) | null) => html`<input onblur=${handler}>Click me</input>` 95 94 96 - const handler = vi.fn() 97 - root.render(template(handler)) 98 - expect(handler).not.toBeCalled() 95 + const handler = t.mock.fn() 96 + root.render(template(handler)) 97 + t.assert.strictEqual(handler.mock.callCount(), 0) 99 98 100 - const event = new Event('blur') 101 - el.querySelector('input')!.dispatchEvent(event) 102 - expect(handler).toHaveBeenCalledExactlyOnceWith(event) 99 + const event = new Event('blur') 100 + el.querySelector('input')!.dispatchEvent(event) 101 + t.assert.strictEqual(handler.mock.callCount(), 1) 102 + t.assert.strictEqual(handler.mock.calls[0].arguments[0], event) 103 103 104 - root.render(template(null)) 105 - el.querySelector('input')!.dispatchEvent(new Event('blur')) 106 - expect(handler).toHaveBeenCalledOnce() 107 - }) 104 + root.render(template(null)) 105 + el.querySelector('input')!.dispatchEvent(new Event('blur')) 106 + t.assert.strictEqual(handler.mock.callCount(), 1) 108 107 })
+131 -128
src/client/tests/basic.test.ts
··· 1 1 import { html, type Displayable } from 'dhtml' 2 - import { describe, expect, it } from 'vitest' 3 - import { setup } from './setup' 2 + import test, { type TestContext } from 'node:test' 3 + import { setup } from './setup.ts' 4 4 5 - describe('basic', () => { 6 - it('should render basic html', () => { 7 - const { root, el } = setup() 5 + test('basic html renders correctly', (t: TestContext) => { 6 + const { root, el } = setup() 8 7 9 - root.render(html`<h1>Hello, world!</h1>`) 10 - expect(el.innerHTML).toBe('<h1>Hello, world!</h1>') 11 - }) 8 + root.render(html`<h1>Hello, world!</h1>`) 9 + t.assert.strictEqual(el.innerHTML, '<h1>Hello, world!</h1>') 10 + }) 12 11 13 - it('should render inner content', () => { 14 - const { root, el } = setup() 12 + test('inner content renders correctly', (t: TestContext) => { 13 + const { root, el } = setup() 15 14 16 - root.render(html`<h1>${html`Inner content!`}</h1>`) 17 - expect(el.innerHTML).toBe('<h1>Inner content!</h1>') 18 - }) 15 + root.render(html`<h1>${html`Inner content!`}</h1>`) 16 + t.assert.strictEqual(el.innerHTML, '<h1>Inner content!</h1>') 17 + }) 19 18 20 - it('should render template with number', () => { 21 - const { root, el } = setup() 19 + test('template with number renders correctly', (t: TestContext) => { 20 + const { root, el } = setup() 22 21 23 - const template = (n: number) => html`<h1>Hello, ${n}!</h1>` 22 + const template = (n: number) => html`<h1>Hello, ${n}!</h1>` 24 23 25 - root.render(template(1)) 26 - expect(el.innerHTML).toBe('<h1>Hello, 1!</h1>') 24 + root.render(template(1)) 25 + t.assert.strictEqual(el.innerHTML, '<h1>Hello, 1!</h1>') 27 26 28 - root.render(template(2)) 29 - expect(el.innerHTML).toBe('<h1>Hello, 2!</h1>') 30 - }) 27 + root.render(template(2)) 28 + t.assert.strictEqual(el.innerHTML, '<h1>Hello, 2!</h1>') 29 + }) 31 30 32 - it('should not clobber external sibling nodes', () => { 33 - const { root, el } = setup('<div>before</div>') 31 + test('external sibling nodes are not clobbered', (t: TestContext) => { 32 + const { root, el } = setup('<div>before</div>') 34 33 35 - root.render(html`<h1>Hello, world!</h1>`) 36 - expect(el.innerHTML).toMatchInlineSnapshot(`"<div>before</div><h1>Hello, world!</h1>"`) 34 + root.render(html`<h1>Hello, world!</h1>`) 35 + t.assert.strictEqual(el.innerHTML, '<div>before</div><h1>Hello, world!</h1>') 37 36 38 - el.appendChild(document.createElement('div')).textContent = 'after' 39 - expect(el.innerHTML).toMatchInlineSnapshot(`"<div>before</div><h1>Hello, world!</h1><div>after</div>"`) 37 + el.appendChild(document.createElement('div')).textContent = 'after' 38 + t.assert.strictEqual(el.innerHTML, '<div>before</div><h1>Hello, world!</h1><div>after</div>') 40 39 41 - root.render(html`<h2>Goodbye, world!</h2>`) 42 - expect(el.innerHTML).toMatchInlineSnapshot(`"<div>before</div><h2>Goodbye, world!</h2><div>after</div>"`) 40 + root.render(html`<h2>Goodbye, world!</h2>`) 41 + t.assert.strictEqual(el.innerHTML, '<div>before</div><h2>Goodbye, world!</h2><div>after</div>') 43 42 44 - root.render(html``) 45 - expect(el.innerHTML).toMatchInlineSnapshot(`"<div>before</div><div>after</div>"`) 43 + root.render(html``) 44 + t.assert.strictEqual(el.innerHTML, '<div>before</div><div>after</div>') 46 45 47 - root.render(html`<h1>Hello, world!</h1>`) 48 - expect(el.innerHTML).toMatchInlineSnapshot(`"<div>before</div><h1>Hello, world!</h1><div>after</div>"`) 49 - }) 46 + root.render(html`<h1>Hello, world!</h1>`) 47 + t.assert.strictEqual(el.innerHTML, '<div>before</div><h1>Hello, world!</h1><div>after</div>') 48 + }) 50 49 51 - it('update identity', () => { 52 - const { root, el } = setup() 50 + test('identity is updated correctly', (t: TestContext) => { 51 + const { root, el } = setup() 53 52 54 - const template = (n: Displayable) => html`<h1>Hello, ${n}!</h1>` 55 - const template2 = (n: Displayable) => html`<h1>Hello, ${n}!</h1>` 53 + const template = (n: Displayable) => html`<h1>Hello, ${n}!</h1>` 54 + const template2 = (n: Displayable) => html`<h1>Hello, ${n}!</h1>` 56 55 57 - root.render(template(1)) 58 - expect(el.innerHTML).toMatchInlineSnapshot(`"<h1>Hello, 1!</h1>"`) 59 - let h1 = el.children[0] 60 - const text = h1.childNodes[1] as Text 61 - expect(text).toBeInstanceOf(Text) 62 - expect(text.data).toBe('1') 56 + root.render(template(1)) 57 + t.assert.strictEqual(el.innerHTML, '<h1>Hello, 1!</h1>') 58 + let h1 = el.children[0] 59 + const text = h1.childNodes[1] as Text 60 + t.assert.ok(text instanceof Text) 61 + t.assert.strictEqual(text.data, '1') 63 62 64 - root.render(template(2)) 65 - expect(el.innerHTML).toMatchInlineSnapshot(`"<h1>Hello, 2!</h1>"`) 66 - expect(el.children[0]).toBe(h1) 67 - expect(text.data).toBe('2') 68 - expect(h1.childNodes[1]).toBe(text) 63 + root.render(template(2)) 64 + t.assert.strictEqual(el.innerHTML, '<h1>Hello, 2!</h1>') 65 + t.assert.strictEqual(el.children[0], h1) 66 + t.assert.strictEqual(text.data, '2') 67 + t.assert.strictEqual(h1.childNodes[1], text) 69 68 70 - root.render(template2(3)) 71 - expect(el.innerHTML).toMatchInlineSnapshot(`"<h1>Hello, 3!</h1>"`) 72 - expect(el.children[0]).not.toBe(h1) 73 - h1 = el.children[0] 69 + root.render(template2(3)) 70 + t.assert.strictEqual(el.innerHTML, '<h1>Hello, 3!</h1>') 71 + t.assert.notStrictEqual(el.children[0], h1) 72 + h1 = el.children[0] 74 73 75 - root.render(template2(template(template('inner')))) 76 - expect(el.innerHTML).toMatchInlineSnapshot(`"<h1>Hello, <h1>Hello, <h1>Hello, inner!</h1>!</h1>!</h1>"`) 77 - expect(el.children[0]).toBe(h1) 78 - }) 74 + root.render(template2(template(template('inner')))) 75 + t.assert.strictEqual(el.innerHTML, '<h1>Hello, <h1>Hello, <h1>Hello, inner!</h1>!</h1>!</h1>') 76 + t.assert.strictEqual(el.children[0], h1) 77 + }) 79 78 80 - it('basic children', () => { 81 - const { root, el } = setup() 79 + test('basic children render correctly', (t: TestContext) => { 80 + const { root, el } = setup() 82 81 83 - root.render(html`<span>${'This is a'}</span> ${html`test`} ${html`test`} ${html`test`}`) 82 + root.render(html`<span>${'This is a'}</span> ${html`test`} ${html`test`} ${html`test`}`) 84 83 85 - expect(el.innerHTML).toMatchInlineSnapshot(`"<span>This is a</span> test test test"`) 86 - }) 84 + t.assert.strictEqual(el.innerHTML, '<span>This is a</span> test test test') 85 + }) 87 86 88 - it('can embed nodes', () => { 89 - const { root, el } = setup() 87 + test('nodes can be embedded', (t: TestContext) => { 88 + const { root, el } = setup() 90 89 91 - let node: ParentNode = document.createElement('span') 90 + let node: ParentNode = document.createElement('span') 92 91 93 - root.render(html`<div>${node}</div>`) 94 - expect(el.innerHTML).toBe('<div><span></span></div>') 95 - expect(el.children[0].children[0]).toBe(node) 92 + root.render(html`<div>${node}</div>`) 93 + t.assert.strictEqual(el.innerHTML, '<div><span></span></div>') 94 + t.assert.strictEqual(el.children[0].children[0], node) 96 95 97 - node = document.createDocumentFragment() 98 - node.append(document.createElement('h1'), document.createElement('h2'), document.createElement('h3')) 96 + node = document.createDocumentFragment() 97 + node.append(document.createElement('h1'), document.createElement('h2'), document.createElement('h3')) 99 98 100 - root.render(html`<div>${node}</div>`) 101 - expect(el.innerHTML).toBe('<div><h1></h1><h2></h2><h3></h3></div>') 102 - expect(node.children.length).toBe(0) 103 - }) 99 + root.render(html`<div>${node}</div>`) 100 + t.assert.strictEqual(el.innerHTML, '<div><h1></h1><h2></h2><h3></h3></div>') 101 + t.assert.strictEqual(node.children.length, 0) 102 + }) 104 103 105 - it.todo('does not add extra empty text nodes', () => { 106 - const { root, el } = setup() 104 + test.skip('extra empty text nodes are not added', (t: TestContext) => { 105 + const { root, el } = setup() 107 106 108 - root.render(html`${'abc'}`) 109 - expect(el.childNodes.length).toBe(1) 110 - expect(el.firstChild).toBeInstanceOf(Text) 111 - expect((el.firstChild as Text).data).toBe('abc') 112 - }) 107 + root.render(html`${'abc'}`) 108 + t.assert.strictEqual(el.childNodes.length, 1) 109 + t.assert.ok(el.firstChild instanceof Text) 110 + t.assert.strictEqual((el.firstChild as Text).data, 'abc') 111 + }) 113 112 114 - it('shifting ChildPart index', () => { 115 - const { root, el } = setup() 113 + test('ChildPart index shifts correctly', (t: TestContext) => { 114 + const { root, el } = setup() 116 115 117 - root.render(html`${html`A<!--x-->`}B${'C'}`) 116 + root.render(html`${html`A<!--x-->`}B${'C'}`) 118 117 119 - expect(el.innerHTML).toBe('A<!--x-->BC') 120 - }) 118 + t.assert.strictEqual(el.innerHTML, 'A<!--x-->BC') 121 119 }) 122 120 123 - describe('errors', () => { 124 - it('throws cleanly', () => { 125 - const { root, el } = setup() 121 + test('errors are thrown cleanly', (t: TestContext) => { 122 + const { root, el } = setup() 126 123 127 - const oops = new Error('oops') 128 - let thrown 129 - try { 130 - root.render( 131 - html`${{ 132 - render() { 133 - throw oops 134 - }, 135 - }}`, 136 - ) 137 - } catch (error) { 138 - thrown = error 139 - } 140 - expect(thrown).toBe(oops) 124 + const oops = new Error('oops') 125 + let thrown 126 + try { 127 + root.render( 128 + html`${{ 129 + render() { 130 + throw oops 131 + }, 132 + }}`, 133 + ) 134 + } catch (error) { 135 + thrown = error 136 + } 137 + t.assert.strictEqual(thrown, oops) 141 138 142 - // on an error, don't leave any visible artifacts 143 - expect(el.innerHTML).toBe('<!---->') 144 - }) 139 + // on an error, don't leave any visible artifacts 140 + t.assert.strictEqual(el.innerHTML, '<!---->') 141 + }) 145 142 146 - it('warns on invalid part placement', { skip: import.meta.env.PROD }, () => { 147 - const { root, el } = setup() 143 + test('invalid part placement produces warning', { skip: process.env.NODE_ENV === 'production' }, (t: TestContext) => { 144 + const { root, el } = setup() 148 145 149 - expect(() => root.render(html`<${'div'}>${'text'}</${'div'}>`)).toThrowErrorMatchingInlineSnapshot( 150 - `[Error: expected the same number of dynamics as parts. do you have a \${...} in an unsupported place?]`, 151 - ) 152 - expect(el.innerHTML).toBe('') 146 + t.assert.throws(() => root.render(html`<${'div'}>${'text'}</${'div'}>`), { 147 + message: 'expected the same number of dynamics as parts. do you have a ${...} in an unsupported place?', 153 148 }) 149 + t.assert.strictEqual(el.innerHTML, '') 150 + }) 154 151 155 - it('does not throw on parts in comments', () => { 156 - const { root, el } = setup() 152 + test('parts in comments do not throw', (t: TestContext) => { 153 + const { root, el } = setup() 157 154 158 - root.render(html`<!-- ${'text'} -->`) 159 - expect(el.innerHTML).toMatchInlineSnapshot(`"<!-- dyn-$0$ -->"`) 160 - }) 155 + root.render(html`<!-- ${'text'} -->`) 156 + t.assert.strictEqual(el.innerHTML, '<!-- dyn-$0$ -->') 157 + }) 161 158 162 - it('throws when manually specifying internal template syntax', { skip: import.meta.env.PROD }, () => { 159 + test( 160 + 'manually specifying internal template syntax throws', 161 + { skip: process.env.NODE_ENV === 'production' }, 162 + (t: TestContext) => { 163 163 const { root, el } = setup() 164 164 165 - expect(() => { 166 - root.render(html`${1} dyn-$0$`) 167 - }).toThrowErrorMatchingInlineSnapshot(`[Error: got more parts than expected]`) 165 + t.assert.throws( 166 + () => { 167 + root.render(html`${1} dyn-$0$`) 168 + }, 169 + { message: 'got more parts than expected' }, 170 + ) 168 171 169 - expect(el.innerHTML).toMatchInlineSnapshot(`""`) 170 - }) 172 + t.assert.strictEqual(el.innerHTML, '') 173 + }, 174 + ) 171 175 172 - it('does not throw when syntax is close but not exact', () => { 173 - const { root, el } = setup() 176 + test('syntax close but not exact does not throw', (t: TestContext) => { 177 + const { root, el } = setup() 174 178 175 - root.render(html`dyn-$${0}1$`) 179 + root.render(html`dyn-$${0}1$`) 176 180 177 - expect(el.innerHTML).toMatchInlineSnapshot(`"dyn-$01$"`) 178 - }) 181 + t.assert.strictEqual(el.innerHTML, 'dyn-$01$') 179 182 })
+17 -19
src/client/tests/custom-elements.test.ts
··· 1 1 import { html } from 'dhtml' 2 2 import { createRoot } from 'dhtml/client' 3 - import { describe, expect, it } from 'vitest' 4 - import { setup } from './setup' 3 + import test, { type TestContext } from 'node:test' 4 + import { setup } from './setup.ts' 5 5 6 6 class CustomElement extends HTMLElement { 7 7 #thing?: string ··· 20 20 21 21 customElements.define('custom-element', CustomElement) 22 22 23 - describe('custom elements', () => { 24 - it('correctly instantiates custom elements', () => { 25 - const { root, el } = setup() 23 + test('custom elements instantiate correctly', (t: TestContext) => { 24 + const { root, el } = setup() 26 25 27 - root.render(html`<custom-element thing=${'hello'}></custom-element>`) 28 - expect(el.innerHTML).toBe(`<custom-element>inside custom element</custom-element>`) 26 + root.render(html`<custom-element thing=${'hello'}></custom-element>`) 27 + t.assert.strictEqual(el.innerHTML, `<custom-element>inside custom element</custom-element>`) 29 28 30 - const customElement = el.querySelector('custom-element') as CustomElement 31 - expect(customElement).toBeInstanceOf(CustomElement) 32 - expect(customElement.thing).toBe('HELLO') 33 - }) 29 + const customElement = el.querySelector('custom-element') as CustomElement 30 + t.assert.ok(customElement instanceof CustomElement) 31 + t.assert.strictEqual(customElement.thing, 'HELLO') 32 + }) 34 33 35 - it('renders into shadow dom', () => { 36 - const { el } = setup() 37 - const shadowRoot = el.attachShadow({ mode: 'open' }) 34 + test('content renders into shadow dom', (t: TestContext) => { 35 + const { el } = setup() 36 + const shadowRoot = el.attachShadow({ mode: 'open' }) 38 37 39 - const root = createRoot(shadowRoot) 40 - root.render(html`<p>hello</p>`) 38 + const root = createRoot(shadowRoot) 39 + root.render(html`<p>hello</p>`) 41 40 42 - expect(el.innerHTML).toBe(``) 43 - expect(shadowRoot.innerHTML).toBe(`<p>hello</p>`) 44 - }) 41 + t.assert.strictEqual(el.innerHTML, ``) 42 + t.assert.strictEqual(shadowRoot.innerHTML, `<p>hello</p>`) 45 43 })
+65 -69
src/client/tests/directives.test.ts
··· 1 1 import { html } from 'dhtml' 2 2 import { attr, type Directive } from 'dhtml/client' 3 - import { describe, expect, it } from 'vitest' 4 - import { setup } from './setup' 3 + import test, { type TestContext } from 'node:test' 4 + import { setup } from './setup.ts' 5 5 6 - describe('directives', () => { 7 - it('functions', () => { 8 - const { root, el } = setup() 6 + test('directive functions work correctly', (t: TestContext) => { 7 + const { root, el } = setup() 9 8 10 - const redifier: Directive = node => { 11 - if (!(node instanceof HTMLElement)) throw new Error('expected HTMLElement') 12 - node.style.color = 'red' 13 - return () => { 14 - node.style.color = '' 15 - } 9 + const redifier: Directive = node => { 10 + if (!(node instanceof HTMLElement)) throw new Error('expected HTMLElement') 11 + node.style.color = 'red' 12 + return () => { 13 + node.style.color = '' 16 14 } 17 - const flipper: Directive = node => { 18 - if (!(node instanceof HTMLElement)) throw new Error('expected HTMLElement') 19 - node.style.transform = 'scaleX(-1)' 20 - return () => { 21 - node.style.transform = '' 22 - } 15 + } 16 + const flipper: Directive = node => { 17 + if (!(node instanceof HTMLElement)) throw new Error('expected HTMLElement') 18 + node.style.transform = 'scaleX(-1)' 19 + return () => { 20 + node.style.transform = '' 23 21 } 22 + } 24 23 25 - const template = (d: Directive | null) => html`<div ${d}>Hello, world!</div>` 24 + const template = (d: Directive | null) => html`<div ${d}>Hello, world!</div>` 26 25 27 - root.render(template(redifier)) 28 - const div = el.firstChild as HTMLElement 29 - expect(div.tagName).toBe('DIV') 30 - expect(div.style.cssText).toBe('color: red;') 26 + root.render(template(redifier)) 27 + const div = el.firstChild as HTMLElement 28 + t.assert.strictEqual(div.tagName, 'DIV') 29 + t.assert.strictEqual(div.style.cssText, 'color: red;') 31 30 32 - root.render(template(flipper)) 33 - expect(div.style.cssText).toBe('transform: scaleX(-1);') 31 + root.render(template(flipper)) 32 + t.assert.strictEqual(div.style.cssText, 'transform: scaleX(-1);') 34 33 35 - root.render(template(null)) 36 - expect(div.style.cssText).toBe('') 34 + root.render(template(null)) 35 + t.assert.strictEqual(div.style.cssText, '') 37 36 38 - root.render(null) 39 - }) 37 + root.render(null) 38 + }) 40 39 41 - it('functions with values', () => { 42 - const { root, el } = setup() 40 + test('directive functions with values work correctly', (t: TestContext) => { 41 + const { root, el } = setup() 43 42 44 - function classes(value: string[]): Directive { 45 - const values = value.filter(Boolean) 46 - return node => { 47 - node.classList.add(...values) 48 - return () => { 49 - node.classList.remove(...values) 50 - } 43 + function classes(value: string[]): Directive { 44 + const values = value.filter(Boolean) 45 + return node => { 46 + node.classList.add(...values) 47 + return () => { 48 + node.classList.remove(...values) 51 49 } 52 50 } 51 + } 53 52 54 - const template = (c: string[]) => html`<div class="foo" ${classes(c)}>Hello, world!</div>` 53 + const template = (c: string[]) => html`<div class="foo" ${classes(c)}>Hello, world!</div>` 55 54 56 - root.render(template(['a', 'b'])) 57 - const div = el.firstChild as HTMLElement 58 - expect(div.tagName).toBe('DIV') 59 - expect(div.className).toBe('foo a b') 55 + root.render(template(['a', 'b'])) 56 + const div = el.firstChild as HTMLElement 57 + t.assert.strictEqual(div.tagName, 'DIV') 58 + t.assert.strictEqual(div.className, 'foo a b') 60 59 61 - root.render(template(['c', 'd'])) 62 - expect(div.className).toBe('foo c d') 60 + root.render(template(['c', 'd'])) 61 + t.assert.strictEqual(div.className, 'foo c d') 63 62 64 - root.render(template([])) 65 - expect(div.className).toBe('foo') 66 - }) 63 + root.render(template([])) 64 + t.assert.strictEqual(div.className, 'foo') 67 65 }) 68 66 69 - describe('attr', () => { 70 - it('works', () => { 71 - const { root, el } = setup() 67 + test('attr directive works correctly', (t: TestContext) => { 68 + const { root, el } = setup() 72 69 73 - const template = (value: string | null) => html` 74 - <input id="attr-works-input"></input> 75 - <label ${attr('for', value)}>Hello, world!</label> 76 - ` 70 + const template = (value: string | null) => html` 71 + <input id="attr-works-input"></input> 72 + <label ${attr('for', value)}>Hello, world!</label> 73 + ` 77 74 78 - root.render(template('attr-works-input')) 79 - expect(el.querySelector('label')).toHaveProperty('htmlFor', 'attr-works-input') 75 + root.render(template('attr-works-input')) 76 + t.assert.strictEqual(el.querySelector('label')!.htmlFor, 'attr-works-input') 80 77 81 - root.render(template('updated')) 82 - expect(el.querySelector('label')).toHaveProperty('htmlFor', 'updated') 78 + root.render(template('updated')) 79 + t.assert.strictEqual(el.querySelector('label')!.htmlFor, 'updated') 83 80 84 - root.render(template(null)) 85 - expect(el.querySelector('label')).toHaveProperty('htmlFor', '') 86 - }) 81 + root.render(template(null)) 82 + t.assert.strictEqual(el.querySelector('label')!.htmlFor, '') 83 + }) 87 84 88 - it('supports booleans', () => { 89 - const { root, el } = setup() 85 + test('attr directive supports booleans', (t: TestContext) => { 86 + const { root, el } = setup() 90 87 91 - const template = (value: boolean) => html`<input ${attr('disabled', value)} />` 88 + const template = (value: boolean) => html`<input ${attr('disabled', value)} />` 92 89 93 - root.render(template(true)) 94 - expect(el.querySelector('input')).toHaveProperty('disabled', true) 90 + root.render(template(true)) 91 + t.assert.strictEqual(el.querySelector('input')!.disabled, true) 95 92 96 - root.render(template(false)) 97 - expect(el.querySelector('input')).toHaveProperty('disabled', false) 98 - }) 93 + root.render(template(false)) 94 + t.assert.strictEqual(el.querySelector('input')!.disabled, false) 99 95 })
+252 -256
src/client/tests/lists.test.ts
··· 1 1 import { html, type Displayable } from 'dhtml' 2 2 import { keyed } from 'dhtml/client' 3 - import { describe, expect, it } from 'vitest' 4 - import { setup } from './setup' 3 + import test, { type TestContext } from 'node:test' 4 + import { setup } from './setup.ts' 5 5 6 6 function shuffle<T>(array: T[]) { 7 7 for (let i = 0; i < array.length; i++) { ··· 10 10 } 11 11 } 12 12 13 - describe('lists', () => { 14 - it('basic', () => { 15 - const { root, el } = setup() 13 + test('basic list operations work correctly', (t: TestContext) => { 14 + const { root, el } = setup() 16 15 17 - let items: Displayable[] | null = null 18 - const listOfItems = () => html` 19 - <ul> 20 - <li>Before</li> 21 - ${items} 22 - <li>After</li> 23 - </ul> 24 - ` 16 + let items: Displayable[] | null = null 17 + const listOfItems = () => html` 18 + <ul> 19 + <li>Before</li> 20 + ${items} 21 + <li>After</li> 22 + </ul> 23 + ` 25 24 26 - root.render(listOfItems()) 27 - expect(el.innerHTML.replace(/\s+/g, ' ')).toBe(' <ul> <li>Before</li> <li>After</li> </ul> ') 25 + root.render(listOfItems()) 26 + t.assert.strictEqual(el.innerHTML.replace(/\s+/g, ' '), ' <ul> <li>Before</li> <li>After</li> </ul> ') 28 27 29 - items = [html`<li>Item 1</li>`, html`<li>Item 2</li>`, html`<li>Item 3</li>`] 28 + items = [html`<li>Item 1</li>`, html`<li>Item 2</li>`, html`<li>Item 3</li>`] 30 29 31 - root.render(listOfItems()) 32 - expect(el.innerHTML.replace(/\s+/g, ' ')).toBe( 33 - ' <ul> <li>Before</li> <li>Item 1</li><li>Item 2</li><li>Item 3</li> <li>After</li> </ul> ', 34 - ) 35 - const [item1, item2, item3] = el.querySelectorAll('li') 30 + root.render(listOfItems()) 31 + t.assert.strictEqual( 32 + el.innerHTML.replace(/\s+/g, ' '), 33 + ' <ul> <li>Before</li> <li>Item 1</li><li>Item 2</li><li>Item 3</li> <li>After</li> </ul> ', 34 + ) 35 + const [item1, item2, item3] = el.querySelectorAll('li') 36 36 37 - items.push(html`<li>Item 4</li>`) 38 - root.render(listOfItems()) 39 - expect(el.innerHTML.replace(/\s+/g, ' ')).toBe( 40 - ' <ul> <li>Before</li> <li>Item 1</li><li>Item 2</li><li>Item 3</li><li>Item 4</li> <li>After</li> </ul> ', 41 - ) 42 - const [item1b, item2b, item3b] = el.querySelectorAll('li') 43 - expect(item1).toBe(item1b) 44 - expect(item2).toBe(item2b) 45 - expect(item3).toBe(item3b) 37 + items.push(html`<li>Item 4</li>`) 38 + root.render(listOfItems()) 39 + t.assert.strictEqual( 40 + el.innerHTML.replace(/\s+/g, ' '), 41 + ' <ul> <li>Before</li> <li>Item 1</li><li>Item 2</li><li>Item 3</li><li>Item 4</li> <li>After</li> </ul> ', 42 + ) 43 + const [item1b, item2b, item3b] = el.querySelectorAll('li') 44 + t.assert.strictEqual(item1, item1b) 45 + t.assert.strictEqual(item2, item2b) 46 + t.assert.strictEqual(item3, item3b) 46 47 47 - items.pop() 48 - items.pop() 49 - root.render(listOfItems()) 50 - expect(el.innerHTML.replace(/\s+/g, ' ')).toBe( 51 - ' <ul> <li>Before</li> <li>Item 1</li><li>Item 2</li> <li>After</li> </ul> ', 52 - ) 53 - const [item1c, item2c] = el.querySelectorAll('li') 54 - expect(item1).toBe(item1c) 55 - expect(item2).toBe(item2c) 56 - }) 48 + items.pop() 49 + items.pop() 50 + root.render(listOfItems()) 51 + t.assert.strictEqual( 52 + el.innerHTML.replace(/\s+/g, ' '), 53 + ' <ul> <li>Before</li> <li>Item 1</li><li>Item 2</li> <li>After</li> </ul> ', 54 + ) 55 + const [item1c, item2c] = el.querySelectorAll('li') 56 + t.assert.strictEqual(item1, item1c) 57 + t.assert.strictEqual(item2, item2c) 58 + }) 57 59 58 - it('pop', () => { 59 - const { root, el } = setup() 60 + test('pop operation works correctly on lists', (t: TestContext) => { 61 + const { root, el } = setup() 60 62 61 - const items = [html`<p>Item 1</p>`, html`<p>Item 2</p>`, html`<p>Item 3</p>`] 62 - const wrapped = html`[${items}]` 63 + const items = [html`<p>Item 1</p>`, html`<p>Item 2</p>`, html`<p>Item 3</p>`] 64 + const wrapped = html`[${items}]` 63 65 64 - root.render(wrapped) 65 - expect(el.innerHTML).toBe('[<p>Item 1</p><p>Item 2</p><p>Item 3</p>]') 66 - const [item1, item2] = el.children 66 + root.render(wrapped) 67 + t.assert.strictEqual(el.innerHTML, '[<p>Item 1</p><p>Item 2</p><p>Item 3</p>]') 68 + const [item1, item2] = el.children 67 69 68 - items.pop() 69 - root.render(wrapped) 70 - expect(el.innerHTML).toBe('[<p>Item 1</p><p>Item 2</p>]') 71 - expect(el.children[0]).toBe(item1) 72 - expect(el.children[1]).toBe(item2) 70 + items.pop() 71 + root.render(wrapped) 72 + t.assert.strictEqual(el.innerHTML, '[<p>Item 1</p><p>Item 2</p>]') 73 + t.assert.strictEqual(el.children[0], item1) 74 + t.assert.strictEqual(el.children[1], item2) 73 75 74 - items.pop() 75 - root.render(wrapped) 76 - expect(el.innerHTML).toBe('[<p>Item 1</p>]') 77 - expect(el.children[0]).toBe(item1) 76 + items.pop() 77 + root.render(wrapped) 78 + t.assert.strictEqual(el.innerHTML, '[<p>Item 1</p>]') 79 + t.assert.strictEqual(el.children[0], item1) 78 80 79 - items.pop() 80 - root.render(wrapped) 81 - expect(el.innerHTML).toBe('[]') 82 - }) 81 + items.pop() 82 + root.render(wrapped) 83 + t.assert.strictEqual(el.innerHTML, '[]') 84 + }) 83 85 84 - it('swap', () => { 85 - const { root, el } = setup() 86 + test('swap operation works correctly on lists', (t: TestContext) => { 87 + const { root, el } = setup() 86 88 87 - const items = [html`<p>Item 1</p>`, html`<p>Item 2</p>`, html`<p>Item 3</p>`] 88 - const wrapped = html`[${items}]` 89 + const items = [html`<p>Item 1</p>`, html`<p>Item 2</p>`, html`<p>Item 3</p>`] 90 + const wrapped = html`[${items}]` 89 91 90 - root.render(wrapped) 91 - expect(el.innerHTML).toBe('[<p>Item 1</p><p>Item 2</p><p>Item 3</p>]') 92 - const [item1, item2, item3] = el.children 92 + root.render(wrapped) 93 + t.assert.strictEqual(el.innerHTML, '[<p>Item 1</p><p>Item 2</p><p>Item 3</p>]') 94 + const [item1, item2, item3] = el.children 93 95 94 - // swap the first two items 95 - ;[items[0], items[1]] = [items[1], items[0]] 96 - root.render(wrapped) 97 - expect(el.innerHTML).toBe('[<p>Item 2</p><p>Item 1</p><p>Item 3</p>]') 98 - expect(el.children[0]).toBe(item2) 99 - expect(el.children[1]).toBe(item1) 100 - expect(el.children[2]).toBe(item3) 96 + // swap the first two items 97 + ;[items[0], items[1]] = [items[1], items[0]] 98 + root.render(wrapped) 99 + t.assert.strictEqual(el.innerHTML, '[<p>Item 2</p><p>Item 1</p><p>Item 3</p>]') 100 + t.assert.strictEqual(el.children[0], item2) 101 + t.assert.strictEqual(el.children[1], item1) 102 + t.assert.strictEqual(el.children[2], item3) 101 103 102 - // swap the last two items 103 - ;[items[1], items[2]] = [items[2], items[1]] 104 - root.render(wrapped) 105 - expect(el.innerHTML).toBe('[<p>Item 2</p><p>Item 3</p><p>Item 1</p>]') 106 - expect(el.children[0]).toBe(item2) 107 - expect(el.children[1]).toBe(item3) 108 - expect(el.children[2]).toBe(item1) 104 + // swap the last two items 105 + ;[items[1], items[2]] = [items[2], items[1]] 106 + root.render(wrapped) 107 + t.assert.strictEqual(el.innerHTML, '[<p>Item 2</p><p>Item 3</p><p>Item 1</p>]') 108 + t.assert.strictEqual(el.children[0], item2) 109 + t.assert.strictEqual(el.children[1], item3) 110 + t.assert.strictEqual(el.children[2], item1) 109 111 110 - // swap the first and last items 111 - ;[items[0], items[2]] = [items[2], items[0]] 112 - root.render(wrapped) 113 - expect(el.innerHTML).toBe('[<p>Item 1</p><p>Item 3</p><p>Item 2</p>]') 114 - expect(el.children[0]).toBe(item1) 115 - expect(el.children[1]).toBe(item3) 116 - expect(el.children[2]).toBe(item2) 112 + // swap the first and last items 113 + ;[items[0], items[2]] = [items[2], items[0]] 114 + root.render(wrapped) 115 + t.assert.strictEqual(el.innerHTML, '[<p>Item 1</p><p>Item 3</p><p>Item 2</p>]') 116 + t.assert.strictEqual(el.children[0], item1) 117 + t.assert.strictEqual(el.children[1], item3) 118 + t.assert.strictEqual(el.children[2], item2) 117 119 118 - // put things back 119 - ;[items[1], items[2]] = [items[2], items[1]] 120 - root.render(wrapped) 121 - expect(el.innerHTML).toBe('[<p>Item 1</p><p>Item 2</p><p>Item 3</p>]') 122 - expect(el.children[0]).toBe(item1) 123 - expect(el.children[1]).toBe(item2) 124 - expect(el.children[2]).toBe(item3) 125 - }) 120 + // put things back 121 + ;[items[1], items[2]] = [items[2], items[1]] 122 + root.render(wrapped) 123 + t.assert.strictEqual(el.innerHTML, '[<p>Item 1</p><p>Item 2</p><p>Item 3</p>]') 124 + t.assert.strictEqual(el.children[0], item1) 125 + t.assert.strictEqual(el.children[1], item2) 126 + t.assert.strictEqual(el.children[2], item3) 127 + }) 126 128 127 - it('shift', () => { 128 - const { root, el } = setup() 129 + test('shift operation works correctly on lists', (t: TestContext) => { 130 + const { root, el } = setup() 129 131 130 - const items = [html`<p>Item 1</p>`, html`<p>Item 2</p>`, html`<p>Item 3</p>`] 131 - const wrapped = html`[${items}]` 132 + const items = [html`<p>Item 1</p>`, html`<p>Item 2</p>`, html`<p>Item 3</p>`] 133 + const wrapped = html`[${items}]` 132 134 133 - root.render(wrapped) 134 - expect(el.innerHTML).toBe('[<p>Item 1</p><p>Item 2</p><p>Item 3</p>]') 135 - const [, item2, item3] = el.children 135 + root.render(wrapped) 136 + t.assert.strictEqual(el.innerHTML, '[<p>Item 1</p><p>Item 2</p><p>Item 3</p>]') 137 + const [, item2, item3] = el.children 136 138 137 - items.shift() 138 - root.render(wrapped) 139 - expect(el.innerHTML).toBe('[<p>Item 2</p><p>Item 3</p>]') 140 - expect(el.children[0]).toBe(item2) 141 - expect(el.children[1]).toBe(item3) 139 + items.shift() 140 + root.render(wrapped) 141 + t.assert.strictEqual(el.innerHTML, '[<p>Item 2</p><p>Item 3</p>]') 142 + t.assert.strictEqual(el.children[0], item2) 143 + t.assert.strictEqual(el.children[1], item3) 142 144 143 - items.shift() 144 - root.render(wrapped) 145 - expect(el.innerHTML).toBe('[<p>Item 3</p>]') 146 - expect(el.children[0]).toBe(item3) 145 + items.shift() 146 + root.render(wrapped) 147 + t.assert.strictEqual(el.innerHTML, '[<p>Item 3</p>]') 148 + t.assert.strictEqual(el.children[0], item3) 147 149 148 - items.shift() 149 - root.render(wrapped) 150 - expect(el.innerHTML).toBe('[]') 151 - }) 150 + items.shift() 151 + root.render(wrapped) 152 + t.assert.strictEqual(el.innerHTML, '[]') 153 + }) 152 154 153 - it('full then empty then full', () => { 154 - const { root, el } = setup() 155 + test('full then empty then full list renders correctly', (t: TestContext) => { 156 + const { root, el } = setup() 155 157 156 - root.render([1]) 157 - expect(el.innerHTML).toBe('1') 158 + root.render([1]) 159 + t.assert.strictEqual(el.innerHTML, '1') 158 160 159 - root.render([]) 160 - expect(el.innerHTML).toBe('') 161 + root.render([]) 162 + t.assert.strictEqual(el.innerHTML, '') 161 163 162 - root.render([2]) 163 - expect(el.innerHTML).toBe('2') 164 - }) 164 + root.render([2]) 165 + t.assert.strictEqual(el.innerHTML, '2') 166 + }) 165 167 166 - it('can disappear', () => { 167 - const { root, el } = setup() 168 + test('list can disappear when condition changes', (t: TestContext) => { 169 + const { root, el } = setup() 168 170 169 - const app = { 170 - show: true, 171 - render() { 172 - if (!this.show) return null 173 - return [1, 2, 3].map(i => html`<div>${i}</div>`) 174 - }, 175 - } 171 + const app = { 172 + show: true, 173 + render() { 174 + if (!this.show) return null 175 + return [1, 2, 3].map(i => html`<div>${i}</div>`) 176 + }, 177 + } 176 178 177 - root.render(app) 178 - expect(el.innerHTML).toMatchInlineSnapshot(`"<div>1</div><div>2</div><div>3</div>"`) 179 + root.render(app) 180 + t.assert.strictEqual(el.innerHTML, '<div>1</div><div>2</div><div>3</div>') 179 181 180 - app.show = false 181 - root.render(app) 182 - expect(el.innerHTML).toMatchInlineSnapshot(`""`) 183 - }) 182 + app.show = false 183 + root.render(app) 184 + t.assert.strictEqual(el.innerHTML, '') 184 185 }) 185 186 186 - describe('list reordering', () => { 187 - it('unkeyed', () => { 188 - const { root, el } = setup() 187 + test('unkeyed lists recreate elements when reordered', (t: TestContext) => { 188 + const { root, el } = setup() 189 189 190 - const a = () => html`<h1>Item 1</h1>` 191 - const b = () => html`<h2>Item 2</h2>` 190 + const a = () => html`<h1>Item 1</h1>` 191 + const b = () => html`<h2>Item 2</h2>` 192 192 193 - root.render([a(), b()]) 194 - expect(el.innerHTML).toBe('<h1>Item 1</h1><h2>Item 2</h2>') 193 + root.render([a(), b()]) 194 + t.assert.strictEqual(el.innerHTML, '<h1>Item 1</h1><h2>Item 2</h2>') 195 195 196 - const [h1, h2] = el.children 197 - expect(h1.tagName).toBe('H1') 198 - expect(h2.tagName).toBe('H2') 196 + const [h1, h2] = el.children 197 + t.assert.strictEqual(h1.tagName, 'H1') 198 + t.assert.strictEqual(h2.tagName, 'H2') 199 199 200 - root.render([b(), a()]) 201 - expect(el.innerHTML).toBe('<h2>Item 2</h2><h1>Item 1</h1>') 200 + root.render([b(), a()]) 201 + t.assert.strictEqual(el.innerHTML, '<h2>Item 2</h2><h1>Item 1</h1>') 202 202 203 - // visually they should be swapped 204 - expect(el.children[0]).toEqual(h2) 205 - expect(el.children[1]).toEqual(h1) 203 + // visually they should be swapped 204 + t.assert.strictEqual(el.children[0].innerHTML, h2.innerHTML) 205 + t.assert.strictEqual(el.children[1].innerHTML, h1.innerHTML) 206 206 207 - // but there's no stable identity, so they're recreated 208 - expect(el.children[0]).not.toBe(h2) 209 - expect(el.children[1]).not.toBe(h1) 210 - }) 207 + // but there's no stable identity, so they're recreated 208 + t.assert.notStrictEqual(el.children[0], h2) 209 + t.assert.notStrictEqual(el.children[1], h1) 210 + }) 211 211 212 - it('explicit keyed', () => { 213 - const { root, el } = setup() 212 + test('explicit keyed lists preserve identity when reordered', (t: TestContext) => { 213 + const { root, el } = setup() 214 214 215 - const a = () => keyed(html`<h1>Item 1</h1>`, 1) 216 - const b = () => keyed(html`<h2>Item 2</h2>`, 2) 215 + const a = () => keyed(html`<h1>Item 1</h1>`, 1) 216 + const b = () => keyed(html`<h2>Item 2</h2>`, 2) 217 217 218 - root.render([a(), b()]) 219 - expect(el.innerHTML).toBe('<h1>Item 1</h1><h2>Item 2</h2>') 218 + root.render([a(), b()]) 219 + t.assert.strictEqual(el.innerHTML, '<h1>Item 1</h1><h2>Item 2</h2>') 220 220 221 - const [h1, h2] = el.children 222 - expect(h1.tagName).toBe('H1') 223 - expect(h2.tagName).toBe('H2') 221 + const [h1, h2] = el.children 222 + t.assert.strictEqual(h1.tagName, 'H1') 223 + t.assert.strictEqual(h2.tagName, 'H2') 224 224 225 - root.render([b(), a()]) 226 - expect(el.innerHTML).toBe('<h2>Item 2</h2><h1>Item 1</h1>') 225 + root.render([b(), a()]) 226 + t.assert.strictEqual(el.innerHTML, '<h2>Item 2</h2><h1>Item 1</h1>') 227 227 228 - expect(el.children[0]).toBe(h2) 229 - expect(el.children[1]).toBe(h1) 230 - }) 228 + t.assert.strictEqual(el.children[0], h2) 229 + t.assert.strictEqual(el.children[1], h1) 230 + }) 231 231 232 - it('implicit keyed', () => { 233 - const { root, el } = setup() 232 + test('implicit keyed lists preserve identity when reordered', (t: TestContext) => { 233 + const { root, el } = setup() 234 234 235 - const items = [html`<h1>Item 1</h1>`, html`<h2>Item 2</h2>`] 235 + const items = [html`<h1>Item 1</h1>`, html`<h2>Item 2</h2>`] 236 236 237 - root.render(items) 238 - expect(el.innerHTML).toBe('<h1>Item 1</h1><h2>Item 2</h2>') 237 + root.render(items) 238 + t.assert.strictEqual(el.innerHTML, '<h1>Item 1</h1><h2>Item 2</h2>') 239 239 240 - const [h1, h2] = el.children 241 - expect(h1.tagName).toBe('H1') 242 - expect(h2.tagName).toBe('H2') 243 - ;[items[0], items[1]] = [items[1], items[0]] 240 + const [h1, h2] = el.children 241 + t.assert.strictEqual(h1.tagName, 'H1') 242 + t.assert.strictEqual(h2.tagName, 'H2') 243 + ;[items[0], items[1]] = [items[1], items[0]] 244 244 245 - root.render(items) 246 - expect(el.innerHTML).toBe('<h2>Item 2</h2><h1>Item 1</h1>') 247 - expect(el.children[0].tagName).toBe('H2') 248 - expect(el.children[1].tagName).toBe('H1') 245 + root.render(items) 246 + t.assert.strictEqual(el.innerHTML, '<h2>Item 2</h2><h1>Item 1</h1>') 247 + t.assert.strictEqual(el.children[0].tagName, 'H2') 248 + t.assert.strictEqual(el.children[1].tagName, 'H1') 249 249 250 - expect(el.children[0]).toBe(h2) 251 - expect(el.children[1]).toBe(h1) 252 - }) 250 + t.assert.strictEqual(el.children[0], h2) 251 + t.assert.strictEqual(el.children[1], h1) 252 + }) 253 253 254 - it('implicit keyed resize', () => { 255 - const { root, el } = setup() 254 + test('implicit keyed lists with multiple elements preserve identity when resized', (t: TestContext) => { 255 + const { root, el } = setup() 256 256 257 - const items = [ 258 - html`<h1>Item 1</h1>`, 259 - html` 260 - <h2>Item 2</h2> 261 - <p>Body content</p> 262 - `, 263 - ] 257 + const items = [ 258 + html`<h1>Item 1</h1>`, 259 + html` 260 + <h2>Item 2</h2> 261 + <p>Body content</p> 262 + `, 263 + ] 264 264 265 - root.render(items) 266 - expect(el.innerHTML.replace(/\s+/g, ' ')).toBe('<h1>Item 1</h1> <h2>Item 2</h2> <p>Body content</p> ') 265 + root.render(items) 266 + t.assert.strictEqual(el.innerHTML.replace(/\s+/g, ' '), '<h1>Item 1</h1> <h2>Item 2</h2> <p>Body content</p> ') 267 267 268 - const [h1, h2, p] = el.children 269 - expect(h1.tagName).toBe('H1') 270 - expect(h2.tagName).toBe('H2') 271 - expect(p.tagName).toBe('P') 268 + const [h1, h2, p] = el.children 269 + t.assert.strictEqual(h1.tagName, 'H1') 270 + t.assert.strictEqual(h2.tagName, 'H2') 271 + t.assert.strictEqual(p.tagName, 'P') 272 272 273 - // Swap 274 - ;[items[0], items[1]] = [items[1], items[0]] 275 - root.render(items) 276 - expect(el.innerHTML.replace(/\s+/g, ' ')).toBe(' <h2>Item 2</h2> <p>Body content</p> <h1>Item 1</h1>') 277 - expect(el.children[0].tagName).toBe('H2') 278 - expect(el.children[1].tagName).toBe('P') 279 - expect(el.children[2].tagName).toBe('H1') 273 + // Swap 274 + ;[items[0], items[1]] = [items[1], items[0]] 275 + root.render(items) 276 + t.assert.strictEqual(el.innerHTML.replace(/\s+/g, ' '), ' <h2>Item 2</h2> <p>Body content</p> <h1>Item 1</h1>') 277 + t.assert.strictEqual(el.children[0].tagName, 'H2') 278 + t.assert.strictEqual(el.children[1].tagName, 'P') 279 + t.assert.strictEqual(el.children[2].tagName, 'H1') 280 280 281 - expect(el.children[0]).toBe(h2) 282 - expect(el.children[1]).toBe(p) 283 - expect(el.children[2]).toBe(h1) 281 + t.assert.strictEqual(el.children[0], h2) 282 + t.assert.strictEqual(el.children[1], p) 283 + t.assert.strictEqual(el.children[2], h1) 284 284 285 - // Swap back 286 - ;[items[0], items[1]] = [items[1], items[0]] 287 - root.render(items) 288 - expect(el.innerHTML.replace(/\s+/g, ' ')).toBe('<h1>Item 1</h1> <h2>Item 2</h2> <p>Body content</p> ') 289 - expect(el.children[0].tagName).toBe('H1') 290 - expect(el.children[1].tagName).toBe('H2') 291 - expect(el.children[2].tagName).toBe('P') 292 - expect(el.children[0]).toBe(h1) 293 - expect(el.children[1]).toBe(h2) 294 - expect(el.children[2]).toBe(p) 295 - }) 285 + // Swap back 286 + ;[items[0], items[1]] = [items[1], items[0]] 287 + root.render(items) 288 + t.assert.strictEqual(el.innerHTML.replace(/\s+/g, ' '), '<h1>Item 1</h1> <h2>Item 2</h2> <p>Body content</p> ') 289 + t.assert.strictEqual(el.children[0].tagName, 'H1') 290 + t.assert.strictEqual(el.children[1].tagName, 'H2') 291 + t.assert.strictEqual(el.children[2].tagName, 'P') 292 + t.assert.strictEqual(el.children[0], h1) 293 + t.assert.strictEqual(el.children[1], h2) 294 + t.assert.strictEqual(el.children[2], p) 295 + }) 296 296 297 - it('implicit keyed renderable', () => { 298 - const { root, el } = setup() 297 + test('implicit keyed renderable lists preserve identity when reordered', (t: TestContext) => { 298 + const { root, el } = setup() 299 299 300 - const items = [{ render: () => html`<h1>Item 1</h1>` }, { render: () => html`<h2>Item 2</h2>` }] 300 + const items = [{ render: () => html`<h1>Item 1</h1>` }, { render: () => html`<h2>Item 2</h2>` }] 301 301 302 - root.render(items) 303 - expect(el.innerHTML).toBe('<h1>Item 1</h1><h2>Item 2</h2>') 302 + root.render(items) 303 + t.assert.strictEqual(el.innerHTML, '<h1>Item 1</h1><h2>Item 2</h2>') 304 304 305 - const [h1, h2] = el.children 306 - expect(h1.tagName).toBe('H1') 307 - expect(h2.tagName).toBe('H2') 308 - ;[items[0], items[1]] = [items[1], items[0]] 305 + const [h1, h2] = el.children 306 + t.assert.strictEqual(h1.tagName, 'H1') 307 + t.assert.strictEqual(h2.tagName, 'H2') 308 + ;[items[0], items[1]] = [items[1], items[0]] 309 309 310 - root.render(items) 311 - expect(el.innerHTML).toBe('<h2>Item 2</h2><h1>Item 1</h1>') 312 - expect(el.children[0].tagName).toBe('H2') 313 - expect(el.children[1].tagName).toBe('H1') 310 + root.render(items) 311 + t.assert.strictEqual(el.innerHTML, '<h2>Item 2</h2><h1>Item 1</h1>') 312 + t.assert.strictEqual(el.children[0].tagName, 'H2') 313 + t.assert.strictEqual(el.children[1].tagName, 'H1') 314 314 315 - expect(el.children[0]).toBe(h2) 316 - expect(el.children[1]).toBe(h1) 317 - }) 315 + t.assert.strictEqual(el.children[0], h2) 316 + t.assert.strictEqual(el.children[1], h1) 317 + }) 318 318 319 - it('reorders many items', () => { 320 - const { root, el } = setup() 319 + test('many items can be reordered', (t: TestContext) => { 320 + const { root, el } = setup() 321 321 322 - const items = Array.from({ length: 10 }, (_, i) => [html`<p>Item ${i}</p>`, `<p>Item ${i}</p>`]) 322 + const items = Array.from({ length: 10 }, (_, i) => [html`<p>Item ${i}</p>`, `<p>Item ${i}</p>`]) 323 323 324 - root.render(items.map(([item]) => item)) 325 - expect(el.innerHTML).toBe(items.map(([, html]) => html).join('')) 324 + root.render(items.map(([item]) => item)) 325 + t.assert.strictEqual(el.innerHTML, items.map(([, html]) => html).join('')) 326 326 327 - shuffle(items) 328 - // items.reverse() 327 + shuffle(items) 329 328 330 - root.render(items.map(([item]) => item)) 331 - expect(el.innerHTML).toBe(items.map(([, html]) => html).join('')) 332 - }) 329 + root.render(items.map(([item]) => item)) 330 + t.assert.strictEqual(el.innerHTML, items.map(([, html]) => html).join('')) 333 331 }) 334 332 335 - describe('list with keys', { skip: import.meta.env.PROD }, () => { 336 - it("can't key something twice", () => { 337 - expect(() => keyed(html``, 1)).not.toThrow() 338 - expect(() => keyed(keyed(html``, 1), 1)).toThrow() 339 - }) 333 + test('keying something twice throws an error', { skip: process.env.NODE_ENV === 'production' }, (t: TestContext) => { 334 + t.assert.doesNotThrow(() => keyed(html``, 1)) 335 + t.assert.throws(() => keyed(keyed(html``, 1), 1)) 340 336 })
+25 -27
src/client/tests/recursion.test.ts
··· 1 1 import { html } from 'dhtml' 2 - import { setup } from './setup' 3 - import { describe, it } from 'vitest' 2 + import test, { type TestContext } from 'node:test' 3 + import { setup } from './setup.ts' 4 4 5 5 const DEPTH = 10 6 6 7 - describe('recursion', () => { 8 - it('handles basic recursion', ({ expect }) => { 9 - const { root, el } = setup() 7 + test('basic recursion is handled correctly', (t: TestContext) => { 8 + const { root, el } = setup() 10 9 11 - const app = { 12 - renders: 0, 13 - render() { 14 - if (++this.renders > DEPTH) return 'hello!' 15 - return this 16 - }, 17 - } 18 - root.render(app) 19 - expect(el.innerHTML).toBe('hello!') 20 - }) 10 + const app = { 11 + renders: 0, 12 + render() { 13 + if (++this.renders > DEPTH) return 'hello!' 14 + return this 15 + }, 16 + } 17 + root.render(app) 18 + t.assert.strictEqual(el.innerHTML, 'hello!') 19 + }) 21 20 22 - it('handles nested recursion', ({ expect }) => { 23 - const { root, el } = setup() 21 + test('nested recursion is handled correctly', (t: TestContext) => { 22 + const { root, el } = setup() 24 23 25 - const app = { 26 - renders: 0, 27 - render() { 28 - if (++this.renders > DEPTH) return 'hello!' 29 - return html`<span>${this}</span>` 30 - }, 31 - } 32 - root.render(app) 33 - expect(el.innerHTML).toBe('<span>'.repeat(DEPTH) + 'hello!' + '</span>'.repeat(DEPTH)) 34 - }) 24 + const app = { 25 + renders: 0, 26 + render() { 27 + if (++this.renders > DEPTH) return 'hello!' 28 + return html`<span>${this}</span>` 29 + }, 30 + } 31 + root.render(app) 32 + t.assert.strictEqual(el.innerHTML, '<span>'.repeat(DEPTH) + 'hello!' + '</span>'.repeat(DEPTH)) 35 33 })
+332 -423
src/client/tests/renderable.test.ts
··· 1 1 import { html, type Renderable } from 'dhtml' 2 2 import { getParentNode, invalidate, onMount, onUnmount } from 'dhtml/client' 3 - import { describe, expect, it, vi } from 'vitest' 4 - import { setup } from './setup' 5 - 6 - describe('renderables', () => { 7 - it('works', async () => { 8 - const { root, el } = setup() 3 + import test, { type TestContext } from 'node:test' 4 + import { setup } from './setup.ts' 9 5 10 - root.render( 11 - html`${{ 12 - render() { 13 - return html`<h1>Hello, world!</h1>` 14 - }, 15 - }}`, 16 - ) 17 - expect(el.innerHTML).toBe('<h1>Hello, world!</h1>') 6 + test('renderables work correctly', async (t: TestContext) => { 7 + const { root, el } = setup() 18 8 19 - const app = { 20 - i: 0, 9 + root.render( 10 + html`${{ 21 11 render() { 22 - return html`Count: ${this.i++}` 12 + return html`<h1>Hello, world!</h1>` 23 13 }, 24 - } 25 - root.render(app) 26 - expect(el.innerHTML).toBe('Count: 0') 27 - root.render(app) 28 - expect(el.innerHTML).toBe('Count: 1') 29 - await invalidate(app) 30 - expect(el.innerHTML).toBe('Count: 2') 31 - await invalidate(app) 32 - expect(el.innerHTML).toBe('Count: 3') 33 - expect(app.i).toBe(4) 34 - }) 14 + }}`, 15 + ) 16 + t.assert.strictEqual(el.innerHTML, '<h1>Hello, world!</h1>') 35 17 36 - it('handles undefined', () => { 37 - const { root, el } = setup() 18 + const app = { 19 + i: 0, 20 + render() { 21 + return html`Count: ${this.i++}` 22 + }, 23 + } 24 + root.render(app) 25 + t.assert.strictEqual(el.innerHTML, 'Count: 0') 26 + root.render(app) 27 + t.assert.strictEqual(el.innerHTML, 'Count: 1') 28 + await invalidate(app) 29 + t.assert.strictEqual(el.innerHTML, 'Count: 2') 30 + await invalidate(app) 31 + t.assert.strictEqual(el.innerHTML, 'Count: 3') 32 + t.assert.strictEqual(app.i, 4) 33 + }) 38 34 39 - root.render({ 40 - // @ts-expect-error 41 - render() {}, 42 - }) 35 + test('renderables handle undefined correctly', (t: TestContext) => { 36 + const { root, el } = setup() 43 37 44 - expect(el.innerHTML).toBe('') 38 + root.render({ 39 + // @ts-expect-error 40 + render() {}, 45 41 }) 42 + 43 + t.assert.strictEqual(el.innerHTML, '') 46 44 }) 47 45 48 - describe('onMount', () => { 49 - it('calls in the right order', () => { 50 - const { root, el } = setup() 46 + test('onMount calls in the right order', (t: TestContext) => { 47 + const { root, el } = setup() 51 48 52 - const sequence: string[] = [] 49 + const sequence: string[] = [] 53 50 54 - const inner = { 55 - attached: false, 56 - render() { 57 - sequence.push('inner render') 58 - if (!this.attached) { 59 - this.attached = true 60 - onMount(this, () => { 61 - sequence.push('inner mount') 62 - return () => { 63 - sequence.push('inner cleanup') 64 - } 65 - }) 66 - } 67 - return 'inner' 68 - }, 69 - } 51 + const inner = { 52 + attached: false, 53 + render() { 54 + sequence.push('inner render') 55 + if (!this.attached) { 56 + this.attached = true 57 + onMount(this, () => { 58 + sequence.push('inner mount') 59 + return () => { 60 + sequence.push('inner cleanup') 61 + } 62 + }) 63 + } 64 + return 'inner' 65 + }, 66 + } 70 67 71 - const outer = { 72 - attached: false, 73 - show: true, 74 - render() { 75 - sequence.push('outer render') 76 - if (!this.attached) { 77 - this.attached = true 78 - onMount(this, () => { 79 - sequence.push('outer mount') 80 - return () => { 81 - sequence.push('outer cleanup') 82 - } 83 - }) 84 - } 85 - if (!this.show) return null 86 - return inner 87 - }, 88 - } 68 + const outer = { 69 + attached: false, 70 + show: true, 71 + render() { 72 + sequence.push('outer render') 73 + if (!this.attached) { 74 + this.attached = true 75 + onMount(this, () => { 76 + sequence.push('outer mount') 77 + return () => { 78 + sequence.push('outer cleanup') 79 + } 80 + }) 81 + } 82 + if (!this.show) return null 83 + return inner 84 + }, 85 + } 89 86 90 - outer.show = true 91 - root.render(outer) 92 - expect(el.innerHTML).toBe('inner') 93 - expect(sequence).toMatchInlineSnapshot(` 94 - [ 95 - "outer render", 96 - "inner render", 97 - "inner mount", 98 - "outer mount", 99 - ] 100 - `) 101 - sequence.length = 0 87 + outer.show = true 88 + root.render(outer) 89 + t.assert.strictEqual(el.innerHTML, 'inner') 90 + t.assert.deepStrictEqual(sequence, ['outer render', 'inner render', 'inner mount', 'outer mount']) 91 + sequence.length = 0 102 92 103 - outer.show = false 104 - root.render(outer) 105 - expect(el.innerHTML).toBe('') 106 - expect(sequence).toMatchInlineSnapshot(` 107 - [ 108 - "outer render", 109 - "inner cleanup", 110 - ] 111 - `) 112 - sequence.length = 0 93 + outer.show = false 94 + root.render(outer) 95 + t.assert.strictEqual(el.innerHTML, '') 96 + t.assert.deepStrictEqual(sequence, ['outer render', 'inner cleanup']) 97 + sequence.length = 0 113 98 114 - outer.show = true 115 - root.render(outer) 116 - expect(el.innerHTML).toBe('inner') 117 - expect(sequence).toMatchInlineSnapshot(` 118 - [ 119 - "outer render", 120 - "inner render", 121 - ] 122 - `) 123 - sequence.length = 0 124 - }) 99 + outer.show = true 100 + root.render(outer) 101 + t.assert.strictEqual(el.innerHTML, 'inner') 102 + t.assert.deepStrictEqual(sequence, ['outer render', 'inner render']) 103 + sequence.length = 0 104 + }) 125 105 126 - it('registers multiple callbacks', () => { 127 - const { root } = setup() 106 + test('onMount registers multiple callbacks', (t: TestContext) => { 107 + const { root } = setup() 128 108 129 - const sequence: string[] = [] 109 + const sequence: string[] = [] 130 110 131 - const app = { 132 - render() { 133 - onMount(this, () => { 134 - sequence.push('mount 1') 135 - return () => sequence.push('cleanup 1') 136 - }) 111 + const app = { 112 + render() { 113 + onMount(this, () => { 114 + sequence.push('mount 1') 115 + return () => sequence.push('cleanup 1') 116 + }) 137 117 138 - onMount(this, () => { 139 - sequence.push('mount 2') 140 - return () => sequence.push('cleanup 2') 141 - }) 118 + onMount(this, () => { 119 + sequence.push('mount 2') 120 + return () => sequence.push('cleanup 2') 121 + }) 142 122 143 - return 'app' 144 - }, 145 - } 123 + return 'app' 124 + }, 125 + } 146 126 147 - root.render(app) 148 - expect(sequence).toMatchInlineSnapshot(` 149 - [ 150 - "mount 1", 151 - "mount 2", 152 - ] 153 - `) 154 - sequence.length = 0 127 + root.render(app) 128 + t.assert.deepStrictEqual(sequence, ['mount 1', 'mount 2']) 129 + sequence.length = 0 155 130 156 - root.render(null) 157 - expect(sequence).toMatchInlineSnapshot(` 158 - [ 159 - "cleanup 1", 160 - "cleanup 2", 161 - ] 162 - `) 163 - }) 131 + root.render(null) 132 + t.assert.deepStrictEqual(sequence, ['cleanup 1', 'cleanup 2']) 133 + }) 164 134 165 - it('registers a fixed callback once', () => { 166 - const { root } = setup() 135 + test('onMount registers a fixed callback once', (t: TestContext) => { 136 + const { root } = setup() 167 137 168 - const sequence: string[] = [] 138 + const sequence: string[] = [] 169 139 170 - function callback() { 171 - sequence.push('mount') 172 - return () => sequence.push('cleanup') 173 - } 140 + function callback() { 141 + sequence.push('mount') 142 + return () => sequence.push('cleanup') 143 + } 174 144 175 - const app = { 176 - render() { 177 - onMount(this, callback) 178 - onMount(this, callback) 179 - return 'app' 180 - }, 181 - } 145 + const app = { 146 + render() { 147 + onMount(this, callback) 148 + onMount(this, callback) 149 + return 'app' 150 + }, 151 + } 182 152 183 - root.render(app) 184 - expect(sequence).toMatchInlineSnapshot(` 185 - [ 186 - "mount", 187 - ] 188 - `) 189 - sequence.length = 0 153 + root.render(app) 154 + t.assert.deepStrictEqual(sequence, ['mount']) 155 + sequence.length = 0 190 156 191 - root.render(null) 192 - expect(sequence).toMatchInlineSnapshot(` 193 - [ 194 - "cleanup", 195 - ] 196 - `) 197 - }) 157 + root.render(null) 158 + t.assert.deepStrictEqual(sequence, ['cleanup']) 159 + }) 198 160 199 - it('registers callbacks outside of render', () => { 200 - const { root } = setup() 161 + test('onMount registers callbacks outside of render', (t: TestContext) => { 162 + const { root } = setup() 201 163 202 - const sequence: string[] = [] 164 + const sequence: string[] = [] 203 165 204 - const app = { 205 - render() { 206 - sequence.push('render') 207 - return 'app' 208 - }, 209 - } 166 + const app = { 167 + render() { 168 + sequence.push('render') 169 + return 'app' 170 + }, 171 + } 210 172 211 - onMount(app, () => { 212 - sequence.push('mount') 213 - return () => sequence.push('cleanup') 214 - }) 173 + onMount(app, () => { 174 + sequence.push('mount') 175 + return () => sequence.push('cleanup') 176 + }) 215 177 216 - expect(sequence).toMatchInlineSnapshot(`[]`) 178 + t.assert.deepStrictEqual(sequence, []) 217 179 218 - root.render(app) 219 - expect(sequence).toMatchInlineSnapshot(` 220 - [ 221 - "render", 222 - "mount", 223 - ] 224 - `) 225 - sequence.length = 0 180 + root.render(app) 181 + t.assert.deepStrictEqual(sequence, ['render', 'mount']) 182 + sequence.length = 0 226 183 227 - root.render(null) 228 - expect(sequence).toMatchInlineSnapshot(` 229 - [ 230 - "cleanup", 231 - ] 232 - `) 233 - }) 184 + root.render(null) 185 + t.assert.deepStrictEqual(sequence, ['cleanup']) 186 + }) 234 187 235 - it('can access the dom in callback', () => { 236 - const { root } = setup() 188 + test('onMount can access the dom in callback', (t: TestContext) => { 189 + const { root } = setup() 237 190 238 - const app = { 239 - render() { 240 - onMount(this, () => { 241 - const parent = getParentNode(this) as Element 242 - expect(parent.firstElementChild).toBeInstanceOf(HTMLParagraphElement) 243 - }) 244 - return html`<p>Hello, world!</p>` 245 - }, 246 - } 191 + const app = { 192 + render() { 193 + onMount(this, () => { 194 + const parent = getParentNode(this) as Element 195 + t.assert.ok(parent.firstElementChild instanceof HTMLParagraphElement) 196 + }) 197 + return html`<p>Hello, world!</p>` 198 + }, 199 + } 247 200 248 - root.render(app) 249 - }) 201 + root.render(app) 202 + }) 250 203 251 - it('works after render', () => { 252 - const { root } = setup() 204 + test('onMount works after render', (t: TestContext) => { 205 + const { root } = setup() 253 206 254 - const app = { 255 - render() { 256 - return 'app' 257 - }, 258 - } 207 + const app = { 208 + render() { 209 + return 'app' 210 + }, 211 + } 259 212 260 - root.render(app) 213 + root.render(app) 261 214 262 - const mounted = vi.fn() 263 - onMount(app, mounted) 264 - expect(mounted).toHaveBeenCalledOnce() 265 - }) 215 + const mounted = t.mock.fn() 216 + onMount(app, mounted) 217 + t.assert.strictEqual(mounted.mock.callCount(), 1) 266 218 }) 267 219 268 - describe('onUnmount', () => { 269 - it('unmount deep', () => { 270 - const { root, el } = setup() 220 + test('onUnmount deep works correctly', (t: TestContext) => { 221 + const { root, el } = setup() 271 222 272 - const sequence: string[] = [] 223 + const sequence: string[] = [] 273 224 274 - const inner = { 275 - attached: false, 276 - render() { 277 - sequence.push('inner render') 278 - if (!this.attached) { 279 - this.attached = true 280 - onUnmount(this, () => { 281 - this.attached = false 282 - sequence.push('inner abort') 283 - }) 284 - } 285 - return 'inner' 286 - }, 287 - } 225 + const inner = { 226 + attached: false, 227 + render() { 228 + sequence.push('inner render') 229 + if (!this.attached) { 230 + this.attached = true 231 + onUnmount(this, () => { 232 + this.attached = false 233 + sequence.push('inner abort') 234 + }) 235 + } 236 + return 'inner' 237 + }, 238 + } 288 239 289 - const outer = { 290 - attached: false, 291 - show: true, 292 - render() { 293 - sequence.push('outer render') 294 - if (!this.attached) { 295 - this.attached = true 296 - onUnmount(this, () => { 297 - this.attached = false 298 - sequence.push('outer abort') 299 - }) 300 - } 301 - if (!this.show) return null 302 - return inner 303 - }, 304 - } 240 + const outer = { 241 + attached: false, 242 + show: true, 243 + render() { 244 + sequence.push('outer render') 245 + if (!this.attached) { 246 + this.attached = true 247 + onUnmount(this, () => { 248 + this.attached = false 249 + sequence.push('outer abort') 250 + }) 251 + } 252 + if (!this.show) return null 253 + return inner 254 + }, 255 + } 305 256 306 - outer.show = true 307 - root.render(outer) 308 - expect(el.innerHTML).toBe('inner') 309 - expect(sequence).toMatchInlineSnapshot(` 310 - [ 311 - "outer render", 312 - "inner render", 313 - ] 314 - `) 315 - sequence.length = 0 257 + outer.show = true 258 + root.render(outer) 259 + t.assert.strictEqual(el.innerHTML, 'inner') 260 + t.assert.deepStrictEqual(sequence, ['outer render', 'inner render']) 261 + sequence.length = 0 316 262 317 - outer.show = false 318 - root.render(outer) 319 - expect(el.innerHTML).toBe('') 320 - expect(sequence).toMatchInlineSnapshot(` 321 - [ 322 - "outer render", 323 - "inner abort", 324 - ] 325 - `) 326 - sequence.length = 0 263 + outer.show = false 264 + root.render(outer) 265 + t.assert.strictEqual(el.innerHTML, '') 266 + t.assert.deepStrictEqual(sequence, ['outer render', 'inner abort']) 267 + sequence.length = 0 327 268 328 - outer.show = true 329 - root.render(outer) 330 - expect(el.innerHTML).toBe('inner') 331 - expect(sequence).toMatchInlineSnapshot(` 332 - [ 333 - "outer render", 334 - "inner render", 335 - ] 336 - `) 337 - sequence.length = 0 269 + outer.show = true 270 + root.render(outer) 271 + t.assert.strictEqual(el.innerHTML, 'inner') 272 + t.assert.deepStrictEqual(sequence, ['outer render', 'inner render']) 273 + sequence.length = 0 338 274 339 - outer.show = false 340 - root.render(outer) 341 - expect(el.innerHTML).toBe('') 342 - expect(sequence).toMatchInlineSnapshot(` 343 - [ 344 - "outer render", 345 - "inner abort", 346 - ] 347 - `) 348 - sequence.length = 0 349 - }) 275 + outer.show = false 276 + root.render(outer) 277 + t.assert.strictEqual(el.innerHTML, '') 278 + t.assert.deepStrictEqual(sequence, ['outer render', 'inner abort']) 279 + sequence.length = 0 280 + }) 350 281 351 - it('unmount shallow', () => { 352 - const { root, el } = setup() 282 + test('onUnmount shallow works correctly', (t: TestContext) => { 283 + const { root, el } = setup() 353 284 354 - const sequence: string[] = [] 285 + const sequence: string[] = [] 355 286 356 - const inner = { 357 - attached: false, 358 - render() { 359 - sequence.push('inner render') 360 - if (!this.attached) { 361 - this.attached = true 362 - onUnmount(this, () => { 363 - this.attached = false 364 - sequence.push('inner abort') 365 - }) 366 - } 367 - return 'inner' 368 - }, 369 - } 287 + const inner = { 288 + attached: false, 289 + render() { 290 + sequence.push('inner render') 291 + if (!this.attached) { 292 + this.attached = true 293 + onUnmount(this, () => { 294 + this.attached = false 295 + sequence.push('inner abort') 296 + }) 297 + } 298 + return 'inner' 299 + }, 300 + } 370 301 371 - const outer = { 372 - attached: false, 373 - show: true, 374 - render() { 375 - sequence.push('outer render') 376 - if (!this.attached) { 377 - this.attached = true 378 - onUnmount(this, () => { 379 - this.attached = false 380 - sequence.push('outer abort') 381 - }) 382 - } 383 - return html`${this.show ? inner : null}` 384 - }, 385 - } 302 + const outer = { 303 + attached: false, 304 + show: true, 305 + render() { 306 + sequence.push('outer render') 307 + if (!this.attached) { 308 + this.attached = true 309 + onUnmount(this, () => { 310 + this.attached = false 311 + sequence.push('outer abort') 312 + }) 313 + } 314 + return html`${this.show ? inner : null}` 315 + }, 316 + } 386 317 387 - outer.show = true 388 - root.render(outer) 389 - expect(el.innerHTML).toBe('inner') 390 - expect(sequence).toMatchInlineSnapshot(` 391 - [ 392 - "outer render", 393 - "inner render", 394 - ] 395 - `) 396 - sequence.length = 0 318 + outer.show = true 319 + root.render(outer) 320 + t.assert.strictEqual(el.innerHTML, 'inner') 321 + t.assert.deepStrictEqual(sequence, ['outer render', 'inner render']) 322 + sequence.length = 0 397 323 398 - outer.show = false 399 - root.render(outer) 400 - expect(el.innerHTML).toBe('') 401 - expect(sequence).toMatchInlineSnapshot(` 402 - [ 403 - "outer render", 404 - "inner abort", 405 - ] 406 - `) 407 - sequence.length = 0 324 + outer.show = false 325 + root.render(outer) 326 + t.assert.strictEqual(el.innerHTML, '') 327 + t.assert.deepStrictEqual(sequence, ['outer render', 'inner abort']) 328 + sequence.length = 0 408 329 409 - outer.show = true 410 - root.render(outer) 411 - expect(el.innerHTML).toBe('inner') 412 - expect(sequence).toMatchInlineSnapshot(` 413 - [ 414 - "outer render", 415 - "inner render", 416 - ] 417 - `) 418 - sequence.length = 0 330 + outer.show = true 331 + root.render(outer) 332 + t.assert.strictEqual(el.innerHTML, 'inner') 333 + t.assert.deepStrictEqual(sequence, ['outer render', 'inner render']) 334 + sequence.length = 0 419 335 420 - outer.show = false 421 - root.render(outer) 422 - expect(el.innerHTML).toBe('') 423 - expect(sequence).toMatchInlineSnapshot(` 424 - [ 425 - "outer render", 426 - "inner abort", 427 - ] 428 - `) 429 - sequence.length = 0 430 - }) 336 + outer.show = false 337 + root.render(outer) 338 + t.assert.strictEqual(el.innerHTML, '') 339 + t.assert.deepStrictEqual(sequence, ['outer render', 'inner abort']) 340 + sequence.length = 0 341 + }) 431 342 432 - it('works externally', async () => { 433 - const { root, el } = setup() 343 + test('onUnmount works externally', async (t: TestContext) => { 344 + const { root, el } = setup() 434 345 435 - const app = { 436 - render() { 437 - return [1, 2, 3].map(i => html`<div>${i}</div>`) 438 - }, 439 - } 346 + const app = { 347 + render() { 348 + return [1, 2, 3].map(i => html`<div>${i}</div>`) 349 + }, 350 + } 440 351 441 - const unmounted = vi.fn() 442 - onUnmount(app, unmounted) 352 + const unmounted = t.mock.fn() 353 + onUnmount(app, unmounted) 443 354 444 - root.render(app) 445 - expect(el.innerHTML).toMatchInlineSnapshot(`"<div>1</div><div>2</div><div>3</div>"`) 446 - expect(unmounted).not.toHaveBeenCalled() 355 + root.render(app) 356 + t.assert.strictEqual(el.innerHTML, '<div>1</div><div>2</div><div>3</div>') 357 + t.assert.strictEqual(unmounted.mock.callCount(), 0) 447 358 448 - root.render(null) 449 - expect(unmounted).toHaveBeenCalledOnce() 450 - }) 359 + root.render(null) 360 + t.assert.strictEqual(unmounted.mock.callCount(), 1) 451 361 }) 452 362 453 - describe('getParentNode', () => { 454 - it('works externally', () => { 455 - const { root, el } = setup() 363 + test('getParentNode works externally', (t: TestContext) => { 364 + const { root, el } = setup() 456 365 457 - const app = { 458 - render() { 459 - return html`<div></div>` 460 - }, 461 - } 366 + const app = { 367 + render() { 368 + return html`<div></div>` 369 + }, 370 + } 462 371 463 - root.render(app) 464 - expect(el.innerHTML).toBe('<div></div>') 465 - expect(getParentNode(app)).toBe(el) 466 - }) 372 + root.render(app) 373 + t.assert.strictEqual(el.innerHTML, '<div></div>') 374 + t.assert.strictEqual(getParentNode(app), el) 375 + }) 467 376 468 - it('works internally', () => { 469 - const { root, el } = setup() 470 - 471 - root.render({ 472 - render() { 473 - return html`<div>${getParentNode(this) === el}</div>` 474 - }, 475 - } satisfies Renderable) 377 + test('getParentNode works internally', (t: TestContext) => { 378 + const { root, el } = setup() 476 379 477 - expect(el.innerHTML).toBe('<div>true</div>') 478 - }) 380 + root.render({ 381 + render() { 382 + return html`<div>${getParentNode(this) === el}</div>` 383 + }, 384 + } satisfies Renderable) 479 385 480 - it('handles nesting', () => { 481 - const { root, el } = setup() 386 + t.assert.strictEqual(el.innerHTML, '<div>true</div>') 387 + }) 482 388 483 - const inner = { 484 - render() { 485 - const parent = getParentNode(this) 389 + test('getParentNode handles nesting', (t: TestContext) => { 390 + const { root, el } = setup() 486 391 487 - expect(parent).toBeInstanceOf(HTMLDivElement) 488 - expect((parent as HTMLDivElement).outerHTML).toMatchInlineSnapshot(`"<div class="the-app"><!----></div>"`) 489 - expect(parent.parentNode).toBe(el) 392 + const inner = { 393 + render() { 394 + const parent = getParentNode(this) 490 395 491 - return null 492 - }, 493 - } 396 + t.assert.ok(parent instanceof HTMLDivElement) 397 + t.assert.strictEqual((parent as HTMLDivElement).outerHTML, '<div class="the-app"><!----></div>') 398 + t.assert.strictEqual(parent.parentNode, el) 494 399 495 - const spy = vi.spyOn(inner, 'render') 400 + return null 401 + }, 402 + } 496 403 497 - root.render({ 498 - render() { 499 - return html`<div class="the-app">${inner}</div>` 500 - }, 501 - }) 404 + const spy = t.mock.fn(inner.render) 405 + inner.render = spy 502 406 503 - expect(spy).toHaveBeenCalledOnce() 407 + root.render({ 408 + render() { 409 + return html`<div class="the-app">${inner}</div>` 410 + }, 504 411 }) 412 + 413 + t.assert.strictEqual(spy.mock.callCount(), 1) 505 414 })
+6 -16
src/client/tests/setup.ts
··· 1 - /// <reference types='vite/client' /> 2 - /// <reference types='@vitest/browser/providers/playwright' /> 1 + import { GlobalRegistrator } from '@happy-dom/global-registrator' 2 + import { createRoot, type Root } from 'dhtml/client' 3 + import { afterEach } from 'node:test' 3 4 4 - import '../../../reset.css' 5 - 6 - import { createRoot, type Root } from 'dhtml/client' 7 - import { afterEach, expect } from 'vitest' 5 + GlobalRegistrator.register() 6 + globalThis.__DEV__ = process.env.NODE_ENV !== 'production' 8 7 9 8 const roots: Root[] = [] 10 9 11 10 export function setup(initialHtml = ''): { root: Root; el: HTMLDivElement } { 12 - const state = expect.getState() 13 - const parentEl = document.createElement('div') 14 - Object.assign(parentEl.style, { 15 - border: '1px solid black', 16 - padding: '0.5em', 17 - margin: '0.5em', 18 - }) 19 - parentEl.appendChild(document.createElement('small')).textContent = state.currentTestName ?? 'test' 20 - 21 11 const el = document.createElement('div') 22 12 el.innerHTML = initialHtml 23 - document.body.appendChild(parentEl).appendChild(el) 13 + document.body.appendChild(el) 24 14 25 15 const root = createRoot(el) 26 16 roots.push(root)
-2
src/client/util.ts
··· 1 - import type { Renderable } from '../shared.ts' 2 - 3 1 export type Cleanup = (() => void) | void | undefined | null 4 2 5 3 export const is_element = (node: Node): node is Element => node.nodeType === (1 satisfies typeof Node.ELEMENT_NODE)
+1 -3
src/shared.ts
··· 14 14 typeof value === 'object' && value !== null && Symbol.iterator in value 15 15 16 16 declare global { 17 - const __DEV__: boolean 17 + var __DEV__: boolean 18 18 } 19 19 20 - /* v8 ignore start */ 21 20 export function assert(value: unknown, message?: string): asserts value { 22 21 if (!__DEV__) return 23 22 if (!value) throw new Error(message ?? 'assertion failed') 24 23 } 25 - /* v8 ignore stop */ 26 24 27 25 const tag: unique symbol = Symbol() 28 26
+2 -2
tsconfig.json
··· 4 4 "noImplicitAny": true, 5 5 "skipLibCheck": true, 6 6 "noEmit": true, 7 - "module": "es2020", 7 + "module": "nodenext", 8 8 "target": "es2020", 9 9 "verbatimModuleSyntax": true, 10 - "moduleResolution": "bundler", 10 + "moduleResolution": "nodenext", 11 11 "allowImportingTsExtensions": true, 12 12 "stripInternal": true, 13 13 "isolatedDeclarations": true,
-24
vitest.config.js
··· 1 - import { defineConfig } from 'vitest/config' 2 - 3 - export default defineConfig({ 4 - define: { 5 - __DEV__: !process.env.PROD, 6 - }, 7 - test: { 8 - clearMocks: true, 9 - coverage: { 10 - enabled: true, 11 - reporter: ['text', 'json-summary', 'json', 'html'], 12 - reportOnFailure: true, 13 - include: ['src/client/**', 'src/client.ts'], 14 - exclude: ['**/tests'], 15 - }, 16 - browser: { 17 - enabled: true, 18 - headless: true, 19 - screenshotFailures: false, 20 - provider: 'playwright', 21 - instances: [{ browser: 'chromium' }], 22 - }, 23 - }, 24 - })