├── .prettierignore ├── src ├── error.ts ├── globals.d.ts ├── flags.ts ├── constants.ts ├── effect.ts ├── index.ts ├── owner.ts └── core.ts ├── README.md ├── .gitignore ├── .prettierrc.json ├── tsconfig.build.json ├── vite.config.ts ├── vitest_gc.js ├── tsconfig.json ├── tests ├── getOwner.test.ts ├── createSignal.test.ts ├── runWithOwner.test.ts ├── flushSync.test.ts ├── errorPropagation.test.ts ├── gc.test.ts ├── catchError.test.ts ├── createRoot.test.ts ├── untrack.test.ts ├── onCleanup.test.ts ├── createMemo.test.ts ├── async.test.ts ├── createEffect.test.ts └── graph.test.ts ├── LICENSE ├── .eslintrc.cjs ├── tsup.config.ts ├── package.json └── pnpm-lock.yaml /.prettierignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | coverage/ 3 | pnpm-lock.yaml 4 | -------------------------------------------------------------------------------- /src/error.ts: -------------------------------------------------------------------------------- 1 | export class NotReadyError extends Error {} 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Bubble Reactivity 2 | 3 | The start of something good. 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | dist/ 3 | coverage/ 4 | types/ 5 | sandbox/ 6 | .DS_STORE 7 | -------------------------------------------------------------------------------- /src/globals.d.ts: -------------------------------------------------------------------------------- 1 | declare global { 2 | const __DEV__: boolean 3 | } 4 | 5 | export {} 6 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "es5", 3 | "tabWidth": 2, 4 | "semi": false, 5 | "singleQuote": true 6 | } 7 | -------------------------------------------------------------------------------- /tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { "types": [] }, 3 | "extends": "./tsconfig.json", 4 | "include": ["src"] 5 | } 6 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vitest/config' 2 | 3 | export default defineConfig({ 4 | define: { 5 | __DEV__: 'true', 6 | }, 7 | test: { 8 | globals: true, 9 | }, 10 | }) 11 | -------------------------------------------------------------------------------- /vitest_gc.js: -------------------------------------------------------------------------------- 1 | import { createVitest } from 'vitest/node' 2 | import process from 'node:process' 3 | 4 | const vitest = await createVitest('test', { 5 | include: [`tests/gc.test.ts`], 6 | globals: true, 7 | watch: process.argv.includes('--watch'), 8 | }) 9 | 10 | await vitest.start() 11 | -------------------------------------------------------------------------------- /src/flags.ts: -------------------------------------------------------------------------------- 1 | export type Flags = number 2 | 3 | export const ERROR_OFFSET = 0 4 | export const ERROR_BIT = 1 << ERROR_OFFSET 5 | export const ERROR: unique symbol = Symbol('ERROR') 6 | 7 | export const LOADING_OFFSET = 1 8 | export const LOADING_BIT = 1 << LOADING_OFFSET 9 | export const LOADING: unique symbol = Symbol('LOADING') 10 | 11 | export const DEFAULT_FLAGS = ERROR_BIT 12 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "declaration": true, 4 | "emitDeclarationOnly": true, 5 | "target": "esnext", 6 | "newLine": "LF", 7 | "moduleResolution": "node", 8 | "noImplicitAny": true, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "outDir": "dist/types", 12 | "module": "esnext", 13 | "types": ["vitest/globals"] 14 | }, 15 | "include": ["src", "tests"], 16 | "exclude": ["dist", "node_modules"] 17 | } 18 | -------------------------------------------------------------------------------- /src/constants.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * See https://dev.to/modderme123/super-charging-fine-grained-reactive-performance-47ph 3 | * State clean corresponds to a node where all the sources are fully up to date 4 | * State check corresponds to a node where some sources (including grandparents) may have changed 5 | * State dirty corresponds to a node where the direct parents of a node has changed 6 | */ 7 | export const STATE_CLEAN = 0 8 | export const STATE_CHECK = 1 9 | export const STATE_DIRTY = 2 10 | export const STATE_DISPOSED = 3 11 | -------------------------------------------------------------------------------- /tests/getOwner.test.ts: -------------------------------------------------------------------------------- 1 | import { createEffect, createRoot, getOwner, untrack } from '../src' 2 | 3 | it('should return current owner', () => { 4 | createRoot(() => { 5 | const owner = getOwner() 6 | expect(owner).toBeDefined() 7 | createEffect(() => { 8 | expect(getOwner()).toBeDefined() 9 | expect(getOwner()).not.toBe(owner) 10 | }) 11 | }) 12 | }) 13 | 14 | it('should return parent scope from inside untrack', () => { 15 | createRoot(() => { 16 | untrack(() => { 17 | expect(getOwner()).toBeDefined() 18 | }) 19 | }) 20 | }) 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Portions of this code fall under the following license: 2 | 3 | ISC License 4 | 5 | Copyright 2023 SolidJS Team 6 | 7 | Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 10 | -------------------------------------------------------------------------------- /tests/createSignal.test.ts: -------------------------------------------------------------------------------- 1 | import { createSignal, flushSync } from '../src' 2 | 3 | afterEach(() => flushSync()) 4 | 5 | it('should store and return value on read', () => { 6 | const [$x] = createSignal(1) 7 | expect($x).toBeInstanceOf(Function) 8 | expect($x()).toBe(1) 9 | }) 10 | 11 | it('should update signal via setter', () => { 12 | const [$x, setX] = createSignal(1) 13 | setX(2) 14 | expect($x()).toBe(2) 15 | }) 16 | 17 | it('should accept equals option', () => { 18 | const [$x, setX] = createSignal(1, { 19 | // Skip even numbers. 20 | equals: (prev, next) => prev + 1 === next, 21 | }) 22 | 23 | setX(11) 24 | expect($x()).toBe(11) 25 | 26 | setX(12) 27 | expect($x()).toBe(11) 28 | 29 | setX(13) 30 | expect($x()).toBe(13) 31 | 32 | setX(14) 33 | expect($x()).toBe(13) 34 | }) 35 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: [ 3 | 'prettier', 4 | 'eslint:recommended', 5 | 'plugin:@typescript-eslint/recommended', 6 | 'plugin:@typescript-eslint/recommended-requiring-type-checking', 7 | ], 8 | 9 | parser: '@typescript-eslint/parser', 10 | plugins: ['@typescript-eslint', 'simple-import-sort'], 11 | 12 | ignorePatterns: [ 13 | 'dist', 14 | 'coverage', 15 | 'vite.config.ts', 16 | 'vitest.js', 17 | 'tsup.config.ts', 18 | '.eslintrc.cjs', 19 | ], 20 | 21 | parserOptions: { 22 | project: './tsconfig.json', 23 | }, 24 | 25 | rules: { 26 | '@typescript-eslint/no-non-null-assertion': 'off', 27 | 'one-var': ['warn', 'never'], 28 | 'sort-imports': 'off', 29 | 'simple-import-sort/imports': 'error', 30 | 'simple-import-sort/exports': 'error', 31 | }, 32 | } 33 | -------------------------------------------------------------------------------- /tests/runWithOwner.test.ts: -------------------------------------------------------------------------------- 1 | import { catchError, createRoot, getOwner, runWithOwner } from '../src' 2 | import { Owner } from '../src/owner' 3 | 4 | it('should scope function to current scope', () => { 5 | let owner!: Owner | null 6 | 7 | createRoot(() => { 8 | owner = getOwner()! 9 | owner._context = { foo: 1 } 10 | }) 11 | 12 | runWithOwner(owner, () => { 13 | expect(getOwner()!._context?.foo).toBe(1) 14 | }) 15 | }) 16 | 17 | it('should return value', () => { 18 | expect(runWithOwner(null, () => 100)).toBe(100) 19 | }) 20 | 21 | it('should handle errors', () => { 22 | const error = new Error() 23 | const handler = vi.fn() 24 | 25 | let owner!: Owner | null 26 | catchError(() => { 27 | owner = getOwner() 28 | }, handler) 29 | 30 | runWithOwner(owner, () => { 31 | throw error 32 | }) 33 | 34 | expect(handler).toHaveBeenCalledWith(error) 35 | }) 36 | -------------------------------------------------------------------------------- /tsup.config.ts: -------------------------------------------------------------------------------- 1 | import { type Options, defineConfig } from 'tsup' 2 | 3 | interface BundleOptions { 4 | dev?: boolean 5 | node?: boolean 6 | } 7 | 8 | function options({ dev, node }: BundleOptions): Options { 9 | return { 10 | entry: { 11 | [node ? 'node' : dev ? 'dev' : 'prod']: 'src/index.ts', 12 | }, 13 | outDir: 'dist', 14 | treeshake: true, 15 | bundle: true, 16 | format: node ? 'cjs' : 'esm', 17 | // minify: true, 18 | platform: node ? 'node' : 'browser', 19 | target: node ? 'node16' : 'esnext', 20 | define: { 21 | __DEV__: dev ? 'true' : 'false', 22 | }, 23 | esbuildOptions(opts) { 24 | opts.mangleProps = !dev ? /^_/ : undefined 25 | }, 26 | } 27 | } 28 | 29 | export default defineConfig([ 30 | options({ dev: true }), // dev 31 | options({ dev: false }), // prod 32 | options({ node: true }), // server 33 | ]) 34 | -------------------------------------------------------------------------------- /tests/flushSync.test.ts: -------------------------------------------------------------------------------- 1 | import { createEffect, createSignal, flushSync } from '../src' 2 | 3 | afterEach(() => flushSync()) 4 | 5 | it('should batch updates', () => { 6 | const [$x, setX] = createSignal(10) 7 | const effect = vi.fn($x) 8 | 9 | createEffect(effect) 10 | flushSync() 11 | 12 | setX(20) 13 | setX(30) 14 | setX(40) 15 | 16 | expect(effect).to.toHaveBeenCalledTimes(1) 17 | flushSync() 18 | expect(effect).to.toHaveBeenCalledTimes(2) 19 | }) 20 | 21 | it('should wait for queue to flush', () => { 22 | const [$x, setX] = createSignal(10) 23 | const $effect = vi.fn($x) 24 | 25 | createEffect($effect) 26 | flushSync() 27 | 28 | expect($effect).to.toHaveBeenCalledTimes(1) 29 | 30 | setX(20) 31 | flushSync() 32 | expect($effect).to.toHaveBeenCalledTimes(2) 33 | 34 | setX(30) 35 | flushSync() 36 | expect($effect).to.toHaveBeenCalledTimes(3) 37 | }) 38 | 39 | it('should not fail if called while flushing', () => { 40 | const [$a, setA] = createSignal(10) 41 | 42 | const effect = vi.fn(() => { 43 | $a() 44 | flushSync() 45 | }) 46 | 47 | createEffect(effect) 48 | flushSync() 49 | 50 | expect(effect).to.toHaveBeenCalledTimes(1) 51 | 52 | setA(20) 53 | flushSync() 54 | expect(effect).to.toHaveBeenCalledTimes(2) 55 | }) 56 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bubble-reactivity", 3 | "version": "0.0.0", 4 | "description": "", 5 | "license": "ISC", 6 | "type": "module", 7 | "main": "dist/node.cjs", 8 | "module": "dist/prod.js", 9 | "types": "dist/types/index.d.ts", 10 | "files": [ 11 | "dist" 12 | ], 13 | "scripts": { 14 | "build": "rimraf dist && tsup && pnpm types", 15 | "types": "tsc -p tsconfig.build.json", 16 | "format": "prettier --write .", 17 | "test": "vitest", 18 | "lint": "eslint .", 19 | "gc": "node --expose-gc ./vitest_gc.js", 20 | "coverage": "vitest run --coverage" 21 | }, 22 | "devDependencies": { 23 | "@types/node": "^24.0.14", 24 | "@typescript-eslint/eslint-plugin": "^8.37.0", 25 | "@typescript-eslint/parser": "^8.37.0", 26 | "@vitest/coverage-v8": "^3.2.4", 27 | "eslint": "^9.31.0", 28 | "eslint-config-prettier": "^10.1.8", 29 | "eslint-plugin-simple-import-sort": "^12.1.1", 30 | "prettier": "^3.6.2", 31 | "rimraf": "^6.0.1", 32 | "tsup": "^8.5.0", 33 | "typescript": "5.8.3", 34 | "vite": "^7.0.5", 35 | "vitest": "^3.2.4" 36 | }, 37 | "exports": { 38 | ".": { 39 | "types": "./dist/types/index.d.ts", 40 | "import": { 41 | "test": "./dist/dev.js", 42 | "development": "./dist/dev.js", 43 | "default": "./dist/prod.js" 44 | }, 45 | "require": "./dist/node.cjs" 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /tests/errorPropagation.test.ts: -------------------------------------------------------------------------------- 1 | import { Computation } from '../src/core' 2 | import { Effect, flushSync } from '../src/effect' 3 | 4 | it('should propagate errors through memos', () => { 5 | const m = new Computation(undefined, () => { 6 | throw new Error('test') 7 | }) 8 | const c = new Computation(undefined, () => { 9 | return m.read() 10 | }) 11 | expect(() => c.read()).toThrowError('test') 12 | }) 13 | 14 | it('should recover from errors', () => { 15 | const errorThrower = vi.fn(() => { 16 | throw new Error('test') 17 | }) 18 | const s = new Computation(1, null) 19 | const m = new Computation(undefined, () => { 20 | if (s.read() === 1) errorThrower() 21 | else return 2 22 | }) 23 | const c = new Computation(undefined, () => { 24 | return m.read() 25 | }) 26 | expect(() => c.read()).toThrowError('test') 27 | s.write(2) 28 | expect(() => c.read()).not.toThrow() 29 | }) 30 | 31 | it('should subscribe to errors', () => { 32 | const errorThrower = vi.fn(() => { 33 | throw new Error('test') 34 | }) 35 | const s = new Computation(false, null) 36 | const m = new Computation(undefined, () => { 37 | if (s.read()) errorThrower() 38 | else return 2 39 | }) 40 | let errored = false 41 | new Effect(undefined, () => { 42 | errored = m.error() 43 | }) 44 | flushSync() 45 | expect(errored).toBe(false) 46 | s.write(true) 47 | flushSync() 48 | expect(errored).toBe(true) 49 | }) 50 | -------------------------------------------------------------------------------- /tests/gc.test.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-unused-vars */ 2 | import { 3 | Accessor, 4 | createEffect, 5 | createMemo, 6 | createRoot, 7 | createSignal, 8 | flushSync, 9 | getOwner, 10 | } from '../src' 11 | import { Owner } from '../src/owner' 12 | 13 | function gc() { 14 | return new Promise((resolve) => 15 | setTimeout(() => { 16 | flushSync() // flush call stack (holds a reference) 17 | globalThis.gc!() 18 | resolve(void 0) 19 | }, 0) 20 | ) 21 | } 22 | 23 | if (globalThis.gc) { 24 | it('should gc computed if there are no observers', async () => { 25 | const [$x] = createSignal(0) 26 | const ref = new WeakRef(createMemo(() => $x())) 27 | 28 | await gc() 29 | expect(ref.deref()).toBeUndefined() 30 | }) 31 | 32 | it('should _not_ gc computed if there are observers', async () => { 33 | const [$x] = createSignal(0) 34 | let pointer 35 | 36 | const ref = new WeakRef((pointer = createMemo(() => $x()))) 37 | 38 | ref.deref()!() 39 | 40 | await gc() 41 | expect(ref.deref()).toBeDefined() 42 | 43 | pointer = undefined 44 | await gc() 45 | expect(ref.deref()).toBeUndefined() 46 | }) 47 | 48 | it('should gc root if disposed', async () => { 49 | const [$x] = createSignal(0) 50 | let ref!: WeakRef> 51 | let pointer 52 | 53 | const dispose = createRoot((dispose) => { 54 | ref = new WeakRef( 55 | (pointer = createMemo(() => { 56 | $x() 57 | })) 58 | ) 59 | 60 | return dispose 61 | }) 62 | 63 | await gc() 64 | expect(ref.deref()).toBeDefined() 65 | 66 | dispose() 67 | await gc() 68 | expect(ref.deref()).toBeDefined() 69 | 70 | pointer = undefined 71 | await gc() 72 | expect(ref.deref()).toBeUndefined() 73 | }) 74 | 75 | it('should gc effect lazily', async () => { 76 | const [$x, setX] = createSignal(0) 77 | let ref!: WeakRef 78 | 79 | const dispose = createRoot((dispose) => { 80 | createEffect(() => { 81 | $x() 82 | ref = new WeakRef(getOwner()!) 83 | }) 84 | 85 | return dispose 86 | }) 87 | 88 | await gc() 89 | expect(ref.deref()).toBeDefined() 90 | 91 | dispose() 92 | setX(1) 93 | 94 | await gc() 95 | expect(ref.deref()).toBeUndefined() 96 | }) 97 | } else { 98 | it('', () => { 99 | // Ignored when there is no access to global.gc 100 | }) 101 | } 102 | -------------------------------------------------------------------------------- /tests/catchError.test.ts: -------------------------------------------------------------------------------- 1 | import { 2 | catchError, 3 | createEffect, 4 | createRoot, 5 | createSignal, 6 | flushSync, 7 | } from '../src' 8 | 9 | it('should let errors bubble up when not handled', () => { 10 | const error = new Error() 11 | expect(() => { 12 | createRoot(() => { 13 | createEffect(() => { 14 | throw error 15 | }) 16 | }) 17 | flushSync() 18 | }).toThrowError(error) 19 | }) 20 | 21 | it('should handle error', () => { 22 | const error = new Error() 23 | const handler = vi.fn() 24 | 25 | catchError(() => { 26 | throw error 27 | }, handler) 28 | 29 | expect(handler).toHaveBeenCalledWith(error) 30 | }) 31 | 32 | it('should forward error to another handler', () => { 33 | const error = new Error() 34 | const rootHandler = vi.fn() 35 | 36 | const [$x, setX] = createSignal(0) 37 | 38 | catchError(() => { 39 | createEffect(() => { 40 | $x() 41 | catchError( 42 | () => { 43 | throw error 44 | }, 45 | (e) => { 46 | expect(e).toBe(error) 47 | throw e 48 | } 49 | ) 50 | }) 51 | }, rootHandler) 52 | 53 | flushSync() 54 | expect(rootHandler).toHaveBeenCalledWith(error) 55 | 56 | setX(1) 57 | flushSync() 58 | expect(rootHandler).toHaveBeenCalledTimes(2) 59 | }) 60 | 61 | it('should not duplicate error handler', () => { 62 | const error = new Error() 63 | const handler = vi.fn() 64 | 65 | const [$x, setX] = createSignal(0) 66 | let shouldThrow = false 67 | 68 | createEffect(() => { 69 | $x() 70 | catchError(() => { 71 | if (shouldThrow) throw error 72 | }, handler) 73 | }) 74 | 75 | setX(1) 76 | flushSync() 77 | 78 | shouldThrow = true 79 | setX(2) 80 | flushSync() 81 | expect(handler).toHaveBeenCalledTimes(1) 82 | }) 83 | 84 | it('should not trigger wrong handler', () => { 85 | const error = new Error() 86 | const rootHandler = vi.fn() 87 | const handler = vi.fn() 88 | 89 | const [$x, setX] = createSignal(0) 90 | let shouldThrow = false 91 | 92 | createRoot(() => { 93 | catchError(() => { 94 | createEffect(() => { 95 | $x() 96 | if (shouldThrow) throw error 97 | }) 98 | 99 | createEffect(() => { 100 | catchError(() => { 101 | // no-op 102 | }, handler) 103 | }) 104 | }, rootHandler) 105 | }) 106 | 107 | flushSync() 108 | shouldThrow = true 109 | setX(1) 110 | flushSync() 111 | 112 | expect(rootHandler).toHaveBeenCalledWith(error) 113 | expect(handler).not.toHaveBeenCalledWith(error) 114 | }) 115 | -------------------------------------------------------------------------------- /tests/createRoot.test.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Accessor, 3 | Signal, 4 | createEffect, 5 | createMemo, 6 | createRoot, 7 | createSignal, 8 | flushSync, 9 | getOwner, 10 | onCleanup, 11 | } from '../src' 12 | import { Computation } from '../src/core' 13 | 14 | afterEach(() => flushSync()) 15 | 16 | it('should dispose of inner computations', () => { 17 | let $x: Signal 18 | let $y: Accessor 19 | 20 | const memo = vi.fn(() => $x[0]() + 10) 21 | 22 | createRoot((dispose) => { 23 | $x = createSignal(10) 24 | $y = createMemo(memo) 25 | expect($y()).toBe(20) 26 | dispose() 27 | }) 28 | 29 | expect(() => $y!()).toThrow() 30 | expect(memo).toHaveBeenCalledTimes(1) 31 | 32 | flushSync() 33 | 34 | $x![1](50) 35 | flushSync() 36 | 37 | expect(() => $y!()).toThrow() 38 | expect(memo).toHaveBeenCalledTimes(1) 39 | }) 40 | 41 | it('should return result', () => { 42 | const result = createRoot((dispose) => { 43 | dispose() 44 | return 10 45 | }) 46 | 47 | expect(result).toBe(10) 48 | }) 49 | 50 | it('should create new tracking scope', () => { 51 | const [$x, setX] = createSignal(0) 52 | const effect = vi.fn((n: number) => n) 53 | 54 | const stopEffect = createRoot((dispose) => { 55 | createEffect(() => { 56 | $x() 57 | createRoot(() => void createEffect(() => effect($x()))) 58 | }) 59 | 60 | return dispose 61 | }) 62 | 63 | flushSync() 64 | expect(effect).toHaveBeenCalledWith(0) 65 | expect(effect).toHaveBeenCalledTimes(1) 66 | 67 | stopEffect() 68 | 69 | setX(10) 70 | flushSync() 71 | expect(effect).not.toHaveBeenCalledWith(10) 72 | expect(effect).toHaveBeenCalledTimes(1) 73 | }) 74 | 75 | it('should not be reactive', () => { 76 | let $x: Signal 77 | 78 | const root = vi.fn() 79 | 80 | createRoot(() => { 81 | $x = createSignal(0) 82 | $x[0]() 83 | root() 84 | }) 85 | 86 | expect(root).toHaveBeenCalledTimes(1) 87 | 88 | $x![1](1) 89 | flushSync() 90 | expect(root).toHaveBeenCalledTimes(1) 91 | }) 92 | 93 | it('should hold parent tracking', () => { 94 | createRoot(() => { 95 | const parent = getOwner() 96 | createRoot(() => { 97 | expect(getOwner()!._parent).toBe(parent) 98 | }) 99 | }) 100 | }) 101 | 102 | it('should not observe', () => { 103 | const [$x] = createSignal(0) 104 | createRoot(() => { 105 | $x() 106 | const owner = getOwner() as Computation 107 | expect(owner._sources).toBeUndefined() 108 | expect(owner._observers).toBeUndefined() 109 | }) 110 | }) 111 | 112 | it('should not throw if dispose called during active disposal process', () => { 113 | createRoot((dispose) => { 114 | onCleanup(() => dispose()) 115 | dispose() 116 | }) 117 | }) 118 | -------------------------------------------------------------------------------- /tests/untrack.test.ts: -------------------------------------------------------------------------------- 1 | import { 2 | createEffect, 3 | createMemo, 4 | createRoot, 5 | createSignal, 6 | flushSync, 7 | onCleanup, 8 | untrack, 9 | } from '../src' 10 | 11 | afterEach(() => flushSync()) 12 | 13 | it('should not create dependency', () => { 14 | const effect = vi.fn() 15 | const memo = vi.fn() 16 | 17 | const [$x, setX] = createSignal(10) 18 | 19 | const $a = createMemo(() => $x() + 10) 20 | const $b = createMemo(() => { 21 | memo() 22 | return untrack($a) + 10 23 | }) 24 | 25 | createEffect(() => { 26 | effect() 27 | expect(untrack($x)).toBe(10) 28 | expect(untrack($a)).toBe(20) 29 | expect(untrack($b)).toBe(30) 30 | }) 31 | 32 | flushSync() 33 | expect(effect).toHaveBeenCalledTimes(1) 34 | expect(memo).toHaveBeenCalledTimes(1) 35 | 36 | setX(20) 37 | flushSync() 38 | expect(effect).toHaveBeenCalledTimes(1) 39 | expect(memo).toHaveBeenCalledTimes(1) 40 | }) 41 | 42 | it('should not affect deep dependency being created', () => { 43 | const effect = vi.fn() 44 | const memo = vi.fn() 45 | 46 | const [$x, setX] = createSignal(10) 47 | const [$y, setY] = createSignal(10) 48 | const [$z, setZ] = createSignal(10) 49 | 50 | const $a = createMemo(() => { 51 | memo() 52 | return $x() + untrack($y) + untrack($z) + 10 53 | }) 54 | 55 | createEffect(() => { 56 | effect() 57 | expect(untrack($x)).toBe(10) 58 | expect(untrack($a)).toBe(40) 59 | }) 60 | 61 | flushSync() 62 | expect(effect).toHaveBeenCalledTimes(1) 63 | expect($a()).toBe(40) 64 | expect(memo).toHaveBeenCalledTimes(1) 65 | 66 | setX(20) 67 | flushSync() 68 | expect(effect).toHaveBeenCalledTimes(1) 69 | expect($a()).toBe(50) 70 | expect(memo).toHaveBeenCalledTimes(2) 71 | 72 | setY(20) 73 | flushSync() 74 | expect(effect).toHaveBeenCalledTimes(1) 75 | expect($a()).toBe(50) 76 | expect(memo).toHaveBeenCalledTimes(2) 77 | 78 | setZ(20) 79 | flushSync() 80 | expect(effect).toHaveBeenCalledTimes(1) 81 | expect($a()).toBe(50) 82 | expect(memo).toHaveBeenCalledTimes(2) 83 | }) 84 | 85 | it('should track owner across peeks', () => { 86 | const [$x, setX] = createSignal(0) 87 | 88 | const childCompute = vi.fn() 89 | const childDispose = vi.fn() 90 | 91 | function createChild() { 92 | const $a = createMemo(() => $x() * 2) 93 | createEffect(() => { 94 | childCompute($a()) 95 | onCleanup(childDispose) 96 | }) 97 | } 98 | 99 | const dispose = createRoot((dispose) => { 100 | untrack(() => createChild()) 101 | return dispose 102 | }) 103 | 104 | flushSync() 105 | setX(1) 106 | flushSync() 107 | expect(childCompute).toHaveBeenCalledWith(2) 108 | expect(childDispose).toHaveBeenCalledTimes(1) 109 | 110 | dispose() 111 | expect(childDispose).toHaveBeenCalledTimes(2) 112 | 113 | setX(2) 114 | flushSync() 115 | expect(childCompute).not.toHaveBeenCalledWith(4) 116 | expect(childDispose).toHaveBeenCalledTimes(2) 117 | }) 118 | -------------------------------------------------------------------------------- /src/effect.ts: -------------------------------------------------------------------------------- 1 | import { Computation, type MemoOptions } from './core' 2 | import { STATE_CLEAN, STATE_DISPOSED } from './constants' 3 | import type { Owner } from './owner' 4 | import { handleError } from './owner' 5 | 6 | let scheduledEffects = false 7 | let runningEffects = false 8 | let effects: Effect[] = [] 9 | 10 | /** 11 | * By default, changes are batched on the microtask queue which is an async process. You can flush the queue 12 | * synchronously to get the latest updates by calling `flushSync()`. 13 | */ 14 | export function flushSync(): void { 15 | if (!runningEffects) runEffects() 16 | } 17 | 18 | function flushEffects() { 19 | scheduledEffects = true 20 | queueMicrotask(runEffects) 21 | } 22 | 23 | /** 24 | * When re-executing nodes, we want to be extra careful to avoid double execution of nested owners 25 | * In particular, it is important that we check all of our parents to see if they will rerun 26 | * See tests/createEffect: "should run parent effect before child effect" and "should run parent memo before child effect" 27 | */ 28 | function runTop(node: Computation): void { 29 | const ancestors: Computation[] = [] 30 | 31 | for ( 32 | let current: Owner | null = node; 33 | current !== null; 34 | current = current._parent 35 | ) { 36 | if (current._state !== STATE_CLEAN) { 37 | ancestors.push(current as Computation) 38 | } 39 | } 40 | 41 | for (let i = ancestors.length - 1; i >= 0; i--) { 42 | if (ancestors[i]._state !== STATE_DISPOSED) 43 | ancestors[i]._updateIfNecessary() 44 | } 45 | } 46 | 47 | function runEffects() { 48 | if (!effects.length) { 49 | scheduledEffects = false 50 | return 51 | } 52 | 53 | runningEffects = true 54 | 55 | try { 56 | for (let i = 0; i < effects.length; i++) { 57 | if (effects[i]._state !== STATE_CLEAN) { 58 | runTop(effects[i]) 59 | } 60 | } 61 | } finally { 62 | effects = [] 63 | scheduledEffects = false 64 | runningEffects = false 65 | } 66 | } 67 | 68 | /** 69 | * Effects are the leaf nodes of our reactive graph. When their sources change, they are automatically 70 | * added to the queue of effects to re-execute, which will cause them to fetch their sources and recompute 71 | */ 72 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 73 | export class Effect extends Computation { 74 | constructor(initialValue: T, compute: () => T, options?: MemoOptions) { 75 | super(initialValue, compute, options) 76 | effects.push(this) 77 | } 78 | 79 | override _notify(state: number): void { 80 | if (this._state >= state) return 81 | 82 | if (this._state === STATE_CLEAN) { 83 | effects.push(this) 84 | if (!scheduledEffects) flushEffects() 85 | } 86 | 87 | this._state = state 88 | } 89 | 90 | override write(value: T): T { 91 | this._value = value 92 | 93 | return value 94 | } 95 | 96 | override _setError(error: unknown): void { 97 | handleError(this, error) 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /tests/onCleanup.test.ts: -------------------------------------------------------------------------------- 1 | import { createEffect, createRoot, flushSync, onCleanup } from '../src' 2 | 3 | afterEach(() => flushSync()) 4 | 5 | it('should be invoked when computation is disposed', () => { 6 | const disposeA = vi.fn() 7 | const disposeB = vi.fn() 8 | const disposeC = vi.fn() 9 | 10 | const stopEffect = createRoot((dispose) => { 11 | createEffect(() => { 12 | onCleanup(disposeA) 13 | onCleanup(disposeB) 14 | onCleanup(disposeC) 15 | }) 16 | 17 | return dispose 18 | }) 19 | 20 | flushSync() 21 | stopEffect() 22 | 23 | expect(disposeA).toHaveBeenCalled() 24 | expect(disposeB).toHaveBeenCalled() 25 | expect(disposeC).toHaveBeenCalled() 26 | }) 27 | 28 | it('should not trigger wrong onCleanup', () => { 29 | const dispose = vi.fn() 30 | 31 | createRoot(() => { 32 | createEffect(() => { 33 | onCleanup(dispose) 34 | }) 35 | 36 | const stopEffect = createRoot((dispose) => { 37 | createEffect(() => { 38 | // Empty effect with no disposal 39 | }) 40 | return dispose 41 | }) 42 | 43 | stopEffect() 44 | flushSync() 45 | 46 | expect(dispose).toHaveBeenCalledTimes(0) 47 | }) 48 | }) 49 | 50 | it('should clean up in reverse order', () => { 51 | const disposeParent = vi.fn<[number], void>() 52 | const disposeA = vi.fn<[number], void>() 53 | const disposeB = vi.fn<[number], void>() 54 | 55 | let calls = 0 56 | 57 | const stopEffect = createRoot((dispose) => { 58 | createEffect(() => { 59 | onCleanup(() => disposeParent(++calls)) 60 | 61 | createEffect(() => { 62 | onCleanup(() => disposeA(++calls)) 63 | }) 64 | 65 | createEffect(() => { 66 | onCleanup(() => disposeB(++calls)) 67 | }) 68 | }) 69 | 70 | return dispose 71 | }) 72 | 73 | flushSync() 74 | stopEffect() 75 | 76 | expect(disposeB).toHaveBeenCalled() 77 | expect(disposeA).toHaveBeenCalled() 78 | expect(disposeParent).toHaveBeenCalled() 79 | 80 | expect(disposeB).toHaveBeenCalledWith(1) 81 | expect(disposeA).toHaveBeenCalledWith(2) 82 | expect(disposeParent).toHaveBeenCalledWith(3) 83 | }) 84 | 85 | it('should dispose all roots', () => { 86 | const disposals: string[] = [] 87 | 88 | const dispose = createRoot((dispose) => { 89 | createRoot(() => { 90 | onCleanup(() => disposals.push('SUBTREE 1')) 91 | createEffect(() => onCleanup(() => disposals.push('+A1'))) 92 | createEffect(() => onCleanup(() => disposals.push('+B1'))) 93 | createEffect(() => onCleanup(() => disposals.push('+C1'))) 94 | }) 95 | 96 | createRoot(() => { 97 | onCleanup(() => disposals.push('SUBTREE 2')) 98 | createEffect(() => onCleanup(() => disposals.push('+A2'))) 99 | createEffect(() => onCleanup(() => disposals.push('+B2'))) 100 | createEffect(() => onCleanup(() => disposals.push('+C2'))) 101 | }) 102 | 103 | onCleanup(() => disposals.push('ROOT')) 104 | 105 | return dispose 106 | }) 107 | 108 | flushSync() 109 | dispose() 110 | 111 | expect(disposals).toMatchInlineSnapshot(` 112 | [ 113 | "+C2", 114 | "+B2", 115 | "+A2", 116 | "SUBTREE 2", 117 | "+C1", 118 | "+B1", 119 | "+A1", 120 | "SUBTREE 1", 121 | "ROOT", 122 | ] 123 | `) 124 | }) 125 | -------------------------------------------------------------------------------- /tests/createMemo.test.ts: -------------------------------------------------------------------------------- 1 | import { createEffect, createMemo, createSignal, flushSync } from '../src' 2 | 3 | afterEach(() => flushSync()) 4 | 5 | it('should store and return value on read', () => { 6 | const [$x] = createSignal(1) 7 | const [$y] = createSignal(1) 8 | 9 | const $a = createMemo(() => $x() + $y()) 10 | 11 | expect($a()).toBe(2) 12 | flushSync() 13 | 14 | // Try again to ensure state is maintained. 15 | expect($a()).toBe(2) 16 | }) 17 | 18 | it('should update when dependency is updated', () => { 19 | const [$x, setX] = createSignal(1) 20 | const [$y, setY] = createSignal(1) 21 | 22 | const $a = createMemo(() => $x() + $y()) 23 | 24 | setX(2) 25 | expect($a()).toBe(3) 26 | 27 | setY(2) 28 | expect($a()).toBe(4) 29 | }) 30 | 31 | it('should update when deep dependency is updated', () => { 32 | const [$x, setX] = createSignal(1) 33 | const [$y] = createSignal(1) 34 | 35 | const $a = createMemo(() => $x() + $y()) 36 | const $b = createMemo(() => $a()) 37 | 38 | setX(2) 39 | expect($b()).toBe(3) 40 | }) 41 | 42 | it('should update when deep computed dependency is updated', () => { 43 | const [$x, setX] = createSignal(10) 44 | const [$y] = createSignal(10) 45 | 46 | const $a = createMemo(() => $x() + $y()) 47 | const $b = createMemo(() => $a()) 48 | const $c = createMemo(() => $b()) 49 | 50 | setX(20) 51 | expect($c()).toBe(30) 52 | }) 53 | 54 | it('should only re-compute when needed', () => { 55 | const computed = vi.fn((n: number) => n) 56 | 57 | const [$x, setX] = createSignal(10) 58 | const [$y, setY] = createSignal(10) 59 | 60 | const $a = createMemo(() => computed($x() + $y())) 61 | 62 | expect(computed).not.toHaveBeenCalled() 63 | 64 | $a() 65 | expect(computed).toHaveBeenCalledTimes(1) 66 | expect(computed).toHaveBeenCalledWith(20) 67 | 68 | $a() 69 | expect(computed).toHaveBeenCalledTimes(1) 70 | 71 | setX(20) 72 | $a() 73 | expect(computed).toHaveBeenCalledTimes(2) 74 | 75 | setY(20) 76 | $a() 77 | expect(computed).toHaveBeenCalledTimes(3) 78 | 79 | $a() 80 | expect(computed).toHaveBeenCalledTimes(3) 81 | }) 82 | 83 | it('should only re-compute whats needed', () => { 84 | const memoA = vi.fn((n: number) => n) 85 | const memoB = vi.fn((n: number) => n) 86 | 87 | const [$x, setX] = createSignal(10) 88 | const [$y, setY] = createSignal(10) 89 | 90 | const $a = createMemo(() => memoA($x())) 91 | const $b = createMemo(() => memoB($y())) 92 | const $c = createMemo(() => $a() + $b()) 93 | 94 | expect(memoA).not.toHaveBeenCalled() 95 | expect(memoB).not.toHaveBeenCalled() 96 | 97 | $c() 98 | expect(memoA).toHaveBeenCalledTimes(1) 99 | expect(memoB).toHaveBeenCalledTimes(1) 100 | expect($c()).toBe(20) 101 | 102 | setX(20) 103 | flushSync() 104 | 105 | $c() 106 | expect(memoA).toHaveBeenCalledTimes(2) 107 | expect(memoB).toHaveBeenCalledTimes(1) 108 | expect($c()).toBe(30) 109 | 110 | setY(20) 111 | flushSync() 112 | 113 | $c() 114 | expect(memoA).toHaveBeenCalledTimes(2) 115 | expect(memoB).toHaveBeenCalledTimes(2) 116 | expect($c()).toBe(40) 117 | }) 118 | 119 | it('should discover new dependencies', () => { 120 | const [$x, setX] = createSignal(1) 121 | const [$y, setY] = createSignal(0) 122 | 123 | const $c = createMemo(() => { 124 | if ($x()) { 125 | return $x() 126 | } else { 127 | return $y() 128 | } 129 | }) 130 | 131 | expect($c()).toBe(1) 132 | 133 | setX(0) 134 | flushSync() 135 | expect($c()).toBe(0) 136 | 137 | setY(10) 138 | flushSync() 139 | expect($c()).toBe(10) 140 | }) 141 | 142 | it('should accept equals option', () => { 143 | const [$x, setX] = createSignal(0) 144 | 145 | const $a = createMemo(() => $x(), 0, { 146 | // Skip even numbers. 147 | equals: (prev, next) => prev + 1 === next, 148 | }) 149 | 150 | const effectA = vi.fn((n: number) => n) 151 | createEffect(() => effectA($a())) 152 | 153 | flushSync() 154 | expect($a()).toBe(0) 155 | expect(effectA).toHaveBeenCalledTimes(1) 156 | 157 | setX(2) 158 | flushSync() 159 | expect($a()).toBe(2) 160 | expect(effectA).toHaveBeenCalledTimes(2) 161 | 162 | // no-change 163 | setX(3) 164 | flushSync() 165 | expect($a()).toBe(2) 166 | expect(effectA).toHaveBeenCalledTimes(2) 167 | }) 168 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import type { MemoOptions, SignalOptions } from './core' 2 | import { Computation, compute, UNCHANGED } from './core' 3 | import { Effect } from './effect' 4 | import { ERROR_BIT, LOADING_BIT } from './flags' 5 | import { handleError, HANDLER, Owner } from './owner' 6 | 7 | export type Accessor = () => T 8 | export type Setter = (value: T) => T 9 | export type Signal = [read: Accessor, write: Setter] 10 | 11 | /** 12 | * Wraps the given value into a signal. The signal will return the current value when invoked 13 | * `fn()`, and provide a simple write API via `write()`. The value can now be observed 14 | * when used inside other computations created with `computed` and `effect`. 15 | */ 16 | export function createSignal( 17 | initialValue: T, 18 | options?: SignalOptions 19 | ): Signal { 20 | const node = new Computation(initialValue, null, options) 21 | return [() => node.read(), (v) => node.write(v)] 22 | } 23 | 24 | export function _createPromise( 25 | promise: Promise, 26 | initial?: T, 27 | options?: SignalOptions 28 | ): Computation { 29 | const signal = new Computation(initial, null, options) 30 | signal.write(UNCHANGED, LOADING_BIT) 31 | promise.then( 32 | (value) => { 33 | signal.write(value, 0) 34 | }, 35 | (error) => { 36 | signal.write(error as T, ERROR_BIT) 37 | } 38 | ) 39 | return signal 40 | } 41 | 42 | export function createPromise( 43 | promise: Promise, 44 | initial?: T, 45 | options?: SignalOptions 46 | ): Accessor { 47 | const signal = _createPromise(promise, initial, options) 48 | return () => signal.read() 49 | } 50 | 51 | export function _createAsync( 52 | fn: () => Promise, 53 | initial?: T, 54 | options?: SignalOptions 55 | ): Computation { 56 | const lhs = new Computation(undefined, () => { 57 | const promise = Promise.resolve(fn()) 58 | return _createPromise(promise, initial) 59 | }) 60 | const rhs = new Computation(undefined, () => lhs.read().read(), options) 61 | return rhs 62 | } 63 | 64 | export function createAsync( 65 | fn: () => Promise, 66 | initial?: T, 67 | options?: SignalOptions 68 | ): Accessor { 69 | const rhs = _createAsync(fn, initial, options) 70 | return () => rhs.read() 71 | } 72 | 73 | /** 74 | * Creates a new computation whose value is computed and returned by the given function. The given 75 | * compute function is _only_ re-run when one of it's dependencies are updated. Dependencies are 76 | * are all signals that are read during execution. 77 | */ 78 | export function createMemo( 79 | compute: () => T, 80 | initialValue?: T, 81 | options?: MemoOptions 82 | ): Accessor { 83 | const node = new Computation(initialValue, compute, options) 84 | return () => node.read() 85 | } 86 | 87 | /** 88 | * Invokes the given function each time any of the signals that are read inside are updated 89 | * (i.e., their value changes). The effect is immediately invoked on initialization. 90 | */ 91 | export function createEffect( 92 | effect: () => T, 93 | initialValue?: T, 94 | options?: { name?: string } 95 | ): void { 96 | void new Effect( 97 | initialValue, 98 | effect, 99 | __DEV__ ? { name: options?.name ?? 'effect' } : undefined 100 | ) 101 | } 102 | 103 | /** 104 | * Creates a computation root which is given a `dispose()` function to dispose of all inner 105 | * computations. 106 | */ 107 | export function createRoot( 108 | init: ((dispose: () => void) => T) | (() => T) 109 | ): T { 110 | const owner = new Owner() 111 | return compute( 112 | owner, 113 | !init.length ? (init as () => T) : () => init(() => owner.dispose()), 114 | null 115 | ) 116 | } 117 | 118 | /** 119 | * Runs the given function in the given owner so that error handling and cleanups continue to work. 120 | * 121 | * Warning: Usually there are simpler ways of modeling a problem that avoid using this function 122 | */ 123 | export function runWithOwner( 124 | owner: Owner | null, 125 | run: () => T 126 | ): T | undefined { 127 | try { 128 | return compute(owner, run, null) 129 | } catch (error) { 130 | handleError(owner, error) 131 | return undefined 132 | } 133 | } 134 | 135 | /** 136 | * Runs the given function when an error is thrown in a child owner. If the error is thrown again 137 | * inside the error handler, it will trigger the next available parent owner handler. 138 | */ 139 | export function catchError( 140 | fn: () => T, 141 | handler: (error: U) => void 142 | ): void { 143 | const owner = new Owner() 144 | owner._context = { [HANDLER]: handler } 145 | try { 146 | compute(owner, fn, null) 147 | } catch (error) { 148 | handleError(owner, error) 149 | } 150 | } 151 | 152 | export { untrack } from './core' 153 | export { flushSync } from './effect' 154 | export { getOwner, onCleanup } from './owner' 155 | -------------------------------------------------------------------------------- /src/owner.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Owner tracking is used to enable nested tracking scopes with automatic cleanup. 3 | * We also use owners to also keep track of which error handling context we are in. 4 | * 5 | * If you write the following 6 | * 7 | * const a = createOwner(() => { 8 | * const b = createOwner(() => {}); 9 | * 10 | * const c = createOwner(() => { 11 | * const d = createOwner(() => {}); 12 | * }); 13 | * 14 | * const e = createOwner(() => {}); 15 | * }); 16 | * 17 | * The owner tree will look like this: 18 | * 19 | * a 20 | * /|\ 21 | * b-c-e 22 | * | 23 | * d 24 | * 25 | * Following the _nextSibling pointers of each owner will first give you its children, and then its siblings (in reverse). 26 | * a -> e -> c -> d -> b 27 | * 28 | * Note that the owner tree is largely orthogonal to the reactivity tree, and is much closer to the component tree. 29 | */ 30 | 31 | import { STATE_CLEAN, STATE_DISPOSED } from './constants' 32 | import type { Computation } from './core' 33 | 34 | export type ContextRecord = Record 35 | export type Disposable = () => void 36 | 37 | export const HANDLER = Symbol('ERROR_HANDLER') 38 | 39 | let currentOwner: Owner | null = null 40 | 41 | export function setCurrentOwner(owner: Owner | null): Owner | null { 42 | const out = currentOwner 43 | currentOwner = owner 44 | return out 45 | } 46 | 47 | /** 48 | * Returns the currently executing parent owner. 49 | */ 50 | export function getOwner(): Owner | null { 51 | return currentOwner 52 | } 53 | 54 | export class Owner { 55 | // We flatten the owner tree into a linked list so that we don't need a pointer to .firstChild 56 | // However, the children are actually added in reverse creation order 57 | // See comment at the top of the file for an example of the _nextSibling traversal 58 | _parent: Owner | null = null 59 | _nextSibling: Owner | null = null 60 | _prevSibling: Owner | null = null 61 | 62 | _state: number = STATE_CLEAN 63 | 64 | _disposal: Disposable | Disposable[] | null = null 65 | _context: null | ContextRecord = null 66 | 67 | constructor(signal = false) { 68 | if (currentOwner && !signal) currentOwner.append(this) 69 | } 70 | 71 | append(owner: Owner): void { 72 | owner._parent = this 73 | owner._prevSibling = this 74 | if (this._nextSibling) this._nextSibling._prevSibling = owner 75 | owner._nextSibling = this._nextSibling 76 | this._nextSibling = owner 77 | } 78 | 79 | dispose(this: Owner, self = true): void { 80 | if (this._state === STATE_DISPOSED) return 81 | 82 | let current = this._nextSibling as Computation | null 83 | 84 | while (current && current._parent === this) { 85 | current.dispose(true) 86 | 87 | current = current._nextSibling as Computation 88 | } 89 | 90 | const head = self ? this._prevSibling : this 91 | if (self) this._disposeNode() 92 | else if (current) current._prevSibling = this 93 | if (head) head._nextSibling = current 94 | } 95 | 96 | _disposeNode(): void { 97 | if (this._nextSibling) this._nextSibling._prevSibling = this._prevSibling 98 | // the other direction of links is handled by the dispose function 99 | this._parent = null 100 | this._prevSibling = null 101 | this._context = null 102 | this._state = STATE_DISPOSED 103 | this.emptyDisposal() 104 | } 105 | 106 | emptyDisposal(): void { 107 | if (!this._disposal) return 108 | 109 | if (Array.isArray(this._disposal)) { 110 | for (let i = 0; i < this._disposal.length; i++) { 111 | const callable = this._disposal[i] 112 | callable.call(callable) 113 | } 114 | } else { 115 | this._disposal.call(this._disposal) 116 | } 117 | 118 | this._disposal = null 119 | } 120 | } 121 | 122 | /** 123 | * Runs the given function when the parent owner computation is being disposed. 124 | */ 125 | export function onCleanup(disposable: Disposable): void { 126 | if (!currentOwner) return 127 | 128 | const node = currentOwner 129 | 130 | if (!node._disposal) { 131 | node._disposal = disposable 132 | } else if (Array.isArray(node._disposal)) { 133 | node._disposal.push(disposable) 134 | } else { 135 | node._disposal = [node._disposal, disposable] 136 | } 137 | } 138 | 139 | export function lookup(owner: Owner | null, key: string | symbol): unknown { 140 | if (!owner) return 141 | 142 | let current: Owner | null = owner 143 | let value 144 | 145 | while (current) { 146 | value = current._context?.[key] 147 | if (value !== undefined) return value 148 | current = current._parent 149 | } 150 | } 151 | 152 | export function handleError(owner: Owner | null, error: unknown): void { 153 | const handler = lookup(owner, HANDLER) as undefined | ((error: Error) => void) 154 | 155 | if (!handler) throw error 156 | 157 | try { 158 | const coercedError = 159 | error instanceof Error ? error : Error(JSON.stringify(error)) 160 | handler(coercedError) 161 | } catch (error) { 162 | handleError(owner!._parent, error) 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /tests/async.test.ts: -------------------------------------------------------------------------------- 1 | import { _createAsync, _createPromise, createEffect, flushSync } from '../src' 2 | import { Computation } from '../src/core' 3 | import { Effect } from '../src/effect' 4 | 5 | it('should propagate loading state when calling read', async () => { 6 | let resolve: (value: unknown) => void 7 | const promise = new Promise((r) => { 8 | resolve = r 9 | }) 10 | const comp = _createPromise(promise) 11 | const chain = new Computation(undefined, () => comp.read()) 12 | 13 | expect(chain.read()).toBeUndefined() 14 | expect(comp.loading()).toBe(true) 15 | expect(chain.loading()).toBe(true) 16 | resolve!(1) 17 | await Promise.resolve() 18 | expect(comp.loading()).toBe(false) 19 | expect(chain.read()).toBe(1) 20 | expect(chain.loading()).toBe(false) 21 | }) 22 | 23 | it('should handle two async sources', async () => { 24 | let resolve1: (value: number) => void 25 | let resolve2: (value: number) => void 26 | const promise1 = new Promise((r) => (resolve1 = r)) 27 | const promise2 = new Promise((r) => (resolve2 = r)) 28 | const comp1 = _createPromise(promise1) 29 | const comp2 = _createPromise(promise2) 30 | const chain = new Computation(undefined, () => { 31 | const c1 = comp1.read() 32 | const c2 = comp2.read() 33 | if (c1 && c2) return c1 + c2 34 | }) 35 | 36 | expect(chain.read()).toBeUndefined() 37 | expect(comp1.loading()).toBe(true) 38 | expect(comp2.loading()).toBe(true) 39 | resolve1!(1) 40 | await Promise.resolve() 41 | expect(comp1.loading()).toBe(false) 42 | expect(comp2.loading()).toBe(true) 43 | expect(chain.read()).toBeUndefined() 44 | resolve2!(2) 45 | await Promise.resolve() 46 | expect(comp2.loading()).toBe(false) 47 | expect(chain.read()).toBe(3) 48 | }) 49 | 50 | it('should handle async memos', async () => { 51 | let resolve1: (value: number) => void 52 | const comp = _createPromise(new Promise((r) => (resolve1 = r))) 53 | const chain = new Computation(undefined, () => { 54 | const c2 = comp.read() 55 | if (c2) return c2 + 1 56 | }) 57 | 58 | expect(chain.read()).toBeUndefined() 59 | expect(comp.loading()).toBe(true) 60 | expect(chain.loading()).toBe(true) 61 | resolve1!(1) 62 | await Promise.resolve() 63 | expect(chain.read()).toBe(2) 64 | expect(comp.loading()).toBe(false) 65 | expect(chain.loading()).toBe(false) 66 | }) 67 | 68 | it('should handle async memos chaining', async () => { 69 | let resolve1: (value: number) => void 70 | let resolve2: (value: number) => void 71 | const comp1 = _createPromise(new Promise((r) => (resolve1 = r))) 72 | const comp2 = _createAsync(() => { 73 | comp1.read() 74 | return new Promise((r) => (resolve2 = r)) 75 | }, 1) 76 | 77 | comp2.read() 78 | expect(comp2.loading()).toBe(true) 79 | expect(comp1.loading()).toBe(true) 80 | resolve2!(1) 81 | await Promise.resolve() 82 | flushSync() 83 | expect(comp2.read()).toBe(1) 84 | expect(comp2.loading()).toBe(true) 85 | expect(comp1.loading()).toBe(true) 86 | resolve1!(2) 87 | await Promise.resolve() 88 | flushSync() 89 | expect(comp2.read()).toBe(1) 90 | expect(comp1.loading()).toBe(false) 91 | expect(comp2.loading()).toBe(true) 92 | }) 93 | 94 | it('should handle effects watching async memo state', async () => { 95 | let resolve1: (value: number) => void 96 | const comp1 = _createPromise(new Promise((r) => (resolve1 = r))) 97 | 98 | const effect = vi.fn(() => comp1.loading()) 99 | createEffect(effect) 100 | 101 | comp1.read() 102 | flushSync() 103 | expect(effect).toBeCalledTimes(1) 104 | resolve1!(1) 105 | await Promise.resolve() 106 | flushSync() 107 | expect(effect).toBeCalledTimes(2) 108 | }) 109 | 110 | it('should not rerun observers of async memos that load to same value', async () => { 111 | let resolve1: (value: number) => void 112 | const comp = _createPromise(new Promise((r) => (resolve1 = r)), 1) 113 | const child = vi.fn(() => comp.read()) 114 | const comp2 = new Computation(undefined, child) 115 | 116 | comp2.read() 117 | resolve1!(1) 118 | await Promise.resolve() 119 | comp2.read() 120 | expect(child).toBeCalledTimes(1) 121 | }) 122 | 123 | it('should handle .wait() on async memos', async () => { 124 | let resolve1: (value: number) => void 125 | const comp = _createPromise(new Promise((r) => (resolve1 = r))) 126 | const before = vi.fn() 127 | const compute = vi.fn() 128 | const chain = new Computation(undefined, () => { 129 | before() 130 | const c2 = comp.wait() 131 | compute() 132 | return c2 + 1 133 | }) 134 | chain.read() 135 | expect(compute).toBeCalledTimes(0) 136 | resolve1!(1) 137 | chain.read() 138 | await Promise.resolve() 139 | chain.read() 140 | expect(compute).toBeCalledTimes(1) 141 | expect(before).toBeCalledTimes(2) 142 | }) 143 | 144 | it('should handle async propagation to an effect resetting when value changes', async () => { 145 | const promiseFactory = vi.fn(() => { 146 | return new Promise(() => { 147 | // never resolves 148 | }) 149 | }) 150 | const s = new Computation(1, null) 151 | const m = _createAsync(async () => { 152 | if (s.read() === 1) return promiseFactory() 153 | else return 2 154 | }) 155 | let loading = false 156 | new Effect(undefined, () => { 157 | loading = m.loading() 158 | }) 159 | flushSync() 160 | expect(loading).toBe(true) 161 | s.write(2) 162 | await Promise.resolve() 163 | // Requires two rounds of promises! 164 | await Promise.resolve() 165 | flushSync() 166 | expect(loading).toBe(false) 167 | }) 168 | 169 | it('should handle async propagation to an effect completing', async () => { 170 | let resolve1: (value: number) => void 171 | const s = new Computation(1, null) 172 | const p = _createPromise(new Promise((r) => (resolve1 = r))) 173 | const m = new Computation(undefined, () => { 174 | if (s.read() === 1) return p.read() 175 | else return 2 176 | }) 177 | let loading = false 178 | new Effect(undefined, () => { 179 | loading = m.loading() 180 | }) 181 | flushSync() 182 | expect(loading).toBe(true) 183 | resolve1!(1) 184 | await Promise.resolve() 185 | await Promise.resolve() 186 | flushSync() 187 | expect(loading).toBe(false) 188 | }) 189 | 190 | it('should mark downstream memos as loading on returning a promise', () => { 191 | const unresolvedPromise = new Promise(() => { 192 | // never resolves 193 | }) 194 | const s = new Computation(false, null) 195 | const p = _createPromise(unresolvedPromise) 196 | const m = new Computation(undefined, () => { 197 | if (s.read()) return p.read() 198 | else return 2 199 | }) 200 | const m2 = new Computation(undefined, () => m.read()) 201 | expect(m.loading()).toBe(false) 202 | expect(m2.loading()).toBe(false) 203 | s.write(true) 204 | expect(m2.loading()).toBe(true) 205 | }) 206 | 207 | it('should throw when a promise rejects', async () => { 208 | let reject1: () => void 209 | const rejectedPromise = new Promise((_, reject) => { 210 | reject1 = () => reject(new Error('test')) 211 | }) 212 | const m = _createPromise(rejectedPromise) 213 | m.read() 214 | reject1!() 215 | await Promise.resolve() 216 | flushSync() 217 | expect(() => m.read()).toThrow('test') 218 | }) 219 | 220 | it('should throw when a computation promise rejects', async () => { 221 | let reject1: () => void 222 | const rejectedPromise = new Promise((_, reject) => { 223 | reject1 = () => reject(new Error('test')) 224 | }) 225 | const m = _createAsync(() => rejectedPromise) 226 | m.read() 227 | reject1!() 228 | await Promise.resolve() 229 | flushSync() 230 | expect(() => m.read()).toThrow('test') 231 | }) 232 | 233 | it('should not be marked as clean if stale promise is resolved', async () => { 234 | let resolve1: (value: number) => void 235 | const promise1 = new Promise((r) => (resolve1 = r)) 236 | const promise2 = new Promise(() => { 237 | // never resolves 238 | }) 239 | const switcher = new Computation(true, null) 240 | const comp1 = _createAsync(() => { 241 | if (switcher.read()) return promise1 242 | else return promise2 243 | }) 244 | const waiting = vi.fn(() => comp1.read()) 245 | const comp2 = new Computation(undefined, waiting) 246 | expect(comp2.loading()).toBe(true) 247 | expect(waiting).toBeCalledTimes(1) 248 | switcher.write(false) 249 | expect(comp2.loading()).toBe(true) 250 | resolve1!(1) 251 | await Promise.resolve() 252 | flushSync() 253 | expect(comp2.loading()).toBe(true) 254 | }) 255 | 256 | it('should not be loading when a promise resolves to the same value', async () => { 257 | let resolve1: (value: number) => void 258 | const comp1 = _createPromise(new Promise((r) => (resolve1 = r)), 1) 259 | const comp2 = new Computation(undefined, () => comp1.read()) 260 | expect(comp2.loading()).toBe(true) 261 | expect(comp2.loading()).toBe(true) 262 | resolve1!(1) 263 | await Promise.resolve() 264 | flushSync() 265 | expect(comp1.loading()).toBe(false) 266 | expect(comp2.loading()).toBe(false) 267 | }) 268 | -------------------------------------------------------------------------------- /tests/createEffect.test.ts: -------------------------------------------------------------------------------- 1 | import { 2 | createEffect, 3 | createMemo, 4 | createRoot, 5 | createSignal, 6 | flushSync, 7 | onCleanup, 8 | untrack, 9 | } from '../src' 10 | 11 | afterEach(() => flushSync()) 12 | 13 | it('should run effect', () => { 14 | const [$x, setX] = createSignal(0) 15 | const effect = vi.fn(() => void $x()) 16 | 17 | createEffect(effect) 18 | flushSync() 19 | expect(effect).toHaveBeenCalledTimes(1) 20 | 21 | setX(1) 22 | flushSync() 23 | expect(effect).toHaveBeenCalledTimes(2) 24 | }) 25 | 26 | it('should run effect on change', () => { 27 | const effect = vi.fn((n: number) => n) 28 | 29 | const [$x, setX] = createSignal(10) 30 | const [$y, setY] = createSignal(10) 31 | 32 | const $a = createMemo(() => $x() + $y()) 33 | const $b = createMemo(() => $a()) 34 | 35 | createEffect(() => effect($b())) 36 | flushSync() 37 | 38 | expect(effect).to.toHaveBeenCalledTimes(1) 39 | 40 | setX(20) 41 | flushSync() 42 | expect(effect).to.toHaveBeenCalledTimes(2) 43 | 44 | setY(20) 45 | flushSync() 46 | expect(effect).to.toHaveBeenCalledTimes(3) 47 | 48 | setX(20) 49 | setY(20) 50 | flushSync() 51 | expect(effect).to.toHaveBeenCalledTimes(3) 52 | }) 53 | 54 | it('should handle nested effect', () => { 55 | const [$x, setX] = createSignal(0) 56 | const [$y, setY] = createSignal(0) 57 | 58 | const outerEffect = vi.fn() 59 | const innerEffect = vi.fn() 60 | const innerDispose = vi.fn() 61 | 62 | const stopEffect = createRoot((dispose) => { 63 | createEffect(() => { 64 | $x() 65 | outerEffect() 66 | createEffect(() => { 67 | $y() 68 | innerEffect() 69 | onCleanup(innerDispose) 70 | }) 71 | }) 72 | 73 | return dispose 74 | }) 75 | 76 | flushSync() 77 | expect(outerEffect).toHaveBeenCalledTimes(1) 78 | expect(innerEffect).toHaveBeenCalledTimes(1) 79 | expect(innerDispose).toHaveBeenCalledTimes(0) 80 | 81 | setY(1) 82 | flushSync() 83 | expect(outerEffect).toHaveBeenCalledTimes(1) 84 | expect(innerEffect).toHaveBeenCalledTimes(2) 85 | expect(innerDispose).toHaveBeenCalledTimes(1) 86 | 87 | setY(2) 88 | flushSync() 89 | expect(outerEffect).toHaveBeenCalledTimes(1) 90 | expect(innerEffect).toHaveBeenCalledTimes(3) 91 | expect(innerDispose).toHaveBeenCalledTimes(2) 92 | 93 | innerEffect.mockReset() 94 | innerDispose.mockReset() 95 | 96 | setX(1) 97 | flushSync() 98 | expect(outerEffect).toHaveBeenCalledTimes(2) 99 | expect(innerEffect).toHaveBeenCalledTimes(1) // new one is created 100 | expect(innerDispose).toHaveBeenCalledTimes(1) 101 | 102 | setY(3) 103 | flushSync() 104 | expect(outerEffect).toHaveBeenCalledTimes(2) 105 | expect(innerEffect).toHaveBeenCalledTimes(2) 106 | expect(innerDispose).toHaveBeenCalledTimes(2) 107 | 108 | stopEffect() 109 | setX(10) 110 | setY(10) 111 | expect(outerEffect).toHaveBeenCalledTimes(2) 112 | expect(innerEffect).toHaveBeenCalledTimes(2) 113 | expect(innerDispose).toHaveBeenCalledTimes(3) 114 | }) 115 | 116 | it('should stop effect', () => { 117 | const effect = vi.fn((n: number) => n) 118 | 119 | const [$x, setX] = createSignal(10) 120 | 121 | const stopEffect = createRoot((dispose) => { 122 | createEffect(() => effect($x())) 123 | return dispose 124 | }) 125 | flushSync() 126 | stopEffect() 127 | 128 | setX(20) 129 | flushSync() 130 | expect(effect).toHaveBeenCalledTimes(1) 131 | }) 132 | 133 | it('should run all disposals before each new run', () => { 134 | const effect = vi.fn() 135 | const disposeA = vi.fn() 136 | const disposeB = vi.fn() 137 | 138 | function fnA() { 139 | onCleanup(disposeA) 140 | } 141 | 142 | function fnB() { 143 | onCleanup(disposeB) 144 | } 145 | 146 | const [$x, setX] = createSignal(0) 147 | 148 | createEffect(() => { 149 | effect() 150 | fnA(), fnB(), $x() 151 | }) 152 | flushSync() 153 | expect(effect).toHaveBeenCalledTimes(1) 154 | expect(disposeA).toHaveBeenCalledTimes(0) 155 | expect(disposeB).toHaveBeenCalledTimes(0) 156 | 157 | for (let i = 1; i <= 3; i += 1) { 158 | setX(i) 159 | flushSync() 160 | expect(effect).toHaveBeenCalledTimes(i + 1) 161 | expect(disposeA).toHaveBeenCalledTimes(i) 162 | expect(disposeB).toHaveBeenCalledTimes(i) 163 | } 164 | }) 165 | 166 | it('should dispose of nested effect', () => { 167 | const [$x, setX] = createSignal(0) 168 | const innerEffect = vi.fn() 169 | 170 | const stopEffect = createRoot((dispose) => { 171 | createEffect(() => { 172 | createEffect(() => { 173 | innerEffect($x()) 174 | }) 175 | }) 176 | 177 | return dispose 178 | }) 179 | 180 | flushSync() 181 | stopEffect() 182 | 183 | setX(10) 184 | flushSync() 185 | expect(innerEffect).toHaveBeenCalledTimes(1) 186 | expect(innerEffect).not.toHaveBeenCalledWith(10) 187 | }) 188 | 189 | it('should conditionally observe', () => { 190 | const [$x, setX] = createSignal(0) 191 | const [$y, setY] = createSignal(0) 192 | const [$condition, setCondition] = createSignal(true) 193 | 194 | const $a = createMemo(() => ($condition() ? $x() : $y())) 195 | const effect = vi.fn((n: number) => n) 196 | 197 | createEffect(() => effect($a())) 198 | 199 | flushSync() 200 | expect(effect).toHaveBeenCalledTimes(1) 201 | 202 | setY(1) 203 | flushSync() 204 | expect(effect).toHaveBeenCalledTimes(1) 205 | 206 | setX(1) 207 | flushSync() 208 | expect(effect).toHaveBeenCalledTimes(2) 209 | 210 | setCondition(false) 211 | flushSync() 212 | expect(effect).toHaveBeenCalledTimes(2) 213 | 214 | setY(2) 215 | flushSync() 216 | expect(effect).toHaveBeenCalledTimes(3) 217 | 218 | setX(3) 219 | flushSync() 220 | expect(effect).toHaveBeenCalledTimes(3) 221 | }) 222 | 223 | it('should dispose of nested conditional effect', () => { 224 | const [$condition, setCondition] = createSignal(true) 225 | 226 | const disposeA = vi.fn() 227 | const disposeB = vi.fn() 228 | 229 | function fnA() { 230 | createEffect(() => { 231 | onCleanup(disposeA) 232 | }) 233 | } 234 | 235 | function fnB() { 236 | createEffect(() => { 237 | onCleanup(disposeB) 238 | }) 239 | } 240 | 241 | createEffect(() => ($condition() ? fnA() : fnB())) 242 | 243 | flushSync() 244 | setCondition(false) 245 | flushSync() 246 | expect(disposeA).toHaveBeenCalledTimes(1) 247 | }) 248 | 249 | // https://github.com/preactjs/signals/issues/152 250 | it('should handle looped effects', () => { 251 | let values: number[] = [] 252 | let loop = 2 253 | 254 | const [$value, setValue] = createSignal(0) 255 | 256 | createEffect(() => { 257 | values.push($value()) 258 | for (let i = 0; i < loop; i++) { 259 | createEffect(() => { 260 | values.push($value() + i) 261 | }) 262 | } 263 | }) 264 | 265 | flushSync() 266 | 267 | expect(values).toHaveLength(3) 268 | expect(values.join(',')).toBe('0,0,1') 269 | 270 | loop = 1 271 | values = [] 272 | setValue(1) 273 | flushSync() 274 | 275 | expect(values).toHaveLength(2) 276 | expect(values.join(',')).toBe('1,1') 277 | 278 | values = [] 279 | setValue(2) 280 | flushSync() 281 | 282 | expect(values).toHaveLength(2) 283 | expect(values.join(',')).toBe('2,2') 284 | }) 285 | 286 | it('should apply changes in effect in same flush', async () => { 287 | const [$x, setX] = createSignal(0) 288 | const [$y, setY] = createSignal(0) 289 | 290 | const $a = createMemo(() => { 291 | return $x() + 1 292 | }) 293 | const $b = createMemo(() => { 294 | return $a() + 2 295 | }) 296 | 297 | createEffect(() => { 298 | setX(untrack(() => $x()) + 1) 299 | $y() 300 | }) 301 | 302 | flushSync() 303 | expect($x()).toBe(1) 304 | expect($b()).toBe(4) 305 | expect($a()).toBe(2) 306 | 307 | setY(1) 308 | 309 | await Promise.resolve() 310 | 311 | expect($x()).toBe(2) 312 | expect($b()).toBe(5) 313 | expect($a()).toBe(3) 314 | 315 | setY(2) 316 | 317 | await Promise.resolve() 318 | 319 | expect($x()).toBe(3) 320 | expect($b()).toBe(6) 321 | expect($a()).toBe(4) 322 | }) 323 | 324 | it('should run parent effect before child effect', () => { 325 | const [$x, setX] = createSignal(0) 326 | const $condition = createMemo(() => $x()) 327 | 328 | let calls = 0 329 | 330 | createEffect(() => { 331 | createEffect(() => { 332 | $x() 333 | calls++ 334 | }) 335 | 336 | $condition() 337 | }) 338 | 339 | flushSync() 340 | setX(1) 341 | flushSync() 342 | expect(calls).toBe(2) 343 | }) 344 | 345 | // TODO: right now if a memo contains an effect, it will only be eagerly evaluated when it is used 346 | // or when an effect depends on it. Instead, we should make sure that any memos that contain effects 347 | // as descendants are eagerly evaluated. (We should probably do this through necessitation, by 348 | // linking and unlinking nodes) 349 | it('should run parent memo before child effect', () => { 350 | const [$x, setX] = createSignal(0) 351 | const $condition = createMemo(() => $x()) 352 | 353 | let calls = 0 354 | 355 | const m = createMemo(() => { 356 | createEffect(() => { 357 | $x() 358 | calls++ 359 | }) 360 | 361 | $condition() 362 | }) 363 | 364 | m() 365 | flushSync() 366 | setX(1) 367 | flushSync() 368 | expect(calls).toBe(2) 369 | }) 370 | -------------------------------------------------------------------------------- /tests/graph.test.ts: -------------------------------------------------------------------------------- 1 | // https://github.com/preactjs/signals/blob/main/packages/core/test/signal.test.tsx#L1249 2 | 3 | import { createMemo, createSignal } from '../src' 4 | 5 | it('should drop X->B->X updates', () => { 6 | // X 7 | // / | 8 | // A | <- Looks like a flag doesn't it? :D 9 | // \ | 10 | // B 11 | // | 12 | // C 13 | 14 | const [$x, setX] = createSignal(2) 15 | 16 | const $a = createMemo(() => $x() - 1) 17 | const $b = createMemo(() => $x() + $a()) 18 | 19 | const compute = vi.fn(() => `c: ${$b()}`) 20 | const $c = createMemo(compute) 21 | 22 | expect($c()).toBe('c: 3') 23 | expect(compute).toHaveBeenCalledTimes(1) 24 | compute.mockReset() 25 | 26 | setX(4) 27 | $c() 28 | expect(compute).toHaveBeenCalledTimes(1) 29 | }) 30 | 31 | it('should only update every signal once (diamond graph)', () => { 32 | // In this scenario "D" should only update once when "A" receive an update. This is sometimes 33 | // referred to as the "diamond" scenario. 34 | // X 35 | // / \ 36 | // A B 37 | // \ / 38 | // C 39 | 40 | const [$x, setX] = createSignal('a') 41 | const $a = createMemo(() => $x()) 42 | const $b = createMemo(() => $x()) 43 | 44 | const spy = vi.fn(() => $a() + ' ' + $b()) 45 | const $c = createMemo(spy) 46 | 47 | expect($c()).toBe('a a') 48 | expect(spy).toHaveBeenCalledTimes(1) 49 | 50 | setX('aa') 51 | expect($c()).toBe('aa aa') 52 | expect(spy).toHaveBeenCalledTimes(2) 53 | }) 54 | 55 | it('should only update every signal once (diamond graph + tail)', () => { 56 | // "D" will be likely updated twice if our mark+sweep logic is buggy. 57 | // X 58 | // / \ 59 | // A B 60 | // \ / 61 | // C 62 | // | 63 | // D 64 | 65 | const [$x, setX] = createSignal('a') 66 | 67 | const $a = createMemo(() => $x()) 68 | const $b = createMemo(() => $x()) 69 | const $c = createMemo(() => $a() + ' ' + $b()) 70 | 71 | const spy = vi.fn(() => $c()) 72 | const $d = createMemo(spy) 73 | 74 | expect($d()).toBe('a a') 75 | expect(spy).toHaveBeenCalledTimes(1) 76 | 77 | setX('aa') 78 | expect($d()).toBe('aa aa') 79 | expect(spy).toHaveBeenCalledTimes(2) 80 | }) 81 | 82 | it('should bail out if result is the same', () => { 83 | // Bail out if value of "A" never changes 84 | // X->A->B 85 | 86 | const [$x, setX] = createSignal('a') 87 | 88 | const $a = createMemo(() => { 89 | $x() 90 | return 'foo' 91 | }) 92 | 93 | const spy = vi.fn(() => $a()) 94 | const $b = createMemo(spy) 95 | 96 | expect($b()).toBe('foo') 97 | expect(spy).toHaveBeenCalledTimes(1) 98 | 99 | setX('aa') 100 | expect($b()).toBe('foo') 101 | expect(spy).toHaveBeenCalledTimes(1) 102 | }) 103 | 104 | it('should only update every signal once (jagged diamond graph + tails)', () => { 105 | // "E" and "F" will be likely updated >3 if our mark+sweep logic is buggy. 106 | // X 107 | // / \ 108 | // A B 109 | // | | 110 | // | C 111 | // \ / 112 | // D 113 | // / \ 114 | // E F 115 | 116 | const [$x, setX] = createSignal('a') 117 | 118 | const $a = createMemo(() => $x()) 119 | const $b = createMemo(() => $x()) 120 | const $c = createMemo(() => $b()) 121 | 122 | const dSpy = vi.fn(() => $a() + ' ' + $c()) 123 | const $d = createMemo(dSpy) 124 | 125 | const eSpy = vi.fn(() => $d()) 126 | const $e = createMemo(eSpy) 127 | const fSpy = vi.fn(() => $d()) 128 | const $f = createMemo(fSpy) 129 | 130 | expect($e()).toBe('a a') 131 | expect(eSpy).toHaveBeenCalledTimes(1) 132 | 133 | expect($f()).toBe('a a') 134 | expect(fSpy).toHaveBeenCalledTimes(1) 135 | 136 | setX('b') 137 | 138 | expect($d()).toBe('b b') 139 | expect(dSpy).toHaveBeenCalledTimes(2) 140 | 141 | expect($e()).toBe('b b') 142 | expect(eSpy).toHaveBeenCalledTimes(2) 143 | 144 | expect($f()).toBe('b b') 145 | expect(fSpy).toHaveBeenCalledTimes(2) 146 | 147 | setX('c') 148 | 149 | expect($d()).toBe('c c') 150 | expect(dSpy).toHaveBeenCalledTimes(3) 151 | 152 | expect($e()).toBe('c c') 153 | expect(eSpy).toHaveBeenCalledTimes(3) 154 | 155 | expect($f()).toBe('c c') 156 | expect(fSpy).toHaveBeenCalledTimes(3) 157 | }) 158 | 159 | it('should ensure subs update even if one dep is static', () => { 160 | // X 161 | // / \ 162 | // A *B <- returns same value every time 163 | // \ / 164 | // C 165 | 166 | const [$x, setX] = createSignal('a') 167 | 168 | const $a = createMemo(() => $x()) 169 | const $b = createMemo(() => { 170 | $x() 171 | return 'c' 172 | }) 173 | 174 | const spy = vi.fn(() => $a() + ' ' + $b()) 175 | const $c = createMemo(spy) 176 | 177 | expect($c()).toBe('a c') 178 | 179 | setX('aa') 180 | 181 | expect($c()).toBe('aa c') 182 | expect(spy).toHaveBeenCalledTimes(2) 183 | }) 184 | 185 | it('should ensure subs update even if two deps mark it clean', () => { 186 | // In this scenario both "B" and "C" always return the same value. But "D" must still update 187 | // because "X" marked it. If "D" isn't updated, then we have a bug. 188 | // X 189 | // / | \ 190 | // A *B *C 191 | // \ | / 192 | // D 193 | 194 | const [$x, setX] = createSignal('a') 195 | 196 | const $b = createMemo(() => $x()) 197 | const $c = createMemo(() => { 198 | $x() 199 | return 'c' 200 | }) 201 | const $d = createMemo(() => { 202 | $x() 203 | return 'd' 204 | }) 205 | 206 | const spy = vi.fn(() => $b() + ' ' + $c() + ' ' + $d()) 207 | const $e = createMemo(spy) 208 | 209 | expect($e()).toBe('a c d') 210 | 211 | setX('aa') 212 | 213 | expect($e()).toBe('aa c d') 214 | expect(spy).toHaveBeenCalledTimes(2) 215 | }) 216 | 217 | it('propagates in topological order', () => { 218 | // 219 | // c1 220 | // / \ 221 | // / \ 222 | // b1 b2 223 | // \ / 224 | // \ / 225 | // a1 226 | // 227 | let seq = '' 228 | const [a1, setA1] = createSignal(false) 229 | const b1 = createMemo( 230 | () => { 231 | a1() 232 | seq += 'b1' 233 | }, 234 | undefined, 235 | { equals: false } 236 | ) 237 | const b2 = createMemo( 238 | () => { 239 | a1() 240 | seq += 'b2' 241 | }, 242 | undefined, 243 | { equals: false } 244 | ) 245 | const c1 = createMemo( 246 | () => { 247 | b1(), b2() 248 | seq += 'c1' 249 | }, 250 | undefined, 251 | { equals: false } 252 | ) 253 | 254 | c1() 255 | seq = '' 256 | setA1(true) 257 | c1() 258 | expect(seq).toBe('b1b2c1') 259 | }) 260 | 261 | it('only propagates once with linear convergences', () => { 262 | // d 263 | // | 264 | // +---+---+---+---+ 265 | // v v v v v 266 | // f1 f2 f3 f4 f5 267 | // | | | | | 268 | // +---+---+---+---+ 269 | // v 270 | // g 271 | const [d, setD] = createSignal(0) 272 | const f1 = createMemo(() => d()) 273 | const f2 = createMemo(() => d()) 274 | const f3 = createMemo(() => d()) 275 | const f4 = createMemo(() => d()) 276 | const f5 = createMemo(() => d()) 277 | let gcount = 0 278 | const g = createMemo(() => { 279 | gcount++ 280 | return f1() + f2() + f3() + f4() + f5() 281 | }) 282 | 283 | g() 284 | gcount = 0 285 | setD(1) 286 | g() 287 | expect(gcount).toBe(1) 288 | }) 289 | 290 | it('only propagates once with exponential convergence', () => { 291 | // d 292 | // | 293 | // +---+---+ 294 | // v v v 295 | // f1 f2 f3 296 | // \ | / 297 | // O 298 | // / | \ 299 | // v v v 300 | // g1 g2 g3 301 | // +---+---+ 302 | // v 303 | // h 304 | const [d, setD] = createSignal(0) 305 | const f1 = createMemo(() => { 306 | return d() 307 | }) 308 | const f2 = createMemo(() => { 309 | return d() 310 | }) 311 | const f3 = createMemo(() => { 312 | return d() 313 | }) 314 | const g1 = createMemo(() => { 315 | return f1() + f2() + f3() 316 | }) 317 | const g2 = createMemo(() => { 318 | return f1() + f2() + f3() 319 | }) 320 | const g3 = createMemo(() => { 321 | return f1() + f2() + f3() 322 | }) 323 | let hcount = 0 324 | const h = createMemo(() => { 325 | hcount++ 326 | return g1() + g2() + g3() 327 | }) 328 | h() 329 | hcount = 0 330 | setD(1) 331 | h() 332 | expect(hcount).toBe(1) 333 | }) 334 | 335 | it('does not trigger downstream computations unless changed', () => { 336 | const [s1, set] = createSignal(1, { equals: false }) 337 | let order = '' 338 | const t1 = createMemo(() => { 339 | order += 't1' 340 | return s1() 341 | }) 342 | const t2 = createMemo(() => { 343 | order += 'c1' 344 | t1() 345 | }) 346 | t2() 347 | expect(order).toBe('c1t1') 348 | order = '' 349 | set(1) 350 | t2() 351 | expect(order).toBe('t1') 352 | order = '' 353 | set(2) 354 | t2() 355 | expect(order).toBe('t1c1') 356 | }) 357 | 358 | it('applies updates to changed dependees in same order as createMemo', () => { 359 | const [s1, set] = createSignal(0) 360 | let order = '' 361 | const t1 = createMemo(() => { 362 | order += 't1' 363 | return s1() === 0 364 | }) 365 | const t2 = createMemo(() => { 366 | order += 'c1' 367 | return s1() 368 | }) 369 | const t3 = createMemo(() => { 370 | order += 'c2' 371 | return t1() 372 | }) 373 | t2() 374 | t3() 375 | expect(order).toBe('c1c2t1') 376 | order = '' 377 | set(1) 378 | t2() 379 | t3() 380 | expect(order).toBe('c1t1c2') 381 | }) 382 | 383 | it('updates downstream pending computations', () => { 384 | const [s1, set] = createSignal(0) 385 | const [s2] = createSignal(0) 386 | let order = '' 387 | const t1 = createMemo(() => { 388 | order += 't1' 389 | return s1() === 0 390 | }) 391 | const t2 = createMemo(() => { 392 | order += 'c1' 393 | return s1() 394 | }) 395 | const t3 = createMemo(() => { 396 | order += 'c2' 397 | t1() 398 | return createMemo(() => { 399 | order += 'c2_1' 400 | return s2() 401 | }) 402 | }) 403 | order = '' 404 | set(1) 405 | t2() 406 | t3()() 407 | expect(order).toBe('c1c2t1c2_1') 408 | }) 409 | 410 | describe('with changing dependencies', () => { 411 | let i: () => boolean 412 | let setI: (v: boolean) => void 413 | let t: () => number 414 | let setT: (v: number) => void 415 | let e: () => number 416 | let setE: (v: number) => void 417 | let fevals: number 418 | let f: () => number 419 | 420 | function init() { 421 | ;[i, setI] = createSignal(true) 422 | ;[t, setT] = createSignal(1) 423 | ;[e, setE] = createSignal(2) 424 | fevals = 0 425 | f = createMemo(() => { 426 | fevals++ 427 | return i() ? t() : e() 428 | }) 429 | f() 430 | fevals = 0 431 | } 432 | 433 | it('updates on active dependencies', () => { 434 | init() 435 | setT(5) 436 | expect(f()).toBe(5) 437 | expect(fevals).toBe(1) 438 | }) 439 | 440 | it('does not update on inactive dependencies', () => { 441 | init() 442 | setE(5) 443 | expect(f()).toBe(1) 444 | expect(fevals).toBe(0) 445 | }) 446 | 447 | it('deactivates obsolete dependencies', () => { 448 | init() 449 | setI(false) 450 | f() 451 | fevals = 0 452 | setT(5) 453 | f() 454 | expect(fevals).toBe(0) 455 | }) 456 | 457 | it('activates new dependencies', () => { 458 | init() 459 | setI(false) 460 | fevals = 0 461 | setE(5) 462 | f() 463 | expect(fevals).toBe(1) 464 | }) 465 | 466 | it('ensures that new dependencies are updated before dependee', () => { 467 | let order = '' 468 | const [a, setA] = createSignal(0) 469 | const b = createMemo(() => { 470 | order += 'b' 471 | return a() + 1 472 | }) 473 | const c = createMemo(() => { 474 | order += 'c' 475 | const check = b() 476 | if (check) { 477 | return check 478 | } 479 | return e() 480 | }) 481 | const d = createMemo(() => { 482 | return a() 483 | }) 484 | const e = createMemo(() => { 485 | order += 'd' 486 | return d() + 10 487 | }) 488 | 489 | c() 490 | e() 491 | expect(order).toBe('cbd') 492 | 493 | order = '' 494 | setA(-1) 495 | c() 496 | e() 497 | 498 | expect(order).toBe('bcd') 499 | expect(c()).toBe(9) 500 | 501 | order = '' 502 | setA(0) 503 | c() 504 | e() 505 | expect(order).toBe('bcd') 506 | expect(c()).toBe(1) 507 | }) 508 | }) 509 | 510 | it('does not update subsequent pending computations after stale invocations', () => { 511 | const [s1, set1] = createSignal(1) 512 | const [s2, set2] = createSignal(false) 513 | let count = 0 514 | /* 515 | s1 516 | | 517 | +---+---+ 518 | t1 t2 c1 t3 519 | \ / 520 | c3 521 | [PN,PN,STL,void] 522 | */ 523 | const t1 = createMemo(() => s1() > 0) 524 | const t2 = createMemo(() => s1() > 0) 525 | const c1 = createMemo(() => s1()) 526 | const t3 = createMemo(() => { 527 | const a = s1() 528 | const b = s2() 529 | return a && b 530 | }) 531 | const c3 = createMemo(() => { 532 | t1() 533 | t2() 534 | c1() 535 | t3() 536 | count++ 537 | }) 538 | c3() 539 | set2(true) 540 | c3() 541 | expect(count).toBe(2) 542 | set1(2) 543 | c3() 544 | expect(count).toBe(3) 545 | }) 546 | 547 | it('evaluates stale computations before dependees when trackers stay unchanged', () => { 548 | const [s1, set] = createSignal(1, { equals: false }) 549 | let order = '' 550 | const t1 = createMemo(() => { 551 | order += 't1' 552 | return s1() > 2 553 | }) 554 | const t2 = createMemo(() => { 555 | order += 't2' 556 | return s1() > 2 557 | }) 558 | const c1 = createMemo( 559 | () => { 560 | order += 'c1' 561 | s1() 562 | }, 563 | undefined, 564 | { equals: false } 565 | ) 566 | const c2 = createMemo(() => { 567 | order += 'c2' 568 | t1() 569 | t2() 570 | c1() 571 | }) 572 | c2() 573 | order = '' 574 | set(1) 575 | c2() 576 | expect(order).toBe('t1t2c1c2') 577 | order = '' 578 | set(3) 579 | c2() 580 | expect(order).toBe('t1c2t2c1') 581 | }) 582 | 583 | it('correctly marks downstream computations as stale on change', () => { 584 | const [s1, set] = createSignal(1) 585 | let order = '' 586 | const t1 = createMemo(() => { 587 | order += 't1' 588 | return s1() 589 | }) 590 | const c1 = createMemo(() => { 591 | order += 'c1' 592 | return t1() 593 | }) 594 | const c2 = createMemo(() => { 595 | order += 'c2' 596 | return c1() 597 | }) 598 | const c3 = createMemo(() => { 599 | order += 'c3' 600 | return c2() 601 | }) 602 | c3() 603 | order = '' 604 | set(2) 605 | c3() 606 | expect(order).toBe('t1c1c2c3') 607 | }) 608 | -------------------------------------------------------------------------------- /src/core.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Nodes for constructing a graph of reactive values and reactive computations. 3 | * The graph is acyclic. 4 | * The user inputs new values into the graph by calling .write() on one more computation nodes. 5 | * The user retrieves computed results from the graph by calling .read() on one or more computation nodes. 6 | * The library is responsible for running any necessary computations so that .read() is 7 | * up to date with all prior .write() calls anywhere in the graph. 8 | * 9 | * We call the input nodes 'roots' and the output nodes 'leaves' of the graph here. 10 | * Changes flow from roots to leaves. It would be effective but inefficient to immediately propagate 11 | * all changes from a root through the graph to descendant leaves. Instead, we defer change 12 | * most change propagation computation until a leaf is accessed. This allows us to coalesce 13 | * computations and skip altogether recalculating unused sections of the graph. 14 | * 15 | * Each computation node tracks its sources and its observers (observers are other 16 | * elements that have this node as a source). Source and observer links are updated automatically 17 | * as observer computations re-evaluate and call get() on their sources. 18 | * 19 | * Each node stores a cache state (clean/check/dirty) to support the change propagation algorithm: 20 | * In general, execution proceeds in three passes: 21 | * 1. write() propagates changes down the graph to the leaves 22 | * direct children are marked as dirty and their deeper descendants marked as check 23 | * (no computations are evaluated) 24 | * 2. read() requests that parent nodes updateIfNecessary(), which proceeds recursively up the tree 25 | * to decide whether the node is clean (parents unchanged) or dirty (parents changed) 26 | * 3. updateIfNecessary() evaluates the computation if the node is dirty 27 | * (the computations are executed in root to leaf order) 28 | */ 29 | 30 | import { 31 | STATE_CHECK, 32 | STATE_CLEAN, 33 | STATE_DIRTY, 34 | STATE_DISPOSED, 35 | } from './constants' 36 | import { NotReadyError } from './error' 37 | import { DEFAULT_FLAGS, ERROR_BIT, Flags, LOADING_BIT } from './flags' 38 | import { getOwner, Owner, setCurrentOwner } from './owner' 39 | 40 | export interface SignalOptions { 41 | name?: string 42 | equals?: ((prev: T, next: T) => boolean) | false 43 | } 44 | 45 | export interface MemoOptions extends SignalOptions { 46 | initial?: T 47 | } 48 | 49 | interface SourceType { 50 | _observers: ObserverType[] | null 51 | _updateIfNecessary: () => void 52 | 53 | _stateFlags: Flags 54 | } 55 | 56 | interface ObserverType { 57 | _sources: SourceType[] | null 58 | _notify: (state: number) => void 59 | 60 | _handlerMask: Flags 61 | _notifyFlags: (mask: Flags, newFlags: Flags) => void 62 | } 63 | 64 | let currentObserver: ObserverType | null = null 65 | let currentMask: Flags = DEFAULT_FLAGS 66 | 67 | let newSources: SourceType[] | null = null 68 | let newSourcesIndex = 0 69 | let newFlags = 0 70 | 71 | export const UNCHANGED: unique symbol = Symbol('unchanged') 72 | export type UNCHANGED = typeof UNCHANGED 73 | 74 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 75 | export class Computation 76 | extends Owner 77 | implements SourceType, ObserverType 78 | { 79 | _sources: SourceType[] | null = null 80 | _observers: ObserverType[] | null = null 81 | _value: T | undefined 82 | _compute: null | (() => T) 83 | 84 | // Used in __DEV__ mode, hopefully removed in production 85 | _name: string | undefined 86 | 87 | // Using false is an optimization as an alternative to _equals: () => false 88 | // which could enable more efficient DIRTY notification 89 | _equals: false | ((a: T, b: T) => boolean) = isEqual 90 | 91 | /** Whether the computation is an error or has ancestors that are unresolved */ 92 | _stateFlags = 0 93 | 94 | /** Which flags raised by sources are handled, vs. being passed through. */ 95 | _handlerMask = DEFAULT_FLAGS 96 | 97 | _error: Computation | null = null 98 | _loading: Computation | null = null 99 | 100 | constructor( 101 | initialValue: T | undefined, 102 | compute: null | (() => T), 103 | options?: MemoOptions 104 | ) { 105 | // Initialize self as a node in the Owner tree, for tracking cleanups. 106 | // If we aren't passed a compute function, we don't need to track nested computations 107 | // because there is no way to create a nested computation (a child to the owner tree) 108 | super(compute === null) 109 | 110 | this._compute = compute 111 | 112 | this._state = compute ? STATE_DIRTY : STATE_CLEAN 113 | this._value = initialValue 114 | 115 | // Used when debugging the graph; it is often helpful to know the names of sources/observers 116 | if (__DEV__) 117 | this._name = options?.name ?? (this._compute ? 'computed' : 'signal') 118 | 119 | if (options?.equals !== undefined) this._equals = options.equals 120 | } 121 | 122 | _read(): T { 123 | if (this._compute) this._updateIfNecessary() 124 | 125 | // When the currentObserver reads this._value, the want to add this computation as a source 126 | // so that when this._value changes, the currentObserver will be re-executed 127 | track(this) 128 | 129 | // TODO do a handler lookup instead 130 | newFlags |= this._stateFlags & ~currentMask 131 | 132 | if (this._stateFlags & ERROR_BIT) { 133 | throw this._value as Error 134 | } else { 135 | return this._value! 136 | } 137 | } 138 | 139 | /** 140 | * Return the current value of this computation 141 | * Automatically re-executes the surrounding computation when the value changes 142 | */ 143 | read(): T { 144 | return this._read() 145 | } 146 | 147 | /** 148 | * Return the current value of this computation 149 | * Automatically re-executes the surrounding computation when the value changes 150 | * 151 | * If the computation has any unresolved ancestors, this function waits for the value to resolve 152 | * before continuing 153 | */ 154 | wait(): T { 155 | if (this.loading()) { 156 | throw new NotReadyError() 157 | } 158 | return this._read() 159 | } 160 | 161 | /** 162 | * Return true if the computation is the value is dependent on an unresolved promise 163 | * Triggers re-execution of the computation when the loading state changes 164 | * 165 | * This is useful especially when effects want to re-execute when a computation's 166 | * loading state changes 167 | */ 168 | loading(): boolean { 169 | if (this._loading === null) { 170 | this._loading = loadingState(this) 171 | } 172 | return this._loading.read() 173 | } 174 | 175 | /** 176 | * Return true if the computation is the computation threw an error 177 | * Triggers re-execution of the computation when the error state changes 178 | */ 179 | error(): boolean { 180 | if (this._error === null) { 181 | this._error = errorState(this) 182 | } 183 | return this._error.read() 184 | } 185 | 186 | /** Update the computation with a new value. */ 187 | write(value: T | UNCHANGED, flags = 0): T { 188 | const valueChanged = 189 | value !== UNCHANGED && 190 | (!!(flags & ERROR_BIT) || 191 | this._equals === false || 192 | !this._equals(this._value!, value)) 193 | 194 | if (valueChanged) this._value = value 195 | 196 | const changedFlagsMask = this._stateFlags ^ flags 197 | const changedFlags = changedFlagsMask & flags 198 | this._stateFlags = flags 199 | 200 | // Our value has changed, so we need to notify all of our observers that the value has 201 | // changed and so they must rerun 202 | if (this._observers) { 203 | for (let i = 0; i < this._observers.length; i++) { 204 | if (valueChanged) { 205 | this._observers[i]._notify(STATE_DIRTY) 206 | } else if (changedFlagsMask) { 207 | this._observers[i]._notifyFlags(changedFlagsMask, changedFlags) 208 | } 209 | } 210 | } 211 | 212 | // We return the value so that .write can be used in an expression 213 | // (although it is not usually recommended) 214 | return this._value! 215 | } 216 | 217 | /** 218 | * Set the current node's state, and recursively mark all of this node's observers as STATE_CHECK 219 | */ 220 | _notify(state: number): void { 221 | // If the state is already STATE_DIRTY and we are trying to set it to STATE_CHECK, 222 | // then we don't need to do anything. Similarly, if the state is already STATE_CHECK 223 | // and we are trying to set it to STATE_CHECK, then we don't need to do anything because 224 | // a previous _notify call has already set this state and all observers as STATE_CHECK 225 | if (this._state >= state) return 226 | 227 | this._state = state 228 | if (this._observers) { 229 | for (let i = 0; i < this._observers.length; i++) { 230 | this._observers[i]._notify(STATE_CHECK) 231 | } 232 | } 233 | } 234 | 235 | /** 236 | * Notify the computation that one of its sources has changed flags. 237 | * 238 | * @param mask A bitmask for which flag(s) were changed. 239 | * @param newFlags The source's new flags, masked to just the changed ones. 240 | */ 241 | _notifyFlags(mask: Flags, newFlags: Flags): void { 242 | // If we're dirty, none of the things we do can matter. 243 | if (this._state >= STATE_DIRTY) return 244 | 245 | // If the changed flags have side effects attached, we have to re-run. 246 | if (mask & this._handlerMask) { 247 | this._notify(STATE_DIRTY) 248 | return 249 | } 250 | 251 | // If we're already check, we can delay this propagation until we check. 252 | if (this._state >= STATE_CHECK) return 253 | 254 | // If we're clean, and none of these flags have a handler, we can try to 255 | // propagate them. 256 | const prevFlags = this._stateFlags & mask 257 | const deltaFlags = prevFlags ^ newFlags 258 | 259 | if (newFlags === prevFlags) { 260 | // No work to do if the flags are unchanged. 261 | } else if (deltaFlags & prevFlags & mask) { 262 | // One of the changed flags was previously _on_, so we can't eagerly 263 | // propagate anything; we'll wait until we're checked. 264 | this._notify(STATE_CHECK) 265 | } else { 266 | // The changed flags were previously _off_, which means we can remain 267 | // clean with updated flags and pass this notification on transitively. 268 | this._stateFlags ^= deltaFlags 269 | if (this._observers) { 270 | for (let i = 0; i < this._observers.length; i++) { 271 | this._observers[i]._notifyFlags(mask, newFlags) 272 | } 273 | } 274 | } 275 | } 276 | 277 | _setError(error: unknown): void { 278 | this.write(error as T, this._stateFlags | ERROR_BIT) 279 | } 280 | 281 | /** 282 | * This is the core part of the reactivity system, which makes sure that the values are updated 283 | * before they are read. We've also adapted it to return the loading state of the computation, 284 | * so that we can propagate that to the computation's observers. 285 | * 286 | * This function will ensure that the value and states we read from the computation are up to date 287 | */ 288 | _updateIfNecessary(): void { 289 | // If the user tries to read a computation that has been disposed, we throw an error, because 290 | // they probably kept a reference to it as the parent reran, so there is likely a new computation 291 | // with the same _compute function that they should be reading instead. 292 | if (this._state === STATE_DISPOSED) { 293 | throw new Error('Tried to read a disposed computation') 294 | } 295 | 296 | // If the computation is already clean, none of our sources have changed, so we know that 297 | // our value and stateFlags are up to date, and we can just return. 298 | if (this._state === STATE_CLEAN) { 299 | return 300 | } 301 | 302 | // Otherwise, our sources' values may have changed, or one of our sources' loading states 303 | // may have been set to no longer loading. In either case, what we need to do is make sure our 304 | // sources all have up to date values and loading states and then update our own value and 305 | // loading state 306 | 307 | // We keep track of whether any of our sources have changed loading state so that we can update 308 | // our loading state. This is only necessary if none of them change value because update() will 309 | // also cause us to recompute our loading state. 310 | let observerFlags: Flags = 0 311 | 312 | // STATE_CHECK means one of our grandparent sources may have changed value or loading state, 313 | // so we need to recursively call _updateIfNecessary to update the state of all of our sources 314 | // and then update our value and loading state. 315 | if (this._state === STATE_CHECK) { 316 | for (let i = 0; i < this._sources!.length; i++) { 317 | // Make sure the parent is up to date. If it changed value, then it will mark us as 318 | // STATE_DIRTY, and we will know to rerun 319 | this._sources![i]._updateIfNecessary() 320 | 321 | // If the parent is loading, then we are waiting 322 | observerFlags |= this._sources![i]._stateFlags 323 | 324 | // If the parent changed value, it will mark us as STATE_DIRTY and we need to call update() 325 | // Cast because the _updateIfNecessary call above can change our state 326 | if ((this._state as number) === STATE_DIRTY) { 327 | // Stop the loop here so we won't trigger updates on other parents unnecessarily 328 | // If our computation changes to no longer use some sources, we don't 329 | // want to update() a source we used last time, but now don't use. 330 | break 331 | } 332 | } 333 | } 334 | 335 | if (this._state === STATE_DIRTY) { 336 | update(this) 337 | } else { 338 | // isWaiting has now coallesced all of our parents' loading states 339 | this.write(UNCHANGED, observerFlags) 340 | 341 | // None of our parents changed value, so our value is up to date (STATE_CLEAN) 342 | this._state = STATE_CLEAN 343 | } 344 | } 345 | 346 | /** 347 | * Remove ourselves from the owner graph and the computation graph 348 | */ 349 | override _disposeNode(): void { 350 | // If we've already been disposed, don't try to dispose twice 351 | if (this._state === STATE_DISPOSED) return 352 | 353 | // Unlink ourselves from our sources' observers array so that we can be garbage collected 354 | // This removes us from the computation graph 355 | if (this._sources) removeSourceObservers(this, 0) 356 | 357 | // Remove ourselves from the ownership tree as well 358 | super._disposeNode() 359 | } 360 | } 361 | 362 | function loadingState(node: Computation): Computation { 363 | const prevOwner = setCurrentOwner(node._parent) 364 | const options = __DEV__ 365 | ? { name: node._name ? `loading ${node._name}` : 'loading' } 366 | : undefined 367 | 368 | const s = new Computation( 369 | undefined, 370 | () => { 371 | track(node) 372 | node._updateIfNecessary() 373 | return !!(node._stateFlags & LOADING_BIT) 374 | }, 375 | options 376 | ) 377 | s._handlerMask = ERROR_BIT | LOADING_BIT 378 | setCurrentOwner(prevOwner) 379 | return s 380 | } 381 | 382 | function errorState(node: Computation): Computation { 383 | const prevOwner = setCurrentOwner(node._parent) 384 | const options = __DEV__ 385 | ? { name: node._name ? `error ${node._name}` : 'error' } 386 | : undefined 387 | 388 | const s = new Computation( 389 | undefined, 390 | () => { 391 | track(node) 392 | node._updateIfNecessary() 393 | return !!(node._stateFlags & ERROR_BIT) 394 | }, 395 | options 396 | ) 397 | s._handlerMask = ERROR_BIT 398 | setCurrentOwner(prevOwner) 399 | return s 400 | } 401 | 402 | /** 403 | * Instead of wiping the sources immediately on `update`, we compare them to the new sources 404 | * by checking if the source we want to add is the same as the old source at the same index. 405 | * 406 | * This way when the sources don't change, we are just doing a fast comparison: 407 | * 408 | * _sources: [a, b, c] 409 | * ^ 410 | * | 411 | * newSourcesIndex 412 | * 413 | * When the sources do change, we create newSources and push the values that we read into it 414 | */ 415 | function track(computation: SourceType): void { 416 | if (currentObserver) { 417 | if ( 418 | !newSources && 419 | currentObserver._sources && 420 | currentObserver._sources[newSourcesIndex] === computation 421 | ) { 422 | newSourcesIndex++ 423 | } else if (!newSources) newSources = [computation] 424 | else if (computation !== newSources[newSources.length - 1]) { 425 | // If the computation is the same as the last source we read, we don't need to add it to newSources 426 | // https://github.com/solidjs/solid/issues/46#issuecomment-515717924 427 | newSources.push(computation) 428 | } 429 | } 430 | } 431 | 432 | /** 433 | * Reruns a computation's _compute function, producing a new value and keeping track of dependencies. 434 | * 435 | * It handles the updating of sources and observers, disposal of previous executions, 436 | * and error handling if the _compute function throws. It also sets the node as loading 437 | * if it reads any parents that are currently loading. 438 | */ 439 | export function update(node: Computation): void { 440 | const prevSources = newSources 441 | const prevSourcesIndex = newSourcesIndex 442 | const prevFlags = newFlags 443 | 444 | newSources = null as Computation[] | null 445 | newSourcesIndex = 0 446 | newFlags = 0 447 | 448 | try { 449 | node.dispose(false) 450 | node.emptyDisposal() 451 | 452 | // Rerun the node's _compute function, setting node as owner and listener so that any 453 | // computations read are added to node's sources and any computations are automatically disposed 454 | // if `node` is rerun 455 | const result = compute(node, node._compute!, node) 456 | 457 | // Update the node's value 458 | node.write(result, newFlags) 459 | } catch (error) { 460 | node._setError(error) 461 | } finally { 462 | if (newSources) { 463 | // If there are new sources, that means the end of the sources array has changed 464 | // newSourcesIndex keeps track of the index of the first new source 465 | // See track() above for more info 466 | 467 | // We need to remove any old sources after newSourcesIndex 468 | if (node._sources) removeSourceObservers(node, newSourcesIndex) 469 | 470 | // First we update our own sources array (uplinks) 471 | if (node._sources && newSourcesIndex > 0) { 472 | // If we shared some sources with the previous execution, we need to copy those over to the 473 | // new sources array 474 | 475 | // First we need to make sure the sources array is long enough to hold all the new sources 476 | node._sources.length = newSourcesIndex + newSources.length 477 | 478 | // Then we copy the new sources over 479 | for (let i = 0; i < newSources.length; i++) { 480 | node._sources[newSourcesIndex + i] = newSources[i] 481 | } 482 | } else { 483 | // If we didn't share any sources with the previous execution, set the sources array to newSources 484 | node._sources = newSources 485 | } 486 | 487 | // For each new source, we need to add this `node` to the source's observers array (downlinks) 488 | let source: SourceType 489 | for (let i = newSourcesIndex; i < node._sources.length; i++) { 490 | source = node._sources[i] 491 | if (!source._observers) source._observers = [node] 492 | else source._observers.push(node) 493 | } 494 | } else if (node._sources && newSourcesIndex < node._sources.length) { 495 | // If there are no new sources, but the sources array is longer than newSourcesIndex, 496 | // that means the sources array has just shrunk so we remove the tail end 497 | removeSourceObservers(node, newSourcesIndex) 498 | node._sources.length = newSourcesIndex 499 | } 500 | 501 | // Reset global context after computation 502 | newSources = prevSources 503 | newSourcesIndex = prevSourcesIndex 504 | newFlags = prevFlags 505 | 506 | // By now, we have updated the node's value and sources array, so we can mark it as clean 507 | // TODO: This assumes that the computation didn't write to any signals, throw an error if it did 508 | node._state = STATE_CLEAN 509 | } 510 | } 511 | 512 | function removeSourceObservers(node: ObserverType, index: number): void { 513 | let source: SourceType 514 | let swap: number 515 | for (let i = index; i < node._sources!.length; i++) { 516 | source = node._sources![i] 517 | if (source._observers) { 518 | swap = source._observers.indexOf(node) 519 | source._observers[swap] = source._observers[source._observers.length - 1] 520 | source._observers.pop() 521 | } 522 | } 523 | } 524 | 525 | function isEqual(a: T, b: T): boolean { 526 | return a === b 527 | } 528 | 529 | /** 530 | * Returns the current value stored inside the given compute function without triggering any 531 | * dependencies. Use `untrack` if you want to also disable owner tracking. 532 | */ 533 | export function untrack(fn: () => T): T { 534 | if (currentObserver === null) return fn() 535 | return compute(getOwner(), fn, null) 536 | } 537 | 538 | /** 539 | * A convenient wrapper that calls `compute` with the `owner` and `observer` and is guaranteed 540 | * to reset the global context after the computation is finished even if an error is thrown. 541 | */ 542 | export function compute( 543 | owner: Owner | null, 544 | compute: (val: T) => T, 545 | observer: Computation 546 | ): T 547 | export function compute( 548 | owner: Owner | null, 549 | compute: (val: undefined) => T, 550 | observer: null 551 | ): T 552 | export function compute( 553 | owner: Owner | null, 554 | compute: (val?: T) => T, 555 | observer: Computation | null 556 | ): T { 557 | const prevOwner = setCurrentOwner(owner) 558 | const prevObserver = currentObserver 559 | const prevMask = currentMask 560 | currentObserver = observer 561 | currentMask = observer?._handlerMask ?? DEFAULT_FLAGS 562 | 563 | try { 564 | return compute(observer ? observer._value : undefined) 565 | } catch (e) { 566 | if (!(e instanceof NotReadyError)) { 567 | throw e 568 | } else { 569 | // TODO: figure out what should go here 570 | return observer!._value! 571 | } 572 | } finally { 573 | setCurrentOwner(prevOwner) 574 | currentObserver = prevObserver 575 | currentMask = prevMask 576 | } 577 | } 578 | -------------------------------------------------------------------------------- /pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: '9.0' 2 | 3 | settings: 4 | autoInstallPeers: true 5 | excludeLinksFromLockfile: false 6 | 7 | importers: 8 | 9 | .: 10 | devDependencies: 11 | '@types/node': 12 | specifier: ^24.0.14 13 | version: 24.0.14 14 | '@typescript-eslint/eslint-plugin': 15 | specifier: ^8.37.0 16 | version: 8.37.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0)(typescript@5.8.3) 17 | '@typescript-eslint/parser': 18 | specifier: ^8.37.0 19 | version: 8.37.0(eslint@9.31.0)(typescript@5.8.3) 20 | '@vitest/coverage-v8': 21 | specifier: ^3.2.4 22 | version: 3.2.4(vitest@3.2.4(@types/node@24.0.14)) 23 | eslint: 24 | specifier: ^9.31.0 25 | version: 9.31.0 26 | eslint-config-prettier: 27 | specifier: ^10.1.8 28 | version: 10.1.8(eslint@9.31.0) 29 | eslint-plugin-simple-import-sort: 30 | specifier: ^12.1.1 31 | version: 12.1.1(eslint@9.31.0) 32 | prettier: 33 | specifier: ^3.6.2 34 | version: 3.6.2 35 | rimraf: 36 | specifier: ^6.0.1 37 | version: 6.0.1 38 | tsup: 39 | specifier: ^8.5.0 40 | version: 8.5.0(postcss@8.5.6)(typescript@5.8.3) 41 | typescript: 42 | specifier: 5.8.3 43 | version: 5.8.3 44 | vite: 45 | specifier: ^7.0.5 46 | version: 7.0.5(@types/node@24.0.14) 47 | vitest: 48 | specifier: ^3.2.4 49 | version: 3.2.4(@types/node@24.0.14) 50 | 51 | packages: 52 | 53 | '@ampproject/remapping@2.3.0': 54 | resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} 55 | engines: {node: '>=6.0.0'} 56 | 57 | '@babel/helper-string-parser@7.27.1': 58 | resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} 59 | engines: {node: '>=6.9.0'} 60 | 61 | '@babel/helper-validator-identifier@7.27.1': 62 | resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} 63 | engines: {node: '>=6.9.0'} 64 | 65 | '@babel/parser@7.28.0': 66 | resolution: {integrity: sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==} 67 | engines: {node: '>=6.0.0'} 68 | hasBin: true 69 | 70 | '@babel/types@7.28.1': 71 | resolution: {integrity: sha512-x0LvFTekgSX+83TI28Y9wYPUfzrnl2aT5+5QLnO6v7mSJYtEEevuDRN0F0uSHRk1G1IWZC43o00Y0xDDrpBGPQ==} 72 | engines: {node: '>=6.9.0'} 73 | 74 | '@bcoe/v8-coverage@1.0.2': 75 | resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} 76 | engines: {node: '>=18'} 77 | 78 | '@esbuild/aix-ppc64@0.25.7': 79 | resolution: {integrity: sha512-uD0kKFHh6ETr8TqEtaAcV+dn/2qnYbH/+8wGEdY70Qf7l1l/jmBUbrmQqwiPKAQE6cOQ7dTj6Xr0HzQDGHyceQ==} 80 | engines: {node: '>=18'} 81 | cpu: [ppc64] 82 | os: [aix] 83 | 84 | '@esbuild/android-arm64@0.25.7': 85 | resolution: {integrity: sha512-p0ohDnwyIbAtztHTNUTzN5EGD/HJLs1bwysrOPgSdlIA6NDnReoVfoCyxG6W1d85jr2X80Uq5KHftyYgaK9LPQ==} 86 | engines: {node: '>=18'} 87 | cpu: [arm64] 88 | os: [android] 89 | 90 | '@esbuild/android-arm@0.25.7': 91 | resolution: {integrity: sha512-Jhuet0g1k9rAJHrXGIh7sFknFuT4sfytYZpZpuZl7YKDhnPByVAm5oy2LEBmMbuYf3ejWVYCc2seX81Mk+madA==} 92 | engines: {node: '>=18'} 93 | cpu: [arm] 94 | os: [android] 95 | 96 | '@esbuild/android-x64@0.25.7': 97 | resolution: {integrity: sha512-mMxIJFlSgVK23HSsII3ZX9T2xKrBCDGyk0qiZnIW10LLFFtZLkFD6imZHu7gUo2wkNZwS9Yj3mOtZD3ZPcjCcw==} 98 | engines: {node: '>=18'} 99 | cpu: [x64] 100 | os: [android] 101 | 102 | '@esbuild/darwin-arm64@0.25.7': 103 | resolution: {integrity: sha512-jyOFLGP2WwRwxM8F1VpP6gcdIJc8jq2CUrURbbTouJoRO7XCkU8GdnTDFIHdcifVBT45cJlOYsZ1kSlfbKjYUQ==} 104 | engines: {node: '>=18'} 105 | cpu: [arm64] 106 | os: [darwin] 107 | 108 | '@esbuild/darwin-x64@0.25.7': 109 | resolution: {integrity: sha512-m9bVWqZCwQ1BthruifvG64hG03zzz9gE2r/vYAhztBna1/+qXiHyP9WgnyZqHgGeXoimJPhAmxfbeU+nMng6ZA==} 110 | engines: {node: '>=18'} 111 | cpu: [x64] 112 | os: [darwin] 113 | 114 | '@esbuild/freebsd-arm64@0.25.7': 115 | resolution: {integrity: sha512-Bss7P4r6uhr3kDzRjPNEnTm/oIBdTPRNQuwaEFWT/uvt6A1YzK/yn5kcx5ZxZ9swOga7LqeYlu7bDIpDoS01bA==} 116 | engines: {node: '>=18'} 117 | cpu: [arm64] 118 | os: [freebsd] 119 | 120 | '@esbuild/freebsd-x64@0.25.7': 121 | resolution: {integrity: sha512-S3BFyjW81LXG7Vqmr37ddbThrm3A84yE7ey/ERBlK9dIiaWgrjRlre3pbG7txh1Uaxz8N7wGGQXmC9zV+LIpBQ==} 122 | engines: {node: '>=18'} 123 | cpu: [x64] 124 | os: [freebsd] 125 | 126 | '@esbuild/linux-arm64@0.25.7': 127 | resolution: {integrity: sha512-HfQZQqrNOfS1Okn7PcsGUqHymL1cWGBslf78dGvtrj8q7cN3FkapFgNA4l/a5lXDwr7BqP2BSO6mz9UremNPbg==} 128 | engines: {node: '>=18'} 129 | cpu: [arm64] 130 | os: [linux] 131 | 132 | '@esbuild/linux-arm@0.25.7': 133 | resolution: {integrity: sha512-JZMIci/1m5vfQuhKoFXogCKVYVfYQmoZJg8vSIMR4TUXbF+0aNlfXH3DGFEFMElT8hOTUF5hisdZhnrZO/bkDw==} 134 | engines: {node: '>=18'} 135 | cpu: [arm] 136 | os: [linux] 137 | 138 | '@esbuild/linux-ia32@0.25.7': 139 | resolution: {integrity: sha512-9Jex4uVpdeofiDxnwHRgen+j6398JlX4/6SCbbEFEXN7oMO2p0ueLN+e+9DdsdPLUdqns607HmzEFnxwr7+5wQ==} 140 | engines: {node: '>=18'} 141 | cpu: [ia32] 142 | os: [linux] 143 | 144 | '@esbuild/linux-loong64@0.25.7': 145 | resolution: {integrity: sha512-TG1KJqjBlN9IHQjKVUYDB0/mUGgokfhhatlay8aZ/MSORMubEvj/J1CL8YGY4EBcln4z7rKFbsH+HeAv0d471w==} 146 | engines: {node: '>=18'} 147 | cpu: [loong64] 148 | os: [linux] 149 | 150 | '@esbuild/linux-mips64el@0.25.7': 151 | resolution: {integrity: sha512-Ty9Hj/lx7ikTnhOfaP7ipEm/ICcBv94i/6/WDg0OZ3BPBHhChsUbQancoWYSO0WNkEiSW5Do4febTTy4x1qYQQ==} 152 | engines: {node: '>=18'} 153 | cpu: [mips64el] 154 | os: [linux] 155 | 156 | '@esbuild/linux-ppc64@0.25.7': 157 | resolution: {integrity: sha512-MrOjirGQWGReJl3BNQ58BLhUBPpWABnKrnq8Q/vZWWwAB1wuLXOIxS2JQ1LT3+5T+3jfPh0tyf5CpbyQHqnWIQ==} 158 | engines: {node: '>=18'} 159 | cpu: [ppc64] 160 | os: [linux] 161 | 162 | '@esbuild/linux-riscv64@0.25.7': 163 | resolution: {integrity: sha512-9pr23/pqzyqIZEZmQXnFyqp3vpa+KBk5TotfkzGMqpw089PGm0AIowkUppHB9derQzqniGn3wVXgck19+oqiOw==} 164 | engines: {node: '>=18'} 165 | cpu: [riscv64] 166 | os: [linux] 167 | 168 | '@esbuild/linux-s390x@0.25.7': 169 | resolution: {integrity: sha512-4dP11UVGh9O6Y47m8YvW8eoA3r8qL2toVZUbBKyGta8j6zdw1cn9F/Rt59/Mhv0OgY68pHIMjGXWOUaykCnx+w==} 170 | engines: {node: '>=18'} 171 | cpu: [s390x] 172 | os: [linux] 173 | 174 | '@esbuild/linux-x64@0.25.7': 175 | resolution: {integrity: sha512-ghJMAJTdw/0uhz7e7YnpdX1xVn7VqA0GrWrAO2qKMuqbvgHT2VZiBv1BQ//VcHsPir4wsL3P2oPggfKPzTKoCA==} 176 | engines: {node: '>=18'} 177 | cpu: [x64] 178 | os: [linux] 179 | 180 | '@esbuild/netbsd-arm64@0.25.7': 181 | resolution: {integrity: sha512-bwXGEU4ua45+u5Ci/a55B85KWaDSRS8NPOHtxy2e3etDjbz23wlry37Ffzapz69JAGGc4089TBo+dGzydQmydg==} 182 | engines: {node: '>=18'} 183 | cpu: [arm64] 184 | os: [netbsd] 185 | 186 | '@esbuild/netbsd-x64@0.25.7': 187 | resolution: {integrity: sha512-tUZRvLtgLE5OyN46sPSYlgmHoBS5bx2URSrgZdW1L1teWPYVmXh+QN/sKDqkzBo/IHGcKcHLKDhBeVVkO7teEA==} 188 | engines: {node: '>=18'} 189 | cpu: [x64] 190 | os: [netbsd] 191 | 192 | '@esbuild/openbsd-arm64@0.25.7': 193 | resolution: {integrity: sha512-bTJ50aoC+WDlDGBReWYiObpYvQfMjBNlKztqoNUL0iUkYtwLkBQQeEsTq/I1KyjsKA5tyov6VZaPb8UdD6ci6Q==} 194 | engines: {node: '>=18'} 195 | cpu: [arm64] 196 | os: [openbsd] 197 | 198 | '@esbuild/openbsd-x64@0.25.7': 199 | resolution: {integrity: sha512-TA9XfJrgzAipFUU895jd9j2SyDh9bbNkK2I0gHcvqb/o84UeQkBpi/XmYX3cO1q/9hZokdcDqQxIi6uLVrikxg==} 200 | engines: {node: '>=18'} 201 | cpu: [x64] 202 | os: [openbsd] 203 | 204 | '@esbuild/openharmony-arm64@0.25.7': 205 | resolution: {integrity: sha512-5VTtExUrWwHHEUZ/N+rPlHDwVFQ5aME7vRJES8+iQ0xC/bMYckfJ0l2n3yGIfRoXcK/wq4oXSItZAz5wslTKGw==} 206 | engines: {node: '>=18'} 207 | cpu: [arm64] 208 | os: [openharmony] 209 | 210 | '@esbuild/sunos-x64@0.25.7': 211 | resolution: {integrity: sha512-umkbn7KTxsexhv2vuuJmj9kggd4AEtL32KodkJgfhNOHMPtQ55RexsaSrMb+0+jp9XL4I4o2y91PZauVN4cH3A==} 212 | engines: {node: '>=18'} 213 | cpu: [x64] 214 | os: [sunos] 215 | 216 | '@esbuild/win32-arm64@0.25.7': 217 | resolution: {integrity: sha512-j20JQGP/gz8QDgzl5No5Gr4F6hurAZvtkFxAKhiv2X49yi/ih8ECK4Y35YnjlMogSKJk931iNMcd35BtZ4ghfw==} 218 | engines: {node: '>=18'} 219 | cpu: [arm64] 220 | os: [win32] 221 | 222 | '@esbuild/win32-ia32@0.25.7': 223 | resolution: {integrity: sha512-4qZ6NUfoiiKZfLAXRsvFkA0hoWVM+1y2bSHXHkpdLAs/+r0LgwqYohmfZCi985c6JWHhiXP30mgZawn/XrqAkQ==} 224 | engines: {node: '>=18'} 225 | cpu: [ia32] 226 | os: [win32] 227 | 228 | '@esbuild/win32-x64@0.25.7': 229 | resolution: {integrity: sha512-FaPsAHTwm+1Gfvn37Eg3E5HIpfR3i6x1AIcla/MkqAIupD4BW3MrSeUqfoTzwwJhk3WE2/KqUn4/eenEJC76VA==} 230 | engines: {node: '>=18'} 231 | cpu: [x64] 232 | os: [win32] 233 | 234 | '@eslint-community/eslint-utils@4.7.0': 235 | resolution: {integrity: sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==} 236 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 237 | peerDependencies: 238 | eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 239 | 240 | '@eslint-community/regexpp@4.12.1': 241 | resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} 242 | engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} 243 | 244 | '@eslint/config-array@0.21.0': 245 | resolution: {integrity: sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==} 246 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 247 | 248 | '@eslint/config-helpers@0.3.0': 249 | resolution: {integrity: sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==} 250 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 251 | 252 | '@eslint/core@0.15.1': 253 | resolution: {integrity: sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==} 254 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 255 | 256 | '@eslint/eslintrc@3.3.1': 257 | resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} 258 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 259 | 260 | '@eslint/js@9.31.0': 261 | resolution: {integrity: sha512-LOm5OVt7D4qiKCqoiPbA7LWmI+tbw1VbTUowBcUMgQSuM6poJufkFkYDcQpo5KfgD39TnNySV26QjOh7VFpSyw==} 262 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 263 | 264 | '@eslint/object-schema@2.1.6': 265 | resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==} 266 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 267 | 268 | '@eslint/plugin-kit@0.3.3': 269 | resolution: {integrity: sha512-1+WqvgNMhmlAambTvT3KPtCl/Ibr68VldY2XY40SL1CE0ZXiakFR/cbTspaF5HsnpDMvcYYoJHfl4980NBjGag==} 270 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 271 | 272 | '@humanfs/core@0.19.1': 273 | resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} 274 | engines: {node: '>=18.18.0'} 275 | 276 | '@humanfs/node@0.16.6': 277 | resolution: {integrity: sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==} 278 | engines: {node: '>=18.18.0'} 279 | 280 | '@humanwhocodes/module-importer@1.0.1': 281 | resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} 282 | engines: {node: '>=12.22'} 283 | 284 | '@humanwhocodes/retry@0.3.1': 285 | resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==} 286 | engines: {node: '>=18.18'} 287 | 288 | '@humanwhocodes/retry@0.4.3': 289 | resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} 290 | engines: {node: '>=18.18'} 291 | 292 | '@isaacs/balanced-match@4.0.1': 293 | resolution: {integrity: sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==} 294 | engines: {node: 20 || >=22} 295 | 296 | '@isaacs/brace-expansion@5.0.0': 297 | resolution: {integrity: sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==} 298 | engines: {node: 20 || >=22} 299 | 300 | '@isaacs/cliui@8.0.2': 301 | resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} 302 | engines: {node: '>=12'} 303 | 304 | '@istanbuljs/schema@0.1.3': 305 | resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} 306 | engines: {node: '>=8'} 307 | 308 | '@jridgewell/gen-mapping@0.3.12': 309 | resolution: {integrity: sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==} 310 | 311 | '@jridgewell/resolve-uri@3.1.2': 312 | resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} 313 | engines: {node: '>=6.0.0'} 314 | 315 | '@jridgewell/sourcemap-codec@1.5.4': 316 | resolution: {integrity: sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==} 317 | 318 | '@jridgewell/trace-mapping@0.3.29': 319 | resolution: {integrity: sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==} 320 | 321 | '@nodelib/fs.scandir@2.1.5': 322 | resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} 323 | engines: {node: '>= 8'} 324 | 325 | '@nodelib/fs.stat@2.0.5': 326 | resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} 327 | engines: {node: '>= 8'} 328 | 329 | '@nodelib/fs.walk@1.2.8': 330 | resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} 331 | engines: {node: '>= 8'} 332 | 333 | '@pkgjs/parseargs@0.11.0': 334 | resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} 335 | engines: {node: '>=14'} 336 | 337 | '@rollup/rollup-android-arm-eabi@4.45.1': 338 | resolution: {integrity: sha512-NEySIFvMY0ZQO+utJkgoMiCAjMrGvnbDLHvcmlA33UXJpYBCvlBEbMMtV837uCkS+plG2umfhn0T5mMAxGrlRA==} 339 | cpu: [arm] 340 | os: [android] 341 | 342 | '@rollup/rollup-android-arm64@4.45.1': 343 | resolution: {integrity: sha512-ujQ+sMXJkg4LRJaYreaVx7Z/VMgBBd89wGS4qMrdtfUFZ+TSY5Rs9asgjitLwzeIbhwdEhyj29zhst3L1lKsRQ==} 344 | cpu: [arm64] 345 | os: [android] 346 | 347 | '@rollup/rollup-darwin-arm64@4.45.1': 348 | resolution: {integrity: sha512-FSncqHvqTm3lC6Y13xncsdOYfxGSLnP+73k815EfNmpewPs+EyM49haPS105Rh4aF5mJKywk9X0ogzLXZzN9lA==} 349 | cpu: [arm64] 350 | os: [darwin] 351 | 352 | '@rollup/rollup-darwin-x64@4.45.1': 353 | resolution: {integrity: sha512-2/vVn/husP5XI7Fsf/RlhDaQJ7x9zjvC81anIVbr4b/f0xtSmXQTFcGIQ/B1cXIYM6h2nAhJkdMHTnD7OtQ9Og==} 354 | cpu: [x64] 355 | os: [darwin] 356 | 357 | '@rollup/rollup-freebsd-arm64@4.45.1': 358 | resolution: {integrity: sha512-4g1kaDxQItZsrkVTdYQ0bxu4ZIQ32cotoQbmsAnW1jAE4XCMbcBPDirX5fyUzdhVCKgPcrwWuucI8yrVRBw2+g==} 359 | cpu: [arm64] 360 | os: [freebsd] 361 | 362 | '@rollup/rollup-freebsd-x64@4.45.1': 363 | resolution: {integrity: sha512-L/6JsfiL74i3uK1Ti2ZFSNsp5NMiM4/kbbGEcOCps99aZx3g8SJMO1/9Y0n/qKlWZfn6sScf98lEOUe2mBvW9A==} 364 | cpu: [x64] 365 | os: [freebsd] 366 | 367 | '@rollup/rollup-linux-arm-gnueabihf@4.45.1': 368 | resolution: {integrity: sha512-RkdOTu2jK7brlu+ZwjMIZfdV2sSYHK2qR08FUWcIoqJC2eywHbXr0L8T/pONFwkGukQqERDheaGTeedG+rra6Q==} 369 | cpu: [arm] 370 | os: [linux] 371 | 372 | '@rollup/rollup-linux-arm-musleabihf@4.45.1': 373 | resolution: {integrity: sha512-3kJ8pgfBt6CIIr1o+HQA7OZ9mp/zDk3ctekGl9qn/pRBgrRgfwiffaUmqioUGN9hv0OHv2gxmvdKOkARCtRb8Q==} 374 | cpu: [arm] 375 | os: [linux] 376 | 377 | '@rollup/rollup-linux-arm64-gnu@4.45.1': 378 | resolution: {integrity: sha512-k3dOKCfIVixWjG7OXTCOmDfJj3vbdhN0QYEqB+OuGArOChek22hn7Uy5A/gTDNAcCy5v2YcXRJ/Qcnm4/ma1xw==} 379 | cpu: [arm64] 380 | os: [linux] 381 | 382 | '@rollup/rollup-linux-arm64-musl@4.45.1': 383 | resolution: {integrity: sha512-PmI1vxQetnM58ZmDFl9/Uk2lpBBby6B6rF4muJc65uZbxCs0EA7hhKCk2PKlmZKuyVSHAyIw3+/SiuMLxKxWog==} 384 | cpu: [arm64] 385 | os: [linux] 386 | 387 | '@rollup/rollup-linux-loongarch64-gnu@4.45.1': 388 | resolution: {integrity: sha512-9UmI0VzGmNJ28ibHW2GpE2nF0PBQqsyiS4kcJ5vK+wuwGnV5RlqdczVocDSUfGX/Na7/XINRVoUgJyFIgipoRg==} 389 | cpu: [loong64] 390 | os: [linux] 391 | 392 | '@rollup/rollup-linux-powerpc64le-gnu@4.45.1': 393 | resolution: {integrity: sha512-7nR2KY8oEOUTD3pBAxIBBbZr0U7U+R9HDTPNy+5nVVHDXI4ikYniH1oxQz9VoB5PbBU1CZuDGHkLJkd3zLMWsg==} 394 | cpu: [ppc64] 395 | os: [linux] 396 | 397 | '@rollup/rollup-linux-riscv64-gnu@4.45.1': 398 | resolution: {integrity: sha512-nlcl3jgUultKROfZijKjRQLUu9Ma0PeNv/VFHkZiKbXTBQXhpytS8CIj5/NfBeECZtY2FJQubm6ltIxm/ftxpw==} 399 | cpu: [riscv64] 400 | os: [linux] 401 | 402 | '@rollup/rollup-linux-riscv64-musl@4.45.1': 403 | resolution: {integrity: sha512-HJV65KLS51rW0VY6rvZkiieiBnurSzpzore1bMKAhunQiECPuxsROvyeaot/tcK3A3aGnI+qTHqisrpSgQrpgA==} 404 | cpu: [riscv64] 405 | os: [linux] 406 | 407 | '@rollup/rollup-linux-s390x-gnu@4.45.1': 408 | resolution: {integrity: sha512-NITBOCv3Qqc6hhwFt7jLV78VEO/il4YcBzoMGGNxznLgRQf43VQDae0aAzKiBeEPIxnDrACiMgbqjuihx08OOw==} 409 | cpu: [s390x] 410 | os: [linux] 411 | 412 | '@rollup/rollup-linux-x64-gnu@4.45.1': 413 | resolution: {integrity: sha512-+E/lYl6qu1zqgPEnTrs4WysQtvc/Sh4fC2nByfFExqgYrqkKWp1tWIbe+ELhixnenSpBbLXNi6vbEEJ8M7fiHw==} 414 | cpu: [x64] 415 | os: [linux] 416 | 417 | '@rollup/rollup-linux-x64-musl@4.45.1': 418 | resolution: {integrity: sha512-a6WIAp89p3kpNoYStITT9RbTbTnqarU7D8N8F2CV+4Cl9fwCOZraLVuVFvlpsW0SbIiYtEnhCZBPLoNdRkjQFw==} 419 | cpu: [x64] 420 | os: [linux] 421 | 422 | '@rollup/rollup-win32-arm64-msvc@4.45.1': 423 | resolution: {integrity: sha512-T5Bi/NS3fQiJeYdGvRpTAP5P02kqSOpqiopwhj0uaXB6nzs5JVi2XMJb18JUSKhCOX8+UE1UKQufyD6Or48dJg==} 424 | cpu: [arm64] 425 | os: [win32] 426 | 427 | '@rollup/rollup-win32-ia32-msvc@4.45.1': 428 | resolution: {integrity: sha512-lxV2Pako3ujjuUe9jiU3/s7KSrDfH6IgTSQOnDWr9aJ92YsFd7EurmClK0ly/t8dzMkDtd04g60WX6yl0sGfdw==} 429 | cpu: [ia32] 430 | os: [win32] 431 | 432 | '@rollup/rollup-win32-x64-msvc@4.45.1': 433 | resolution: {integrity: sha512-M/fKi4sasCdM8i0aWJjCSFm2qEnYRR8AMLG2kxp6wD13+tMGA4Z1tVAuHkNRjud5SW2EM3naLuK35w9twvf6aA==} 434 | cpu: [x64] 435 | os: [win32] 436 | 437 | '@types/chai@5.2.2': 438 | resolution: {integrity: sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==} 439 | 440 | '@types/deep-eql@4.0.2': 441 | resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} 442 | 443 | '@types/estree@1.0.8': 444 | resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} 445 | 446 | '@types/json-schema@7.0.15': 447 | resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} 448 | 449 | '@types/node@24.0.14': 450 | resolution: {integrity: sha512-4zXMWD91vBLGRtHK3YbIoFMia+1nqEz72coM42C5ETjnNCa/heoj7NT1G67iAfOqMmcfhuCZ4uNpyz8EjlAejw==} 451 | 452 | '@typescript-eslint/eslint-plugin@8.37.0': 453 | resolution: {integrity: sha512-jsuVWeIkb6ggzB+wPCsR4e6loj+rM72ohW6IBn2C+5NCvfUVY8s33iFPySSVXqtm5Hu29Ne/9bnA0JmyLmgenA==} 454 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 455 | peerDependencies: 456 | '@typescript-eslint/parser': ^8.37.0 457 | eslint: ^8.57.0 || ^9.0.0 458 | typescript: '>=4.8.4 <5.9.0' 459 | 460 | '@typescript-eslint/parser@8.37.0': 461 | resolution: {integrity: sha512-kVIaQE9vrN9RLCQMQ3iyRlVJpTiDUY6woHGb30JDkfJErqrQEmtdWH3gV0PBAfGZgQXoqzXOO0T3K6ioApbbAA==} 462 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 463 | peerDependencies: 464 | eslint: ^8.57.0 || ^9.0.0 465 | typescript: '>=4.8.4 <5.9.0' 466 | 467 | '@typescript-eslint/project-service@8.37.0': 468 | resolution: {integrity: sha512-BIUXYsbkl5A1aJDdYJCBAo8rCEbAvdquQ8AnLb6z5Lp1u3x5PNgSSx9A/zqYc++Xnr/0DVpls8iQ2cJs/izTXA==} 469 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 470 | peerDependencies: 471 | typescript: '>=4.8.4 <5.9.0' 472 | 473 | '@typescript-eslint/scope-manager@8.37.0': 474 | resolution: {integrity: sha512-0vGq0yiU1gbjKob2q691ybTg9JX6ShiVXAAfm2jGf3q0hdP6/BruaFjL/ManAR/lj05AvYCH+5bbVo0VtzmjOA==} 475 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 476 | 477 | '@typescript-eslint/tsconfig-utils@8.37.0': 478 | resolution: {integrity: sha512-1/YHvAVTimMM9mmlPvTec9NP4bobA1RkDbMydxG8omqwJJLEW/Iy2C4adsAESIXU3WGLXFHSZUU+C9EoFWl4Zg==} 479 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 480 | peerDependencies: 481 | typescript: '>=4.8.4 <5.9.0' 482 | 483 | '@typescript-eslint/type-utils@8.37.0': 484 | resolution: {integrity: sha512-SPkXWIkVZxhgwSwVq9rqj/4VFo7MnWwVaRNznfQDc/xPYHjXnPfLWn+4L6FF1cAz6e7dsqBeMawgl7QjUMj4Ow==} 485 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 486 | peerDependencies: 487 | eslint: ^8.57.0 || ^9.0.0 488 | typescript: '>=4.8.4 <5.9.0' 489 | 490 | '@typescript-eslint/types@8.37.0': 491 | resolution: {integrity: sha512-ax0nv7PUF9NOVPs+lmQ7yIE7IQmAf8LGcXbMvHX5Gm+YJUYNAl340XkGnrimxZ0elXyoQJuN5sbg6C4evKA4SQ==} 492 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 493 | 494 | '@typescript-eslint/typescript-estree@8.37.0': 495 | resolution: {integrity: sha512-zuWDMDuzMRbQOM+bHyU4/slw27bAUEcKSKKs3hcv2aNnc/tvE/h7w60dwVw8vnal2Pub6RT1T7BI8tFZ1fE+yg==} 496 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 497 | peerDependencies: 498 | typescript: '>=4.8.4 <5.9.0' 499 | 500 | '@typescript-eslint/utils@8.37.0': 501 | resolution: {integrity: sha512-TSFvkIW6gGjN2p6zbXo20FzCABbyUAuq6tBvNRGsKdsSQ6a7rnV6ADfZ7f4iI3lIiXc4F4WWvtUfDw9CJ9pO5A==} 502 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 503 | peerDependencies: 504 | eslint: ^8.57.0 || ^9.0.0 505 | typescript: '>=4.8.4 <5.9.0' 506 | 507 | '@typescript-eslint/visitor-keys@8.37.0': 508 | resolution: {integrity: sha512-YzfhzcTnZVPiLfP/oeKtDp2evwvHLMe0LOy7oe+hb9KKIumLNohYS9Hgp1ifwpu42YWxhZE8yieggz6JpqO/1w==} 509 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 510 | 511 | '@vitest/coverage-v8@3.2.4': 512 | resolution: {integrity: sha512-EyF9SXU6kS5Ku/U82E259WSnvg6c8KTjppUncuNdm5QHpe17mwREHnjDzozC8x9MZ0xfBUFSaLkRv4TMA75ALQ==} 513 | peerDependencies: 514 | '@vitest/browser': 3.2.4 515 | vitest: 3.2.4 516 | peerDependenciesMeta: 517 | '@vitest/browser': 518 | optional: true 519 | 520 | '@vitest/expect@3.2.4': 521 | resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==} 522 | 523 | '@vitest/mocker@3.2.4': 524 | resolution: {integrity: sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==} 525 | peerDependencies: 526 | msw: ^2.4.9 527 | vite: ^5.0.0 || ^6.0.0 || ^7.0.0-0 528 | peerDependenciesMeta: 529 | msw: 530 | optional: true 531 | vite: 532 | optional: true 533 | 534 | '@vitest/pretty-format@3.2.4': 535 | resolution: {integrity: sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==} 536 | 537 | '@vitest/runner@3.2.4': 538 | resolution: {integrity: sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==} 539 | 540 | '@vitest/snapshot@3.2.4': 541 | resolution: {integrity: sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==} 542 | 543 | '@vitest/spy@3.2.4': 544 | resolution: {integrity: sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==} 545 | 546 | '@vitest/utils@3.2.4': 547 | resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==} 548 | 549 | acorn-jsx@5.3.2: 550 | resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} 551 | peerDependencies: 552 | acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 553 | 554 | acorn@8.15.0: 555 | resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} 556 | engines: {node: '>=0.4.0'} 557 | hasBin: true 558 | 559 | ajv@6.12.6: 560 | resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} 561 | 562 | ansi-regex@5.0.1: 563 | resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} 564 | engines: {node: '>=8'} 565 | 566 | ansi-regex@6.1.0: 567 | resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==} 568 | engines: {node: '>=12'} 569 | 570 | ansi-styles@4.3.0: 571 | resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} 572 | engines: {node: '>=8'} 573 | 574 | ansi-styles@6.2.1: 575 | resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} 576 | engines: {node: '>=12'} 577 | 578 | any-promise@1.3.0: 579 | resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} 580 | 581 | argparse@2.0.1: 582 | resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} 583 | 584 | assertion-error@2.0.1: 585 | resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} 586 | engines: {node: '>=12'} 587 | 588 | ast-v8-to-istanbul@0.3.3: 589 | resolution: {integrity: sha512-MuXMrSLVVoA6sYN/6Hke18vMzrT4TZNbZIj/hvh0fnYFpO+/kFXcLIaiPwXXWaQUPg4yJD8fj+lfJ7/1EBconw==} 590 | 591 | balanced-match@1.0.2: 592 | resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} 593 | 594 | brace-expansion@1.1.12: 595 | resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} 596 | 597 | brace-expansion@2.0.2: 598 | resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} 599 | 600 | braces@3.0.3: 601 | resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} 602 | engines: {node: '>=8'} 603 | 604 | bundle-require@5.1.0: 605 | resolution: {integrity: sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==} 606 | engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} 607 | peerDependencies: 608 | esbuild: '>=0.18' 609 | 610 | cac@6.7.14: 611 | resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} 612 | engines: {node: '>=8'} 613 | 614 | callsites@3.1.0: 615 | resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} 616 | engines: {node: '>=6'} 617 | 618 | chai@5.2.1: 619 | resolution: {integrity: sha512-5nFxhUrX0PqtyogoYOA8IPswy5sZFTOsBFl/9bNsmDLgsxYTzSZQJDPppDnZPTQbzSEm0hqGjWPzRemQCYbD6A==} 620 | engines: {node: '>=18'} 621 | 622 | chalk@4.1.2: 623 | resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} 624 | engines: {node: '>=10'} 625 | 626 | check-error@2.1.1: 627 | resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} 628 | engines: {node: '>= 16'} 629 | 630 | chokidar@4.0.3: 631 | resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} 632 | engines: {node: '>= 14.16.0'} 633 | 634 | color-convert@2.0.1: 635 | resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} 636 | engines: {node: '>=7.0.0'} 637 | 638 | color-name@1.1.4: 639 | resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} 640 | 641 | commander@4.1.1: 642 | resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} 643 | engines: {node: '>= 6'} 644 | 645 | concat-map@0.0.1: 646 | resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} 647 | 648 | confbox@0.1.8: 649 | resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} 650 | 651 | consola@3.4.2: 652 | resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==} 653 | engines: {node: ^14.18.0 || >=16.10.0} 654 | 655 | cross-spawn@7.0.6: 656 | resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} 657 | engines: {node: '>= 8'} 658 | 659 | debug@4.4.1: 660 | resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} 661 | engines: {node: '>=6.0'} 662 | peerDependencies: 663 | supports-color: '*' 664 | peerDependenciesMeta: 665 | supports-color: 666 | optional: true 667 | 668 | deep-eql@5.0.2: 669 | resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} 670 | engines: {node: '>=6'} 671 | 672 | deep-is@0.1.4: 673 | resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} 674 | 675 | eastasianwidth@0.2.0: 676 | resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} 677 | 678 | emoji-regex@8.0.0: 679 | resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} 680 | 681 | emoji-regex@9.2.2: 682 | resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} 683 | 684 | es-module-lexer@1.7.0: 685 | resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} 686 | 687 | esbuild@0.25.7: 688 | resolution: {integrity: sha512-daJB0q2dmTzo90L9NjRaohhRWrCzYxWNFTjEi72/h+p5DcY3yn4MacWfDakHmaBaDzDiuLJsCh0+6LK/iX+c+Q==} 689 | engines: {node: '>=18'} 690 | hasBin: true 691 | 692 | escape-string-regexp@4.0.0: 693 | resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} 694 | engines: {node: '>=10'} 695 | 696 | eslint-config-prettier@10.1.8: 697 | resolution: {integrity: sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==} 698 | hasBin: true 699 | peerDependencies: 700 | eslint: '>=7.0.0' 701 | 702 | eslint-plugin-simple-import-sort@12.1.1: 703 | resolution: {integrity: sha512-6nuzu4xwQtE3332Uz0to+TxDQYRLTKRESSc2hefVT48Zc8JthmN23Gx9lnYhu0FtkRSL1oxny3kJ2aveVhmOVA==} 704 | peerDependencies: 705 | eslint: '>=5.0.0' 706 | 707 | eslint-scope@8.4.0: 708 | resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} 709 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 710 | 711 | eslint-visitor-keys@3.4.3: 712 | resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} 713 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 714 | 715 | eslint-visitor-keys@4.2.1: 716 | resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} 717 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 718 | 719 | eslint@9.31.0: 720 | resolution: {integrity: sha512-QldCVh/ztyKJJZLr4jXNUByx3gR+TDYZCRXEktiZoUR3PGy4qCmSbkxcIle8GEwGpb5JBZazlaJ/CxLidXdEbQ==} 721 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 722 | hasBin: true 723 | peerDependencies: 724 | jiti: '*' 725 | peerDependenciesMeta: 726 | jiti: 727 | optional: true 728 | 729 | espree@10.4.0: 730 | resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} 731 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 732 | 733 | esquery@1.6.0: 734 | resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} 735 | engines: {node: '>=0.10'} 736 | 737 | esrecurse@4.3.0: 738 | resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} 739 | engines: {node: '>=4.0'} 740 | 741 | estraverse@5.3.0: 742 | resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} 743 | engines: {node: '>=4.0'} 744 | 745 | estree-walker@3.0.3: 746 | resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} 747 | 748 | esutils@2.0.3: 749 | resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} 750 | engines: {node: '>=0.10.0'} 751 | 752 | expect-type@1.2.2: 753 | resolution: {integrity: sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==} 754 | engines: {node: '>=12.0.0'} 755 | 756 | fast-deep-equal@3.1.3: 757 | resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} 758 | 759 | fast-glob@3.3.3: 760 | resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} 761 | engines: {node: '>=8.6.0'} 762 | 763 | fast-json-stable-stringify@2.1.0: 764 | resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} 765 | 766 | fast-levenshtein@2.0.6: 767 | resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} 768 | 769 | fastq@1.19.1: 770 | resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} 771 | 772 | fdir@6.4.6: 773 | resolution: {integrity: sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==} 774 | peerDependencies: 775 | picomatch: ^3 || ^4 776 | peerDependenciesMeta: 777 | picomatch: 778 | optional: true 779 | 780 | file-entry-cache@8.0.0: 781 | resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} 782 | engines: {node: '>=16.0.0'} 783 | 784 | fill-range@7.1.1: 785 | resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} 786 | engines: {node: '>=8'} 787 | 788 | find-up@5.0.0: 789 | resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} 790 | engines: {node: '>=10'} 791 | 792 | fix-dts-default-cjs-exports@1.0.1: 793 | resolution: {integrity: sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg==} 794 | 795 | flat-cache@4.0.1: 796 | resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} 797 | engines: {node: '>=16'} 798 | 799 | flatted@3.3.3: 800 | resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} 801 | 802 | foreground-child@3.3.1: 803 | resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} 804 | engines: {node: '>=14'} 805 | 806 | fsevents@2.3.3: 807 | resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} 808 | engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} 809 | os: [darwin] 810 | 811 | glob-parent@5.1.2: 812 | resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} 813 | engines: {node: '>= 6'} 814 | 815 | glob-parent@6.0.2: 816 | resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} 817 | engines: {node: '>=10.13.0'} 818 | 819 | glob@10.4.5: 820 | resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} 821 | hasBin: true 822 | 823 | glob@11.0.3: 824 | resolution: {integrity: sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA==} 825 | engines: {node: 20 || >=22} 826 | hasBin: true 827 | 828 | globals@14.0.0: 829 | resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} 830 | engines: {node: '>=18'} 831 | 832 | graphemer@1.4.0: 833 | resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} 834 | 835 | has-flag@4.0.0: 836 | resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} 837 | engines: {node: '>=8'} 838 | 839 | html-escaper@2.0.2: 840 | resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} 841 | 842 | ignore@5.3.2: 843 | resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} 844 | engines: {node: '>= 4'} 845 | 846 | ignore@7.0.5: 847 | resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} 848 | engines: {node: '>= 4'} 849 | 850 | import-fresh@3.3.1: 851 | resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} 852 | engines: {node: '>=6'} 853 | 854 | imurmurhash@0.1.4: 855 | resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} 856 | engines: {node: '>=0.8.19'} 857 | 858 | is-extglob@2.1.1: 859 | resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} 860 | engines: {node: '>=0.10.0'} 861 | 862 | is-fullwidth-code-point@3.0.0: 863 | resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} 864 | engines: {node: '>=8'} 865 | 866 | is-glob@4.0.3: 867 | resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} 868 | engines: {node: '>=0.10.0'} 869 | 870 | is-number@7.0.0: 871 | resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} 872 | engines: {node: '>=0.12.0'} 873 | 874 | isexe@2.0.0: 875 | resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} 876 | 877 | istanbul-lib-coverage@3.2.2: 878 | resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} 879 | engines: {node: '>=8'} 880 | 881 | istanbul-lib-report@3.0.1: 882 | resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} 883 | engines: {node: '>=10'} 884 | 885 | istanbul-lib-source-maps@5.0.6: 886 | resolution: {integrity: sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==} 887 | engines: {node: '>=10'} 888 | 889 | istanbul-reports@3.1.7: 890 | resolution: {integrity: sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==} 891 | engines: {node: '>=8'} 892 | 893 | jackspeak@3.4.3: 894 | resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} 895 | 896 | jackspeak@4.1.1: 897 | resolution: {integrity: sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==} 898 | engines: {node: 20 || >=22} 899 | 900 | joycon@3.1.1: 901 | resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} 902 | engines: {node: '>=10'} 903 | 904 | js-tokens@9.0.1: 905 | resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==} 906 | 907 | js-yaml@4.1.0: 908 | resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} 909 | hasBin: true 910 | 911 | json-buffer@3.0.1: 912 | resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} 913 | 914 | json-schema-traverse@0.4.1: 915 | resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} 916 | 917 | json-stable-stringify-without-jsonify@1.0.1: 918 | resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} 919 | 920 | keyv@4.5.4: 921 | resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} 922 | 923 | levn@0.4.1: 924 | resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} 925 | engines: {node: '>= 0.8.0'} 926 | 927 | lilconfig@3.1.3: 928 | resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} 929 | engines: {node: '>=14'} 930 | 931 | lines-and-columns@1.2.4: 932 | resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} 933 | 934 | load-tsconfig@0.2.5: 935 | resolution: {integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==} 936 | engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} 937 | 938 | locate-path@6.0.0: 939 | resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} 940 | engines: {node: '>=10'} 941 | 942 | lodash.merge@4.6.2: 943 | resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} 944 | 945 | lodash.sortby@4.7.0: 946 | resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==} 947 | 948 | loupe@3.1.4: 949 | resolution: {integrity: sha512-wJzkKwJrheKtknCOKNEtDK4iqg/MxmZheEMtSTYvnzRdEYaZzmgH976nenp8WdJRdx5Vc1X/9MO0Oszl6ezeXg==} 950 | 951 | lru-cache@10.4.3: 952 | resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} 953 | 954 | lru-cache@11.1.0: 955 | resolution: {integrity: sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==} 956 | engines: {node: 20 || >=22} 957 | 958 | magic-string@0.30.17: 959 | resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} 960 | 961 | magicast@0.3.5: 962 | resolution: {integrity: sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==} 963 | 964 | make-dir@4.0.0: 965 | resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} 966 | engines: {node: '>=10'} 967 | 968 | merge2@1.4.1: 969 | resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} 970 | engines: {node: '>= 8'} 971 | 972 | micromatch@4.0.8: 973 | resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} 974 | engines: {node: '>=8.6'} 975 | 976 | minimatch@10.0.3: 977 | resolution: {integrity: sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==} 978 | engines: {node: 20 || >=22} 979 | 980 | minimatch@3.1.2: 981 | resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} 982 | 983 | minimatch@9.0.5: 984 | resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} 985 | engines: {node: '>=16 || 14 >=14.17'} 986 | 987 | minipass@7.1.2: 988 | resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} 989 | engines: {node: '>=16 || 14 >=14.17'} 990 | 991 | mlly@1.7.4: 992 | resolution: {integrity: sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==} 993 | 994 | ms@2.1.3: 995 | resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} 996 | 997 | mz@2.7.0: 998 | resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} 999 | 1000 | nanoid@3.3.11: 1001 | resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} 1002 | engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} 1003 | hasBin: true 1004 | 1005 | natural-compare@1.4.0: 1006 | resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} 1007 | 1008 | object-assign@4.1.1: 1009 | resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} 1010 | engines: {node: '>=0.10.0'} 1011 | 1012 | optionator@0.9.4: 1013 | resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} 1014 | engines: {node: '>= 0.8.0'} 1015 | 1016 | p-limit@3.1.0: 1017 | resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} 1018 | engines: {node: '>=10'} 1019 | 1020 | p-locate@5.0.0: 1021 | resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} 1022 | engines: {node: '>=10'} 1023 | 1024 | package-json-from-dist@1.0.1: 1025 | resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} 1026 | 1027 | parent-module@1.0.1: 1028 | resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} 1029 | engines: {node: '>=6'} 1030 | 1031 | path-exists@4.0.0: 1032 | resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} 1033 | engines: {node: '>=8'} 1034 | 1035 | path-key@3.1.1: 1036 | resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} 1037 | engines: {node: '>=8'} 1038 | 1039 | path-scurry@1.11.1: 1040 | resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} 1041 | engines: {node: '>=16 || 14 >=14.18'} 1042 | 1043 | path-scurry@2.0.0: 1044 | resolution: {integrity: sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==} 1045 | engines: {node: 20 || >=22} 1046 | 1047 | pathe@2.0.3: 1048 | resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} 1049 | 1050 | pathval@2.0.1: 1051 | resolution: {integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==} 1052 | engines: {node: '>= 14.16'} 1053 | 1054 | picocolors@1.1.1: 1055 | resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} 1056 | 1057 | picomatch@2.3.1: 1058 | resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} 1059 | engines: {node: '>=8.6'} 1060 | 1061 | picomatch@4.0.3: 1062 | resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} 1063 | engines: {node: '>=12'} 1064 | 1065 | pirates@4.0.7: 1066 | resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} 1067 | engines: {node: '>= 6'} 1068 | 1069 | pkg-types@1.3.1: 1070 | resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} 1071 | 1072 | postcss-load-config@6.0.1: 1073 | resolution: {integrity: sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==} 1074 | engines: {node: '>= 18'} 1075 | peerDependencies: 1076 | jiti: '>=1.21.0' 1077 | postcss: '>=8.0.9' 1078 | tsx: ^4.8.1 1079 | yaml: ^2.4.2 1080 | peerDependenciesMeta: 1081 | jiti: 1082 | optional: true 1083 | postcss: 1084 | optional: true 1085 | tsx: 1086 | optional: true 1087 | yaml: 1088 | optional: true 1089 | 1090 | postcss@8.5.6: 1091 | resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} 1092 | engines: {node: ^10 || ^12 || >=14} 1093 | 1094 | prelude-ls@1.2.1: 1095 | resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} 1096 | engines: {node: '>= 0.8.0'} 1097 | 1098 | prettier@3.6.2: 1099 | resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==} 1100 | engines: {node: '>=14'} 1101 | hasBin: true 1102 | 1103 | punycode@2.3.1: 1104 | resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} 1105 | engines: {node: '>=6'} 1106 | 1107 | queue-microtask@1.2.3: 1108 | resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} 1109 | 1110 | readdirp@4.1.2: 1111 | resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} 1112 | engines: {node: '>= 14.18.0'} 1113 | 1114 | resolve-from@4.0.0: 1115 | resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} 1116 | engines: {node: '>=4'} 1117 | 1118 | resolve-from@5.0.0: 1119 | resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} 1120 | engines: {node: '>=8'} 1121 | 1122 | reusify@1.1.0: 1123 | resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} 1124 | engines: {iojs: '>=1.0.0', node: '>=0.10.0'} 1125 | 1126 | rimraf@6.0.1: 1127 | resolution: {integrity: sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A==} 1128 | engines: {node: 20 || >=22} 1129 | hasBin: true 1130 | 1131 | rollup@4.45.1: 1132 | resolution: {integrity: sha512-4iya7Jb76fVpQyLoiVpzUrsjQ12r3dM7fIVz+4NwoYvZOShknRmiv+iu9CClZml5ZLGb0XMcYLutK6w9tgxHDw==} 1133 | engines: {node: '>=18.0.0', npm: '>=8.0.0'} 1134 | hasBin: true 1135 | 1136 | run-parallel@1.2.0: 1137 | resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} 1138 | 1139 | semver@7.7.2: 1140 | resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} 1141 | engines: {node: '>=10'} 1142 | hasBin: true 1143 | 1144 | shebang-command@2.0.0: 1145 | resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} 1146 | engines: {node: '>=8'} 1147 | 1148 | shebang-regex@3.0.0: 1149 | resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} 1150 | engines: {node: '>=8'} 1151 | 1152 | siginfo@2.0.0: 1153 | resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} 1154 | 1155 | signal-exit@4.1.0: 1156 | resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} 1157 | engines: {node: '>=14'} 1158 | 1159 | source-map-js@1.2.1: 1160 | resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} 1161 | engines: {node: '>=0.10.0'} 1162 | 1163 | source-map@0.8.0-beta.0: 1164 | resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==} 1165 | engines: {node: '>= 8'} 1166 | 1167 | stackback@0.0.2: 1168 | resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} 1169 | 1170 | std-env@3.9.0: 1171 | resolution: {integrity: sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==} 1172 | 1173 | string-width@4.2.3: 1174 | resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} 1175 | engines: {node: '>=8'} 1176 | 1177 | string-width@5.1.2: 1178 | resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} 1179 | engines: {node: '>=12'} 1180 | 1181 | strip-ansi@6.0.1: 1182 | resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} 1183 | engines: {node: '>=8'} 1184 | 1185 | strip-ansi@7.1.0: 1186 | resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} 1187 | engines: {node: '>=12'} 1188 | 1189 | strip-json-comments@3.1.1: 1190 | resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} 1191 | engines: {node: '>=8'} 1192 | 1193 | strip-literal@3.0.0: 1194 | resolution: {integrity: sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==} 1195 | 1196 | sucrase@3.35.0: 1197 | resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==} 1198 | engines: {node: '>=16 || 14 >=14.17'} 1199 | hasBin: true 1200 | 1201 | supports-color@7.2.0: 1202 | resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} 1203 | engines: {node: '>=8'} 1204 | 1205 | test-exclude@7.0.1: 1206 | resolution: {integrity: sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==} 1207 | engines: {node: '>=18'} 1208 | 1209 | thenify-all@1.6.0: 1210 | resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} 1211 | engines: {node: '>=0.8'} 1212 | 1213 | thenify@3.3.1: 1214 | resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} 1215 | 1216 | tinybench@2.9.0: 1217 | resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} 1218 | 1219 | tinyexec@0.3.2: 1220 | resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} 1221 | 1222 | tinyglobby@0.2.14: 1223 | resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==} 1224 | engines: {node: '>=12.0.0'} 1225 | 1226 | tinypool@1.1.1: 1227 | resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==} 1228 | engines: {node: ^18.0.0 || >=20.0.0} 1229 | 1230 | tinyrainbow@2.0.0: 1231 | resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} 1232 | engines: {node: '>=14.0.0'} 1233 | 1234 | tinyspy@4.0.3: 1235 | resolution: {integrity: sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A==} 1236 | engines: {node: '>=14.0.0'} 1237 | 1238 | to-regex-range@5.0.1: 1239 | resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} 1240 | engines: {node: '>=8.0'} 1241 | 1242 | tr46@1.0.1: 1243 | resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==} 1244 | 1245 | tree-kill@1.2.2: 1246 | resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} 1247 | hasBin: true 1248 | 1249 | ts-api-utils@2.1.0: 1250 | resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==} 1251 | engines: {node: '>=18.12'} 1252 | peerDependencies: 1253 | typescript: '>=4.8.4' 1254 | 1255 | ts-interface-checker@0.1.13: 1256 | resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} 1257 | 1258 | tsup@8.5.0: 1259 | resolution: {integrity: sha512-VmBp77lWNQq6PfuMqCHD3xWl22vEoWsKajkF8t+yMBawlUS8JzEI+vOVMeuNZIuMML8qXRizFKi9oD5glKQVcQ==} 1260 | engines: {node: '>=18'} 1261 | hasBin: true 1262 | peerDependencies: 1263 | '@microsoft/api-extractor': ^7.36.0 1264 | '@swc/core': ^1 1265 | postcss: ^8.4.12 1266 | typescript: '>=4.5.0' 1267 | peerDependenciesMeta: 1268 | '@microsoft/api-extractor': 1269 | optional: true 1270 | '@swc/core': 1271 | optional: true 1272 | postcss: 1273 | optional: true 1274 | typescript: 1275 | optional: true 1276 | 1277 | type-check@0.4.0: 1278 | resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} 1279 | engines: {node: '>= 0.8.0'} 1280 | 1281 | typescript@5.8.3: 1282 | resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} 1283 | engines: {node: '>=14.17'} 1284 | hasBin: true 1285 | 1286 | ufo@1.6.1: 1287 | resolution: {integrity: sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==} 1288 | 1289 | undici-types@7.8.0: 1290 | resolution: {integrity: sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==} 1291 | 1292 | uri-js@4.4.1: 1293 | resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} 1294 | 1295 | vite-node@3.2.4: 1296 | resolution: {integrity: sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==} 1297 | engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} 1298 | hasBin: true 1299 | 1300 | vite@7.0.5: 1301 | resolution: {integrity: sha512-1mncVwJxy2C9ThLwz0+2GKZyEXuC3MyWtAAlNftlZZXZDP3AJt5FmwcMit/IGGaNZ8ZOB2BNO/HFUB+CpN0NQw==} 1302 | engines: {node: ^20.19.0 || >=22.12.0} 1303 | hasBin: true 1304 | peerDependencies: 1305 | '@types/node': ^20.19.0 || >=22.12.0 1306 | jiti: '>=1.21.0' 1307 | less: ^4.0.0 1308 | lightningcss: ^1.21.0 1309 | sass: ^1.70.0 1310 | sass-embedded: ^1.70.0 1311 | stylus: '>=0.54.8' 1312 | sugarss: ^5.0.0 1313 | terser: ^5.16.0 1314 | tsx: ^4.8.1 1315 | yaml: ^2.4.2 1316 | peerDependenciesMeta: 1317 | '@types/node': 1318 | optional: true 1319 | jiti: 1320 | optional: true 1321 | less: 1322 | optional: true 1323 | lightningcss: 1324 | optional: true 1325 | sass: 1326 | optional: true 1327 | sass-embedded: 1328 | optional: true 1329 | stylus: 1330 | optional: true 1331 | sugarss: 1332 | optional: true 1333 | terser: 1334 | optional: true 1335 | tsx: 1336 | optional: true 1337 | yaml: 1338 | optional: true 1339 | 1340 | vitest@3.2.4: 1341 | resolution: {integrity: sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==} 1342 | engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} 1343 | hasBin: true 1344 | peerDependencies: 1345 | '@edge-runtime/vm': '*' 1346 | '@types/debug': ^4.1.12 1347 | '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 1348 | '@vitest/browser': 3.2.4 1349 | '@vitest/ui': 3.2.4 1350 | happy-dom: '*' 1351 | jsdom: '*' 1352 | peerDependenciesMeta: 1353 | '@edge-runtime/vm': 1354 | optional: true 1355 | '@types/debug': 1356 | optional: true 1357 | '@types/node': 1358 | optional: true 1359 | '@vitest/browser': 1360 | optional: true 1361 | '@vitest/ui': 1362 | optional: true 1363 | happy-dom: 1364 | optional: true 1365 | jsdom: 1366 | optional: true 1367 | 1368 | webidl-conversions@4.0.2: 1369 | resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==} 1370 | 1371 | whatwg-url@7.1.0: 1372 | resolution: {integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==} 1373 | 1374 | which@2.0.2: 1375 | resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} 1376 | engines: {node: '>= 8'} 1377 | hasBin: true 1378 | 1379 | why-is-node-running@2.3.0: 1380 | resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} 1381 | engines: {node: '>=8'} 1382 | hasBin: true 1383 | 1384 | word-wrap@1.2.5: 1385 | resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} 1386 | engines: {node: '>=0.10.0'} 1387 | 1388 | wrap-ansi@7.0.0: 1389 | resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} 1390 | engines: {node: '>=10'} 1391 | 1392 | wrap-ansi@8.1.0: 1393 | resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} 1394 | engines: {node: '>=12'} 1395 | 1396 | yocto-queue@0.1.0: 1397 | resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} 1398 | engines: {node: '>=10'} 1399 | 1400 | snapshots: 1401 | 1402 | '@ampproject/remapping@2.3.0': 1403 | dependencies: 1404 | '@jridgewell/gen-mapping': 0.3.12 1405 | '@jridgewell/trace-mapping': 0.3.29 1406 | 1407 | '@babel/helper-string-parser@7.27.1': {} 1408 | 1409 | '@babel/helper-validator-identifier@7.27.1': {} 1410 | 1411 | '@babel/parser@7.28.0': 1412 | dependencies: 1413 | '@babel/types': 7.28.1 1414 | 1415 | '@babel/types@7.28.1': 1416 | dependencies: 1417 | '@babel/helper-string-parser': 7.27.1 1418 | '@babel/helper-validator-identifier': 7.27.1 1419 | 1420 | '@bcoe/v8-coverage@1.0.2': {} 1421 | 1422 | '@esbuild/aix-ppc64@0.25.7': 1423 | optional: true 1424 | 1425 | '@esbuild/android-arm64@0.25.7': 1426 | optional: true 1427 | 1428 | '@esbuild/android-arm@0.25.7': 1429 | optional: true 1430 | 1431 | '@esbuild/android-x64@0.25.7': 1432 | optional: true 1433 | 1434 | '@esbuild/darwin-arm64@0.25.7': 1435 | optional: true 1436 | 1437 | '@esbuild/darwin-x64@0.25.7': 1438 | optional: true 1439 | 1440 | '@esbuild/freebsd-arm64@0.25.7': 1441 | optional: true 1442 | 1443 | '@esbuild/freebsd-x64@0.25.7': 1444 | optional: true 1445 | 1446 | '@esbuild/linux-arm64@0.25.7': 1447 | optional: true 1448 | 1449 | '@esbuild/linux-arm@0.25.7': 1450 | optional: true 1451 | 1452 | '@esbuild/linux-ia32@0.25.7': 1453 | optional: true 1454 | 1455 | '@esbuild/linux-loong64@0.25.7': 1456 | optional: true 1457 | 1458 | '@esbuild/linux-mips64el@0.25.7': 1459 | optional: true 1460 | 1461 | '@esbuild/linux-ppc64@0.25.7': 1462 | optional: true 1463 | 1464 | '@esbuild/linux-riscv64@0.25.7': 1465 | optional: true 1466 | 1467 | '@esbuild/linux-s390x@0.25.7': 1468 | optional: true 1469 | 1470 | '@esbuild/linux-x64@0.25.7': 1471 | optional: true 1472 | 1473 | '@esbuild/netbsd-arm64@0.25.7': 1474 | optional: true 1475 | 1476 | '@esbuild/netbsd-x64@0.25.7': 1477 | optional: true 1478 | 1479 | '@esbuild/openbsd-arm64@0.25.7': 1480 | optional: true 1481 | 1482 | '@esbuild/openbsd-x64@0.25.7': 1483 | optional: true 1484 | 1485 | '@esbuild/openharmony-arm64@0.25.7': 1486 | optional: true 1487 | 1488 | '@esbuild/sunos-x64@0.25.7': 1489 | optional: true 1490 | 1491 | '@esbuild/win32-arm64@0.25.7': 1492 | optional: true 1493 | 1494 | '@esbuild/win32-ia32@0.25.7': 1495 | optional: true 1496 | 1497 | '@esbuild/win32-x64@0.25.7': 1498 | optional: true 1499 | 1500 | '@eslint-community/eslint-utils@4.7.0(eslint@9.31.0)': 1501 | dependencies: 1502 | eslint: 9.31.0 1503 | eslint-visitor-keys: 3.4.3 1504 | 1505 | '@eslint-community/regexpp@4.12.1': {} 1506 | 1507 | '@eslint/config-array@0.21.0': 1508 | dependencies: 1509 | '@eslint/object-schema': 2.1.6 1510 | debug: 4.4.1 1511 | minimatch: 3.1.2 1512 | transitivePeerDependencies: 1513 | - supports-color 1514 | 1515 | '@eslint/config-helpers@0.3.0': {} 1516 | 1517 | '@eslint/core@0.15.1': 1518 | dependencies: 1519 | '@types/json-schema': 7.0.15 1520 | 1521 | '@eslint/eslintrc@3.3.1': 1522 | dependencies: 1523 | ajv: 6.12.6 1524 | debug: 4.4.1 1525 | espree: 10.4.0 1526 | globals: 14.0.0 1527 | ignore: 5.3.2 1528 | import-fresh: 3.3.1 1529 | js-yaml: 4.1.0 1530 | minimatch: 3.1.2 1531 | strip-json-comments: 3.1.1 1532 | transitivePeerDependencies: 1533 | - supports-color 1534 | 1535 | '@eslint/js@9.31.0': {} 1536 | 1537 | '@eslint/object-schema@2.1.6': {} 1538 | 1539 | '@eslint/plugin-kit@0.3.3': 1540 | dependencies: 1541 | '@eslint/core': 0.15.1 1542 | levn: 0.4.1 1543 | 1544 | '@humanfs/core@0.19.1': {} 1545 | 1546 | '@humanfs/node@0.16.6': 1547 | dependencies: 1548 | '@humanfs/core': 0.19.1 1549 | '@humanwhocodes/retry': 0.3.1 1550 | 1551 | '@humanwhocodes/module-importer@1.0.1': {} 1552 | 1553 | '@humanwhocodes/retry@0.3.1': {} 1554 | 1555 | '@humanwhocodes/retry@0.4.3': {} 1556 | 1557 | '@isaacs/balanced-match@4.0.1': {} 1558 | 1559 | '@isaacs/brace-expansion@5.0.0': 1560 | dependencies: 1561 | '@isaacs/balanced-match': 4.0.1 1562 | 1563 | '@isaacs/cliui@8.0.2': 1564 | dependencies: 1565 | string-width: 5.1.2 1566 | string-width-cjs: string-width@4.2.3 1567 | strip-ansi: 7.1.0 1568 | strip-ansi-cjs: strip-ansi@6.0.1 1569 | wrap-ansi: 8.1.0 1570 | wrap-ansi-cjs: wrap-ansi@7.0.0 1571 | 1572 | '@istanbuljs/schema@0.1.3': {} 1573 | 1574 | '@jridgewell/gen-mapping@0.3.12': 1575 | dependencies: 1576 | '@jridgewell/sourcemap-codec': 1.5.4 1577 | '@jridgewell/trace-mapping': 0.3.29 1578 | 1579 | '@jridgewell/resolve-uri@3.1.2': {} 1580 | 1581 | '@jridgewell/sourcemap-codec@1.5.4': {} 1582 | 1583 | '@jridgewell/trace-mapping@0.3.29': 1584 | dependencies: 1585 | '@jridgewell/resolve-uri': 3.1.2 1586 | '@jridgewell/sourcemap-codec': 1.5.4 1587 | 1588 | '@nodelib/fs.scandir@2.1.5': 1589 | dependencies: 1590 | '@nodelib/fs.stat': 2.0.5 1591 | run-parallel: 1.2.0 1592 | 1593 | '@nodelib/fs.stat@2.0.5': {} 1594 | 1595 | '@nodelib/fs.walk@1.2.8': 1596 | dependencies: 1597 | '@nodelib/fs.scandir': 2.1.5 1598 | fastq: 1.19.1 1599 | 1600 | '@pkgjs/parseargs@0.11.0': 1601 | optional: true 1602 | 1603 | '@rollup/rollup-android-arm-eabi@4.45.1': 1604 | optional: true 1605 | 1606 | '@rollup/rollup-android-arm64@4.45.1': 1607 | optional: true 1608 | 1609 | '@rollup/rollup-darwin-arm64@4.45.1': 1610 | optional: true 1611 | 1612 | '@rollup/rollup-darwin-x64@4.45.1': 1613 | optional: true 1614 | 1615 | '@rollup/rollup-freebsd-arm64@4.45.1': 1616 | optional: true 1617 | 1618 | '@rollup/rollup-freebsd-x64@4.45.1': 1619 | optional: true 1620 | 1621 | '@rollup/rollup-linux-arm-gnueabihf@4.45.1': 1622 | optional: true 1623 | 1624 | '@rollup/rollup-linux-arm-musleabihf@4.45.1': 1625 | optional: true 1626 | 1627 | '@rollup/rollup-linux-arm64-gnu@4.45.1': 1628 | optional: true 1629 | 1630 | '@rollup/rollup-linux-arm64-musl@4.45.1': 1631 | optional: true 1632 | 1633 | '@rollup/rollup-linux-loongarch64-gnu@4.45.1': 1634 | optional: true 1635 | 1636 | '@rollup/rollup-linux-powerpc64le-gnu@4.45.1': 1637 | optional: true 1638 | 1639 | '@rollup/rollup-linux-riscv64-gnu@4.45.1': 1640 | optional: true 1641 | 1642 | '@rollup/rollup-linux-riscv64-musl@4.45.1': 1643 | optional: true 1644 | 1645 | '@rollup/rollup-linux-s390x-gnu@4.45.1': 1646 | optional: true 1647 | 1648 | '@rollup/rollup-linux-x64-gnu@4.45.1': 1649 | optional: true 1650 | 1651 | '@rollup/rollup-linux-x64-musl@4.45.1': 1652 | optional: true 1653 | 1654 | '@rollup/rollup-win32-arm64-msvc@4.45.1': 1655 | optional: true 1656 | 1657 | '@rollup/rollup-win32-ia32-msvc@4.45.1': 1658 | optional: true 1659 | 1660 | '@rollup/rollup-win32-x64-msvc@4.45.1': 1661 | optional: true 1662 | 1663 | '@types/chai@5.2.2': 1664 | dependencies: 1665 | '@types/deep-eql': 4.0.2 1666 | 1667 | '@types/deep-eql@4.0.2': {} 1668 | 1669 | '@types/estree@1.0.8': {} 1670 | 1671 | '@types/json-schema@7.0.15': {} 1672 | 1673 | '@types/node@24.0.14': 1674 | dependencies: 1675 | undici-types: 7.8.0 1676 | 1677 | '@typescript-eslint/eslint-plugin@8.37.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0)(typescript@5.8.3)': 1678 | dependencies: 1679 | '@eslint-community/regexpp': 4.12.1 1680 | '@typescript-eslint/parser': 8.37.0(eslint@9.31.0)(typescript@5.8.3) 1681 | '@typescript-eslint/scope-manager': 8.37.0 1682 | '@typescript-eslint/type-utils': 8.37.0(eslint@9.31.0)(typescript@5.8.3) 1683 | '@typescript-eslint/utils': 8.37.0(eslint@9.31.0)(typescript@5.8.3) 1684 | '@typescript-eslint/visitor-keys': 8.37.0 1685 | eslint: 9.31.0 1686 | graphemer: 1.4.0 1687 | ignore: 7.0.5 1688 | natural-compare: 1.4.0 1689 | ts-api-utils: 2.1.0(typescript@5.8.3) 1690 | typescript: 5.8.3 1691 | transitivePeerDependencies: 1692 | - supports-color 1693 | 1694 | '@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3)': 1695 | dependencies: 1696 | '@typescript-eslint/scope-manager': 8.37.0 1697 | '@typescript-eslint/types': 8.37.0 1698 | '@typescript-eslint/typescript-estree': 8.37.0(typescript@5.8.3) 1699 | '@typescript-eslint/visitor-keys': 8.37.0 1700 | debug: 4.4.1 1701 | eslint: 9.31.0 1702 | typescript: 5.8.3 1703 | transitivePeerDependencies: 1704 | - supports-color 1705 | 1706 | '@typescript-eslint/project-service@8.37.0(typescript@5.8.3)': 1707 | dependencies: 1708 | '@typescript-eslint/tsconfig-utils': 8.37.0(typescript@5.8.3) 1709 | '@typescript-eslint/types': 8.37.0 1710 | debug: 4.4.1 1711 | typescript: 5.8.3 1712 | transitivePeerDependencies: 1713 | - supports-color 1714 | 1715 | '@typescript-eslint/scope-manager@8.37.0': 1716 | dependencies: 1717 | '@typescript-eslint/types': 8.37.0 1718 | '@typescript-eslint/visitor-keys': 8.37.0 1719 | 1720 | '@typescript-eslint/tsconfig-utils@8.37.0(typescript@5.8.3)': 1721 | dependencies: 1722 | typescript: 5.8.3 1723 | 1724 | '@typescript-eslint/type-utils@8.37.0(eslint@9.31.0)(typescript@5.8.3)': 1725 | dependencies: 1726 | '@typescript-eslint/types': 8.37.0 1727 | '@typescript-eslint/typescript-estree': 8.37.0(typescript@5.8.3) 1728 | '@typescript-eslint/utils': 8.37.0(eslint@9.31.0)(typescript@5.8.3) 1729 | debug: 4.4.1 1730 | eslint: 9.31.0 1731 | ts-api-utils: 2.1.0(typescript@5.8.3) 1732 | typescript: 5.8.3 1733 | transitivePeerDependencies: 1734 | - supports-color 1735 | 1736 | '@typescript-eslint/types@8.37.0': {} 1737 | 1738 | '@typescript-eslint/typescript-estree@8.37.0(typescript@5.8.3)': 1739 | dependencies: 1740 | '@typescript-eslint/project-service': 8.37.0(typescript@5.8.3) 1741 | '@typescript-eslint/tsconfig-utils': 8.37.0(typescript@5.8.3) 1742 | '@typescript-eslint/types': 8.37.0 1743 | '@typescript-eslint/visitor-keys': 8.37.0 1744 | debug: 4.4.1 1745 | fast-glob: 3.3.3 1746 | is-glob: 4.0.3 1747 | minimatch: 9.0.5 1748 | semver: 7.7.2 1749 | ts-api-utils: 2.1.0(typescript@5.8.3) 1750 | typescript: 5.8.3 1751 | transitivePeerDependencies: 1752 | - supports-color 1753 | 1754 | '@typescript-eslint/utils@8.37.0(eslint@9.31.0)(typescript@5.8.3)': 1755 | dependencies: 1756 | '@eslint-community/eslint-utils': 4.7.0(eslint@9.31.0) 1757 | '@typescript-eslint/scope-manager': 8.37.0 1758 | '@typescript-eslint/types': 8.37.0 1759 | '@typescript-eslint/typescript-estree': 8.37.0(typescript@5.8.3) 1760 | eslint: 9.31.0 1761 | typescript: 5.8.3 1762 | transitivePeerDependencies: 1763 | - supports-color 1764 | 1765 | '@typescript-eslint/visitor-keys@8.37.0': 1766 | dependencies: 1767 | '@typescript-eslint/types': 8.37.0 1768 | eslint-visitor-keys: 4.2.1 1769 | 1770 | '@vitest/coverage-v8@3.2.4(vitest@3.2.4(@types/node@24.0.14))': 1771 | dependencies: 1772 | '@ampproject/remapping': 2.3.0 1773 | '@bcoe/v8-coverage': 1.0.2 1774 | ast-v8-to-istanbul: 0.3.3 1775 | debug: 4.4.1 1776 | istanbul-lib-coverage: 3.2.2 1777 | istanbul-lib-report: 3.0.1 1778 | istanbul-lib-source-maps: 5.0.6 1779 | istanbul-reports: 3.1.7 1780 | magic-string: 0.30.17 1781 | magicast: 0.3.5 1782 | std-env: 3.9.0 1783 | test-exclude: 7.0.1 1784 | tinyrainbow: 2.0.0 1785 | vitest: 3.2.4(@types/node@24.0.14) 1786 | transitivePeerDependencies: 1787 | - supports-color 1788 | 1789 | '@vitest/expect@3.2.4': 1790 | dependencies: 1791 | '@types/chai': 5.2.2 1792 | '@vitest/spy': 3.2.4 1793 | '@vitest/utils': 3.2.4 1794 | chai: 5.2.1 1795 | tinyrainbow: 2.0.0 1796 | 1797 | '@vitest/mocker@3.2.4(vite@7.0.5(@types/node@24.0.14))': 1798 | dependencies: 1799 | '@vitest/spy': 3.2.4 1800 | estree-walker: 3.0.3 1801 | magic-string: 0.30.17 1802 | optionalDependencies: 1803 | vite: 7.0.5(@types/node@24.0.14) 1804 | 1805 | '@vitest/pretty-format@3.2.4': 1806 | dependencies: 1807 | tinyrainbow: 2.0.0 1808 | 1809 | '@vitest/runner@3.2.4': 1810 | dependencies: 1811 | '@vitest/utils': 3.2.4 1812 | pathe: 2.0.3 1813 | strip-literal: 3.0.0 1814 | 1815 | '@vitest/snapshot@3.2.4': 1816 | dependencies: 1817 | '@vitest/pretty-format': 3.2.4 1818 | magic-string: 0.30.17 1819 | pathe: 2.0.3 1820 | 1821 | '@vitest/spy@3.2.4': 1822 | dependencies: 1823 | tinyspy: 4.0.3 1824 | 1825 | '@vitest/utils@3.2.4': 1826 | dependencies: 1827 | '@vitest/pretty-format': 3.2.4 1828 | loupe: 3.1.4 1829 | tinyrainbow: 2.0.0 1830 | 1831 | acorn-jsx@5.3.2(acorn@8.15.0): 1832 | dependencies: 1833 | acorn: 8.15.0 1834 | 1835 | acorn@8.15.0: {} 1836 | 1837 | ajv@6.12.6: 1838 | dependencies: 1839 | fast-deep-equal: 3.1.3 1840 | fast-json-stable-stringify: 2.1.0 1841 | json-schema-traverse: 0.4.1 1842 | uri-js: 4.4.1 1843 | 1844 | ansi-regex@5.0.1: {} 1845 | 1846 | ansi-regex@6.1.0: {} 1847 | 1848 | ansi-styles@4.3.0: 1849 | dependencies: 1850 | color-convert: 2.0.1 1851 | 1852 | ansi-styles@6.2.1: {} 1853 | 1854 | any-promise@1.3.0: {} 1855 | 1856 | argparse@2.0.1: {} 1857 | 1858 | assertion-error@2.0.1: {} 1859 | 1860 | ast-v8-to-istanbul@0.3.3: 1861 | dependencies: 1862 | '@jridgewell/trace-mapping': 0.3.29 1863 | estree-walker: 3.0.3 1864 | js-tokens: 9.0.1 1865 | 1866 | balanced-match@1.0.2: {} 1867 | 1868 | brace-expansion@1.1.12: 1869 | dependencies: 1870 | balanced-match: 1.0.2 1871 | concat-map: 0.0.1 1872 | 1873 | brace-expansion@2.0.2: 1874 | dependencies: 1875 | balanced-match: 1.0.2 1876 | 1877 | braces@3.0.3: 1878 | dependencies: 1879 | fill-range: 7.1.1 1880 | 1881 | bundle-require@5.1.0(esbuild@0.25.7): 1882 | dependencies: 1883 | esbuild: 0.25.7 1884 | load-tsconfig: 0.2.5 1885 | 1886 | cac@6.7.14: {} 1887 | 1888 | callsites@3.1.0: {} 1889 | 1890 | chai@5.2.1: 1891 | dependencies: 1892 | assertion-error: 2.0.1 1893 | check-error: 2.1.1 1894 | deep-eql: 5.0.2 1895 | loupe: 3.1.4 1896 | pathval: 2.0.1 1897 | 1898 | chalk@4.1.2: 1899 | dependencies: 1900 | ansi-styles: 4.3.0 1901 | supports-color: 7.2.0 1902 | 1903 | check-error@2.1.1: {} 1904 | 1905 | chokidar@4.0.3: 1906 | dependencies: 1907 | readdirp: 4.1.2 1908 | 1909 | color-convert@2.0.1: 1910 | dependencies: 1911 | color-name: 1.1.4 1912 | 1913 | color-name@1.1.4: {} 1914 | 1915 | commander@4.1.1: {} 1916 | 1917 | concat-map@0.0.1: {} 1918 | 1919 | confbox@0.1.8: {} 1920 | 1921 | consola@3.4.2: {} 1922 | 1923 | cross-spawn@7.0.6: 1924 | dependencies: 1925 | path-key: 3.1.1 1926 | shebang-command: 2.0.0 1927 | which: 2.0.2 1928 | 1929 | debug@4.4.1: 1930 | dependencies: 1931 | ms: 2.1.3 1932 | 1933 | deep-eql@5.0.2: {} 1934 | 1935 | deep-is@0.1.4: {} 1936 | 1937 | eastasianwidth@0.2.0: {} 1938 | 1939 | emoji-regex@8.0.0: {} 1940 | 1941 | emoji-regex@9.2.2: {} 1942 | 1943 | es-module-lexer@1.7.0: {} 1944 | 1945 | esbuild@0.25.7: 1946 | optionalDependencies: 1947 | '@esbuild/aix-ppc64': 0.25.7 1948 | '@esbuild/android-arm': 0.25.7 1949 | '@esbuild/android-arm64': 0.25.7 1950 | '@esbuild/android-x64': 0.25.7 1951 | '@esbuild/darwin-arm64': 0.25.7 1952 | '@esbuild/darwin-x64': 0.25.7 1953 | '@esbuild/freebsd-arm64': 0.25.7 1954 | '@esbuild/freebsd-x64': 0.25.7 1955 | '@esbuild/linux-arm': 0.25.7 1956 | '@esbuild/linux-arm64': 0.25.7 1957 | '@esbuild/linux-ia32': 0.25.7 1958 | '@esbuild/linux-loong64': 0.25.7 1959 | '@esbuild/linux-mips64el': 0.25.7 1960 | '@esbuild/linux-ppc64': 0.25.7 1961 | '@esbuild/linux-riscv64': 0.25.7 1962 | '@esbuild/linux-s390x': 0.25.7 1963 | '@esbuild/linux-x64': 0.25.7 1964 | '@esbuild/netbsd-arm64': 0.25.7 1965 | '@esbuild/netbsd-x64': 0.25.7 1966 | '@esbuild/openbsd-arm64': 0.25.7 1967 | '@esbuild/openbsd-x64': 0.25.7 1968 | '@esbuild/openharmony-arm64': 0.25.7 1969 | '@esbuild/sunos-x64': 0.25.7 1970 | '@esbuild/win32-arm64': 0.25.7 1971 | '@esbuild/win32-ia32': 0.25.7 1972 | '@esbuild/win32-x64': 0.25.7 1973 | 1974 | escape-string-regexp@4.0.0: {} 1975 | 1976 | eslint-config-prettier@10.1.8(eslint@9.31.0): 1977 | dependencies: 1978 | eslint: 9.31.0 1979 | 1980 | eslint-plugin-simple-import-sort@12.1.1(eslint@9.31.0): 1981 | dependencies: 1982 | eslint: 9.31.0 1983 | 1984 | eslint-scope@8.4.0: 1985 | dependencies: 1986 | esrecurse: 4.3.0 1987 | estraverse: 5.3.0 1988 | 1989 | eslint-visitor-keys@3.4.3: {} 1990 | 1991 | eslint-visitor-keys@4.2.1: {} 1992 | 1993 | eslint@9.31.0: 1994 | dependencies: 1995 | '@eslint-community/eslint-utils': 4.7.0(eslint@9.31.0) 1996 | '@eslint-community/regexpp': 4.12.1 1997 | '@eslint/config-array': 0.21.0 1998 | '@eslint/config-helpers': 0.3.0 1999 | '@eslint/core': 0.15.1 2000 | '@eslint/eslintrc': 3.3.1 2001 | '@eslint/js': 9.31.0 2002 | '@eslint/plugin-kit': 0.3.3 2003 | '@humanfs/node': 0.16.6 2004 | '@humanwhocodes/module-importer': 1.0.1 2005 | '@humanwhocodes/retry': 0.4.3 2006 | '@types/estree': 1.0.8 2007 | '@types/json-schema': 7.0.15 2008 | ajv: 6.12.6 2009 | chalk: 4.1.2 2010 | cross-spawn: 7.0.6 2011 | debug: 4.4.1 2012 | escape-string-regexp: 4.0.0 2013 | eslint-scope: 8.4.0 2014 | eslint-visitor-keys: 4.2.1 2015 | espree: 10.4.0 2016 | esquery: 1.6.0 2017 | esutils: 2.0.3 2018 | fast-deep-equal: 3.1.3 2019 | file-entry-cache: 8.0.0 2020 | find-up: 5.0.0 2021 | glob-parent: 6.0.2 2022 | ignore: 5.3.2 2023 | imurmurhash: 0.1.4 2024 | is-glob: 4.0.3 2025 | json-stable-stringify-without-jsonify: 1.0.1 2026 | lodash.merge: 4.6.2 2027 | minimatch: 3.1.2 2028 | natural-compare: 1.4.0 2029 | optionator: 0.9.4 2030 | transitivePeerDependencies: 2031 | - supports-color 2032 | 2033 | espree@10.4.0: 2034 | dependencies: 2035 | acorn: 8.15.0 2036 | acorn-jsx: 5.3.2(acorn@8.15.0) 2037 | eslint-visitor-keys: 4.2.1 2038 | 2039 | esquery@1.6.0: 2040 | dependencies: 2041 | estraverse: 5.3.0 2042 | 2043 | esrecurse@4.3.0: 2044 | dependencies: 2045 | estraverse: 5.3.0 2046 | 2047 | estraverse@5.3.0: {} 2048 | 2049 | estree-walker@3.0.3: 2050 | dependencies: 2051 | '@types/estree': 1.0.8 2052 | 2053 | esutils@2.0.3: {} 2054 | 2055 | expect-type@1.2.2: {} 2056 | 2057 | fast-deep-equal@3.1.3: {} 2058 | 2059 | fast-glob@3.3.3: 2060 | dependencies: 2061 | '@nodelib/fs.stat': 2.0.5 2062 | '@nodelib/fs.walk': 1.2.8 2063 | glob-parent: 5.1.2 2064 | merge2: 1.4.1 2065 | micromatch: 4.0.8 2066 | 2067 | fast-json-stable-stringify@2.1.0: {} 2068 | 2069 | fast-levenshtein@2.0.6: {} 2070 | 2071 | fastq@1.19.1: 2072 | dependencies: 2073 | reusify: 1.1.0 2074 | 2075 | fdir@6.4.6(picomatch@4.0.3): 2076 | optionalDependencies: 2077 | picomatch: 4.0.3 2078 | 2079 | file-entry-cache@8.0.0: 2080 | dependencies: 2081 | flat-cache: 4.0.1 2082 | 2083 | fill-range@7.1.1: 2084 | dependencies: 2085 | to-regex-range: 5.0.1 2086 | 2087 | find-up@5.0.0: 2088 | dependencies: 2089 | locate-path: 6.0.0 2090 | path-exists: 4.0.0 2091 | 2092 | fix-dts-default-cjs-exports@1.0.1: 2093 | dependencies: 2094 | magic-string: 0.30.17 2095 | mlly: 1.7.4 2096 | rollup: 4.45.1 2097 | 2098 | flat-cache@4.0.1: 2099 | dependencies: 2100 | flatted: 3.3.3 2101 | keyv: 4.5.4 2102 | 2103 | flatted@3.3.3: {} 2104 | 2105 | foreground-child@3.3.1: 2106 | dependencies: 2107 | cross-spawn: 7.0.6 2108 | signal-exit: 4.1.0 2109 | 2110 | fsevents@2.3.3: 2111 | optional: true 2112 | 2113 | glob-parent@5.1.2: 2114 | dependencies: 2115 | is-glob: 4.0.3 2116 | 2117 | glob-parent@6.0.2: 2118 | dependencies: 2119 | is-glob: 4.0.3 2120 | 2121 | glob@10.4.5: 2122 | dependencies: 2123 | foreground-child: 3.3.1 2124 | jackspeak: 3.4.3 2125 | minimatch: 9.0.5 2126 | minipass: 7.1.2 2127 | package-json-from-dist: 1.0.1 2128 | path-scurry: 1.11.1 2129 | 2130 | glob@11.0.3: 2131 | dependencies: 2132 | foreground-child: 3.3.1 2133 | jackspeak: 4.1.1 2134 | minimatch: 10.0.3 2135 | minipass: 7.1.2 2136 | package-json-from-dist: 1.0.1 2137 | path-scurry: 2.0.0 2138 | 2139 | globals@14.0.0: {} 2140 | 2141 | graphemer@1.4.0: {} 2142 | 2143 | has-flag@4.0.0: {} 2144 | 2145 | html-escaper@2.0.2: {} 2146 | 2147 | ignore@5.3.2: {} 2148 | 2149 | ignore@7.0.5: {} 2150 | 2151 | import-fresh@3.3.1: 2152 | dependencies: 2153 | parent-module: 1.0.1 2154 | resolve-from: 4.0.0 2155 | 2156 | imurmurhash@0.1.4: {} 2157 | 2158 | is-extglob@2.1.1: {} 2159 | 2160 | is-fullwidth-code-point@3.0.0: {} 2161 | 2162 | is-glob@4.0.3: 2163 | dependencies: 2164 | is-extglob: 2.1.1 2165 | 2166 | is-number@7.0.0: {} 2167 | 2168 | isexe@2.0.0: {} 2169 | 2170 | istanbul-lib-coverage@3.2.2: {} 2171 | 2172 | istanbul-lib-report@3.0.1: 2173 | dependencies: 2174 | istanbul-lib-coverage: 3.2.2 2175 | make-dir: 4.0.0 2176 | supports-color: 7.2.0 2177 | 2178 | istanbul-lib-source-maps@5.0.6: 2179 | dependencies: 2180 | '@jridgewell/trace-mapping': 0.3.29 2181 | debug: 4.4.1 2182 | istanbul-lib-coverage: 3.2.2 2183 | transitivePeerDependencies: 2184 | - supports-color 2185 | 2186 | istanbul-reports@3.1.7: 2187 | dependencies: 2188 | html-escaper: 2.0.2 2189 | istanbul-lib-report: 3.0.1 2190 | 2191 | jackspeak@3.4.3: 2192 | dependencies: 2193 | '@isaacs/cliui': 8.0.2 2194 | optionalDependencies: 2195 | '@pkgjs/parseargs': 0.11.0 2196 | 2197 | jackspeak@4.1.1: 2198 | dependencies: 2199 | '@isaacs/cliui': 8.0.2 2200 | 2201 | joycon@3.1.1: {} 2202 | 2203 | js-tokens@9.0.1: {} 2204 | 2205 | js-yaml@4.1.0: 2206 | dependencies: 2207 | argparse: 2.0.1 2208 | 2209 | json-buffer@3.0.1: {} 2210 | 2211 | json-schema-traverse@0.4.1: {} 2212 | 2213 | json-stable-stringify-without-jsonify@1.0.1: {} 2214 | 2215 | keyv@4.5.4: 2216 | dependencies: 2217 | json-buffer: 3.0.1 2218 | 2219 | levn@0.4.1: 2220 | dependencies: 2221 | prelude-ls: 1.2.1 2222 | type-check: 0.4.0 2223 | 2224 | lilconfig@3.1.3: {} 2225 | 2226 | lines-and-columns@1.2.4: {} 2227 | 2228 | load-tsconfig@0.2.5: {} 2229 | 2230 | locate-path@6.0.0: 2231 | dependencies: 2232 | p-locate: 5.0.0 2233 | 2234 | lodash.merge@4.6.2: {} 2235 | 2236 | lodash.sortby@4.7.0: {} 2237 | 2238 | loupe@3.1.4: {} 2239 | 2240 | lru-cache@10.4.3: {} 2241 | 2242 | lru-cache@11.1.0: {} 2243 | 2244 | magic-string@0.30.17: 2245 | dependencies: 2246 | '@jridgewell/sourcemap-codec': 1.5.4 2247 | 2248 | magicast@0.3.5: 2249 | dependencies: 2250 | '@babel/parser': 7.28.0 2251 | '@babel/types': 7.28.1 2252 | source-map-js: 1.2.1 2253 | 2254 | make-dir@4.0.0: 2255 | dependencies: 2256 | semver: 7.7.2 2257 | 2258 | merge2@1.4.1: {} 2259 | 2260 | micromatch@4.0.8: 2261 | dependencies: 2262 | braces: 3.0.3 2263 | picomatch: 2.3.1 2264 | 2265 | minimatch@10.0.3: 2266 | dependencies: 2267 | '@isaacs/brace-expansion': 5.0.0 2268 | 2269 | minimatch@3.1.2: 2270 | dependencies: 2271 | brace-expansion: 1.1.12 2272 | 2273 | minimatch@9.0.5: 2274 | dependencies: 2275 | brace-expansion: 2.0.2 2276 | 2277 | minipass@7.1.2: {} 2278 | 2279 | mlly@1.7.4: 2280 | dependencies: 2281 | acorn: 8.15.0 2282 | pathe: 2.0.3 2283 | pkg-types: 1.3.1 2284 | ufo: 1.6.1 2285 | 2286 | ms@2.1.3: {} 2287 | 2288 | mz@2.7.0: 2289 | dependencies: 2290 | any-promise: 1.3.0 2291 | object-assign: 4.1.1 2292 | thenify-all: 1.6.0 2293 | 2294 | nanoid@3.3.11: {} 2295 | 2296 | natural-compare@1.4.0: {} 2297 | 2298 | object-assign@4.1.1: {} 2299 | 2300 | optionator@0.9.4: 2301 | dependencies: 2302 | deep-is: 0.1.4 2303 | fast-levenshtein: 2.0.6 2304 | levn: 0.4.1 2305 | prelude-ls: 1.2.1 2306 | type-check: 0.4.0 2307 | word-wrap: 1.2.5 2308 | 2309 | p-limit@3.1.0: 2310 | dependencies: 2311 | yocto-queue: 0.1.0 2312 | 2313 | p-locate@5.0.0: 2314 | dependencies: 2315 | p-limit: 3.1.0 2316 | 2317 | package-json-from-dist@1.0.1: {} 2318 | 2319 | parent-module@1.0.1: 2320 | dependencies: 2321 | callsites: 3.1.0 2322 | 2323 | path-exists@4.0.0: {} 2324 | 2325 | path-key@3.1.1: {} 2326 | 2327 | path-scurry@1.11.1: 2328 | dependencies: 2329 | lru-cache: 10.4.3 2330 | minipass: 7.1.2 2331 | 2332 | path-scurry@2.0.0: 2333 | dependencies: 2334 | lru-cache: 11.1.0 2335 | minipass: 7.1.2 2336 | 2337 | pathe@2.0.3: {} 2338 | 2339 | pathval@2.0.1: {} 2340 | 2341 | picocolors@1.1.1: {} 2342 | 2343 | picomatch@2.3.1: {} 2344 | 2345 | picomatch@4.0.3: {} 2346 | 2347 | pirates@4.0.7: {} 2348 | 2349 | pkg-types@1.3.1: 2350 | dependencies: 2351 | confbox: 0.1.8 2352 | mlly: 1.7.4 2353 | pathe: 2.0.3 2354 | 2355 | postcss-load-config@6.0.1(postcss@8.5.6): 2356 | dependencies: 2357 | lilconfig: 3.1.3 2358 | optionalDependencies: 2359 | postcss: 8.5.6 2360 | 2361 | postcss@8.5.6: 2362 | dependencies: 2363 | nanoid: 3.3.11 2364 | picocolors: 1.1.1 2365 | source-map-js: 1.2.1 2366 | 2367 | prelude-ls@1.2.1: {} 2368 | 2369 | prettier@3.6.2: {} 2370 | 2371 | punycode@2.3.1: {} 2372 | 2373 | queue-microtask@1.2.3: {} 2374 | 2375 | readdirp@4.1.2: {} 2376 | 2377 | resolve-from@4.0.0: {} 2378 | 2379 | resolve-from@5.0.0: {} 2380 | 2381 | reusify@1.1.0: {} 2382 | 2383 | rimraf@6.0.1: 2384 | dependencies: 2385 | glob: 11.0.3 2386 | package-json-from-dist: 1.0.1 2387 | 2388 | rollup@4.45.1: 2389 | dependencies: 2390 | '@types/estree': 1.0.8 2391 | optionalDependencies: 2392 | '@rollup/rollup-android-arm-eabi': 4.45.1 2393 | '@rollup/rollup-android-arm64': 4.45.1 2394 | '@rollup/rollup-darwin-arm64': 4.45.1 2395 | '@rollup/rollup-darwin-x64': 4.45.1 2396 | '@rollup/rollup-freebsd-arm64': 4.45.1 2397 | '@rollup/rollup-freebsd-x64': 4.45.1 2398 | '@rollup/rollup-linux-arm-gnueabihf': 4.45.1 2399 | '@rollup/rollup-linux-arm-musleabihf': 4.45.1 2400 | '@rollup/rollup-linux-arm64-gnu': 4.45.1 2401 | '@rollup/rollup-linux-arm64-musl': 4.45.1 2402 | '@rollup/rollup-linux-loongarch64-gnu': 4.45.1 2403 | '@rollup/rollup-linux-powerpc64le-gnu': 4.45.1 2404 | '@rollup/rollup-linux-riscv64-gnu': 4.45.1 2405 | '@rollup/rollup-linux-riscv64-musl': 4.45.1 2406 | '@rollup/rollup-linux-s390x-gnu': 4.45.1 2407 | '@rollup/rollup-linux-x64-gnu': 4.45.1 2408 | '@rollup/rollup-linux-x64-musl': 4.45.1 2409 | '@rollup/rollup-win32-arm64-msvc': 4.45.1 2410 | '@rollup/rollup-win32-ia32-msvc': 4.45.1 2411 | '@rollup/rollup-win32-x64-msvc': 4.45.1 2412 | fsevents: 2.3.3 2413 | 2414 | run-parallel@1.2.0: 2415 | dependencies: 2416 | queue-microtask: 1.2.3 2417 | 2418 | semver@7.7.2: {} 2419 | 2420 | shebang-command@2.0.0: 2421 | dependencies: 2422 | shebang-regex: 3.0.0 2423 | 2424 | shebang-regex@3.0.0: {} 2425 | 2426 | siginfo@2.0.0: {} 2427 | 2428 | signal-exit@4.1.0: {} 2429 | 2430 | source-map-js@1.2.1: {} 2431 | 2432 | source-map@0.8.0-beta.0: 2433 | dependencies: 2434 | whatwg-url: 7.1.0 2435 | 2436 | stackback@0.0.2: {} 2437 | 2438 | std-env@3.9.0: {} 2439 | 2440 | string-width@4.2.3: 2441 | dependencies: 2442 | emoji-regex: 8.0.0 2443 | is-fullwidth-code-point: 3.0.0 2444 | strip-ansi: 6.0.1 2445 | 2446 | string-width@5.1.2: 2447 | dependencies: 2448 | eastasianwidth: 0.2.0 2449 | emoji-regex: 9.2.2 2450 | strip-ansi: 7.1.0 2451 | 2452 | strip-ansi@6.0.1: 2453 | dependencies: 2454 | ansi-regex: 5.0.1 2455 | 2456 | strip-ansi@7.1.0: 2457 | dependencies: 2458 | ansi-regex: 6.1.0 2459 | 2460 | strip-json-comments@3.1.1: {} 2461 | 2462 | strip-literal@3.0.0: 2463 | dependencies: 2464 | js-tokens: 9.0.1 2465 | 2466 | sucrase@3.35.0: 2467 | dependencies: 2468 | '@jridgewell/gen-mapping': 0.3.12 2469 | commander: 4.1.1 2470 | glob: 10.4.5 2471 | lines-and-columns: 1.2.4 2472 | mz: 2.7.0 2473 | pirates: 4.0.7 2474 | ts-interface-checker: 0.1.13 2475 | 2476 | supports-color@7.2.0: 2477 | dependencies: 2478 | has-flag: 4.0.0 2479 | 2480 | test-exclude@7.0.1: 2481 | dependencies: 2482 | '@istanbuljs/schema': 0.1.3 2483 | glob: 10.4.5 2484 | minimatch: 9.0.5 2485 | 2486 | thenify-all@1.6.0: 2487 | dependencies: 2488 | thenify: 3.3.1 2489 | 2490 | thenify@3.3.1: 2491 | dependencies: 2492 | any-promise: 1.3.0 2493 | 2494 | tinybench@2.9.0: {} 2495 | 2496 | tinyexec@0.3.2: {} 2497 | 2498 | tinyglobby@0.2.14: 2499 | dependencies: 2500 | fdir: 6.4.6(picomatch@4.0.3) 2501 | picomatch: 4.0.3 2502 | 2503 | tinypool@1.1.1: {} 2504 | 2505 | tinyrainbow@2.0.0: {} 2506 | 2507 | tinyspy@4.0.3: {} 2508 | 2509 | to-regex-range@5.0.1: 2510 | dependencies: 2511 | is-number: 7.0.0 2512 | 2513 | tr46@1.0.1: 2514 | dependencies: 2515 | punycode: 2.3.1 2516 | 2517 | tree-kill@1.2.2: {} 2518 | 2519 | ts-api-utils@2.1.0(typescript@5.8.3): 2520 | dependencies: 2521 | typescript: 5.8.3 2522 | 2523 | ts-interface-checker@0.1.13: {} 2524 | 2525 | tsup@8.5.0(postcss@8.5.6)(typescript@5.8.3): 2526 | dependencies: 2527 | bundle-require: 5.1.0(esbuild@0.25.7) 2528 | cac: 6.7.14 2529 | chokidar: 4.0.3 2530 | consola: 3.4.2 2531 | debug: 4.4.1 2532 | esbuild: 0.25.7 2533 | fix-dts-default-cjs-exports: 1.0.1 2534 | joycon: 3.1.1 2535 | picocolors: 1.1.1 2536 | postcss-load-config: 6.0.1(postcss@8.5.6) 2537 | resolve-from: 5.0.0 2538 | rollup: 4.45.1 2539 | source-map: 0.8.0-beta.0 2540 | sucrase: 3.35.0 2541 | tinyexec: 0.3.2 2542 | tinyglobby: 0.2.14 2543 | tree-kill: 1.2.2 2544 | optionalDependencies: 2545 | postcss: 8.5.6 2546 | typescript: 5.8.3 2547 | transitivePeerDependencies: 2548 | - jiti 2549 | - supports-color 2550 | - tsx 2551 | - yaml 2552 | 2553 | type-check@0.4.0: 2554 | dependencies: 2555 | prelude-ls: 1.2.1 2556 | 2557 | typescript@5.8.3: {} 2558 | 2559 | ufo@1.6.1: {} 2560 | 2561 | undici-types@7.8.0: {} 2562 | 2563 | uri-js@4.4.1: 2564 | dependencies: 2565 | punycode: 2.3.1 2566 | 2567 | vite-node@3.2.4(@types/node@24.0.14): 2568 | dependencies: 2569 | cac: 6.7.14 2570 | debug: 4.4.1 2571 | es-module-lexer: 1.7.0 2572 | pathe: 2.0.3 2573 | vite: 7.0.5(@types/node@24.0.14) 2574 | transitivePeerDependencies: 2575 | - '@types/node' 2576 | - jiti 2577 | - less 2578 | - lightningcss 2579 | - sass 2580 | - sass-embedded 2581 | - stylus 2582 | - sugarss 2583 | - supports-color 2584 | - terser 2585 | - tsx 2586 | - yaml 2587 | 2588 | vite@7.0.5(@types/node@24.0.14): 2589 | dependencies: 2590 | esbuild: 0.25.7 2591 | fdir: 6.4.6(picomatch@4.0.3) 2592 | picomatch: 4.0.3 2593 | postcss: 8.5.6 2594 | rollup: 4.45.1 2595 | tinyglobby: 0.2.14 2596 | optionalDependencies: 2597 | '@types/node': 24.0.14 2598 | fsevents: 2.3.3 2599 | 2600 | vitest@3.2.4(@types/node@24.0.14): 2601 | dependencies: 2602 | '@types/chai': 5.2.2 2603 | '@vitest/expect': 3.2.4 2604 | '@vitest/mocker': 3.2.4(vite@7.0.5(@types/node@24.0.14)) 2605 | '@vitest/pretty-format': 3.2.4 2606 | '@vitest/runner': 3.2.4 2607 | '@vitest/snapshot': 3.2.4 2608 | '@vitest/spy': 3.2.4 2609 | '@vitest/utils': 3.2.4 2610 | chai: 5.2.1 2611 | debug: 4.4.1 2612 | expect-type: 1.2.2 2613 | magic-string: 0.30.17 2614 | pathe: 2.0.3 2615 | picomatch: 4.0.3 2616 | std-env: 3.9.0 2617 | tinybench: 2.9.0 2618 | tinyexec: 0.3.2 2619 | tinyglobby: 0.2.14 2620 | tinypool: 1.1.1 2621 | tinyrainbow: 2.0.0 2622 | vite: 7.0.5(@types/node@24.0.14) 2623 | vite-node: 3.2.4(@types/node@24.0.14) 2624 | why-is-node-running: 2.3.0 2625 | optionalDependencies: 2626 | '@types/node': 24.0.14 2627 | transitivePeerDependencies: 2628 | - jiti 2629 | - less 2630 | - lightningcss 2631 | - msw 2632 | - sass 2633 | - sass-embedded 2634 | - stylus 2635 | - sugarss 2636 | - supports-color 2637 | - terser 2638 | - tsx 2639 | - yaml 2640 | 2641 | webidl-conversions@4.0.2: {} 2642 | 2643 | whatwg-url@7.1.0: 2644 | dependencies: 2645 | lodash.sortby: 4.7.0 2646 | tr46: 1.0.1 2647 | webidl-conversions: 4.0.2 2648 | 2649 | which@2.0.2: 2650 | dependencies: 2651 | isexe: 2.0.0 2652 | 2653 | why-is-node-running@2.3.0: 2654 | dependencies: 2655 | siginfo: 2.0.0 2656 | stackback: 0.0.2 2657 | 2658 | word-wrap@1.2.5: {} 2659 | 2660 | wrap-ansi@7.0.0: 2661 | dependencies: 2662 | ansi-styles: 4.3.0 2663 | string-width: 4.2.3 2664 | strip-ansi: 6.0.1 2665 | 2666 | wrap-ansi@8.1.0: 2667 | dependencies: 2668 | ansi-styles: 6.2.1 2669 | string-width: 5.1.2 2670 | strip-ansi: 7.1.0 2671 | 2672 | yocto-queue@0.1.0: {} 2673 | --------------------------------------------------------------------------------