├── .github ├── funding.yml └── workflows │ └── main.yml ├── .gitignore ├── src ├── frameworks │ ├── solid-types.d.ts │ ├── s.ts │ ├── tansu.ts │ ├── uSignal.ts │ ├── signia.ts │ ├── mobx.ts │ ├── preactSignals.ts │ ├── oby.ts │ ├── alienSignals.ts │ ├── reactively.ts │ ├── compostate.ts │ ├── solid.ts │ ├── molWire.ts │ ├── kairo.ts │ ├── vueReactivity.ts │ ├── angularSignals.ts │ ├── svelte.ts │ ├── tc39-proposal-signals-stage-0.ts │ └── valtio.ts ├── kairo │ ├── util │ │ └── index.ts │ ├── mux.ts │ ├── avoidable.ts │ ├── deep.ts │ ├── repeated.ts │ ├── broad.ts │ ├── diamond.ts │ ├── unstable.ts │ └── triangle.ts ├── util │ ├── asyncUtil.ts │ ├── perfUtil.ts │ ├── reactiveFramework.ts │ ├── benchRepeat.ts │ ├── perfTests.ts │ ├── frameworkTypes.ts │ ├── perfLogging.ts │ └── dependencyGraph.ts ├── index.ts ├── kairoBench.ts ├── dynamicBench.ts ├── molBench.ts ├── frameworks.test.ts ├── cellxBench.ts ├── config.ts └── sBench.ts ├── .prettierrc ├── tsconfig.json ├── package.json ├── README.md └── pnpm-lock.yaml /.github/funding.yml: -------------------------------------------------------------------------------- 1 | github: [transitive-bullshit] 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | dist/ 3 | .DS_Store 4 | -------------------------------------------------------------------------------- /src/frameworks/solid-types.d.ts: -------------------------------------------------------------------------------- 1 | declare module "solid-js/dist/solid.js" { 2 | export * from "solid-js"; 3 | } 4 | -------------------------------------------------------------------------------- /src/kairo/util/index.ts: -------------------------------------------------------------------------------- 1 | export function busy() { 2 | let a = 0; 3 | for (let i = 0; i < 1_00; i++) { 4 | a++; 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src/util/asyncUtil.ts: -------------------------------------------------------------------------------- 1 | /** return a promise that completes after a set number of milliseconds */ 2 | export function promiseDelay(timeout = 0): Promise { 3 | return new Promise((resolve) => setTimeout(resolve, timeout)); 4 | } 5 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": false, 3 | "jsxSingleQuote": false, 4 | "semi": true, 5 | "useTabs": false, 6 | "tabWidth": 2, 7 | "bracketSpacing": true, 8 | "bracketSameLine": false, 9 | "arrowParens": "always", 10 | "trailingComma": "es5" 11 | } 12 | -------------------------------------------------------------------------------- /src/util/perfUtil.ts: -------------------------------------------------------------------------------- 1 | export interface TimedResult { 2 | result: T; 3 | time: number; 4 | } 5 | 6 | /** run a function, recording how long it takes */ 7 | export function runTimed(fn: () => T): TimedResult { 8 | const start = performance.now(); 9 | const result = fn(); 10 | const time = performance.now() - start; 11 | return { result, time }; 12 | } 13 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strict": true, 4 | "target": "ESNext", 5 | "moduleResolution": "Bundler", 6 | "lib": ["ESNext", "DOM"], 7 | "types": ["@types/node"], 8 | "noEmit": true, 9 | "experimentalDecorators": true, 10 | "useDefineForClassFields": false, 11 | "allowSyntheticDefaultImports": true, 12 | "noUnusedLocals": true, 13 | "noUnusedParameters": true 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/frameworks/s.ts: -------------------------------------------------------------------------------- 1 | import { ReactiveFramework } from "../util/reactiveFramework"; 2 | import S from "s-js"; 3 | 4 | export const sFramework: ReactiveFramework = { 5 | name: "s-js", 6 | signal: (initial) => { 7 | const data = S.value(initial); 8 | return { 9 | read: () => data(), 10 | write: (v) => data(v), 11 | }; 12 | }, 13 | computed: (fn) => { 14 | const computed = S(fn); 15 | return { 16 | read: () => computed(), 17 | }; 18 | }, 19 | effect: (fn) => S(fn), 20 | withBatch: (fn) => S.freeze(fn), 21 | withBuild: (fn) => S.root(fn), 22 | }; 23 | -------------------------------------------------------------------------------- /src/util/reactiveFramework.ts: -------------------------------------------------------------------------------- 1 | /** interface for a reactive framework. 2 | * 3 | * Implement this interface to add a new reactive framework to the test and performance test suite. 4 | */ 5 | export interface ReactiveFramework { 6 | name: string; 7 | signal(initialValue: T): Signal; 8 | computed(fn: () => T): Computed; 9 | effect(fn: () => void): void; 10 | withBatch(fn: () => T): void; 11 | withBuild(fn: () => T): T; 12 | } 13 | 14 | export interface Signal { 15 | read(): T; 16 | write(v: T): void; 17 | } 18 | 19 | export interface Computed { 20 | read(): T; 21 | } 22 | -------------------------------------------------------------------------------- /src/frameworks/tansu.ts: -------------------------------------------------------------------------------- 1 | import { ReactiveFramework } from "../util/reactiveFramework"; 2 | import { writable, computed, batch } from "@amadeus-it-group/tansu"; 3 | 4 | export const tansuFramework: ReactiveFramework = { 5 | name: "@amadeus-it-group/tansu", 6 | signal: (initialValue) => { 7 | const w = writable(initialValue); 8 | return { 9 | write: w.set, 10 | read: w, 11 | }; 12 | }, 13 | computed: (fn) => { 14 | const c = computed(fn); 15 | return { 16 | read: c, 17 | }; 18 | }, 19 | effect: (fn) => computed(fn).subscribe(() => {}), 20 | withBatch: batch, 21 | withBuild: (fn) => fn(), 22 | }; 23 | -------------------------------------------------------------------------------- /src/frameworks/uSignal.ts: -------------------------------------------------------------------------------- 1 | import { ReactiveFramework } from "../util/reactiveFramework"; 2 | import { batch, computed, effect, signal } from "usignal"; 3 | 4 | export const usignalFramework: ReactiveFramework = { 5 | name: "uSignal", 6 | signal: (initialValue) => { 7 | const s = signal(initialValue); 8 | return { 9 | write: (v) => (s.value = v), 10 | read: () => s.value, 11 | }; 12 | }, 13 | computed: (fn) => { 14 | const c = computed(fn); 15 | return { 16 | read: () => c.value, 17 | }; 18 | }, 19 | effect: (fn) => effect(fn), 20 | withBatch: (fn) => batch(fn), 21 | withBuild: (fn) => fn(), 22 | }; 23 | -------------------------------------------------------------------------------- /src/frameworks/signia.ts: -------------------------------------------------------------------------------- 1 | import { ReactiveFramework } from "../util/reactiveFramework"; 2 | import { atom, computed, react, transact } from "signia"; 3 | 4 | export const signiaFramework: ReactiveFramework = { 5 | name: "Signia", 6 | signal: (initialValue) => { 7 | const s = atom("s", initialValue); 8 | return { 9 | write: (v) => s.set(v), 10 | read: () => s.value, 11 | }; 12 | }, 13 | computed: (fn) => { 14 | const c = computed("c", fn); 15 | return { 16 | read: () => c.value, 17 | }; 18 | }, 19 | effect: (fn) => react("r", fn), 20 | withBatch: (fn) => transact(fn), 21 | withBuild: (fn) => fn(), 22 | }; 23 | -------------------------------------------------------------------------------- /src/frameworks/mobx.ts: -------------------------------------------------------------------------------- 1 | import { computed, observable, autorun, runInAction } from "mobx"; 2 | import { ReactiveFramework } from "../util/reactiveFramework"; 3 | 4 | export const mobxFramework: ReactiveFramework = { 5 | name: "MobX", 6 | signal(initial) { 7 | const s = observable.box(initial, { deep: false }); 8 | return { 9 | read: () => s.get(), 10 | write: (x) => s.set(x), 11 | }; 12 | }, 13 | computed: (fn) => { 14 | const read = computed(fn); 15 | return { 16 | read: () => read.get(), 17 | }; 18 | }, 19 | effect: (fn) => autorun(fn), 20 | withBatch: (fn) => runInAction(fn), 21 | withBuild: (fn) => fn(), 22 | }; 23 | -------------------------------------------------------------------------------- /src/frameworks/preactSignals.ts: -------------------------------------------------------------------------------- 1 | import { ReactiveFramework } from "../util/reactiveFramework"; 2 | import { batch, computed, effect, signal } from "@preact/signals"; 3 | 4 | export const preactSignalFramework: ReactiveFramework = { 5 | name: "Preact Signals", 6 | signal: (initialValue) => { 7 | const s = signal(initialValue); 8 | return { 9 | write: (v) => (s.value = v), 10 | read: () => s.value, 11 | }; 12 | }, 13 | computed: (fn) => { 14 | const c = computed(fn); 15 | return { 16 | read: () => c.value, 17 | }; 18 | }, 19 | effect: (fn) => effect(fn), 20 | withBatch: (fn) => batch(fn), 21 | withBuild: (fn) => fn(), 22 | }; 23 | -------------------------------------------------------------------------------- /src/frameworks/oby.ts: -------------------------------------------------------------------------------- 1 | import { ReactiveFramework } from "../util/reactiveFramework"; 2 | import $ from "oby"; 3 | 4 | export const obyFramework: ReactiveFramework = { 5 | name: "Oby", 6 | signal: (initialValue) => { 7 | const observable = $(initialValue); 8 | return { 9 | write: (v) => observable(v), 10 | read: () => observable(), 11 | }; 12 | }, 13 | computed: (fn) => { 14 | const memo = $.memo(fn); 15 | return { 16 | read: () => memo(), 17 | }; 18 | }, 19 | effect: (fn) => { 20 | fn(); 21 | return $.effect(fn); 22 | }, 23 | withBatch: (fn) => { 24 | fn(); 25 | $.tick(); 26 | }, 27 | withBuild: (fn) => $.root(fn), 28 | }; 29 | -------------------------------------------------------------------------------- /src/frameworks/alienSignals.ts: -------------------------------------------------------------------------------- 1 | import { getDefaultSystem } from "alien-signals/esm"; 2 | import { ReactiveFramework } from "../util/reactiveFramework"; 3 | 4 | const { signal, computed, effect, startBatch, endBatch } = getDefaultSystem(); 5 | 6 | export const alienFramework: ReactiveFramework = { 7 | name: "alien-signals", 8 | signal: (initial) => { 9 | const data = signal(initial); 10 | return { 11 | read: data, 12 | write: data, 13 | }; 14 | }, 15 | computed: (fn) => { 16 | return { 17 | read: computed(fn), 18 | }; 19 | }, 20 | effect: effect, 21 | withBatch: (fn) => { 22 | startBatch(); 23 | fn(); 24 | endBatch(); 25 | }, 26 | withBuild: (fn) => fn(), 27 | }; 28 | -------------------------------------------------------------------------------- /src/frameworks/reactively.ts: -------------------------------------------------------------------------------- 1 | import { Reactive, stabilize } from "@reactively/core"; 2 | import { ReactiveFramework } from "../util/reactiveFramework"; 3 | 4 | export const reactivelyFramework: ReactiveFramework = { 5 | name: "@reactively", 6 | signal: (initialValue) => { 7 | const r = new Reactive(initialValue); 8 | return { 9 | write: (v) => r.set(v), 10 | read: () => r.get(), 11 | }; 12 | }, 13 | computed: (fn) => { 14 | const r = new Reactive(fn); 15 | return { 16 | read: () => r.get(), 17 | }; 18 | }, 19 | effect: (fn) => { 20 | fn(); 21 | return new Reactive(fn, true); 22 | }, 23 | withBatch: (fn) => { 24 | fn(); 25 | stabilize(); 26 | }, 27 | withBuild: (fn) => fn(), 28 | }; 29 | -------------------------------------------------------------------------------- /src/frameworks/compostate.ts: -------------------------------------------------------------------------------- 1 | import { signal, computed, syncEffect, batch, createRoot } from "compostate"; 2 | import { ReactiveFramework } from "../util/reactiveFramework"; 3 | 4 | // NOTE: The compostate adapter is currently not working and unused. 5 | 6 | export const compostateFramework: ReactiveFramework = { 7 | name: "Compostate", 8 | signal: (initialValue) => { 9 | const [get, set] = signal(initialValue); 10 | return { 11 | write: (v) => set(v), 12 | read: () => get(), 13 | }; 14 | }, 15 | computed: (fn) => { 16 | const get = computed(fn); 17 | return { 18 | read: () => get(), 19 | }; 20 | }, 21 | effect: (fn) => syncEffect(fn), 22 | withBatch: (fn) => batch(fn), 23 | withBuild: (fn) => createRoot(fn), 24 | }; 25 | -------------------------------------------------------------------------------- /src/frameworks/solid.ts: -------------------------------------------------------------------------------- 1 | import { ReactiveFramework } from "../util/reactiveFramework"; 2 | import { 3 | batch, 4 | createRenderEffect, 5 | createMemo, 6 | createRoot, 7 | createSignal, 8 | } from "solid-js/dist/solid.js"; 9 | 10 | export const solidFramework: ReactiveFramework = { 11 | name: "SolidJS", 12 | signal: (initialValue) => { 13 | const [getter, setter] = createSignal(initialValue); 14 | return { 15 | write: (v) => setter(v as any), 16 | read: () => getter(), 17 | }; 18 | }, 19 | computed: (fn) => { 20 | const memo = createMemo(fn); 21 | return { 22 | read: () => memo(), 23 | }; 24 | }, 25 | effect: (fn) => createRenderEffect(fn), 26 | withBatch: (fn) => batch(fn), 27 | withBuild: (fn) => createRoot(fn), 28 | }; 29 | -------------------------------------------------------------------------------- /src/frameworks/molWire.ts: -------------------------------------------------------------------------------- 1 | import { ReactiveFramework, Signal } from "../util/reactiveFramework"; 2 | import $ from "mol_wire_lib"; 3 | 4 | const Atom = $.$mol_wire_atom; // fix a bug in mol exports 5 | 6 | export const molWireFramework: ReactiveFramework = { 7 | name: "$mol_wire", 8 | signal: (initialValue: T): Signal => { 9 | const atom = new Atom("", (next: T = initialValue) => next); 10 | return { 11 | write: (v: T) => atom.put(v), 12 | read: () => atom.sync(), 13 | }; 14 | }, 15 | computed: (fn) => { 16 | const atom = new Atom("", fn); 17 | return { 18 | read: () => atom.sync(), 19 | }; 20 | }, 21 | effect: (fn) => new Atom("", fn).sync(), 22 | withBatch: (fn) => { 23 | fn(); 24 | Atom.sync(); 25 | }, 26 | withBuild: (fn) => fn(), 27 | }; 28 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | test: 7 | name: Test Node.js ${{ matrix.node-version }} 8 | runs-on: ubuntu-latest 9 | 10 | strategy: 11 | fail-fast: true 12 | matrix: 13 | node-version: [lts/*] 14 | 15 | steps: 16 | - uses: actions/checkout@v4 17 | - uses: pnpm/action-setup@v4 18 | with: 19 | version: 9 20 | run_install: false 21 | 22 | - uses: actions/setup-node@v4 23 | with: 24 | node-version: ${{ matrix.node-version }} 25 | cache: "pnpm" 26 | 27 | - name: Install dependencies 28 | run: pnpm install --frozen-lockfile --strict-peer-dependencies 29 | 30 | - name: Run test 31 | run: pnpm test 32 | 33 | - name: Run build 34 | run: pnpm build 35 | -------------------------------------------------------------------------------- /src/frameworks/kairo.ts: -------------------------------------------------------------------------------- 1 | import { ReactiveFramework } from "../util/reactiveFramework"; 2 | import { batch, computed, effect, mutValue } from "kairo"; 3 | import { collectScope } from "kairo"; 4 | 5 | // NOTE: The kairo adapter is currently not working and unused. 6 | 7 | export const kairoFramework: ReactiveFramework = { 8 | name: "kairo", 9 | signal: (initialValue) => { 10 | const [get, write] = mutValue(initialValue); 11 | return { 12 | read: () => get.value, 13 | write: (v) => write(v), 14 | }; 15 | }, 16 | computed: (fn) => { 17 | const c = computed(fn); 18 | return { 19 | read: () => c.value, 20 | }; 21 | }, 22 | effect: (fn) => effect(fn), 23 | withBatch: (fn) => batch(fn), 24 | withBuild: (fn) => { 25 | const endCollectScope = collectScope(); 26 | let out = fn(); 27 | endCollectScope(); 28 | return out; 29 | }, 30 | }; 31 | -------------------------------------------------------------------------------- /src/util/benchRepeat.ts: -------------------------------------------------------------------------------- 1 | import { TimingResult } from "./perfTests"; 2 | import { runTimed } from "./perfUtil"; 3 | 4 | /** benchmark a function n times, returning the fastest result and associated timing */ 5 | export async function fastestTest( 6 | times: number, 7 | fn: () => T 8 | ): Promise> { 9 | const results: TimingResult[] = []; 10 | 11 | for (let i = 0; i < times; i++) { 12 | const run = await runTracked(fn); 13 | results.push(run); 14 | } 15 | 16 | const fastest = results.reduce((a, b) => 17 | a.timing.time < b.timing.time ? a : b 18 | ); 19 | 20 | return fastest; 21 | } 22 | 23 | /** run a function, reporting the wall clock time and garbage collection time. */ 24 | async function runTracked(fn: () => T): Promise> { 25 | globalThis.gc?.(); 26 | 27 | const { result, time } = runTimed(fn); 28 | 29 | globalThis.gc?.(); 30 | 31 | return { result, timing: { time } }; 32 | } 33 | -------------------------------------------------------------------------------- /src/kairo/mux.ts: -------------------------------------------------------------------------------- 1 | import { ReactiveFramework } from "../util/reactiveFramework"; 2 | 3 | export function mux(bridge: ReactiveFramework) { 4 | let heads = new Array(100).fill(null).map((_) => bridge.signal(0)); 5 | const mux = bridge.computed(() => { 6 | return Object.fromEntries(heads.map((h) => h.read()).entries()); 7 | }); 8 | const splited = heads 9 | .map((_, index) => bridge.computed(() => mux.read()[index])) 10 | .map((x) => bridge.computed(() => x.read() + 1)); 11 | 12 | splited.forEach((x) => { 13 | bridge.effect(() => x.read()); 14 | }); 15 | return () => { 16 | for (let i = 0; i < 10; i++) { 17 | bridge.withBatch(() => { 18 | heads[i].write(i); 19 | }); 20 | console.assert(splited[i].read() === i + 1); 21 | } 22 | for (let i = 0; i < 10; i++) { 23 | bridge.withBatch(() => { 24 | heads[i].write(i * 2); 25 | }); 26 | console.assert(splited[i].read() === i * 2 + 1); 27 | } 28 | }; 29 | } 30 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { dynamicBench } from "./dynamicBench"; 2 | // import { cellxbench } from "./cellxBench"; 3 | import { sbench } from "./sBench"; 4 | import { frameworkInfo } from "./config"; 5 | import { logPerfResult, perfReportHeaders } from "./util/perfLogging"; 6 | import { molBench } from "./molBench"; 7 | import { kairoBench } from "./kairoBench"; 8 | 9 | async function main() { 10 | logPerfResult(perfReportHeaders()); 11 | (globalThis as any).__DEV__ = true; 12 | 13 | for (const frameworkTest of frameworkInfo) { 14 | const { framework } = frameworkTest; 15 | 16 | await kairoBench(framework); 17 | await molBench(framework); 18 | sbench(framework); 19 | 20 | // MobX, Valtio, and Svelte fail this test currently, so disabling it for now. 21 | // @see https://github.com/mobxjs/mobx/issues/3926 22 | // @see https://github.com/sveltejs/svelte/discussions/13277 23 | // cellxbench(framework); 24 | 25 | await dynamicBench(frameworkTest); 26 | 27 | globalThis.gc?.(); 28 | } 29 | } 30 | 31 | main(); 32 | -------------------------------------------------------------------------------- /src/kairo/avoidable.ts: -------------------------------------------------------------------------------- 1 | import { ReactiveFramework } from "../util/reactiveFramework"; 2 | import { busy } from "./util"; 3 | 4 | /** avoidable change propagation */ 5 | export function avoidablePropagation(bridge: ReactiveFramework) { 6 | let head = bridge.signal(0); 7 | let computed1 = bridge.computed(() => head.read()); 8 | let computed2 = bridge.computed(() => (computed1.read(), 0)); 9 | let computed3 = bridge.computed(() => (busy(), computed2.read() + 1)); // heavy computation 10 | let computed4 = bridge.computed(() => computed3.read() + 2); 11 | let computed5 = bridge.computed(() => computed4.read() + 3); 12 | bridge.effect(() => { 13 | computed5.read(); 14 | busy(); // heavy side effect 15 | }); 16 | 17 | return () => { 18 | bridge.withBatch(() => { 19 | head.write(1); 20 | }); 21 | console.assert(computed5.read() === 6); 22 | for (let i = 0; i < 1000; i++) { 23 | bridge.withBatch(() => { 24 | head.write(i); 25 | }); 26 | console.assert(computed5.read() === 6); 27 | } 28 | }; 29 | } 30 | -------------------------------------------------------------------------------- /src/kairo/deep.ts: -------------------------------------------------------------------------------- 1 | import { Counter } from "../util/dependencyGraph"; 2 | import { Computed, ReactiveFramework } from "./../util/reactiveFramework"; 3 | let len = 50; 4 | 5 | /** deep propagation */ 6 | export function deepPropagation(bridge: ReactiveFramework) { 7 | let head = bridge.signal(0); 8 | let current = head as Computed; 9 | for (let i = 0; i < len; i++) { 10 | let c = current; 11 | current = bridge.computed(() => { 12 | return c.read() + 1; 13 | }); 14 | } 15 | let callCounter = new Counter(); 16 | 17 | bridge.effect(() => { 18 | current.read(); 19 | callCounter.count++; 20 | }); 21 | 22 | const iter = 50; 23 | 24 | return () => { 25 | bridge.withBatch(() => { 26 | head.write(1); 27 | }); 28 | // const atleast = iter; 29 | callCounter.count = 0; 30 | for (let i = 0; i < iter; i++) { 31 | bridge.withBatch(() => { 32 | head.write(i); 33 | }); 34 | console.assert(current.read() === len + i); 35 | } 36 | 37 | // console.assert(callCounter.count === atleast); 38 | }; 39 | } 40 | -------------------------------------------------------------------------------- /src/kairo/repeated.ts: -------------------------------------------------------------------------------- 1 | import { Counter } from "../util/dependencyGraph"; 2 | import { ReactiveFramework } from "../util/reactiveFramework"; 3 | 4 | let size = 30; 5 | 6 | /** repeated observers */ 7 | export function repeatedObservers(bridge: ReactiveFramework) { 8 | let head = bridge.signal(0); 9 | let current = bridge.computed(() => { 10 | let result = 0; 11 | for (let i = 0; i < size; i++) { 12 | // tbh I think it's meanigless to be this big... 13 | result += head.read(); 14 | } 15 | return result; 16 | }); 17 | 18 | let callCounter = new Counter(); 19 | bridge.effect(() => { 20 | current.read(); 21 | callCounter.count++; 22 | }); 23 | 24 | return () => { 25 | bridge.withBatch(() => { 26 | head.write(1); 27 | }); 28 | console.assert(current.read() === size); 29 | // const atleast = 100; 30 | callCounter.count = 0; 31 | for (let i = 0; i < 100; i++) { 32 | bridge.withBatch(() => { 33 | head.write(i); 34 | }); 35 | console.assert(current.read() === i * size); 36 | } 37 | // console.assert(callCounter.count === atleast); 38 | }; 39 | } 40 | -------------------------------------------------------------------------------- /src/kairo/broad.ts: -------------------------------------------------------------------------------- 1 | import { Counter } from "../util/dependencyGraph"; 2 | import { Computed, ReactiveFramework } from "../util/reactiveFramework"; 3 | 4 | /** broad propagation */ 5 | export function broadPropagation(bridge: ReactiveFramework) { 6 | let head = bridge.signal(0); 7 | let last = head as Computed; 8 | let callCounter = new Counter(); 9 | for (let i = 0; i < 50; i++) { 10 | let current = bridge.computed(() => { 11 | return head.read() + i; 12 | }); 13 | let current2 = bridge.computed(() => { 14 | return current.read() + 1; 15 | }); 16 | bridge.effect(() => { 17 | current2.read(); 18 | callCounter.count++; 19 | }); 20 | last = current2; 21 | } 22 | 23 | return () => { 24 | bridge.withBatch(() => { 25 | head.write(1); 26 | }); 27 | // const atleast = 50 * 50; 28 | callCounter.count = 0; 29 | for (let i = 0; i < 50; i++) { 30 | bridge.withBatch(() => { 31 | head.write(i); 32 | }); 33 | console.assert(last.read() === i + 50); 34 | } 35 | // console.assert(callCounter.count === atleast, callCounter.count); 36 | }; 37 | } 38 | -------------------------------------------------------------------------------- /src/util/perfTests.ts: -------------------------------------------------------------------------------- 1 | import { FrameworkInfo, TestConfig } from "./frameworkTypes"; 2 | 3 | export interface TestResult { 4 | sum: number; 5 | count: number; 6 | } 7 | 8 | export interface TimingResult { 9 | result: T; 10 | timing: TestTiming; 11 | } 12 | 13 | export interface TestTiming { 14 | time: number; 15 | } 16 | 17 | export function verifyBenchResult( 18 | perfFramework: FrameworkInfo, 19 | config: TestConfig, 20 | timedResult: TimingResult 21 | ): void { 22 | const { testPullCounts, framework } = perfFramework; 23 | const { expected } = config; 24 | const { result } = timedResult; 25 | 26 | if (expected.sum) { 27 | console.assert( 28 | result.sum == expected.sum, 29 | `sum ${framework.name} ${config.name} result:${result.sum} expected:${expected.sum}` 30 | ); 31 | } 32 | if ( 33 | expected.count && 34 | (config.readFraction === 1 || testPullCounts) && 35 | testPullCounts !== false 36 | ) { 37 | console.assert( 38 | result.count === expected.count, 39 | `count ${framework.name} ${config.name} result:${result.count} expected:${expected.count}` 40 | ); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/kairo/diamond.ts: -------------------------------------------------------------------------------- 1 | import { Counter } from "../util/dependencyGraph"; 2 | import { Computed, ReactiveFramework } from "../util/reactiveFramework"; 3 | 4 | let width = 5; 5 | 6 | export function diamond(bridge: ReactiveFramework) { 7 | let head = bridge.signal(0); 8 | let current: Computed[] = []; 9 | for (let i = 0; i < width; i++) { 10 | current.push( 11 | bridge.computed(() => { 12 | return head.read() + 1; 13 | }) 14 | ); 15 | } 16 | let sum = bridge.computed(() => { 17 | return current.map((x) => x.read()).reduce((a, b) => a + b, 0); 18 | }); 19 | let callCounter = new Counter(); 20 | bridge.effect(() => { 21 | sum.read(); 22 | callCounter.count++; 23 | }); 24 | 25 | return () => { 26 | bridge.withBatch(() => { 27 | head.write(1); 28 | }); 29 | console.assert(sum.read() === 2 * width); 30 | // const atleast = 500; 31 | callCounter.count = 0; 32 | for (let i = 0; i < 500; i++) { 33 | bridge.withBatch(() => { 34 | head.write(i); 35 | }); 36 | console.assert(sum.read() === (i + 1) * width); 37 | } 38 | // console.assert(callCounter.count === atleast); 39 | }; 40 | } 41 | -------------------------------------------------------------------------------- /src/kairo/unstable.ts: -------------------------------------------------------------------------------- 1 | import { Counter } from "../util/dependencyGraph"; 2 | import { ReactiveFramework } from "../util/reactiveFramework"; 3 | 4 | /** worst case. */ 5 | export function unstable(bridge: ReactiveFramework) { 6 | let head = bridge.signal(0); 7 | const double = bridge.computed(() => head.read() * 2); 8 | const inverse = bridge.computed(() => -head.read()); 9 | let current = bridge.computed(() => { 10 | let result = 0; 11 | for (let i = 0; i < 20; i++) { 12 | result += head.read() % 2 ? double.read() : inverse.read(); 13 | } 14 | return result; 15 | }); 16 | 17 | let callCounter = new Counter(); 18 | bridge.effect(() => { 19 | current.read(); 20 | callCounter.count++; 21 | }); 22 | return () => { 23 | bridge.withBatch(() => { 24 | head.write(1); 25 | }); 26 | console.assert(current.read() === 40); 27 | // const atleast = 100; 28 | callCounter.count = 0; 29 | for (let i = 0; i < 100; i++) { 30 | bridge.withBatch(() => { 31 | head.write(i); 32 | }); 33 | // console.assert(current.read() === i % 2 ? i * 2 * 10 : i * -10); 34 | } 35 | // console.assert(callCounter.count === atleast); 36 | }; 37 | } 38 | -------------------------------------------------------------------------------- /src/frameworks/vueReactivity.ts: -------------------------------------------------------------------------------- 1 | import { 2 | computed, 3 | effectScope, 4 | shallowRef, 5 | effect, 6 | ReactiveEffect, 7 | } from "@vue/reactivity"; 8 | import { ReactiveFramework } from "../util/reactiveFramework"; 9 | 10 | let scheduled = [] as ReactiveEffect[]; 11 | let batching = false; 12 | 13 | export const vueReactivityFramework: ReactiveFramework = { 14 | name: "@vue/reactivity", 15 | signal: (initial) => { 16 | const data = shallowRef(initial); 17 | return { 18 | read: () => data.value as any, 19 | write: (v) => (data.value = v as any), 20 | }; 21 | }, 22 | computed: (fn) => { 23 | const c = computed(fn); 24 | return { 25 | read: () => c.value, 26 | }; 27 | }, 28 | effect: (fn) => { 29 | let t = effect(() => fn(), { 30 | scheduler: () => { 31 | scheduled.push(t.effect); 32 | }, 33 | }); 34 | }, 35 | withBatch: (fn) => { 36 | if (batching) { 37 | fn(); 38 | } else { 39 | batching = true; 40 | fn(); 41 | while (scheduled.length) { 42 | scheduled.pop()!.run(); 43 | } 44 | batching = false; 45 | } 46 | }, 47 | withBuild: (fn) => { 48 | const e = effectScope(); 49 | return e.run(fn)!; 50 | }, 51 | }; 52 | -------------------------------------------------------------------------------- /src/frameworks/angularSignals.ts: -------------------------------------------------------------------------------- 1 | import { ReactiveFramework } from "../util/reactiveFramework"; 2 | import { signal, computed } from "@angular/core"; 3 | import { createWatch, Watch } from "@angular/core/primitives/signals"; 4 | 5 | export const angularFramework: ReactiveFramework = { 6 | name: "@angular/signals", 7 | signal: (initialValue) => { 8 | const s = signal(initialValue); 9 | return { 10 | write: (v) => s.set(v), 11 | read: () => s(), 12 | }; 13 | }, 14 | computed: (fn) => { 15 | const c = computed(fn); 16 | return { 17 | read: () => c(), 18 | }; 19 | }, 20 | effect: (fn) => effect(fn), 21 | withBatch: (fn) => { 22 | fn(); 23 | flushEffects(); 24 | }, 25 | withBuild: (fn) => fn(), 26 | }; 27 | 28 | let queue = new Set(); 29 | 30 | /** 31 | * Wrapper around Angular's core effect primitive `Watch`, decoupled from 32 | * dependency injection, cleanup, and other unrelated concepts. 33 | */ 34 | function effect(effectFn: () => void): void { 35 | const w = createWatch(effectFn, queue.add.bind(queue), true); 36 | 37 | // Run effect immediately 38 | w.run(); 39 | } 40 | 41 | function flushEffects(): void { 42 | for (const watch of queue) { 43 | queue.delete(watch); 44 | watch.run(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/frameworks/svelte.ts: -------------------------------------------------------------------------------- 1 | import { ReactiveFramework } from "../util/reactiveFramework"; 2 | // @ts-ignore 3 | import * as $ from "svelte/internal/client"; 4 | 5 | // NOTE: The svelte adapter uses private, internal APIs that are usually only 6 | // used by the Svelte compiler and client runtime. The Svelte team has made the 7 | // decision to not expose these APIs publicly / officially, because it gives 8 | // them more freedom to experiment without making breaking changes, but given 9 | // that Svelte's v5 reactivity API is one of the most actively developed and 10 | // efficient TS implementations available, I wanted to include it in the 11 | // benchmark suite regardless. 12 | 13 | export const svelteFramework: ReactiveFramework = { 14 | name: "Svelte v5", 15 | signal: (initialValue) => { 16 | const s = $.state(initialValue); 17 | return { 18 | write: (v) => $.set(s, v), 19 | read: () => $.get(s), 20 | }; 21 | }, 22 | computed: (fn) => { 23 | const c = $.derived(fn); 24 | return { 25 | read: () => $.get(c), 26 | }; 27 | }, 28 | effect: (fn) => { 29 | $.render_effect(fn); 30 | }, 31 | withBatch: $.flush_sync, 32 | withBuild: (fn: () => T): T => { 33 | let res: T | undefined; 34 | $.effect_root(() => { 35 | res = fn(); 36 | }); 37 | return res!; 38 | }, 39 | }; 40 | -------------------------------------------------------------------------------- /src/kairoBench.ts: -------------------------------------------------------------------------------- 1 | import { avoidablePropagation } from "./kairo/avoidable"; 2 | import { broadPropagation } from "./kairo/broad"; 3 | import { deepPropagation } from "./kairo/deep"; 4 | import { diamond } from "./kairo/diamond"; 5 | import { mux } from "./kairo/mux"; 6 | import { repeatedObservers } from "./kairo/repeated"; 7 | import { triangle } from "./kairo/triangle"; 8 | import { unstable } from "./kairo/unstable"; 9 | import { fastestTest } from "./util/benchRepeat"; 10 | import { logPerfResult } from "./util/perfLogging"; 11 | import { ReactiveFramework } from "./util/reactiveFramework"; 12 | 13 | const cases = [ 14 | avoidablePropagation, 15 | broadPropagation, 16 | deepPropagation, 17 | diamond, 18 | mux, 19 | repeatedObservers, 20 | triangle, 21 | unstable, 22 | ]; 23 | 24 | export async function kairoBench(framework: ReactiveFramework) { 25 | for (const c of cases) { 26 | const iter = framework.withBuild(() => { 27 | const iter = c(framework); 28 | return iter; 29 | }); 30 | 31 | // warm up 32 | iter(); 33 | 34 | const { timing } = await fastestTest(10, () => { 35 | for (let i = 0; i < 1000; i++) { 36 | iter(); 37 | } 38 | }); 39 | 40 | logPerfResult({ 41 | framework: framework.name, 42 | test: c.name, 43 | time: timing.time.toFixed(2), 44 | }); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "js-reactivity-benchmark", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "vitest run", 8 | "build": "esbuild src/index.ts --bundle --format=cjs --platform=node --outdir=dist --sourcemap=external", 9 | "run": "node --expose-gc dist/index.js", 10 | "bench": "esbuild src/index.ts --bundle --format=cjs --platform=node | node --expose-gc" 11 | }, 12 | "keywords": [], 13 | "author": "", 14 | "license": "ISC", 15 | "dependencies": { 16 | "@amadeus-it-group/tansu": "^2.0.0", 17 | "@angular/core": "19.0.6", 18 | "@preact/signals": "^2.0.0", 19 | "@reactively/core": "^0.0.8", 20 | "@vue/reactivity": "^3.5.13", 21 | "alien-signals": "1.0.0-alpha.1", 22 | "compostate": "0.6.0-alpha.1", 23 | "kairo": "0.6.0-rc.0", 24 | "mobx": "^6.13.5", 25 | "mol_wire_lib": "^1.0.1259", 26 | "oby": "15.1.2", 27 | "preact": "^10.25.4", 28 | "random": "^5.1.1", 29 | "react": "^19.0.0", 30 | "s-js": "^0.4.9", 31 | "signal-polyfill": "^0.2.1", 32 | "signia": "^0.1.5", 33 | "solid-js": "^1.9.4", 34 | "svelte": "5.17.3", 35 | "usignal": "^0.9.0", 36 | "valtio": "^2.1.2" 37 | }, 38 | "devDependencies": { 39 | "@types/node": "^22.10.5", 40 | "esbuild": "^0.24.2", 41 | "prettier": "^3.4.2", 42 | "vitest": "^2.1.8" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/kairo/triangle.ts: -------------------------------------------------------------------------------- 1 | import { Counter } from "./../util/dependencyGraph"; 2 | import { Computed, ReactiveFramework } from "../util/reactiveFramework"; 3 | 4 | let width = 10; 5 | 6 | export function triangle(bridge: ReactiveFramework) { 7 | let head = bridge.signal(0); 8 | let current = head as Computed; 9 | let list: Computed[] = []; 10 | for (let i = 0; i < width; i++) { 11 | let c = current; 12 | list.push(current); 13 | current = bridge.computed(() => { 14 | return c.read() + 1; 15 | }); 16 | } 17 | let sum = bridge.computed(() => { 18 | return list.map((x) => x.read()).reduce((a, b) => a + b, 0); 19 | }); 20 | 21 | let callCounter = new Counter(); 22 | bridge.effect(() => { 23 | sum.read(); 24 | callCounter.count++; 25 | }); 26 | 27 | return () => { 28 | const constant = count(width); 29 | bridge.withBatch(() => { 30 | head.write(1); 31 | }); 32 | console.assert(sum.read() === constant); 33 | // const atleast = 100; 34 | callCounter.count = 0; 35 | for (let i = 0; i < 100; i++) { 36 | bridge.withBatch(() => { 37 | head.write(i); 38 | }); 39 | console.assert(sum.read() === constant - width + i * width); 40 | } 41 | // console.assert(callCounter.count === atleast); 42 | }; 43 | } 44 | 45 | function count(number: Number) { 46 | return new Array(number) 47 | .fill(0) 48 | .map((_, i) => i + 1) 49 | .reduce((x, y) => x + y, 0); 50 | } 51 | -------------------------------------------------------------------------------- /src/frameworks/tc39-proposal-signals-stage-0.ts: -------------------------------------------------------------------------------- 1 | import { ReactiveFramework } from "../util/reactiveFramework"; 2 | import { Signal } from "signal-polyfill"; 3 | 4 | export const tc39SignalsProposalStage0: ReactiveFramework = { 5 | name: "TC39 Signals Polyfill", 6 | signal: (initialValue) => { 7 | const s = new Signal.State(initialValue); 8 | return { 9 | write: (v) => s.set(v), 10 | read: () => s.get(), 11 | }; 12 | }, 13 | computed: (fn) => { 14 | const c = new Signal.Computed(fn); 15 | return { 16 | read: () => c.get(), 17 | }; 18 | }, 19 | effect: (fn) => effect(fn), 20 | withBatch: (fn) => { 21 | fn(); 22 | processPending(); 23 | }, 24 | withBuild: (fn) => fn(), 25 | }; 26 | 27 | let needsEnqueue = false; 28 | 29 | const w = new Signal.subtle.Watcher(() => { 30 | if (needsEnqueue) { 31 | needsEnqueue = false; 32 | (async () => { 33 | await Promise.resolve(); 34 | // next micro queue 35 | processPending(); 36 | })(); 37 | } 38 | }); 39 | 40 | function processPending() { 41 | needsEnqueue = true; 42 | 43 | for (const s of w.getPending()) { 44 | s.get(); 45 | } 46 | 47 | w.watch(); 48 | } 49 | 50 | export function effect(callback: any) { 51 | let cleanup: any; 52 | 53 | const computed = new Signal.Computed(() => { 54 | typeof cleanup === "function" && cleanup(); 55 | cleanup = callback(); 56 | }); 57 | 58 | w.watch(computed); 59 | computed.get(); 60 | 61 | return () => { 62 | w.unwatch(computed); 63 | typeof cleanup === "function" && cleanup(); 64 | }; 65 | } 66 | -------------------------------------------------------------------------------- /src/frameworks/valtio.ts: -------------------------------------------------------------------------------- 1 | import { ReactiveFramework } from "../util/reactiveFramework"; 2 | import { proxy } from "valtio/vanilla"; 3 | import { watch } from "valtio/utils"; 4 | 5 | // The Valtio adapter is currently not working and unused: https://github.com/pmndrs/valtio/discussions/949 6 | 7 | type WatchGet = (proxyObject: T) => T; 8 | 9 | // stack of watch getters because Valtio doesn't auto-track dependency reads 10 | let watchGet: Array = []; 11 | 12 | export const valtioFramework: ReactiveFramework = { 13 | name: "Valtio", 14 | signal: (initialValue) => { 15 | const s = proxy({ value: initialValue }); 16 | return { 17 | write: (v) => (s.value = v), 18 | read: () => { 19 | const get = watchGet.at(-1); 20 | if (get) { 21 | return get(s).value; 22 | } else { 23 | return s.value; 24 | } 25 | }, 26 | }; 27 | }, 28 | computed: (fn) => { 29 | const c = proxy({ 30 | get value() { 31 | return fn(); 32 | }, 33 | }); 34 | return { 35 | read: () => { 36 | const get = watchGet.at(-1); 37 | if (get) { 38 | return get(c).value; 39 | } else { 40 | return c.value; 41 | } 42 | }, 43 | }; 44 | }, 45 | effect: (fn) => { 46 | return watch( 47 | (get) => { 48 | watchGet.push(get); 49 | fn(); 50 | watchGet.pop(); 51 | }, 52 | { 53 | sync: true, 54 | } 55 | ); 56 | }, 57 | withBatch: (fn) => fn(), 58 | withBuild: (fn) => fn(), 59 | }; 60 | -------------------------------------------------------------------------------- /src/dynamicBench.ts: -------------------------------------------------------------------------------- 1 | import { Counter, makeGraph, runGraph } from "./util/dependencyGraph"; 2 | import { logPerfResult, perfRowStrings } from "./util/perfLogging"; 3 | import { verifyBenchResult } from "./util/perfTests"; 4 | import { FrameworkInfo } from "./util/frameworkTypes"; 5 | import { perfTests } from "./config"; 6 | import { fastestTest } from "./util/benchRepeat"; 7 | 8 | /** benchmark a single test under single framework. 9 | * The test is run multiple times and the fastest result is logged to the console. 10 | */ 11 | export async function dynamicBench( 12 | frameworkTest: FrameworkInfo, 13 | testRepeats = 1 14 | ): Promise { 15 | const { framework } = frameworkTest; 16 | for (const config of perfTests) { 17 | const { iterations, readFraction } = config; 18 | 19 | let counter = new Counter(); 20 | 21 | function runOnce(): number { 22 | // Create a new graph from scratch for each run to ensure they're independent 23 | // from each other. 24 | try { 25 | const graph = makeGraph(framework, config, counter); 26 | const res = runGraph(graph, iterations, readFraction, framework); 27 | globalThis.gc?.(); 28 | return res; 29 | } catch (err: any) { 30 | console.warn(`Error dynamicBench "${framework.name}":`, err); 31 | return -1; 32 | } 33 | } 34 | 35 | // warm up 36 | runOnce(); 37 | 38 | const timedResult = await fastestTest(testRepeats, () => { 39 | counter.count = 0; 40 | const sum = runOnce(); 41 | return { sum, count: counter.count }; 42 | }); 43 | 44 | logPerfResult(perfRowStrings(framework.name, config, timedResult)); 45 | verifyBenchResult(frameworkTest, config, timedResult); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/molBench.ts: -------------------------------------------------------------------------------- 1 | import { fastestTest } from "./util/benchRepeat"; 2 | import { logPerfResult } from "./util/perfLogging"; 3 | import { ReactiveFramework } from "./util/reactiveFramework"; 4 | 5 | function fib(n: number): number { 6 | if (n < 2) return 1; 7 | return fib(n - 1) + fib(n - 2); 8 | } 9 | 10 | function hard(n: number, _log: string) { 11 | return n + fib(16); 12 | } 13 | 14 | const numbers = Array.from({ length: 5 }, (_, i) => i); 15 | 16 | export async function molBench(framework: ReactiveFramework) { 17 | let res = []; 18 | const iter = framework.withBuild(() => { 19 | const A = framework.signal(0); 20 | const B = framework.signal(0); 21 | const C = framework.computed(() => (A.read() % 2) + (B.read() % 2)); 22 | const D = framework.computed(() => 23 | numbers.map((i) => ({ x: i + (A.read() % 2) - (B.read() % 2) })) 24 | ); 25 | const E = framework.computed(() => 26 | hard(C.read() + A.read() + D.read()[0].x, "E") 27 | ); 28 | const F = framework.computed(() => hard(D.read()[2].x || B.read(), "F")); 29 | const G = framework.computed( 30 | () => C.read() + (C.read() || E.read() % 2) + D.read()[4].x + F.read() 31 | ); 32 | 33 | framework.effect(() => res.push(hard(G.read(), "H"))); 34 | framework.effect(() => res.push(G.read())); 35 | framework.effect(() => res.push(hard(F.read(), "J"))); 36 | 37 | return (i: number) => { 38 | res.length = 0; 39 | framework.withBatch(() => { 40 | B.write(1); 41 | A.write(1 + i * 2); 42 | }); 43 | framework.withBatch(() => { 44 | A.write(2 + i * 2); 45 | B.write(2); 46 | }); 47 | }; 48 | }); 49 | 50 | iter(1); 51 | 52 | const { timing } = await fastestTest(10, () => { 53 | for (let i = 0; i < 1e4; i++) { 54 | iter(i); 55 | } 56 | }); 57 | 58 | logPerfResult({ 59 | framework: framework.name, 60 | test: "molBench", 61 | time: timing.time.toFixed(2), 62 | }); 63 | } 64 | -------------------------------------------------------------------------------- /src/util/frameworkTypes.ts: -------------------------------------------------------------------------------- 1 | import { TestResult } from "./perfTests"; 2 | import { ReactiveFramework } from "./reactiveFramework"; 3 | 4 | /** Parameters for a running a performance benchmark test 5 | * 6 | * The benchmarks create a rectangular grid of reactive elements, with 7 | * mutable signals in the first level, computed elements in the middle levels, 8 | * and read effect elements in the last level. 9 | * 10 | * Each test iteration modifies one signal, and then reads specified 11 | * fraction of the effect elements. 12 | * 13 | * Each non-signal node sums values from a specified number of elements 14 | * in the preceding layer. Some nodes are dynamic, and read vary 15 | * the number of sources the read for the sum. 16 | * 17 | * Tests may optionally provide result values to verify the sum 18 | * of all read effect elements in all iterations, and the total 19 | * number of non-signal updated. 20 | */ 21 | export interface TestConfig { 22 | /** friendly name for the test, should be unique */ 23 | name?: string; 24 | 25 | /** width of dependency graph to construct */ 26 | width: number; 27 | 28 | /** depth of dependency graph to construct */ 29 | totalLayers: number; 30 | 31 | /** fraction of nodes that are static */ // TODO change to dynamicFraction 32 | staticFraction: number; 33 | 34 | /** construct a graph with number of sources in each node */ 35 | nSources: number; 36 | 37 | /** fraction of [0, 1] elements in the last layer from which to read values in each test iteration */ 38 | readFraction: number; 39 | 40 | /** number of test iterations */ 41 | iterations: number; 42 | 43 | /** sum and count of all iterations, for verification */ 44 | expected: Partial; 45 | } 46 | 47 | export interface FrameworkInfo { 48 | /** wrapper/adapter for a benchmarking a reactive framework */ 49 | framework: ReactiveFramework; 50 | 51 | /** verify the number of nodes executed matches the expected number */ 52 | testPullCounts?: boolean; 53 | } 54 | -------------------------------------------------------------------------------- /src/util/perfLogging.ts: -------------------------------------------------------------------------------- 1 | import { TestConfig } from "./frameworkTypes"; 2 | import { TestResult, TimingResult } from "./perfTests"; 3 | 4 | export function logPerfResult(row: PerfRowStrings): void { 5 | const line = Object.values(trimColumns(row)).join(" , "); 6 | console.log(line); 7 | } 8 | 9 | export interface PerfRowStrings { 10 | framework: string; 11 | test: string; 12 | time: string; 13 | } 14 | 15 | const columnWidth = { 16 | framework: 22, 17 | test: 60, 18 | time: 8, 19 | }; 20 | 21 | export function perfReportHeaders(): PerfRowStrings { 22 | const keys: (keyof PerfRowStrings)[] = Object.keys(columnWidth) as any; 23 | const kv = keys.map((key) => [key, key]); 24 | const untrimmed = Object.fromEntries(kv); 25 | return trimColumns(untrimmed); 26 | } 27 | 28 | export function perfRowStrings( 29 | frameworkName: string, 30 | config: TestConfig, 31 | timed: TimingResult 32 | ): PerfRowStrings { 33 | const { timing } = timed; 34 | 35 | return { 36 | framework: frameworkName, 37 | test: `${makeTitle(config)} (${config.name || ""})`, 38 | time: timing.time.toFixed(2), 39 | }; 40 | } 41 | 42 | export function makeTitle(config: TestConfig): string { 43 | const { width, totalLayers, staticFraction, nSources, readFraction } = config; 44 | const dyn = staticFraction < 1 ? " - dynamic" : ""; 45 | const read = readFraction < 1 ? ` - read ${percent(readFraction)}` : ""; 46 | const sources = ` - ${nSources} sources`; 47 | return `${width}x${totalLayers}${sources}${dyn}${read}`; 48 | } 49 | 50 | function percent(n: number): string { 51 | return Math.round(n * 100) + "%"; 52 | } 53 | 54 | function trimColumns(row: PerfRowStrings): PerfRowStrings { 55 | const keys: (keyof PerfRowStrings)[] = Object.keys(columnWidth) as any; 56 | const trimmed = { ...row }; 57 | for (const key of keys) { 58 | const length = columnWidth[key]; 59 | const value = (row[key] || "").slice(0, length).padEnd(length); 60 | trimmed[key] = value; 61 | } 62 | return trimmed; 63 | } 64 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JS Reactivity Benchmark 2 | 3 | ``` 4 | $ pnpm bench 5 | ``` 6 | 7 | ## Features 8 | 9 | - Configurable dependency graph: graph shape, density, read rate are all adjustable 10 | - Easily add new benchmarks and frameworks 11 | - Supports dynamic reactive nodes 12 | - Framework agnostic. Simple API to test new reactive frameworks 13 | - Forces garbage collection between each test 14 | - Outputs a csv file for easy integration with other tools 15 | 16 | Current reactivity benchmarks ([S.js](https://github.com/adamhaile/S/blob/master/bench/bench.js), [CellX](https://github.com/Riim/cellx/blob/master/perf/perf.html)) are focused on creation time, and update time for a static graph. Additionally, existing benchmarks aren't very configurable, and don't test for dynamic dependencies. We've created a new benchmark that allows library authors to compare their frameworks against each other, and against the existing benchmarks, as well as against a new configurable benchmark with dynamically changing sources. 17 | 18 | We're also working on enabling consistent logging and efficient tracking of GC time across all benchmarks. 19 | 20 | ## Frameworks 21 | 22 | - [Alien Signals](https://github.com/stackblitz/alien-signals) 🔥 23 | - [Angular Signals](https://angular.dev/guide/signals/) 24 | - [Compostate](https://github.com/lxsmnsyc/compostate) 25 | - [Kairo](https://github.com/3Shain/kairo) 26 | - [MobX](https://mobx.js.org) 27 | - [mol wire](https://www.npmjs.com/package/mol_wire_lib) 28 | - [Oby](https://github.com/vobyjs/oby) 29 | - [Preact Signals](https://github.com/preactjs/signals) 30 | - [Reactively](https://github.com/milomg/reactively) 31 | - [S.js](https://github.com/adamhaile/S) 32 | - [Signia](https://github.com/tldraw/signia) 33 | - [Solid](https://github.com/solidjs/solid) 34 | - [Svelte v5](https://svelte.dev/blog/runes) 35 | - [TC39 Signals Proposal](https://github.com/tc39/proposal-signals) [polyfill](https://github.com/proposal-signals/signal-polyfill) 36 | - [uSignal](https://github.com/WebReflection/usignal) 37 | - [Valtio](https://github.com/pmndrs/valtio) 38 | - [Vue Reactivity](https://vuejs.org/guide/essentials/reactivity-fundamentals.html) 39 | 40 | ## Results 41 | 42 |

43 | Average benchmark results across frameworks 45 | (lower times are better) 46 |

47 | 48 | These results were last updated _January 10 2025_ on an M3 Macbook Pro using Node.js v22.10.0. 49 | -------------------------------------------------------------------------------- /src/frameworks.test.ts: -------------------------------------------------------------------------------- 1 | import { Counter, makeGraph, runGraph } from "./util/dependencyGraph"; 2 | import { expect, test, vi } from "vitest"; 3 | import { FrameworkInfo, TestConfig } from "./util/frameworkTypes"; 4 | import { frameworkInfo } from "./config"; 5 | 6 | frameworkInfo.forEach((frameworkInfo) => frameworkTests(frameworkInfo)); 7 | 8 | function makeConfig(): TestConfig { 9 | return { 10 | width: 3, 11 | totalLayers: 3, 12 | staticFraction: 1, 13 | nSources: 2, 14 | readFraction: 1, 15 | expected: {}, 16 | iterations: 1, 17 | }; 18 | } 19 | 20 | /** some basic tests to validate the reactive framework 21 | * wrapper works and can run performance tests. 22 | */ 23 | function frameworkTests({ framework, testPullCounts }: FrameworkInfo) { 24 | const name = framework.name; 25 | test(`${name} | simple dependency executes`, () => { 26 | framework.withBuild(() => { 27 | const s = framework.signal(2); 28 | const c = framework.computed(() => s.read() * 2); 29 | 30 | expect(c.read()).toEqual(4); 31 | }); 32 | }); 33 | 34 | test(`${name} | simple write`, () => { 35 | framework.withBuild(() => { 36 | const s = framework.signal(2); 37 | const c = framework.computed(() => s.read() * 2); 38 | expect(s.read()).toEqual(2); 39 | expect(c.read()).toEqual(4); 40 | 41 | s.write(3); 42 | expect(s.read()).toEqual(3); 43 | expect(c.read()).toEqual(6); 44 | }); 45 | }); 46 | 47 | test(`${name} | static graph`, () => { 48 | const config = makeConfig(); 49 | const counter = new Counter(); 50 | const graph = makeGraph(framework, config, counter); 51 | const sum = runGraph(graph, 2, 1, framework); 52 | expect(sum).toEqual(16); 53 | if (testPullCounts) { 54 | expect(counter.count).toEqual(11); 55 | } else { 56 | expect(counter.count).toBeGreaterThanOrEqual(11); 57 | } 58 | }); 59 | 60 | test(`${name} | static graph, read 2/3 of leaves`, () => { 61 | framework.withBuild(() => { 62 | const config = makeConfig(); 63 | config.readFraction = 2 / 3; 64 | config.iterations = 10; 65 | const counter = new Counter(); 66 | const graph = makeGraph(framework, config, counter); 67 | const sum = runGraph(graph, 10, 2 / 3, framework); 68 | 69 | expect(sum).toEqual(73); 70 | if (testPullCounts) { 71 | expect(counter.count).toEqual(41); 72 | } else { 73 | expect(counter.count).toBeGreaterThanOrEqual(41); 74 | } 75 | }); 76 | }); 77 | 78 | test(`${name} | dynamic graph`, () => { 79 | framework.withBuild(() => { 80 | const config = makeConfig(); 81 | config.staticFraction = 0.5; 82 | config.width = 4; 83 | config.totalLayers = 2; 84 | const counter = new Counter(); 85 | const graph = makeGraph(framework, config, counter); 86 | const sum = runGraph(graph, 10, 1, framework); 87 | 88 | expect(sum).toEqual(72); 89 | if (testPullCounts) { 90 | expect(counter.count).toEqual(22); 91 | } else { 92 | expect(counter.count).toBeGreaterThanOrEqual(22); 93 | } 94 | }); 95 | }); 96 | 97 | test(`${name} | withBuild`, () => { 98 | const r = framework.withBuild(() => { 99 | const s = framework.signal(2); 100 | const c = framework.computed(() => s.read() * 2); 101 | 102 | expect(c.read()).toEqual(4); 103 | return c.read(); 104 | }); 105 | 106 | expect(r).toEqual(4); 107 | }); 108 | 109 | test(`${name} | effect`, () => { 110 | const spy = vi.fn(); 111 | 112 | const s = framework.signal(2); 113 | let c: any; 114 | 115 | framework.withBuild(() => { 116 | c = framework.computed(() => s.read() * 2); 117 | 118 | framework.effect(() => { 119 | spy(c.read()); 120 | }); 121 | }); 122 | expect(spy.mock.calls.length).toBe(1); 123 | 124 | framework.withBatch(() => { 125 | s.write(3); 126 | }); 127 | expect(s.read()).toEqual(3); 128 | expect(c.read()).toEqual(6); 129 | expect(spy.mock.calls.length).toBe(2); 130 | }); 131 | } 132 | -------------------------------------------------------------------------------- /src/cellxBench.ts: -------------------------------------------------------------------------------- 1 | // The following is an implementation of the cellx benchmark https://github.com/Riim/cellx/blob/master/perf/perf.html 2 | import { logPerfResult } from "./util/perfLogging"; 3 | import { Computed, ReactiveFramework } from "./util/reactiveFramework"; 4 | 5 | const cellx = (framework: ReactiveFramework, layers: number) => { 6 | return framework.withBuild(() => { 7 | const start = { 8 | prop1: framework.signal(1), 9 | prop2: framework.signal(2), 10 | prop3: framework.signal(3), 11 | prop4: framework.signal(4), 12 | }; 13 | 14 | let layer: { 15 | prop1: Computed; 16 | prop2: Computed; 17 | prop3: Computed; 18 | prop4: Computed; 19 | } = start; 20 | 21 | for (let i = layers; i > 0; i--) { 22 | const m = layer; 23 | const s = { 24 | prop1: framework.computed(() => m.prop2.read()), 25 | prop2: framework.computed(() => m.prop1.read() - m.prop3.read()), 26 | prop3: framework.computed(() => m.prop2.read() + m.prop4.read()), 27 | prop4: framework.computed(() => m.prop3.read()), 28 | }; 29 | 30 | framework.effect(() => s.prop1.read()); 31 | framework.effect(() => s.prop2.read()); 32 | framework.effect(() => s.prop3.read()); 33 | framework.effect(() => s.prop4.read()); 34 | 35 | s.prop1.read(); 36 | s.prop2.read(); 37 | s.prop3.read(); 38 | s.prop4.read(); 39 | 40 | layer = s; 41 | } 42 | 43 | const end = layer; 44 | 45 | const startTime = performance.now(); 46 | 47 | const before = [ 48 | end.prop1.read(), 49 | end.prop2.read(), 50 | end.prop3.read(), 51 | end.prop4.read(), 52 | ] as const; 53 | 54 | framework.withBatch(() => { 55 | start.prop1.write(4); 56 | start.prop2.write(3); 57 | start.prop3.write(2); 58 | start.prop4.write(1); 59 | }); 60 | 61 | const after = [ 62 | end.prop1.read(), 63 | end.prop2.read(), 64 | end.prop3.read(), 65 | end.prop4.read(), 66 | ] as const; 67 | 68 | const endTime = performance.now(); 69 | const elapsedTime = endTime - startTime; 70 | 71 | return [elapsedTime, before, after] as const; 72 | }); 73 | }; 74 | 75 | const arraysEqual = (a: readonly number[], b: readonly number[]) => { 76 | if (a.length !== b.length) return false; 77 | 78 | for (let i = 0; i < a.length; ++i) { 79 | if (a[i] !== b[i]) return false; 80 | } 81 | 82 | return true; 83 | }; 84 | 85 | type BenchmarkResults = [ 86 | readonly [number, number, number, number], 87 | readonly [number, number, number, number], 88 | ]; 89 | 90 | export const cellxbench = (framework: ReactiveFramework) => { 91 | globalThis.gc?.(); 92 | 93 | const expected: Record = { 94 | 1000: [ 95 | [-3, -6, -2, 2], 96 | [-2, -4, 2, 3], 97 | ], 98 | 2500: [ 99 | [-3, -6, -2, 2], 100 | [-2, -4, 2, 3], 101 | ], 102 | 5000: [ 103 | [2, 4, -1, -6], 104 | [-2, 1, -4, -4], 105 | ], 106 | }; 107 | 108 | const results: Record = {}; 109 | 110 | for (const layers in expected) { 111 | let total = 0; 112 | for (let i = 0; i < 10; i++) { 113 | const [elapsed, before, after] = cellx(framework, Number(layers)); 114 | 115 | results[layers] = [before, after]; 116 | 117 | total += elapsed; 118 | } 119 | 120 | logPerfResult({ 121 | framework: framework.name, 122 | test: `cellx${layers}`, 123 | time: total.toFixed(2), 124 | }); 125 | } 126 | 127 | for (const layers in expected) { 128 | const [before, after] = results[layers]; 129 | const [expectedBefore, expectedAfter] = expected[layers]; 130 | 131 | console.assert( 132 | arraysEqual(before, expectedBefore), 133 | `Expected first layer ${expectedBefore}, found first layer ${before}` 134 | ); 135 | 136 | console.assert( 137 | arraysEqual(after, expectedAfter), 138 | `Expected last layer ${expectedAfter}, found last layer ${after}` 139 | ); 140 | } 141 | 142 | globalThis.gc?.(); 143 | }; 144 | -------------------------------------------------------------------------------- /src/config.ts: -------------------------------------------------------------------------------- 1 | import { TestConfig, FrameworkInfo } from "./util/frameworkTypes"; 2 | 3 | import { alienFramework } from "./frameworks/alienSignals"; 4 | import { angularFramework } from "./frameworks/angularSignals"; 5 | import { mobxFramework } from "./frameworks/mobx"; 6 | import { tc39SignalsProposalStage0 } from "./frameworks/tc39-proposal-signals-stage-0"; 7 | import { molWireFramework } from "./frameworks/molWire"; 8 | import { obyFramework } from "./frameworks/oby"; 9 | import { preactSignalFramework } from "./frameworks/preactSignals"; 10 | import { reactivelyFramework } from "./frameworks/reactively"; 11 | import { signiaFramework } from "./frameworks/signia"; 12 | import { solidFramework } from "./frameworks/solid"; 13 | import { sFramework } from "./frameworks/s"; 14 | import { usignalFramework } from "./frameworks/uSignal"; 15 | import { vueReactivityFramework } from "./frameworks/vueReactivity"; 16 | import { svelteFramework } from "./frameworks/svelte"; 17 | import { tansuFramework } from "./frameworks/tansu"; 18 | // import { compostateFramework } from "./frameworks/compostate"; 19 | // import { valtioFramework } from "./frameworks/valtio"; 20 | 21 | export const frameworkInfo: FrameworkInfo[] = [ 22 | { framework: alienFramework, testPullCounts: true }, 23 | { framework: preactSignalFramework, testPullCounts: true }, 24 | { framework: svelteFramework, testPullCounts: true }, 25 | { framework: tc39SignalsProposalStage0, testPullCounts: true }, 26 | { framework: reactivelyFramework, testPullCounts: true }, 27 | { framework: sFramework }, 28 | { framework: tansuFramework, testPullCounts: true }, 29 | { framework: angularFramework, testPullCounts: true }, 30 | { framework: molWireFramework, testPullCounts: true }, 31 | { framework: obyFramework, testPullCounts: true }, 32 | { framework: signiaFramework, testPullCounts: true }, 33 | { framework: solidFramework }, 34 | { framework: usignalFramework, testPullCounts: true }, 35 | { framework: vueReactivityFramework, testPullCounts: true }, 36 | // NOTE: MobX currently hangs on some of the `dynamic` tests and `cellx` tests, so disable it if you want to run them. (https://github.com/mobxjs/mobx/issues/3926) 37 | { framework: mobxFramework, testPullCounts: false }, 38 | 39 | // --- Disabled frameworks --- 40 | // NOTE: the compostate adapter is currently broken and unused. 41 | // { framework: compostateFramework }, 42 | // NOTE: the kairo adapter is currently broken and unused. 43 | // { framework: kairoFramework, testPullCounts: true }, 44 | // NOTE: Valtio currently hangs on some of the `dynamic` tests, so disable it if you want to run them. (https://github.com/pmndrs/valtio/discussions/949) 45 | // { framework: valtioFramework }, 46 | ]; 47 | 48 | export const perfTests: TestConfig[] = [ 49 | { 50 | name: "simple component", 51 | width: 10, // can't change for decorator tests 52 | staticFraction: 1, // can't change for decorator tests 53 | nSources: 2, // can't change for decorator tests 54 | totalLayers: 5, 55 | readFraction: 0.2, 56 | iterations: 600000, 57 | expected: { 58 | sum: 19199832, 59 | count: 2640004, 60 | }, 61 | }, 62 | { 63 | name: "dynamic component", 64 | width: 10, 65 | totalLayers: 10, 66 | staticFraction: 3 / 4, 67 | nSources: 6, 68 | readFraction: 0.2, 69 | iterations: 15000, 70 | expected: { 71 | sum: 302310477864, 72 | count: 1125003, 73 | }, 74 | }, 75 | { 76 | name: "large web app", 77 | width: 1000, 78 | totalLayers: 12, 79 | staticFraction: 0.95, 80 | nSources: 4, 81 | readFraction: 1, 82 | iterations: 7000, 83 | expected: { 84 | sum: 29355933696000, 85 | count: 1473791, 86 | }, 87 | }, 88 | { 89 | name: "wide dense", 90 | width: 1000, 91 | totalLayers: 5, 92 | staticFraction: 1, 93 | nSources: 25, 94 | readFraction: 1, 95 | iterations: 3000, 96 | expected: { 97 | sum: 1171484375000, 98 | count: 735756, 99 | }, 100 | }, 101 | { 102 | name: "deep", 103 | width: 5, 104 | totalLayers: 500, 105 | staticFraction: 1, 106 | nSources: 3, 107 | readFraction: 1, 108 | iterations: 500, 109 | expected: { 110 | sum: 3.0239642676898464e241, 111 | count: 1246502, 112 | }, 113 | }, 114 | // Several frameworks hang on this test, so disabling it for now. 115 | // @see https://github.com/vuejs/core/issues/11928 116 | // { 117 | // name: "very dynamic", 118 | // width: 100, 119 | // totalLayers: 15, 120 | // staticFraction: 0.5, 121 | // nSources: 6, 122 | // readFraction: 1, 123 | // iterations: 2000, 124 | // expected: { 125 | // sum: 15664996402790400, 126 | // count: 1078671, 127 | // }, 128 | // }, 129 | ]; 130 | -------------------------------------------------------------------------------- /src/util/dependencyGraph.ts: -------------------------------------------------------------------------------- 1 | import { TestConfig } from "./frameworkTypes"; 2 | import { Computed, ReactiveFramework, Signal } from "./reactiveFramework"; 3 | import { Random } from "random"; 4 | 5 | export interface Graph { 6 | sources: Signal[]; 7 | layers: Computed[][]; 8 | } 9 | 10 | /** 11 | * Make a rectangular dependency graph, with an equal number of source elements 12 | * and computation elements at every layer. 13 | * 14 | * @param width number of source elements and number of computed elements per layer 15 | * @param totalLayers total number of source and computed layers 16 | * @param staticFraction every nth computed node is static (1 = all static, 3 = 2/3rd are dynamic) 17 | * @returns the graph 18 | */ 19 | export function makeGraph( 20 | framework: ReactiveFramework, 21 | config: TestConfig, 22 | counter: Counter 23 | ): Graph { 24 | const { width, totalLayers, staticFraction, nSources } = config; 25 | 26 | return framework.withBuild(() => { 27 | const sources = new Array(width).fill(0).map((_, i) => framework.signal(i)); 28 | const rows = makeDependentRows( 29 | sources, 30 | totalLayers - 1, 31 | counter, 32 | staticFraction, 33 | nSources, 34 | framework 35 | ); 36 | const graph = { sources, layers: rows }; 37 | return graph; 38 | }); 39 | } 40 | 41 | /** 42 | * Execute the graph by writing one of the sources and reading some or all of the leaves. 43 | * 44 | * @return the sum of all leaf values 45 | */ 46 | export function runGraph( 47 | graph: Graph, 48 | iterations: number, 49 | readFraction: number, 50 | framework: ReactiveFramework 51 | ): number { 52 | const rand = new Random("seed"); 53 | const { sources, layers } = graph; 54 | const leaves = layers[layers.length - 1]; 55 | const skipCount = Math.round(leaves.length * (1 - readFraction)); 56 | const readLeaves = removeElems(leaves, skipCount, rand); 57 | const frameworkName = framework.name.toLowerCase(); 58 | // const start = Date.now(); 59 | let sum = 0; 60 | 61 | if (frameworkName === "s-js" || frameworkName === "solidjs") { 62 | // [S.js freeze](https://github.com/adamhaile/S#sdatavalue) doesn't allow different values to be set during a single batch, so special case it. 63 | for (let i = 0; i < iterations; i++) { 64 | framework.withBatch(() => { 65 | const sourceDex = i % sources.length; 66 | sources[sourceDex].write(i + sourceDex); 67 | }); 68 | 69 | for (const leaf of readLeaves) { 70 | leaf.read(); 71 | } 72 | } 73 | 74 | sum = readLeaves.reduce((total, leaf) => leaf.read() + total, 0); 75 | } else { 76 | framework.withBatch(() => { 77 | for (let i = 0; i < iterations; i++) { 78 | // Useful for debugging edge cases for some frameworks that experience 79 | // dramatic slow downs for certain test configurations. These are generally 80 | // due to `computed` effects not being cached efficiently, and as the number 81 | // of layers increases, the uncached `computed` effects are re-evaluated in 82 | // an `O(n^2)` manner where `n` is the number of layers. 83 | // if (i % 100 === 0) { 84 | // console.log("iteration:", i, "delta:", Date.now() - start); 85 | // } 86 | 87 | const sourceDex = i % sources.length; 88 | sources[sourceDex].write(i + sourceDex); 89 | 90 | for (const leaf of readLeaves) { 91 | leaf.read(); 92 | } 93 | } 94 | 95 | sum = readLeaves.reduce((total, leaf) => leaf.read() + total, 0); 96 | }); 97 | } 98 | 99 | return sum; 100 | } 101 | 102 | function removeElems(src: T[], rmCount: number, rand: Random): T[] { 103 | const copy = src.slice(); 104 | for (let i = 0; i < rmCount; i++) { 105 | const rmDex = rand.int(0, copy.length - 1); 106 | copy.splice(rmDex, 1); 107 | } 108 | return copy; 109 | } 110 | 111 | export class Counter { 112 | count = 0; 113 | } 114 | 115 | function makeDependentRows( 116 | sources: Computed[], 117 | numRows: number, 118 | counter: Counter, 119 | staticFraction: number, 120 | nSources: number, 121 | framework: ReactiveFramework 122 | ): Computed[][] { 123 | let prevRow = sources; 124 | const rand = new Random("seed"); 125 | const rows = []; 126 | for (let l = 0; l < numRows; l++) { 127 | const row = makeRow( 128 | prevRow, 129 | counter, 130 | staticFraction, 131 | nSources, 132 | framework, 133 | l, 134 | rand 135 | ); 136 | rows.push(row); 137 | prevRow = row; 138 | } 139 | return rows; 140 | } 141 | 142 | function makeRow( 143 | sources: Computed[], 144 | counter: Counter, 145 | staticFraction: number, 146 | nSources: number, 147 | framework: ReactiveFramework, 148 | _layer: number, 149 | random: Random 150 | ): Computed[] { 151 | return sources.map((_, myDex) => { 152 | const mySources: Computed[] = []; 153 | for (let sourceDex = 0; sourceDex < nSources; sourceDex++) { 154 | mySources.push(sources[(myDex + sourceDex) % sources.length]); 155 | } 156 | 157 | const staticNode = random.float() < staticFraction; 158 | if (staticNode) { 159 | // static node, always reference sources 160 | return framework.computed(() => { 161 | counter.count++; 162 | 163 | let sum = 0; 164 | for (const src of mySources) { 165 | sum += src.read(); 166 | } 167 | return sum; 168 | }); 169 | } else { 170 | // dynamic node, drops one of the sources depending on the value of the first element 171 | const first = mySources[0]; 172 | const tail = mySources.slice(1); 173 | const node = framework.computed(() => { 174 | counter.count++; 175 | let sum = first.read(); 176 | const shouldDrop = sum & 0x1; 177 | const dropDex = sum % tail.length; 178 | 179 | for (let i = 0; i < tail.length; i++) { 180 | if (shouldDrop && i === dropDex) continue; 181 | sum += tail[i].read(); 182 | } 183 | 184 | return sum; 185 | }); 186 | return node; 187 | } 188 | }); 189 | } 190 | -------------------------------------------------------------------------------- /src/sBench.ts: -------------------------------------------------------------------------------- 1 | // Inspired by https://github.com/solidjs/solid/blob/main/packages/solid/bench/bench.cjs 2 | import { logPerfResult } from "./util/perfLogging"; 3 | import { Computed, Signal, ReactiveFramework } from "./util/reactiveFramework"; 4 | 5 | const COUNT = 1e5; 6 | 7 | type Reader = () => number; 8 | export function sbench(framework: ReactiveFramework) { 9 | bench(createDataSignals, COUNT, COUNT); 10 | bench(createComputations0to1, COUNT, 0); 11 | bench(createComputations1to1, COUNT, COUNT); 12 | bench(createComputations2to1, COUNT / 2, COUNT); 13 | bench(createComputations4to1, COUNT / 4, COUNT); 14 | bench(createComputations1000to1, COUNT / 1000, COUNT); 15 | // createTotal += bench(createComputations8to1, COUNT, 8 * COUNT); 16 | bench(createComputations1to2, COUNT, COUNT / 2); 17 | bench(createComputations1to4, COUNT, COUNT / 4); 18 | bench(createComputations1to8, COUNT, COUNT / 8); 19 | bench(createComputations1to1000, COUNT, COUNT / 1000); 20 | bench(updateComputations1to1, COUNT * 4, 1); 21 | bench(updateComputations2to1, COUNT * 2, 2); 22 | bench(updateComputations4to1, COUNT, 4); 23 | bench(updateComputations1000to1, COUNT / 100, 1000); 24 | bench(updateComputations1to2, COUNT * 4, 1); 25 | bench(updateComputations1to4, COUNT * 4, 1); 26 | bench(updateComputations1to1000, COUNT * 4, 1); 27 | 28 | function bench( 29 | fn: (n: number, sources: any[]) => void, 30 | count: number, 31 | scount: number 32 | ) { 33 | const time = run(fn, count, scount); 34 | logPerfResult({ 35 | framework: framework.name, 36 | test: fn.name, 37 | time: time.toFixed(2), 38 | }); 39 | } 40 | 41 | function run( 42 | fn: (n: number, sources: Computed[]) => void, 43 | n: number, 44 | scount: number 45 | ) { 46 | // prep n * arity sources 47 | let start = 0; 48 | let end = 0; 49 | 50 | framework.withBuild(() => { 51 | // run 3 times to warm up 52 | let sources = createDataSignals(scount, []) as Computed[] | null; 53 | fn(n / 100, sources!); 54 | sources = createDataSignals(scount, []); 55 | fn(n / 100, sources); 56 | sources = createDataSignals(scount, []); 57 | fn(n / 100, sources); 58 | sources = createDataSignals(scount, []); 59 | for (let i = 0; i < scount; i++) { 60 | sources[i].read(); 61 | sources[i].read(); 62 | sources[i].read(); 63 | } 64 | 65 | // start GC clean 66 | globalThis.gc?.(); 67 | 68 | start = performance.now(); 69 | 70 | fn(n, sources); 71 | 72 | // end GC clean 73 | sources = null; 74 | globalThis.gc?.(); 75 | end = performance.now(); 76 | }); 77 | 78 | return end - start; 79 | } 80 | 81 | function createDataSignals(n: number, sources: Computed[]) { 82 | for (let i = 0; i < n; i++) { 83 | sources[i] = framework.signal(i); 84 | } 85 | return sources; 86 | } 87 | 88 | function createComputations0to1(n: number, _sources: Computed[]) { 89 | for (let i = 0; i < n; i++) { 90 | createComputation0(i); 91 | } 92 | } 93 | 94 | function createComputations1to1000(n: number, sources: Computed[]) { 95 | for (let i = 0; i < n / 1000; i++) { 96 | const { read: get } = sources[i]; 97 | for (let j = 0; j < 1000; j++) { 98 | createComputation1(get); 99 | } 100 | } 101 | } 102 | 103 | function createComputations1to8(n: number, sources: Computed[]) { 104 | for (let i = 0; i < n / 8; i++) { 105 | const { read: get } = sources[i]; 106 | createComputation1(get); 107 | createComputation1(get); 108 | createComputation1(get); 109 | createComputation1(get); 110 | createComputation1(get); 111 | createComputation1(get); 112 | createComputation1(get); 113 | createComputation1(get); 114 | } 115 | } 116 | 117 | function createComputations1to4(n: number, sources: Computed[]) { 118 | for (let i = 0; i < n / 4; i++) { 119 | const { read: get } = sources[i]; 120 | createComputation1(get); 121 | createComputation1(get); 122 | createComputation1(get); 123 | createComputation1(get); 124 | } 125 | } 126 | 127 | function createComputations1to2(n: number, sources: Computed[]) { 128 | for (let i = 0; i < n / 2; i++) { 129 | const { read: get } = sources[i]; 130 | createComputation1(get); 131 | createComputation1(get); 132 | } 133 | } 134 | 135 | function createComputations1to1(n: number, sources: Computed[]) { 136 | for (let i = 0; i < n; i++) { 137 | const { read: get } = sources[i]; 138 | createComputation1(get); 139 | } 140 | } 141 | 142 | function createComputations2to1(n: number, sources: Computed[]) { 143 | for (let i = 0; i < n; i++) { 144 | createComputation2(sources[i * 2].read, sources[i * 2 + 1].read); 145 | } 146 | } 147 | 148 | function createComputations4to1(n: number, sources: Computed[]) { 149 | for (let i = 0; i < n; i++) { 150 | createComputation4( 151 | sources[i * 4].read, 152 | sources[i * 4 + 1].read, 153 | sources[i * 4 + 2].read, 154 | sources[i * 4 + 3].read 155 | ); 156 | } 157 | } 158 | 159 | // function createComputations8to1(n: number, sources: Computed[]) { 160 | // for (let i = 0; i < n; i++) { 161 | // createComputation8( 162 | // sources[i * 8].read, 163 | // sources[i * 8 + 1].read, 164 | // sources[i * 8 + 2].read, 165 | // sources[i * 8 + 3].read, 166 | // sources[i * 8 + 4].read, 167 | // sources[i * 8 + 5].read, 168 | // sources[i * 8 + 6].read, 169 | // sources[i * 8 + 7].read 170 | // ); 171 | // } 172 | // } 173 | 174 | // only create n / 100 computations, as otherwise takes too long 175 | function createComputations1000to1(n: number, sources: Computed[]) { 176 | for (let i = 0; i < n; i++) { 177 | createComputation1000(sources, i * 1000); 178 | } 179 | } 180 | 181 | function createComputation0(i: number) { 182 | framework.computed(() => i); 183 | } 184 | 185 | function createComputation1(s1: Reader) { 186 | framework.computed(() => s1()); 187 | } 188 | function createComputation2(s1: Reader, s2: Reader) { 189 | framework.computed(() => s1() + s2()); 190 | } 191 | 192 | function createComputation4(s1: Reader, s2: Reader, s3: Reader, s4: Reader) { 193 | framework.computed(() => s1() + s2() + s3() + s4()); 194 | } 195 | 196 | // function createComputation8( 197 | // s1: Reader, 198 | // s2: Reader, 199 | // s3: Reader, 200 | // s4: Reader, 201 | // s5: Reader, 202 | // s6: Reader, 203 | // s7: Reader, 204 | // s8: Reader 205 | // ) { 206 | // framework.computed( 207 | // () => s1() + s2() + s3() + s4() + s5() + s6() + s7() + s8() 208 | // ); 209 | // } 210 | 211 | function createComputation1000(ss: Computed[], offset: number) { 212 | framework.computed(() => { 213 | let sum = 0; 214 | for (let i = 0; i < 1000; i++) { 215 | sum += ss[offset + i].read(); 216 | } 217 | return sum; 218 | }); 219 | } 220 | 221 | function updateComputations1to1(n: number, sources: Signal[]) { 222 | let { read: get1, write: set1 } = sources[0]; 223 | framework.computed(() => get1()); 224 | for (let i = 0; i < n; i++) { 225 | set1(i); 226 | } 227 | } 228 | 229 | function updateComputations2to1(n: number, sources: Signal[]) { 230 | let { read: get1, write: set1 } = sources[0], 231 | { read: get2 } = sources[1]; 232 | framework.computed(() => get1() + get2()); 233 | for (let i = 0; i < n; i++) { 234 | set1(i); 235 | } 236 | } 237 | 238 | function updateComputations4to1(n: number, sources: Signal[]) { 239 | let { read: get1, write: set1 } = sources[0], 240 | { read: get2 } = sources[1], 241 | { read: get3 } = sources[2], 242 | { read: get4 } = sources[3]; 243 | framework.computed(() => get1() + get2() + get3() + get4()); 244 | for (let i = 0; i < n; i++) { 245 | set1(i); 246 | } 247 | } 248 | 249 | function updateComputations1000to1(n: number, sources: Signal[]) { 250 | let { read: _get1, write: set1 } = sources[0]; 251 | framework.computed(() => { 252 | let sum = 0; 253 | for (let i = 0; i < 1000; i++) { 254 | sum += sources[i].read(); 255 | } 256 | return sum; 257 | }); 258 | for (let i = 0; i < n; i++) { 259 | set1(i); 260 | } 261 | } 262 | 263 | function updateComputations1to2(n: number, sources: Signal[]) { 264 | let { read: get1, write: set1 } = sources[0]; 265 | framework.computed(() => get1()); 266 | framework.computed(() => get1()); 267 | for (let i = 0; i < n / 2; i++) { 268 | set1(i); 269 | } 270 | } 271 | 272 | function updateComputations1to4(n: number, sources: Signal[]) { 273 | let { read: get1, write: set1 } = sources[0]; 274 | framework.computed(() => get1()); 275 | framework.computed(() => get1()); 276 | framework.computed(() => get1()); 277 | framework.computed(() => get1()); 278 | for (let i = 0; i < n / 4; i++) { 279 | set1(i); 280 | } 281 | } 282 | 283 | function updateComputations1to1000(n: number, sources: Signal[]) { 284 | const { read: get1, write: set1 } = sources[0]; 285 | for (let i = 0; i < 1000; i++) { 286 | framework.computed(() => get1()); 287 | } 288 | for (let i = 0; i < n / 1000; i++) { 289 | set1(i); 290 | } 291 | } 292 | } 293 | -------------------------------------------------------------------------------- /pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: '9.0' 2 | 3 | settings: 4 | autoInstallPeers: true 5 | excludeLinksFromLockfile: false 6 | 7 | importers: 8 | 9 | .: 10 | dependencies: 11 | '@amadeus-it-group/tansu': 12 | specifier: ^2.0.0 13 | version: 2.0.0 14 | '@angular/core': 15 | specifier: 19.0.6 16 | version: 19.0.6(rxjs@7.8.1)(zone.js@0.15.0) 17 | '@preact/signals': 18 | specifier: ^2.0.0 19 | version: 2.0.0(preact@10.25.4) 20 | '@reactively/core': 21 | specifier: ^0.0.8 22 | version: 0.0.8 23 | '@vue/reactivity': 24 | specifier: ^3.5.13 25 | version: 3.5.13 26 | alien-signals: 27 | specifier: 1.0.0-alpha.1 28 | version: 1.0.0-alpha.1 29 | compostate: 30 | specifier: 0.6.0-alpha.1 31 | version: 0.6.0-alpha.1 32 | kairo: 33 | specifier: 0.6.0-rc.0 34 | version: 0.6.0-rc.0 35 | mobx: 36 | specifier: ^6.13.5 37 | version: 6.13.5 38 | mol_wire_lib: 39 | specifier: ^1.0.1259 40 | version: 1.0.1259 41 | oby: 42 | specifier: 15.1.2 43 | version: 15.1.2 44 | preact: 45 | specifier: ^10.25.4 46 | version: 10.25.4 47 | random: 48 | specifier: ^5.1.1 49 | version: 5.1.1 50 | react: 51 | specifier: ^19.0.0 52 | version: 19.0.0 53 | s-js: 54 | specifier: ^0.4.9 55 | version: 0.4.9 56 | signal-polyfill: 57 | specifier: ^0.2.1 58 | version: 0.2.1 59 | signia: 60 | specifier: ^0.1.5 61 | version: 0.1.5 62 | solid-js: 63 | specifier: ^1.9.4 64 | version: 1.9.4 65 | svelte: 66 | specifier: 5.17.3 67 | version: 5.17.3 68 | usignal: 69 | specifier: ^0.9.0 70 | version: 0.9.0 71 | valtio: 72 | specifier: ^2.1.2 73 | version: 2.1.2(react@19.0.0) 74 | devDependencies: 75 | '@types/node': 76 | specifier: ^22.10.5 77 | version: 22.10.5 78 | esbuild: 79 | specifier: ^0.24.2 80 | version: 0.24.2 81 | prettier: 82 | specifier: ^3.4.2 83 | version: 3.4.2 84 | vitest: 85 | specifier: ^2.1.8 86 | version: 2.1.8(@types/node@22.10.5) 87 | 88 | packages/bench: 89 | devDependencies: 90 | '@types/node': 91 | specifier: ^18.11.18 92 | version: 18.14.0 93 | chokidar-cli: 94 | specifier: ^3.0.0 95 | version: 3.0.0 96 | esbuild: 97 | specifier: ^0.16.12 98 | version: 0.16.17 99 | mol_wire_lib: 100 | specifier: ^1.0.422 101 | version: 1.0.488 102 | solid-js: 103 | specifier: ^1.6.6 104 | version: 1.6.11 105 | typescript: 106 | specifier: ^4.9.4 107 | version: 4.9.5 108 | usignal: 109 | specifier: ^0.8.9 110 | version: 0.8.14 111 | v8-natives: 112 | specifier: ^1.2.5 113 | version: 1.2.5 114 | 115 | packages: 116 | 117 | '@amadeus-it-group/tansu@2.0.0': 118 | resolution: {integrity: sha512-KebRcEFWkdKix1vN12Y8cuGWdWKqkCzXiesXnxSAZbEULBDlPwm0sey+ygP98PudM1gBygtiWyWzGTeAL5+jIw==} 119 | 120 | '@ampproject/remapping@2.3.0': 121 | resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} 122 | engines: {node: '>=6.0.0'} 123 | 124 | '@angular/core@19.0.6': 125 | resolution: {integrity: sha512-9N7FmdRHtS7zfXC/wnyap/reX7fgiOrWpVivayHjWP4RkLYXJAzJIpLyew0jrx4vf8r3lZnC0Zmq0PW007Ngjw==} 126 | engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0} 127 | peerDependencies: 128 | rxjs: ^6.5.3 || ^7.4.0 129 | zone.js: ~0.15.0 130 | 131 | '@esbuild/aix-ppc64@0.21.5': 132 | resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} 133 | engines: {node: '>=12'} 134 | cpu: [ppc64] 135 | os: [aix] 136 | 137 | '@esbuild/aix-ppc64@0.24.2': 138 | resolution: {integrity: sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==} 139 | engines: {node: '>=18'} 140 | cpu: [ppc64] 141 | os: [aix] 142 | 143 | '@esbuild/android-arm64@0.16.17': 144 | resolution: {integrity: sha512-MIGl6p5sc3RDTLLkYL1MyL8BMRN4tLMRCn+yRJJmEDvYZ2M7tmAf80hx1kbNEUX2KJ50RRtxZ4JHLvCfuB6kBg==} 145 | engines: {node: '>=12'} 146 | cpu: [arm64] 147 | os: [android] 148 | 149 | '@esbuild/android-arm64@0.21.5': 150 | resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} 151 | engines: {node: '>=12'} 152 | cpu: [arm64] 153 | os: [android] 154 | 155 | '@esbuild/android-arm64@0.24.2': 156 | resolution: {integrity: sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==} 157 | engines: {node: '>=18'} 158 | cpu: [arm64] 159 | os: [android] 160 | 161 | '@esbuild/android-arm@0.16.17': 162 | resolution: {integrity: sha512-N9x1CMXVhtWEAMS7pNNONyA14f71VPQN9Cnavj1XQh6T7bskqiLLrSca4O0Vr8Wdcga943eThxnVp3JLnBMYtw==} 163 | engines: {node: '>=12'} 164 | cpu: [arm] 165 | os: [android] 166 | 167 | '@esbuild/android-arm@0.21.5': 168 | resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} 169 | engines: {node: '>=12'} 170 | cpu: [arm] 171 | os: [android] 172 | 173 | '@esbuild/android-arm@0.24.2': 174 | resolution: {integrity: sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==} 175 | engines: {node: '>=18'} 176 | cpu: [arm] 177 | os: [android] 178 | 179 | '@esbuild/android-x64@0.16.17': 180 | resolution: {integrity: sha512-a3kTv3m0Ghh4z1DaFEuEDfz3OLONKuFvI4Xqczqx4BqLyuFaFkuaG4j2MtA6fuWEFeC5x9IvqnX7drmRq/fyAQ==} 181 | engines: {node: '>=12'} 182 | cpu: [x64] 183 | os: [android] 184 | 185 | '@esbuild/android-x64@0.21.5': 186 | resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} 187 | engines: {node: '>=12'} 188 | cpu: [x64] 189 | os: [android] 190 | 191 | '@esbuild/android-x64@0.24.2': 192 | resolution: {integrity: sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==} 193 | engines: {node: '>=18'} 194 | cpu: [x64] 195 | os: [android] 196 | 197 | '@esbuild/darwin-arm64@0.16.17': 198 | resolution: {integrity: sha512-/2agbUEfmxWHi9ARTX6OQ/KgXnOWfsNlTeLcoV7HSuSTv63E4DqtAc+2XqGw1KHxKMHGZgbVCZge7HXWX9Vn+w==} 199 | engines: {node: '>=12'} 200 | cpu: [arm64] 201 | os: [darwin] 202 | 203 | '@esbuild/darwin-arm64@0.21.5': 204 | resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} 205 | engines: {node: '>=12'} 206 | cpu: [arm64] 207 | os: [darwin] 208 | 209 | '@esbuild/darwin-arm64@0.24.2': 210 | resolution: {integrity: sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==} 211 | engines: {node: '>=18'} 212 | cpu: [arm64] 213 | os: [darwin] 214 | 215 | '@esbuild/darwin-x64@0.16.17': 216 | resolution: {integrity: sha512-2By45OBHulkd9Svy5IOCZt376Aa2oOkiE9QWUK9fe6Tb+WDr8hXL3dpqi+DeLiMed8tVXspzsTAvd0jUl96wmg==} 217 | engines: {node: '>=12'} 218 | cpu: [x64] 219 | os: [darwin] 220 | 221 | '@esbuild/darwin-x64@0.21.5': 222 | resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} 223 | engines: {node: '>=12'} 224 | cpu: [x64] 225 | os: [darwin] 226 | 227 | '@esbuild/darwin-x64@0.24.2': 228 | resolution: {integrity: sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==} 229 | engines: {node: '>=18'} 230 | cpu: [x64] 231 | os: [darwin] 232 | 233 | '@esbuild/freebsd-arm64@0.16.17': 234 | resolution: {integrity: sha512-mt+cxZe1tVx489VTb4mBAOo2aKSnJ33L9fr25JXpqQqzbUIw/yzIzi+NHwAXK2qYV1lEFp4OoVeThGjUbmWmdw==} 235 | engines: {node: '>=12'} 236 | cpu: [arm64] 237 | os: [freebsd] 238 | 239 | '@esbuild/freebsd-arm64@0.21.5': 240 | resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} 241 | engines: {node: '>=12'} 242 | cpu: [arm64] 243 | os: [freebsd] 244 | 245 | '@esbuild/freebsd-arm64@0.24.2': 246 | resolution: {integrity: sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==} 247 | engines: {node: '>=18'} 248 | cpu: [arm64] 249 | os: [freebsd] 250 | 251 | '@esbuild/freebsd-x64@0.16.17': 252 | resolution: {integrity: sha512-8ScTdNJl5idAKjH8zGAsN7RuWcyHG3BAvMNpKOBaqqR7EbUhhVHOqXRdL7oZvz8WNHL2pr5+eIT5c65kA6NHug==} 253 | engines: {node: '>=12'} 254 | cpu: [x64] 255 | os: [freebsd] 256 | 257 | '@esbuild/freebsd-x64@0.21.5': 258 | resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} 259 | engines: {node: '>=12'} 260 | cpu: [x64] 261 | os: [freebsd] 262 | 263 | '@esbuild/freebsd-x64@0.24.2': 264 | resolution: {integrity: sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==} 265 | engines: {node: '>=18'} 266 | cpu: [x64] 267 | os: [freebsd] 268 | 269 | '@esbuild/linux-arm64@0.16.17': 270 | resolution: {integrity: sha512-7S8gJnSlqKGVJunnMCrXHU9Q8Q/tQIxk/xL8BqAP64wchPCTzuM6W3Ra8cIa1HIflAvDnNOt2jaL17vaW+1V0g==} 271 | engines: {node: '>=12'} 272 | cpu: [arm64] 273 | os: [linux] 274 | 275 | '@esbuild/linux-arm64@0.21.5': 276 | resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} 277 | engines: {node: '>=12'} 278 | cpu: [arm64] 279 | os: [linux] 280 | 281 | '@esbuild/linux-arm64@0.24.2': 282 | resolution: {integrity: sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==} 283 | engines: {node: '>=18'} 284 | cpu: [arm64] 285 | os: [linux] 286 | 287 | '@esbuild/linux-arm@0.16.17': 288 | resolution: {integrity: sha512-iihzrWbD4gIT7j3caMzKb/RsFFHCwqqbrbH9SqUSRrdXkXaygSZCZg1FybsZz57Ju7N/SHEgPyaR0LZ8Zbe9gQ==} 289 | engines: {node: '>=12'} 290 | cpu: [arm] 291 | os: [linux] 292 | 293 | '@esbuild/linux-arm@0.21.5': 294 | resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} 295 | engines: {node: '>=12'} 296 | cpu: [arm] 297 | os: [linux] 298 | 299 | '@esbuild/linux-arm@0.24.2': 300 | resolution: {integrity: sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==} 301 | engines: {node: '>=18'} 302 | cpu: [arm] 303 | os: [linux] 304 | 305 | '@esbuild/linux-ia32@0.16.17': 306 | resolution: {integrity: sha512-kiX69+wcPAdgl3Lonh1VI7MBr16nktEvOfViszBSxygRQqSpzv7BffMKRPMFwzeJGPxcio0pdD3kYQGpqQ2SSg==} 307 | engines: {node: '>=12'} 308 | cpu: [ia32] 309 | os: [linux] 310 | 311 | '@esbuild/linux-ia32@0.21.5': 312 | resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} 313 | engines: {node: '>=12'} 314 | cpu: [ia32] 315 | os: [linux] 316 | 317 | '@esbuild/linux-ia32@0.24.2': 318 | resolution: {integrity: sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==} 319 | engines: {node: '>=18'} 320 | cpu: [ia32] 321 | os: [linux] 322 | 323 | '@esbuild/linux-loong64@0.16.17': 324 | resolution: {integrity: sha512-dTzNnQwembNDhd654cA4QhbS9uDdXC3TKqMJjgOWsC0yNCbpzfWoXdZvp0mY7HU6nzk5E0zpRGGx3qoQg8T2DQ==} 325 | engines: {node: '>=12'} 326 | cpu: [loong64] 327 | os: [linux] 328 | 329 | '@esbuild/linux-loong64@0.21.5': 330 | resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} 331 | engines: {node: '>=12'} 332 | cpu: [loong64] 333 | os: [linux] 334 | 335 | '@esbuild/linux-loong64@0.24.2': 336 | resolution: {integrity: sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==} 337 | engines: {node: '>=18'} 338 | cpu: [loong64] 339 | os: [linux] 340 | 341 | '@esbuild/linux-mips64el@0.16.17': 342 | resolution: {integrity: sha512-ezbDkp2nDl0PfIUn0CsQ30kxfcLTlcx4Foz2kYv8qdC6ia2oX5Q3E/8m6lq84Dj/6b0FrkgD582fJMIfHhJfSw==} 343 | engines: {node: '>=12'} 344 | cpu: [mips64el] 345 | os: [linux] 346 | 347 | '@esbuild/linux-mips64el@0.21.5': 348 | resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} 349 | engines: {node: '>=12'} 350 | cpu: [mips64el] 351 | os: [linux] 352 | 353 | '@esbuild/linux-mips64el@0.24.2': 354 | resolution: {integrity: sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==} 355 | engines: {node: '>=18'} 356 | cpu: [mips64el] 357 | os: [linux] 358 | 359 | '@esbuild/linux-ppc64@0.16.17': 360 | resolution: {integrity: sha512-dzS678gYD1lJsW73zrFhDApLVdM3cUF2MvAa1D8K8KtcSKdLBPP4zZSLy6LFZ0jYqQdQ29bjAHJDgz0rVbLB3g==} 361 | engines: {node: '>=12'} 362 | cpu: [ppc64] 363 | os: [linux] 364 | 365 | '@esbuild/linux-ppc64@0.21.5': 366 | resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} 367 | engines: {node: '>=12'} 368 | cpu: [ppc64] 369 | os: [linux] 370 | 371 | '@esbuild/linux-ppc64@0.24.2': 372 | resolution: {integrity: sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==} 373 | engines: {node: '>=18'} 374 | cpu: [ppc64] 375 | os: [linux] 376 | 377 | '@esbuild/linux-riscv64@0.16.17': 378 | resolution: {integrity: sha512-ylNlVsxuFjZK8DQtNUwiMskh6nT0vI7kYl/4fZgV1llP5d6+HIeL/vmmm3jpuoo8+NuXjQVZxmKuhDApK0/cKw==} 379 | engines: {node: '>=12'} 380 | cpu: [riscv64] 381 | os: [linux] 382 | 383 | '@esbuild/linux-riscv64@0.21.5': 384 | resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} 385 | engines: {node: '>=12'} 386 | cpu: [riscv64] 387 | os: [linux] 388 | 389 | '@esbuild/linux-riscv64@0.24.2': 390 | resolution: {integrity: sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==} 391 | engines: {node: '>=18'} 392 | cpu: [riscv64] 393 | os: [linux] 394 | 395 | '@esbuild/linux-s390x@0.16.17': 396 | resolution: {integrity: sha512-gzy7nUTO4UA4oZ2wAMXPNBGTzZFP7mss3aKR2hH+/4UUkCOyqmjXiKpzGrY2TlEUhbbejzXVKKGazYcQTZWA/w==} 397 | engines: {node: '>=12'} 398 | cpu: [s390x] 399 | os: [linux] 400 | 401 | '@esbuild/linux-s390x@0.21.5': 402 | resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} 403 | engines: {node: '>=12'} 404 | cpu: [s390x] 405 | os: [linux] 406 | 407 | '@esbuild/linux-s390x@0.24.2': 408 | resolution: {integrity: sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==} 409 | engines: {node: '>=18'} 410 | cpu: [s390x] 411 | os: [linux] 412 | 413 | '@esbuild/linux-x64@0.16.17': 414 | resolution: {integrity: sha512-mdPjPxfnmoqhgpiEArqi4egmBAMYvaObgn4poorpUaqmvzzbvqbowRllQ+ZgzGVMGKaPkqUmPDOOFQRUFDmeUw==} 415 | engines: {node: '>=12'} 416 | cpu: [x64] 417 | os: [linux] 418 | 419 | '@esbuild/linux-x64@0.21.5': 420 | resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} 421 | engines: {node: '>=12'} 422 | cpu: [x64] 423 | os: [linux] 424 | 425 | '@esbuild/linux-x64@0.24.2': 426 | resolution: {integrity: sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==} 427 | engines: {node: '>=18'} 428 | cpu: [x64] 429 | os: [linux] 430 | 431 | '@esbuild/netbsd-arm64@0.24.2': 432 | resolution: {integrity: sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==} 433 | engines: {node: '>=18'} 434 | cpu: [arm64] 435 | os: [netbsd] 436 | 437 | '@esbuild/netbsd-x64@0.16.17': 438 | resolution: {integrity: sha512-/PzmzD/zyAeTUsduZa32bn0ORug+Jd1EGGAUJvqfeixoEISYpGnAezN6lnJoskauoai0Jrs+XSyvDhppCPoKOA==} 439 | engines: {node: '>=12'} 440 | cpu: [x64] 441 | os: [netbsd] 442 | 443 | '@esbuild/netbsd-x64@0.21.5': 444 | resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} 445 | engines: {node: '>=12'} 446 | cpu: [x64] 447 | os: [netbsd] 448 | 449 | '@esbuild/netbsd-x64@0.24.2': 450 | resolution: {integrity: sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==} 451 | engines: {node: '>=18'} 452 | cpu: [x64] 453 | os: [netbsd] 454 | 455 | '@esbuild/openbsd-arm64@0.24.2': 456 | resolution: {integrity: sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==} 457 | engines: {node: '>=18'} 458 | cpu: [arm64] 459 | os: [openbsd] 460 | 461 | '@esbuild/openbsd-x64@0.16.17': 462 | resolution: {integrity: sha512-2yaWJhvxGEz2RiftSk0UObqJa/b+rIAjnODJgv2GbGGpRwAfpgzyrg1WLK8rqA24mfZa9GvpjLcBBg8JHkoodg==} 463 | engines: {node: '>=12'} 464 | cpu: [x64] 465 | os: [openbsd] 466 | 467 | '@esbuild/openbsd-x64@0.21.5': 468 | resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} 469 | engines: {node: '>=12'} 470 | cpu: [x64] 471 | os: [openbsd] 472 | 473 | '@esbuild/openbsd-x64@0.24.2': 474 | resolution: {integrity: sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==} 475 | engines: {node: '>=18'} 476 | cpu: [x64] 477 | os: [openbsd] 478 | 479 | '@esbuild/sunos-x64@0.16.17': 480 | resolution: {integrity: sha512-xtVUiev38tN0R3g8VhRfN7Zl42YCJvyBhRKw1RJjwE1d2emWTVToPLNEQj/5Qxc6lVFATDiy6LjVHYhIPrLxzw==} 481 | engines: {node: '>=12'} 482 | cpu: [x64] 483 | os: [sunos] 484 | 485 | '@esbuild/sunos-x64@0.21.5': 486 | resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} 487 | engines: {node: '>=12'} 488 | cpu: [x64] 489 | os: [sunos] 490 | 491 | '@esbuild/sunos-x64@0.24.2': 492 | resolution: {integrity: sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==} 493 | engines: {node: '>=18'} 494 | cpu: [x64] 495 | os: [sunos] 496 | 497 | '@esbuild/win32-arm64@0.16.17': 498 | resolution: {integrity: sha512-ga8+JqBDHY4b6fQAmOgtJJue36scANy4l/rL97W+0wYmijhxKetzZdKOJI7olaBaMhWt8Pac2McJdZLxXWUEQw==} 499 | engines: {node: '>=12'} 500 | cpu: [arm64] 501 | os: [win32] 502 | 503 | '@esbuild/win32-arm64@0.21.5': 504 | resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} 505 | engines: {node: '>=12'} 506 | cpu: [arm64] 507 | os: [win32] 508 | 509 | '@esbuild/win32-arm64@0.24.2': 510 | resolution: {integrity: sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==} 511 | engines: {node: '>=18'} 512 | cpu: [arm64] 513 | os: [win32] 514 | 515 | '@esbuild/win32-ia32@0.16.17': 516 | resolution: {integrity: sha512-WnsKaf46uSSF/sZhwnqE4L/F89AYNMiD4YtEcYekBt9Q7nj0DiId2XH2Ng2PHM54qi5oPrQ8luuzGszqi/veig==} 517 | engines: {node: '>=12'} 518 | cpu: [ia32] 519 | os: [win32] 520 | 521 | '@esbuild/win32-ia32@0.21.5': 522 | resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} 523 | engines: {node: '>=12'} 524 | cpu: [ia32] 525 | os: [win32] 526 | 527 | '@esbuild/win32-ia32@0.24.2': 528 | resolution: {integrity: sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==} 529 | engines: {node: '>=18'} 530 | cpu: [ia32] 531 | os: [win32] 532 | 533 | '@esbuild/win32-x64@0.16.17': 534 | resolution: {integrity: sha512-y+EHuSchhL7FjHgvQL/0fnnFmO4T1bhvWANX6gcnqTjtnKWbTvUMCpGnv2+t+31d7RzyEAYAd4u2fnIhHL6N/Q==} 535 | engines: {node: '>=12'} 536 | cpu: [x64] 537 | os: [win32] 538 | 539 | '@esbuild/win32-x64@0.21.5': 540 | resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} 541 | engines: {node: '>=12'} 542 | cpu: [x64] 543 | os: [win32] 544 | 545 | '@esbuild/win32-x64@0.24.2': 546 | resolution: {integrity: sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==} 547 | engines: {node: '>=18'} 548 | cpu: [x64] 549 | os: [win32] 550 | 551 | '@jridgewell/gen-mapping@0.3.8': 552 | resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==} 553 | engines: {node: '>=6.0.0'} 554 | 555 | '@jridgewell/resolve-uri@3.1.2': 556 | resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} 557 | engines: {node: '>=6.0.0'} 558 | 559 | '@jridgewell/set-array@1.2.1': 560 | resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} 561 | engines: {node: '>=6.0.0'} 562 | 563 | '@jridgewell/sourcemap-codec@1.5.0': 564 | resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} 565 | 566 | '@jridgewell/trace-mapping@0.3.25': 567 | resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} 568 | 569 | '@preact/signals-core@1.8.0': 570 | resolution: {integrity: sha512-OBvUsRZqNmjzCZXWLxkZfhcgT+Fk8DDcT/8vD6a1xhDemodyy87UJRJfASMuSD8FaAIeGgGm85ydXhm7lr4fyA==} 571 | 572 | '@preact/signals@2.0.0': 573 | resolution: {integrity: sha512-wzx6YEXw2a+vKdE+p+XlIGsmH2bDjJzzevirtshT9ixoNIV2WJg83kG+QErPHs/ZkeR+MsqgRIFB6x22fDVRlg==} 574 | peerDependencies: 575 | preact: 10.x 576 | 577 | '@reactively/core@0.0.8': 578 | resolution: {integrity: sha512-5uAnNf2gQSm3gM7z6Lx079H1/MuDQQI+5aYfwyDFGR9nHZj8yblLY/6aOJVWp+NcBwXVBKuWQ28qWHD9F1qN1w==} 579 | 580 | '@rollup/rollup-android-arm-eabi@4.30.1': 581 | resolution: {integrity: sha512-pSWY+EVt3rJ9fQ3IqlrEUtXh3cGqGtPDH1FQlNZehO2yYxCHEX1SPsz1M//NXwYfbTlcKr9WObLnJX9FsS9K1Q==} 582 | cpu: [arm] 583 | os: [android] 584 | 585 | '@rollup/rollup-android-arm64@4.30.1': 586 | resolution: {integrity: sha512-/NA2qXxE3D/BRjOJM8wQblmArQq1YoBVJjrjoTSBS09jgUisq7bqxNHJ8kjCHeV21W/9WDGwJEWSN0KQ2mtD/w==} 587 | cpu: [arm64] 588 | os: [android] 589 | 590 | '@rollup/rollup-darwin-arm64@4.30.1': 591 | resolution: {integrity: sha512-r7FQIXD7gB0WJ5mokTUgUWPl0eYIH0wnxqeSAhuIwvnnpjdVB8cRRClyKLQr7lgzjctkbp5KmswWszlwYln03Q==} 592 | cpu: [arm64] 593 | os: [darwin] 594 | 595 | '@rollup/rollup-darwin-x64@4.30.1': 596 | resolution: {integrity: sha512-x78BavIwSH6sqfP2xeI1hd1GpHL8J4W2BXcVM/5KYKoAD3nNsfitQhvWSw+TFtQTLZ9OmlF+FEInEHyubut2OA==} 597 | cpu: [x64] 598 | os: [darwin] 599 | 600 | '@rollup/rollup-freebsd-arm64@4.30.1': 601 | resolution: {integrity: sha512-HYTlUAjbO1z8ywxsDFWADfTRfTIIy/oUlfIDmlHYmjUP2QRDTzBuWXc9O4CXM+bo9qfiCclmHk1x4ogBjOUpUQ==} 602 | cpu: [arm64] 603 | os: [freebsd] 604 | 605 | '@rollup/rollup-freebsd-x64@4.30.1': 606 | resolution: {integrity: sha512-1MEdGqogQLccphhX5myCJqeGNYTNcmTyaic9S7CG3JhwuIByJ7J05vGbZxsizQthP1xpVx7kd3o31eOogfEirw==} 607 | cpu: [x64] 608 | os: [freebsd] 609 | 610 | '@rollup/rollup-linux-arm-gnueabihf@4.30.1': 611 | resolution: {integrity: sha512-PaMRNBSqCx7K3Wc9QZkFx5+CX27WFpAMxJNiYGAXfmMIKC7jstlr32UhTgK6T07OtqR+wYlWm9IxzennjnvdJg==} 612 | cpu: [arm] 613 | os: [linux] 614 | 615 | '@rollup/rollup-linux-arm-musleabihf@4.30.1': 616 | resolution: {integrity: sha512-B8Rcyj9AV7ZlEFqvB5BubG5iO6ANDsRKlhIxySXcF1axXYUyqwBok+XZPgIYGBgs7LDXfWfifxhw0Ik57T0Yug==} 617 | cpu: [arm] 618 | os: [linux] 619 | 620 | '@rollup/rollup-linux-arm64-gnu@4.30.1': 621 | resolution: {integrity: sha512-hqVyueGxAj3cBKrAI4aFHLV+h0Lv5VgWZs9CUGqr1z0fZtlADVV1YPOij6AhcK5An33EXaxnDLmJdQikcn5NEw==} 622 | cpu: [arm64] 623 | os: [linux] 624 | 625 | '@rollup/rollup-linux-arm64-musl@4.30.1': 626 | resolution: {integrity: sha512-i4Ab2vnvS1AE1PyOIGp2kXni69gU2DAUVt6FSXeIqUCPIR3ZlheMW3oP2JkukDfu3PsexYRbOiJrY+yVNSk9oA==} 627 | cpu: [arm64] 628 | os: [linux] 629 | 630 | '@rollup/rollup-linux-loongarch64-gnu@4.30.1': 631 | resolution: {integrity: sha512-fARcF5g296snX0oLGkVxPmysetwUk2zmHcca+e9ObOovBR++9ZPOhqFUM61UUZ2EYpXVPN1redgqVoBB34nTpQ==} 632 | cpu: [loong64] 633 | os: [linux] 634 | 635 | '@rollup/rollup-linux-powerpc64le-gnu@4.30.1': 636 | resolution: {integrity: sha512-GLrZraoO3wVT4uFXh67ElpwQY0DIygxdv0BNW9Hkm3X34wu+BkqrDrkcsIapAY+N2ATEbvak0XQ9gxZtCIA5Rw==} 637 | cpu: [ppc64] 638 | os: [linux] 639 | 640 | '@rollup/rollup-linux-riscv64-gnu@4.30.1': 641 | resolution: {integrity: sha512-0WKLaAUUHKBtll0wvOmh6yh3S0wSU9+yas923JIChfxOaaBarmb/lBKPF0w/+jTVozFnOXJeRGZ8NvOxvk/jcw==} 642 | cpu: [riscv64] 643 | os: [linux] 644 | 645 | '@rollup/rollup-linux-s390x-gnu@4.30.1': 646 | resolution: {integrity: sha512-GWFs97Ruxo5Bt+cvVTQkOJ6TIx0xJDD/bMAOXWJg8TCSTEK8RnFeOeiFTxKniTc4vMIaWvCplMAFBt9miGxgkA==} 647 | cpu: [s390x] 648 | os: [linux] 649 | 650 | '@rollup/rollup-linux-x64-gnu@4.30.1': 651 | resolution: {integrity: sha512-UtgGb7QGgXDIO+tqqJ5oZRGHsDLO8SlpE4MhqpY9Llpzi5rJMvrK6ZGhsRCST2abZdBqIBeXW6WPD5fGK5SDwg==} 652 | cpu: [x64] 653 | os: [linux] 654 | 655 | '@rollup/rollup-linux-x64-musl@4.30.1': 656 | resolution: {integrity: sha512-V9U8Ey2UqmQsBT+xTOeMzPzwDzyXmnAoO4edZhL7INkwQcaW1Ckv3WJX3qrrp/VHaDkEWIBWhRwP47r8cdrOow==} 657 | cpu: [x64] 658 | os: [linux] 659 | 660 | '@rollup/rollup-win32-arm64-msvc@4.30.1': 661 | resolution: {integrity: sha512-WabtHWiPaFF47W3PkHnjbmWawnX/aE57K47ZDT1BXTS5GgrBUEpvOzq0FI0V/UYzQJgdb8XlhVNH8/fwV8xDjw==} 662 | cpu: [arm64] 663 | os: [win32] 664 | 665 | '@rollup/rollup-win32-ia32-msvc@4.30.1': 666 | resolution: {integrity: sha512-pxHAU+Zv39hLUTdQQHUVHf4P+0C47y/ZloorHpzs2SXMRqeAWmGghzAhfOlzFHHwjvgokdFAhC4V+6kC1lRRfw==} 667 | cpu: [ia32] 668 | os: [win32] 669 | 670 | '@rollup/rollup-win32-x64-msvc@4.30.1': 671 | resolution: {integrity: sha512-D6qjsXGcvhTjv0kI4fU8tUuBDF/Ueee4SVX79VfNDXZa64TfCW1Slkb6Z7O1p7vflqZjcmOVdZlqf8gvJxc6og==} 672 | cpu: [x64] 673 | os: [win32] 674 | 675 | '@types/estree@1.0.6': 676 | resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} 677 | 678 | '@types/node@18.14.0': 679 | resolution: {integrity: sha512-5EWrvLmglK+imbCJY0+INViFWUHg1AHel1sq4ZVSfdcNqGy9Edv3UB9IIzzg+xPaUcAgZYcfVs2fBcwDeZzU0A==} 680 | 681 | '@types/node@22.10.5': 682 | resolution: {integrity: sha512-F8Q+SeGimwOo86fiovQh8qiXfFEh2/ocYv7tU5pJ3EXMSSxk1Joj5wefpFK2fHTf/N6HKGSxIDBT9f3gCxXPkQ==} 683 | 684 | '@vitest/expect@2.1.8': 685 | resolution: {integrity: sha512-8ytZ/fFHq2g4PJVAtDX57mayemKgDR6X3Oa2Foro+EygiOJHUXhCqBAAKQYYajZpFoIfvBCF1j6R6IYRSIUFuw==} 686 | 687 | '@vitest/mocker@2.1.8': 688 | resolution: {integrity: sha512-7guJ/47I6uqfttp33mgo6ga5Gr1VnL58rcqYKyShoRK9ebu8T5Rs6HN3s1NABiBeVTdWNrwUMcHH54uXZBN4zA==} 689 | peerDependencies: 690 | msw: ^2.4.9 691 | vite: ^5.0.0 692 | peerDependenciesMeta: 693 | msw: 694 | optional: true 695 | vite: 696 | optional: true 697 | 698 | '@vitest/pretty-format@2.1.8': 699 | resolution: {integrity: sha512-9HiSZ9zpqNLKlbIDRWOnAWqgcA7xu+8YxXSekhr0Ykab7PAYFkhkwoqVArPOtJhPmYeE2YHgKZlj3CP36z2AJQ==} 700 | 701 | '@vitest/runner@2.1.8': 702 | resolution: {integrity: sha512-17ub8vQstRnRlIU5k50bG+QOMLHRhYPAna5tw8tYbj+jzjcspnwnwtPtiOlkuKC4+ixDPTuLZiqiWWQ2PSXHVg==} 703 | 704 | '@vitest/snapshot@2.1.8': 705 | resolution: {integrity: sha512-20T7xRFbmnkfcmgVEz+z3AU/3b0cEzZOt/zmnvZEctg64/QZbSDJEVm9fLnnlSi74KibmRsO9/Qabi+t0vCRPg==} 706 | 707 | '@vitest/spy@2.1.8': 708 | resolution: {integrity: sha512-5swjf2q95gXeYPevtW0BLk6H8+bPlMb4Vw/9Em4hFxDcaOxS+e0LOX4yqNxoHzMR2akEB2xfpnWUzkZokmgWDg==} 709 | 710 | '@vitest/utils@2.1.8': 711 | resolution: {integrity: sha512-dwSoui6djdwbfFmIgbIjX2ZhIoG7Ex/+xpxyiEgIGzjliY8xGkcpITKTlp6B4MgtGkF2ilvm97cPM96XZaAgcA==} 712 | 713 | '@vue/reactivity@3.5.13': 714 | resolution: {integrity: sha512-NaCwtw8o48B9I6L1zl2p41OHo/2Z4wqYGGIK1Khu5T7yxrn+ATOixn/Udn2m+6kZKB/J7cuT9DbWWhRxqixACg==} 715 | 716 | '@vue/shared@3.5.13': 717 | resolution: {integrity: sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ==} 718 | 719 | acorn-typescript@1.4.13: 720 | resolution: {integrity: sha512-xsc9Xv0xlVfwp2o7sQ+GCQ1PgbkdcpWdTzrwXxO3xDMTAywVS3oXVOcOHuRjAPkS4P9b+yc/qNF15460v+jp4Q==} 721 | peerDependencies: 722 | acorn: '>=8.9.0' 723 | 724 | acorn@8.14.0: 725 | resolution: {integrity: sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==} 726 | engines: {node: '>=0.4.0'} 727 | hasBin: true 728 | 729 | alien-signals@1.0.0-alpha.1: 730 | resolution: {integrity: sha512-mgqYHniWu3xhHZWXB9zDaMP0ze1mFofqXXH2CXo4mGbNBSSm+HcraGbvY8tAvIsMOviH6F6vgcYD6sBy3qf3vg==} 731 | 732 | ansi-regex@4.1.1: 733 | resolution: {integrity: sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==} 734 | engines: {node: '>=6'} 735 | 736 | ansi-styles@3.2.1: 737 | resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} 738 | engines: {node: '>=4'} 739 | 740 | anymatch@3.1.3: 741 | resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} 742 | engines: {node: '>= 8'} 743 | 744 | aria-query@5.3.2: 745 | resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} 746 | engines: {node: '>= 0.4'} 747 | 748 | assertion-error@2.0.1: 749 | resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} 750 | engines: {node: '>=12'} 751 | 752 | axobject-query@4.1.0: 753 | resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} 754 | engines: {node: '>= 0.4'} 755 | 756 | binary-extensions@2.2.0: 757 | resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} 758 | engines: {node: '>=8'} 759 | 760 | braces@3.0.2: 761 | resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} 762 | engines: {node: '>=8'} 763 | 764 | cac@6.7.14: 765 | resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} 766 | engines: {node: '>=8'} 767 | 768 | camelcase@5.3.1: 769 | resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} 770 | engines: {node: '>=6'} 771 | 772 | chai@5.1.2: 773 | resolution: {integrity: sha512-aGtmf24DW6MLHHG5gCx4zaI3uBq3KRtxeVs0DjFH6Z0rDNbsvTxFASFvdj79pxjxZ8/5u3PIiN3IwEIQkiiuPw==} 774 | engines: {node: '>=12'} 775 | 776 | check-error@2.1.1: 777 | resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} 778 | engines: {node: '>= 16'} 779 | 780 | chokidar-cli@3.0.0: 781 | resolution: {integrity: sha512-xVW+Qeh7z15uZRxHOkP93Ux8A0xbPzwK4GaqD8dQOYc34TlkqUhVSS59fK36DOp5WdJlrRzlYSy02Ht99FjZqQ==} 782 | engines: {node: '>= 8.10.0'} 783 | hasBin: true 784 | 785 | chokidar@3.5.3: 786 | resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} 787 | engines: {node: '>= 8.10.0'} 788 | 789 | cliui@5.0.0: 790 | resolution: {integrity: sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==} 791 | 792 | clsx@2.1.1: 793 | resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} 794 | engines: {node: '>=6'} 795 | 796 | color-convert@1.9.3: 797 | resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} 798 | 799 | color-name@1.1.3: 800 | resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} 801 | 802 | compostate@0.6.0-alpha.1: 803 | resolution: {integrity: sha512-6B/UUeofm+TQLG+U9mPyQGK+JL0NyDe8sZbdwddpUCCdiE4vrAEIe5xnu2oCStygPwMGmD5ljSD2xObBUNxA1w==} 804 | engines: {node: '>=10'} 805 | 806 | csstype@3.1.1: 807 | resolution: {integrity: sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==} 808 | 809 | csstype@3.1.3: 810 | resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} 811 | 812 | debug@4.4.0: 813 | resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} 814 | engines: {node: '>=6.0'} 815 | peerDependencies: 816 | supports-color: '*' 817 | peerDependenciesMeta: 818 | supports-color: 819 | optional: true 820 | 821 | decamelize@1.2.0: 822 | resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} 823 | engines: {node: '>=0.10.0'} 824 | 825 | deep-eql@5.0.2: 826 | resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} 827 | engines: {node: '>=6'} 828 | 829 | emoji-regex@7.0.3: 830 | resolution: {integrity: sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==} 831 | 832 | es-module-lexer@1.6.0: 833 | resolution: {integrity: sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==} 834 | 835 | esbuild@0.16.17: 836 | resolution: {integrity: sha512-G8LEkV0XzDMNwXKgM0Jwu3nY3lSTwSGY6XbxM9cr9+s0T/qSV1q1JVPBGzm3dcjhCic9+emZDmMffkwgPeOeLg==} 837 | engines: {node: '>=12'} 838 | hasBin: true 839 | 840 | esbuild@0.21.5: 841 | resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} 842 | engines: {node: '>=12'} 843 | hasBin: true 844 | 845 | esbuild@0.24.2: 846 | resolution: {integrity: sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==} 847 | engines: {node: '>=18'} 848 | hasBin: true 849 | 850 | esm-env@1.2.2: 851 | resolution: {integrity: sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==} 852 | 853 | esrap@1.4.2: 854 | resolution: {integrity: sha512-FhVlJzvTw7ZLxYZ7RyHwQCFE64dkkpzGNNnphaGCLwjqGk1SQcqzbgdx9FowPCktx6NOSHkzvcZ3vsvdH54YXA==} 855 | 856 | estree-walker@3.0.3: 857 | resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} 858 | 859 | expect-type@1.1.0: 860 | resolution: {integrity: sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA==} 861 | engines: {node: '>=12.0.0'} 862 | 863 | fill-range@7.0.1: 864 | resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} 865 | engines: {node: '>=8'} 866 | 867 | find-up@3.0.0: 868 | resolution: {integrity: sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==} 869 | engines: {node: '>=6'} 870 | 871 | fsevents@2.3.2: 872 | resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} 873 | engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} 874 | os: [darwin] 875 | 876 | fsevents@2.3.3: 877 | resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} 878 | engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} 879 | os: [darwin] 880 | 881 | get-caller-file@2.0.5: 882 | resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} 883 | engines: {node: 6.* || 8.* || >= 10.*} 884 | 885 | glob-parent@5.1.2: 886 | resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} 887 | engines: {node: '>= 6'} 888 | 889 | is-binary-path@2.1.0: 890 | resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} 891 | engines: {node: '>=8'} 892 | 893 | is-extglob@2.1.1: 894 | resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} 895 | engines: {node: '>=0.10.0'} 896 | 897 | is-fullwidth-code-point@2.0.0: 898 | resolution: {integrity: sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==} 899 | engines: {node: '>=4'} 900 | 901 | is-glob@4.0.3: 902 | resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} 903 | engines: {node: '>=0.10.0'} 904 | 905 | is-number@7.0.0: 906 | resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} 907 | engines: {node: '>=0.12.0'} 908 | 909 | is-reference@3.0.3: 910 | resolution: {integrity: sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==} 911 | 912 | kairo@0.6.0-rc.0: 913 | resolution: {integrity: sha512-8yWTOujaeU5a6FtFCQnRhljxo8KaxQVDV6xJFxEpkCKd2azlMBmc5ARazaQUb/KJ2PWzsVdiXRbvFmreP6pbFA==} 914 | 915 | locate-character@3.0.0: 916 | resolution: {integrity: sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==} 917 | 918 | locate-path@3.0.0: 919 | resolution: {integrity: sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==} 920 | engines: {node: '>=6'} 921 | 922 | lodash.debounce@4.0.8: 923 | resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} 924 | 925 | lodash.throttle@4.1.1: 926 | resolution: {integrity: sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==} 927 | 928 | loupe@3.1.2: 929 | resolution: {integrity: sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg==} 930 | 931 | magic-string@0.30.17: 932 | resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} 933 | 934 | mobx@6.13.5: 935 | resolution: {integrity: sha512-/HTWzW2s8J1Gqt+WmUj5Y0mddZk+LInejADc79NJadrWla3rHzmRHki/mnEUH1AvOmbNTZ1BRbKxr8DSgfdjMA==} 936 | 937 | mol_wire_lib@1.0.1259: 938 | resolution: {integrity: sha512-2f6qUDIZJ/MjivSgI3BBbbGE5fo9eXaulMeBaIqeCbv0xrqWDT4lE2qAfrQl/ckdVcW7Ecu7gsvpwaRBZ+PLVQ==} 939 | 940 | mol_wire_lib@1.0.488: 941 | resolution: {integrity: sha512-AqGNHtM5HvxdYKXMt85ESXG+Owj/E6HEUM2a0SKoCpx+R4uHrqM3V8HABt13twXzMDDfjO713TRWQb7Qb4EQRA==} 942 | 943 | ms@2.1.3: 944 | resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} 945 | 946 | nanoid@3.3.8: 947 | resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==} 948 | engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} 949 | hasBin: true 950 | 951 | normalize-path@3.0.0: 952 | resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} 953 | engines: {node: '>=0.10.0'} 954 | 955 | oby@15.1.2: 956 | resolution: {integrity: sha512-6QD9iEoPzV+pMDdcg3RtFWhgDX8pS5hZouVHvgXGDy3Q9RxFfnI3CYv9i62keeuX+qk6iN2z5E9FD3q3OckZ6A==} 957 | 958 | p-limit@2.3.0: 959 | resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} 960 | engines: {node: '>=6'} 961 | 962 | p-locate@3.0.0: 963 | resolution: {integrity: sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==} 964 | engines: {node: '>=6'} 965 | 966 | p-try@2.2.0: 967 | resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} 968 | engines: {node: '>=6'} 969 | 970 | path-exists@3.0.0: 971 | resolution: {integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==} 972 | engines: {node: '>=4'} 973 | 974 | pathe@1.1.2: 975 | resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} 976 | 977 | pathval@2.0.0: 978 | resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==} 979 | engines: {node: '>= 14.16'} 980 | 981 | picocolors@1.1.1: 982 | resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} 983 | 984 | picomatch@2.3.1: 985 | resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} 986 | engines: {node: '>=8.6'} 987 | 988 | postcss@8.4.49: 989 | resolution: {integrity: sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==} 990 | engines: {node: ^10 || ^12 || >=14} 991 | 992 | preact@10.25.4: 993 | resolution: {integrity: sha512-jLdZDb+Q+odkHJ+MpW/9U5cODzqnB+fy2EiHSZES7ldV5LK7yjlVzTp7R8Xy6W6y75kfK8iWYtFVH7lvjwrCMA==} 994 | 995 | prettier@3.4.2: 996 | resolution: {integrity: sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==} 997 | engines: {node: '>=14'} 998 | hasBin: true 999 | 1000 | proxy-compare@3.0.1: 1001 | resolution: {integrity: sha512-V9plBAt3qjMlS1+nC8771KNf6oJ12gExvaxnNzN/9yVRLdTv/lc+oJlnSzrdYDAvBfTStPCoiaCOTmTs0adv7Q==} 1002 | 1003 | random@5.1.1: 1004 | resolution: {integrity: sha512-iidvORUvXY1ItoYxO0eduHCKl22QV0G6460vRHe862dUagJKPhRyjUGwK8ioOCG4NRuFvExHFpqMngsnr2miwA==} 1005 | engines: {node: '>=18'} 1006 | 1007 | react@19.0.0: 1008 | resolution: {integrity: sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==} 1009 | engines: {node: '>=0.10.0'} 1010 | 1011 | readdirp@3.6.0: 1012 | resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} 1013 | engines: {node: '>=8.10.0'} 1014 | 1015 | require-directory@2.1.1: 1016 | resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} 1017 | engines: {node: '>=0.10.0'} 1018 | 1019 | require-main-filename@2.0.0: 1020 | resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} 1021 | 1022 | rollup@4.30.1: 1023 | resolution: {integrity: sha512-mlJ4glW020fPuLi7DkM/lN97mYEZGWeqBnrljzN0gs7GLctqX3lNWxKQ7Gl712UAX+6fog/L3jh4gb7R6aVi3w==} 1024 | engines: {node: '>=18.0.0', npm: '>=8.0.0'} 1025 | hasBin: true 1026 | 1027 | rxjs@7.8.1: 1028 | resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} 1029 | 1030 | s-js@0.4.9: 1031 | resolution: {integrity: sha512-RtpOm+cM6O0sHg6IA70wH+UC3FZcND+rccBZpBAHzlUgNO2Bm5BN+FnM8+OBxzXdwpKWFwX11JGF0MFRkhSoIQ==} 1032 | 1033 | seroval-plugins@1.2.0: 1034 | resolution: {integrity: sha512-hULTbfzSe81jGWLH8TAJjkEvw6JWMqOo9Uq+4V4vg+HNq53hyHldM9ZOfjdzokcFysiTp9aFdV2vJpZFqKeDjQ==} 1035 | engines: {node: '>=10'} 1036 | peerDependencies: 1037 | seroval: ^1.0 1038 | 1039 | seroval@1.2.0: 1040 | resolution: {integrity: sha512-GURoU99ko2UiAgUC3qDCk59Jb3Ss4Po8VIMGkG8j5PFo2Q7y0YSMP8QG9NuL/fJCoTz9V1XZUbpNIMXPOfaGpA==} 1041 | engines: {node: '>=10'} 1042 | 1043 | set-blocking@2.0.0: 1044 | resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} 1045 | 1046 | siginfo@2.0.0: 1047 | resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} 1048 | 1049 | signal-polyfill@0.2.1: 1050 | resolution: {integrity: sha512-HtchWosT4UT1i6yIMmxjg7gqkDT9lXvQtJRGVppi2TDwVjgs8cS+7ATVFqBvgJGThludjxrxJFOvmzzGSAc8OA==} 1051 | 1052 | signia@0.1.5: 1053 | resolution: {integrity: sha512-ViJpywl7H1W6zRfqbu+86xpSCcuq6tpOen7I+gR8axaiyZP8txRNAoeCsL20UkuqzG/Ybtk0u4C2lawkiwPlnw==} 1054 | 1055 | solid-js@1.6.11: 1056 | resolution: {integrity: sha512-JquQQHPArGq+i2PLURxJ99Pcz2/1docpbycSio/cKSA0SeI3z5zRjy0TNcH4NRYvbOLrcini+iovXwnexKabyw==} 1057 | 1058 | solid-js@1.9.4: 1059 | resolution: {integrity: sha512-ipQl8FJ31bFUoBNScDQTG3BjN6+9Rg+Q+f10bUbnO6EOTTf5NGerJeHc7wyu5I4RMHEl/WwZwUmy/PTRgxxZ8g==} 1060 | 1061 | source-map-js@1.2.1: 1062 | resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} 1063 | engines: {node: '>=0.10.0'} 1064 | 1065 | stackback@0.0.2: 1066 | resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} 1067 | 1068 | std-env@3.8.0: 1069 | resolution: {integrity: sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w==} 1070 | 1071 | string-width@3.1.0: 1072 | resolution: {integrity: sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==} 1073 | engines: {node: '>=6'} 1074 | 1075 | strip-ansi@5.2.0: 1076 | resolution: {integrity: sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==} 1077 | engines: {node: '>=6'} 1078 | 1079 | svelte@5.17.3: 1080 | resolution: {integrity: sha512-eLgtpR2JiTgeuNQRCDcLx35Z7Lu9Qe09GPOz+gvtR9nmIZu5xgFd6oFiLGQlxLD0/u7xVyF5AUkjDVyFHe6Bvw==} 1081 | engines: {node: '>=18'} 1082 | 1083 | tinybench@2.9.0: 1084 | resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} 1085 | 1086 | tinyexec@0.3.2: 1087 | resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} 1088 | 1089 | tinypool@1.0.2: 1090 | resolution: {integrity: sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==} 1091 | engines: {node: ^18.0.0 || >=20.0.0} 1092 | 1093 | tinyrainbow@1.2.0: 1094 | resolution: {integrity: sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==} 1095 | engines: {node: '>=14.0.0'} 1096 | 1097 | tinyspy@3.0.2: 1098 | resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==} 1099 | engines: {node: '>=14.0.0'} 1100 | 1101 | to-regex-range@5.0.1: 1102 | resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} 1103 | engines: {node: '>=8.0'} 1104 | 1105 | tslib@2.8.1: 1106 | resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} 1107 | 1108 | typescript@4.9.5: 1109 | resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==} 1110 | engines: {node: '>=4.2.0'} 1111 | hasBin: true 1112 | 1113 | undici-types@6.20.0: 1114 | resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==} 1115 | 1116 | usignal@0.8.14: 1117 | resolution: {integrity: sha512-pPWOcODVi1bT11Mj1j4wfQkbHJA4uhvZrHuiQDs15J+mKy46WgeJhyYRGTGAa4Zox2cKjAmurDtJkoD9lURUnQ==} 1118 | 1119 | usignal@0.9.0: 1120 | resolution: {integrity: sha512-/6/dn+4qKE9MN3k5W76VzMDr5cMPNLswvOR6lHFttTADvf6TB/uhGmSOQpFAxcugTzSwG4vX5NiNEqkiFfzDlg==} 1121 | 1122 | v8-natives@1.2.5: 1123 | resolution: {integrity: sha512-CVNliz6KF2yet3HBIkbFJKZmjlt95C8dsNZDnwoS6X98+QJRpsSz9uxo3TziBqdyJQkWwfD3VG9lRzsQNvF24Q==} 1124 | engines: {node: '>= 0.6.0'} 1125 | 1126 | valtio@2.1.2: 1127 | resolution: {integrity: sha512-fhekN5Rq7dvHULHHBlJeXHrQDl0Jj9GXfNavCm3gkD06crGchaG1nf/J7gSlfZU2wPcRdVS5jBKWHtE2NNz97A==} 1128 | engines: {node: '>=12.20.0'} 1129 | peerDependencies: 1130 | '@types/react': '>=18.0.0' 1131 | react: '>=18.0.0' 1132 | peerDependenciesMeta: 1133 | '@types/react': 1134 | optional: true 1135 | react: 1136 | optional: true 1137 | 1138 | vite-node@2.1.8: 1139 | resolution: {integrity: sha512-uPAwSr57kYjAUux+8E2j0q0Fxpn8M9VoyfGiRI8Kfktz9NcYMCenwY5RnZxnF1WTu3TGiYipirIzacLL3VVGFg==} 1140 | engines: {node: ^18.0.0 || >=20.0.0} 1141 | hasBin: true 1142 | 1143 | vite@5.4.11: 1144 | resolution: {integrity: sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q==} 1145 | engines: {node: ^18.0.0 || >=20.0.0} 1146 | hasBin: true 1147 | peerDependencies: 1148 | '@types/node': ^18.0.0 || >=20.0.0 1149 | less: '*' 1150 | lightningcss: ^1.21.0 1151 | sass: '*' 1152 | sass-embedded: '*' 1153 | stylus: '*' 1154 | sugarss: '*' 1155 | terser: ^5.4.0 1156 | peerDependenciesMeta: 1157 | '@types/node': 1158 | optional: true 1159 | less: 1160 | optional: true 1161 | lightningcss: 1162 | optional: true 1163 | sass: 1164 | optional: true 1165 | sass-embedded: 1166 | optional: true 1167 | stylus: 1168 | optional: true 1169 | sugarss: 1170 | optional: true 1171 | terser: 1172 | optional: true 1173 | 1174 | vitest@2.1.8: 1175 | resolution: {integrity: sha512-1vBKTZskHw/aosXqQUlVWWlGUxSJR8YtiyZDJAFeW2kPAeX6S3Sool0mjspO+kXLuxVWlEDDowBAeqeAQefqLQ==} 1176 | engines: {node: ^18.0.0 || >=20.0.0} 1177 | hasBin: true 1178 | peerDependencies: 1179 | '@edge-runtime/vm': '*' 1180 | '@types/node': ^18.0.0 || >=20.0.0 1181 | '@vitest/browser': 2.1.8 1182 | '@vitest/ui': 2.1.8 1183 | happy-dom: '*' 1184 | jsdom: '*' 1185 | peerDependenciesMeta: 1186 | '@edge-runtime/vm': 1187 | optional: true 1188 | '@types/node': 1189 | optional: true 1190 | '@vitest/browser': 1191 | optional: true 1192 | '@vitest/ui': 1193 | optional: true 1194 | happy-dom: 1195 | optional: true 1196 | jsdom: 1197 | optional: true 1198 | 1199 | which-module@2.0.0: 1200 | resolution: {integrity: sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==} 1201 | 1202 | why-is-node-running@2.3.0: 1203 | resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} 1204 | engines: {node: '>=8'} 1205 | hasBin: true 1206 | 1207 | wrap-ansi@5.1.0: 1208 | resolution: {integrity: sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==} 1209 | engines: {node: '>=6'} 1210 | 1211 | y18n@4.0.3: 1212 | resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==} 1213 | 1214 | yargs-parser@13.1.2: 1215 | resolution: {integrity: sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==} 1216 | 1217 | yargs@13.3.2: 1218 | resolution: {integrity: sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==} 1219 | 1220 | zimmerframe@1.1.2: 1221 | resolution: {integrity: sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==} 1222 | 1223 | zone.js@0.15.0: 1224 | resolution: {integrity: sha512-9oxn0IIjbCZkJ67L+LkhYWRyAy7axphb3VgE2MBDlOqnmHMPWGYMxJxBYFueFq/JGY2GMwS0rU+UCLunEmy5UA==} 1225 | 1226 | snapshots: 1227 | 1228 | '@amadeus-it-group/tansu@2.0.0': {} 1229 | 1230 | '@ampproject/remapping@2.3.0': 1231 | dependencies: 1232 | '@jridgewell/gen-mapping': 0.3.8 1233 | '@jridgewell/trace-mapping': 0.3.25 1234 | 1235 | '@angular/core@19.0.6(rxjs@7.8.1)(zone.js@0.15.0)': 1236 | dependencies: 1237 | rxjs: 7.8.1 1238 | tslib: 2.8.1 1239 | zone.js: 0.15.0 1240 | 1241 | '@esbuild/aix-ppc64@0.21.5': 1242 | optional: true 1243 | 1244 | '@esbuild/aix-ppc64@0.24.2': 1245 | optional: true 1246 | 1247 | '@esbuild/android-arm64@0.16.17': 1248 | optional: true 1249 | 1250 | '@esbuild/android-arm64@0.21.5': 1251 | optional: true 1252 | 1253 | '@esbuild/android-arm64@0.24.2': 1254 | optional: true 1255 | 1256 | '@esbuild/android-arm@0.16.17': 1257 | optional: true 1258 | 1259 | '@esbuild/android-arm@0.21.5': 1260 | optional: true 1261 | 1262 | '@esbuild/android-arm@0.24.2': 1263 | optional: true 1264 | 1265 | '@esbuild/android-x64@0.16.17': 1266 | optional: true 1267 | 1268 | '@esbuild/android-x64@0.21.5': 1269 | optional: true 1270 | 1271 | '@esbuild/android-x64@0.24.2': 1272 | optional: true 1273 | 1274 | '@esbuild/darwin-arm64@0.16.17': 1275 | optional: true 1276 | 1277 | '@esbuild/darwin-arm64@0.21.5': 1278 | optional: true 1279 | 1280 | '@esbuild/darwin-arm64@0.24.2': 1281 | optional: true 1282 | 1283 | '@esbuild/darwin-x64@0.16.17': 1284 | optional: true 1285 | 1286 | '@esbuild/darwin-x64@0.21.5': 1287 | optional: true 1288 | 1289 | '@esbuild/darwin-x64@0.24.2': 1290 | optional: true 1291 | 1292 | '@esbuild/freebsd-arm64@0.16.17': 1293 | optional: true 1294 | 1295 | '@esbuild/freebsd-arm64@0.21.5': 1296 | optional: true 1297 | 1298 | '@esbuild/freebsd-arm64@0.24.2': 1299 | optional: true 1300 | 1301 | '@esbuild/freebsd-x64@0.16.17': 1302 | optional: true 1303 | 1304 | '@esbuild/freebsd-x64@0.21.5': 1305 | optional: true 1306 | 1307 | '@esbuild/freebsd-x64@0.24.2': 1308 | optional: true 1309 | 1310 | '@esbuild/linux-arm64@0.16.17': 1311 | optional: true 1312 | 1313 | '@esbuild/linux-arm64@0.21.5': 1314 | optional: true 1315 | 1316 | '@esbuild/linux-arm64@0.24.2': 1317 | optional: true 1318 | 1319 | '@esbuild/linux-arm@0.16.17': 1320 | optional: true 1321 | 1322 | '@esbuild/linux-arm@0.21.5': 1323 | optional: true 1324 | 1325 | '@esbuild/linux-arm@0.24.2': 1326 | optional: true 1327 | 1328 | '@esbuild/linux-ia32@0.16.17': 1329 | optional: true 1330 | 1331 | '@esbuild/linux-ia32@0.21.5': 1332 | optional: true 1333 | 1334 | '@esbuild/linux-ia32@0.24.2': 1335 | optional: true 1336 | 1337 | '@esbuild/linux-loong64@0.16.17': 1338 | optional: true 1339 | 1340 | '@esbuild/linux-loong64@0.21.5': 1341 | optional: true 1342 | 1343 | '@esbuild/linux-loong64@0.24.2': 1344 | optional: true 1345 | 1346 | '@esbuild/linux-mips64el@0.16.17': 1347 | optional: true 1348 | 1349 | '@esbuild/linux-mips64el@0.21.5': 1350 | optional: true 1351 | 1352 | '@esbuild/linux-mips64el@0.24.2': 1353 | optional: true 1354 | 1355 | '@esbuild/linux-ppc64@0.16.17': 1356 | optional: true 1357 | 1358 | '@esbuild/linux-ppc64@0.21.5': 1359 | optional: true 1360 | 1361 | '@esbuild/linux-ppc64@0.24.2': 1362 | optional: true 1363 | 1364 | '@esbuild/linux-riscv64@0.16.17': 1365 | optional: true 1366 | 1367 | '@esbuild/linux-riscv64@0.21.5': 1368 | optional: true 1369 | 1370 | '@esbuild/linux-riscv64@0.24.2': 1371 | optional: true 1372 | 1373 | '@esbuild/linux-s390x@0.16.17': 1374 | optional: true 1375 | 1376 | '@esbuild/linux-s390x@0.21.5': 1377 | optional: true 1378 | 1379 | '@esbuild/linux-s390x@0.24.2': 1380 | optional: true 1381 | 1382 | '@esbuild/linux-x64@0.16.17': 1383 | optional: true 1384 | 1385 | '@esbuild/linux-x64@0.21.5': 1386 | optional: true 1387 | 1388 | '@esbuild/linux-x64@0.24.2': 1389 | optional: true 1390 | 1391 | '@esbuild/netbsd-arm64@0.24.2': 1392 | optional: true 1393 | 1394 | '@esbuild/netbsd-x64@0.16.17': 1395 | optional: true 1396 | 1397 | '@esbuild/netbsd-x64@0.21.5': 1398 | optional: true 1399 | 1400 | '@esbuild/netbsd-x64@0.24.2': 1401 | optional: true 1402 | 1403 | '@esbuild/openbsd-arm64@0.24.2': 1404 | optional: true 1405 | 1406 | '@esbuild/openbsd-x64@0.16.17': 1407 | optional: true 1408 | 1409 | '@esbuild/openbsd-x64@0.21.5': 1410 | optional: true 1411 | 1412 | '@esbuild/openbsd-x64@0.24.2': 1413 | optional: true 1414 | 1415 | '@esbuild/sunos-x64@0.16.17': 1416 | optional: true 1417 | 1418 | '@esbuild/sunos-x64@0.21.5': 1419 | optional: true 1420 | 1421 | '@esbuild/sunos-x64@0.24.2': 1422 | optional: true 1423 | 1424 | '@esbuild/win32-arm64@0.16.17': 1425 | optional: true 1426 | 1427 | '@esbuild/win32-arm64@0.21.5': 1428 | optional: true 1429 | 1430 | '@esbuild/win32-arm64@0.24.2': 1431 | optional: true 1432 | 1433 | '@esbuild/win32-ia32@0.16.17': 1434 | optional: true 1435 | 1436 | '@esbuild/win32-ia32@0.21.5': 1437 | optional: true 1438 | 1439 | '@esbuild/win32-ia32@0.24.2': 1440 | optional: true 1441 | 1442 | '@esbuild/win32-x64@0.16.17': 1443 | optional: true 1444 | 1445 | '@esbuild/win32-x64@0.21.5': 1446 | optional: true 1447 | 1448 | '@esbuild/win32-x64@0.24.2': 1449 | optional: true 1450 | 1451 | '@jridgewell/gen-mapping@0.3.8': 1452 | dependencies: 1453 | '@jridgewell/set-array': 1.2.1 1454 | '@jridgewell/sourcemap-codec': 1.5.0 1455 | '@jridgewell/trace-mapping': 0.3.25 1456 | 1457 | '@jridgewell/resolve-uri@3.1.2': {} 1458 | 1459 | '@jridgewell/set-array@1.2.1': {} 1460 | 1461 | '@jridgewell/sourcemap-codec@1.5.0': {} 1462 | 1463 | '@jridgewell/trace-mapping@0.3.25': 1464 | dependencies: 1465 | '@jridgewell/resolve-uri': 3.1.2 1466 | '@jridgewell/sourcemap-codec': 1.5.0 1467 | 1468 | '@preact/signals-core@1.8.0': {} 1469 | 1470 | '@preact/signals@2.0.0(preact@10.25.4)': 1471 | dependencies: 1472 | '@preact/signals-core': 1.8.0 1473 | preact: 10.25.4 1474 | 1475 | '@reactively/core@0.0.8': {} 1476 | 1477 | '@rollup/rollup-android-arm-eabi@4.30.1': 1478 | optional: true 1479 | 1480 | '@rollup/rollup-android-arm64@4.30.1': 1481 | optional: true 1482 | 1483 | '@rollup/rollup-darwin-arm64@4.30.1': 1484 | optional: true 1485 | 1486 | '@rollup/rollup-darwin-x64@4.30.1': 1487 | optional: true 1488 | 1489 | '@rollup/rollup-freebsd-arm64@4.30.1': 1490 | optional: true 1491 | 1492 | '@rollup/rollup-freebsd-x64@4.30.1': 1493 | optional: true 1494 | 1495 | '@rollup/rollup-linux-arm-gnueabihf@4.30.1': 1496 | optional: true 1497 | 1498 | '@rollup/rollup-linux-arm-musleabihf@4.30.1': 1499 | optional: true 1500 | 1501 | '@rollup/rollup-linux-arm64-gnu@4.30.1': 1502 | optional: true 1503 | 1504 | '@rollup/rollup-linux-arm64-musl@4.30.1': 1505 | optional: true 1506 | 1507 | '@rollup/rollup-linux-loongarch64-gnu@4.30.1': 1508 | optional: true 1509 | 1510 | '@rollup/rollup-linux-powerpc64le-gnu@4.30.1': 1511 | optional: true 1512 | 1513 | '@rollup/rollup-linux-riscv64-gnu@4.30.1': 1514 | optional: true 1515 | 1516 | '@rollup/rollup-linux-s390x-gnu@4.30.1': 1517 | optional: true 1518 | 1519 | '@rollup/rollup-linux-x64-gnu@4.30.1': 1520 | optional: true 1521 | 1522 | '@rollup/rollup-linux-x64-musl@4.30.1': 1523 | optional: true 1524 | 1525 | '@rollup/rollup-win32-arm64-msvc@4.30.1': 1526 | optional: true 1527 | 1528 | '@rollup/rollup-win32-ia32-msvc@4.30.1': 1529 | optional: true 1530 | 1531 | '@rollup/rollup-win32-x64-msvc@4.30.1': 1532 | optional: true 1533 | 1534 | '@types/estree@1.0.6': {} 1535 | 1536 | '@types/node@18.14.0': {} 1537 | 1538 | '@types/node@22.10.5': 1539 | dependencies: 1540 | undici-types: 6.20.0 1541 | 1542 | '@vitest/expect@2.1.8': 1543 | dependencies: 1544 | '@vitest/spy': 2.1.8 1545 | '@vitest/utils': 2.1.8 1546 | chai: 5.1.2 1547 | tinyrainbow: 1.2.0 1548 | 1549 | '@vitest/mocker@2.1.8(vite@5.4.11(@types/node@22.10.5))': 1550 | dependencies: 1551 | '@vitest/spy': 2.1.8 1552 | estree-walker: 3.0.3 1553 | magic-string: 0.30.17 1554 | optionalDependencies: 1555 | vite: 5.4.11(@types/node@22.10.5) 1556 | 1557 | '@vitest/pretty-format@2.1.8': 1558 | dependencies: 1559 | tinyrainbow: 1.2.0 1560 | 1561 | '@vitest/runner@2.1.8': 1562 | dependencies: 1563 | '@vitest/utils': 2.1.8 1564 | pathe: 1.1.2 1565 | 1566 | '@vitest/snapshot@2.1.8': 1567 | dependencies: 1568 | '@vitest/pretty-format': 2.1.8 1569 | magic-string: 0.30.17 1570 | pathe: 1.1.2 1571 | 1572 | '@vitest/spy@2.1.8': 1573 | dependencies: 1574 | tinyspy: 3.0.2 1575 | 1576 | '@vitest/utils@2.1.8': 1577 | dependencies: 1578 | '@vitest/pretty-format': 2.1.8 1579 | loupe: 3.1.2 1580 | tinyrainbow: 1.2.0 1581 | 1582 | '@vue/reactivity@3.5.13': 1583 | dependencies: 1584 | '@vue/shared': 3.5.13 1585 | 1586 | '@vue/shared@3.5.13': {} 1587 | 1588 | acorn-typescript@1.4.13(acorn@8.14.0): 1589 | dependencies: 1590 | acorn: 8.14.0 1591 | 1592 | acorn@8.14.0: {} 1593 | 1594 | alien-signals@1.0.0-alpha.1: {} 1595 | 1596 | ansi-regex@4.1.1: {} 1597 | 1598 | ansi-styles@3.2.1: 1599 | dependencies: 1600 | color-convert: 1.9.3 1601 | 1602 | anymatch@3.1.3: 1603 | dependencies: 1604 | normalize-path: 3.0.0 1605 | picomatch: 2.3.1 1606 | 1607 | aria-query@5.3.2: {} 1608 | 1609 | assertion-error@2.0.1: {} 1610 | 1611 | axobject-query@4.1.0: {} 1612 | 1613 | binary-extensions@2.2.0: {} 1614 | 1615 | braces@3.0.2: 1616 | dependencies: 1617 | fill-range: 7.0.1 1618 | 1619 | cac@6.7.14: {} 1620 | 1621 | camelcase@5.3.1: {} 1622 | 1623 | chai@5.1.2: 1624 | dependencies: 1625 | assertion-error: 2.0.1 1626 | check-error: 2.1.1 1627 | deep-eql: 5.0.2 1628 | loupe: 3.1.2 1629 | pathval: 2.0.0 1630 | 1631 | check-error@2.1.1: {} 1632 | 1633 | chokidar-cli@3.0.0: 1634 | dependencies: 1635 | chokidar: 3.5.3 1636 | lodash.debounce: 4.0.8 1637 | lodash.throttle: 4.1.1 1638 | yargs: 13.3.2 1639 | 1640 | chokidar@3.5.3: 1641 | dependencies: 1642 | anymatch: 3.1.3 1643 | braces: 3.0.2 1644 | glob-parent: 5.1.2 1645 | is-binary-path: 2.1.0 1646 | is-glob: 4.0.3 1647 | normalize-path: 3.0.0 1648 | readdirp: 3.6.0 1649 | optionalDependencies: 1650 | fsevents: 2.3.2 1651 | 1652 | cliui@5.0.0: 1653 | dependencies: 1654 | string-width: 3.1.0 1655 | strip-ansi: 5.2.0 1656 | wrap-ansi: 5.1.0 1657 | 1658 | clsx@2.1.1: {} 1659 | 1660 | color-convert@1.9.3: 1661 | dependencies: 1662 | color-name: 1.1.3 1663 | 1664 | color-name@1.1.3: {} 1665 | 1666 | compostate@0.6.0-alpha.1: {} 1667 | 1668 | csstype@3.1.1: {} 1669 | 1670 | csstype@3.1.3: {} 1671 | 1672 | debug@4.4.0: 1673 | dependencies: 1674 | ms: 2.1.3 1675 | 1676 | decamelize@1.2.0: {} 1677 | 1678 | deep-eql@5.0.2: {} 1679 | 1680 | emoji-regex@7.0.3: {} 1681 | 1682 | es-module-lexer@1.6.0: {} 1683 | 1684 | esbuild@0.16.17: 1685 | optionalDependencies: 1686 | '@esbuild/android-arm': 0.16.17 1687 | '@esbuild/android-arm64': 0.16.17 1688 | '@esbuild/android-x64': 0.16.17 1689 | '@esbuild/darwin-arm64': 0.16.17 1690 | '@esbuild/darwin-x64': 0.16.17 1691 | '@esbuild/freebsd-arm64': 0.16.17 1692 | '@esbuild/freebsd-x64': 0.16.17 1693 | '@esbuild/linux-arm': 0.16.17 1694 | '@esbuild/linux-arm64': 0.16.17 1695 | '@esbuild/linux-ia32': 0.16.17 1696 | '@esbuild/linux-loong64': 0.16.17 1697 | '@esbuild/linux-mips64el': 0.16.17 1698 | '@esbuild/linux-ppc64': 0.16.17 1699 | '@esbuild/linux-riscv64': 0.16.17 1700 | '@esbuild/linux-s390x': 0.16.17 1701 | '@esbuild/linux-x64': 0.16.17 1702 | '@esbuild/netbsd-x64': 0.16.17 1703 | '@esbuild/openbsd-x64': 0.16.17 1704 | '@esbuild/sunos-x64': 0.16.17 1705 | '@esbuild/win32-arm64': 0.16.17 1706 | '@esbuild/win32-ia32': 0.16.17 1707 | '@esbuild/win32-x64': 0.16.17 1708 | 1709 | esbuild@0.21.5: 1710 | optionalDependencies: 1711 | '@esbuild/aix-ppc64': 0.21.5 1712 | '@esbuild/android-arm': 0.21.5 1713 | '@esbuild/android-arm64': 0.21.5 1714 | '@esbuild/android-x64': 0.21.5 1715 | '@esbuild/darwin-arm64': 0.21.5 1716 | '@esbuild/darwin-x64': 0.21.5 1717 | '@esbuild/freebsd-arm64': 0.21.5 1718 | '@esbuild/freebsd-x64': 0.21.5 1719 | '@esbuild/linux-arm': 0.21.5 1720 | '@esbuild/linux-arm64': 0.21.5 1721 | '@esbuild/linux-ia32': 0.21.5 1722 | '@esbuild/linux-loong64': 0.21.5 1723 | '@esbuild/linux-mips64el': 0.21.5 1724 | '@esbuild/linux-ppc64': 0.21.5 1725 | '@esbuild/linux-riscv64': 0.21.5 1726 | '@esbuild/linux-s390x': 0.21.5 1727 | '@esbuild/linux-x64': 0.21.5 1728 | '@esbuild/netbsd-x64': 0.21.5 1729 | '@esbuild/openbsd-x64': 0.21.5 1730 | '@esbuild/sunos-x64': 0.21.5 1731 | '@esbuild/win32-arm64': 0.21.5 1732 | '@esbuild/win32-ia32': 0.21.5 1733 | '@esbuild/win32-x64': 0.21.5 1734 | 1735 | esbuild@0.24.2: 1736 | optionalDependencies: 1737 | '@esbuild/aix-ppc64': 0.24.2 1738 | '@esbuild/android-arm': 0.24.2 1739 | '@esbuild/android-arm64': 0.24.2 1740 | '@esbuild/android-x64': 0.24.2 1741 | '@esbuild/darwin-arm64': 0.24.2 1742 | '@esbuild/darwin-x64': 0.24.2 1743 | '@esbuild/freebsd-arm64': 0.24.2 1744 | '@esbuild/freebsd-x64': 0.24.2 1745 | '@esbuild/linux-arm': 0.24.2 1746 | '@esbuild/linux-arm64': 0.24.2 1747 | '@esbuild/linux-ia32': 0.24.2 1748 | '@esbuild/linux-loong64': 0.24.2 1749 | '@esbuild/linux-mips64el': 0.24.2 1750 | '@esbuild/linux-ppc64': 0.24.2 1751 | '@esbuild/linux-riscv64': 0.24.2 1752 | '@esbuild/linux-s390x': 0.24.2 1753 | '@esbuild/linux-x64': 0.24.2 1754 | '@esbuild/netbsd-arm64': 0.24.2 1755 | '@esbuild/netbsd-x64': 0.24.2 1756 | '@esbuild/openbsd-arm64': 0.24.2 1757 | '@esbuild/openbsd-x64': 0.24.2 1758 | '@esbuild/sunos-x64': 0.24.2 1759 | '@esbuild/win32-arm64': 0.24.2 1760 | '@esbuild/win32-ia32': 0.24.2 1761 | '@esbuild/win32-x64': 0.24.2 1762 | 1763 | esm-env@1.2.2: {} 1764 | 1765 | esrap@1.4.2: 1766 | dependencies: 1767 | '@jridgewell/sourcemap-codec': 1.5.0 1768 | 1769 | estree-walker@3.0.3: 1770 | dependencies: 1771 | '@types/estree': 1.0.6 1772 | 1773 | expect-type@1.1.0: {} 1774 | 1775 | fill-range@7.0.1: 1776 | dependencies: 1777 | to-regex-range: 5.0.1 1778 | 1779 | find-up@3.0.0: 1780 | dependencies: 1781 | locate-path: 3.0.0 1782 | 1783 | fsevents@2.3.2: 1784 | optional: true 1785 | 1786 | fsevents@2.3.3: 1787 | optional: true 1788 | 1789 | get-caller-file@2.0.5: {} 1790 | 1791 | glob-parent@5.1.2: 1792 | dependencies: 1793 | is-glob: 4.0.3 1794 | 1795 | is-binary-path@2.1.0: 1796 | dependencies: 1797 | binary-extensions: 2.2.0 1798 | 1799 | is-extglob@2.1.1: {} 1800 | 1801 | is-fullwidth-code-point@2.0.0: {} 1802 | 1803 | is-glob@4.0.3: 1804 | dependencies: 1805 | is-extglob: 2.1.1 1806 | 1807 | is-number@7.0.0: {} 1808 | 1809 | is-reference@3.0.3: 1810 | dependencies: 1811 | '@types/estree': 1.0.6 1812 | 1813 | kairo@0.6.0-rc.0: {} 1814 | 1815 | locate-character@3.0.0: {} 1816 | 1817 | locate-path@3.0.0: 1818 | dependencies: 1819 | p-locate: 3.0.0 1820 | path-exists: 3.0.0 1821 | 1822 | lodash.debounce@4.0.8: {} 1823 | 1824 | lodash.throttle@4.1.1: {} 1825 | 1826 | loupe@3.1.2: {} 1827 | 1828 | magic-string@0.30.17: 1829 | dependencies: 1830 | '@jridgewell/sourcemap-codec': 1.5.0 1831 | 1832 | mobx@6.13.5: {} 1833 | 1834 | mol_wire_lib@1.0.1259: {} 1835 | 1836 | mol_wire_lib@1.0.488: {} 1837 | 1838 | ms@2.1.3: {} 1839 | 1840 | nanoid@3.3.8: {} 1841 | 1842 | normalize-path@3.0.0: {} 1843 | 1844 | oby@15.1.2: {} 1845 | 1846 | p-limit@2.3.0: 1847 | dependencies: 1848 | p-try: 2.2.0 1849 | 1850 | p-locate@3.0.0: 1851 | dependencies: 1852 | p-limit: 2.3.0 1853 | 1854 | p-try@2.2.0: {} 1855 | 1856 | path-exists@3.0.0: {} 1857 | 1858 | pathe@1.1.2: {} 1859 | 1860 | pathval@2.0.0: {} 1861 | 1862 | picocolors@1.1.1: {} 1863 | 1864 | picomatch@2.3.1: {} 1865 | 1866 | postcss@8.4.49: 1867 | dependencies: 1868 | nanoid: 3.3.8 1869 | picocolors: 1.1.1 1870 | source-map-js: 1.2.1 1871 | 1872 | preact@10.25.4: {} 1873 | 1874 | prettier@3.4.2: {} 1875 | 1876 | proxy-compare@3.0.1: {} 1877 | 1878 | random@5.1.1: {} 1879 | 1880 | react@19.0.0: {} 1881 | 1882 | readdirp@3.6.0: 1883 | dependencies: 1884 | picomatch: 2.3.1 1885 | 1886 | require-directory@2.1.1: {} 1887 | 1888 | require-main-filename@2.0.0: {} 1889 | 1890 | rollup@4.30.1: 1891 | dependencies: 1892 | '@types/estree': 1.0.6 1893 | optionalDependencies: 1894 | '@rollup/rollup-android-arm-eabi': 4.30.1 1895 | '@rollup/rollup-android-arm64': 4.30.1 1896 | '@rollup/rollup-darwin-arm64': 4.30.1 1897 | '@rollup/rollup-darwin-x64': 4.30.1 1898 | '@rollup/rollup-freebsd-arm64': 4.30.1 1899 | '@rollup/rollup-freebsd-x64': 4.30.1 1900 | '@rollup/rollup-linux-arm-gnueabihf': 4.30.1 1901 | '@rollup/rollup-linux-arm-musleabihf': 4.30.1 1902 | '@rollup/rollup-linux-arm64-gnu': 4.30.1 1903 | '@rollup/rollup-linux-arm64-musl': 4.30.1 1904 | '@rollup/rollup-linux-loongarch64-gnu': 4.30.1 1905 | '@rollup/rollup-linux-powerpc64le-gnu': 4.30.1 1906 | '@rollup/rollup-linux-riscv64-gnu': 4.30.1 1907 | '@rollup/rollup-linux-s390x-gnu': 4.30.1 1908 | '@rollup/rollup-linux-x64-gnu': 4.30.1 1909 | '@rollup/rollup-linux-x64-musl': 4.30.1 1910 | '@rollup/rollup-win32-arm64-msvc': 4.30.1 1911 | '@rollup/rollup-win32-ia32-msvc': 4.30.1 1912 | '@rollup/rollup-win32-x64-msvc': 4.30.1 1913 | fsevents: 2.3.3 1914 | 1915 | rxjs@7.8.1: 1916 | dependencies: 1917 | tslib: 2.8.1 1918 | 1919 | s-js@0.4.9: {} 1920 | 1921 | seroval-plugins@1.2.0(seroval@1.2.0): 1922 | dependencies: 1923 | seroval: 1.2.0 1924 | 1925 | seroval@1.2.0: {} 1926 | 1927 | set-blocking@2.0.0: {} 1928 | 1929 | siginfo@2.0.0: {} 1930 | 1931 | signal-polyfill@0.2.1: {} 1932 | 1933 | signia@0.1.5: {} 1934 | 1935 | solid-js@1.6.11: 1936 | dependencies: 1937 | csstype: 3.1.1 1938 | 1939 | solid-js@1.9.4: 1940 | dependencies: 1941 | csstype: 3.1.3 1942 | seroval: 1.2.0 1943 | seroval-plugins: 1.2.0(seroval@1.2.0) 1944 | 1945 | source-map-js@1.2.1: {} 1946 | 1947 | stackback@0.0.2: {} 1948 | 1949 | std-env@3.8.0: {} 1950 | 1951 | string-width@3.1.0: 1952 | dependencies: 1953 | emoji-regex: 7.0.3 1954 | is-fullwidth-code-point: 2.0.0 1955 | strip-ansi: 5.2.0 1956 | 1957 | strip-ansi@5.2.0: 1958 | dependencies: 1959 | ansi-regex: 4.1.1 1960 | 1961 | svelte@5.17.3: 1962 | dependencies: 1963 | '@ampproject/remapping': 2.3.0 1964 | '@jridgewell/sourcemap-codec': 1.5.0 1965 | '@types/estree': 1.0.6 1966 | acorn: 8.14.0 1967 | acorn-typescript: 1.4.13(acorn@8.14.0) 1968 | aria-query: 5.3.2 1969 | axobject-query: 4.1.0 1970 | clsx: 2.1.1 1971 | esm-env: 1.2.2 1972 | esrap: 1.4.2 1973 | is-reference: 3.0.3 1974 | locate-character: 3.0.0 1975 | magic-string: 0.30.17 1976 | zimmerframe: 1.1.2 1977 | 1978 | tinybench@2.9.0: {} 1979 | 1980 | tinyexec@0.3.2: {} 1981 | 1982 | tinypool@1.0.2: {} 1983 | 1984 | tinyrainbow@1.2.0: {} 1985 | 1986 | tinyspy@3.0.2: {} 1987 | 1988 | to-regex-range@5.0.1: 1989 | dependencies: 1990 | is-number: 7.0.0 1991 | 1992 | tslib@2.8.1: {} 1993 | 1994 | typescript@4.9.5: {} 1995 | 1996 | undici-types@6.20.0: {} 1997 | 1998 | usignal@0.8.14: {} 1999 | 2000 | usignal@0.9.0: {} 2001 | 2002 | v8-natives@1.2.5: {} 2003 | 2004 | valtio@2.1.2(react@19.0.0): 2005 | dependencies: 2006 | proxy-compare: 3.0.1 2007 | optionalDependencies: 2008 | react: 19.0.0 2009 | 2010 | vite-node@2.1.8(@types/node@22.10.5): 2011 | dependencies: 2012 | cac: 6.7.14 2013 | debug: 4.4.0 2014 | es-module-lexer: 1.6.0 2015 | pathe: 1.1.2 2016 | vite: 5.4.11(@types/node@22.10.5) 2017 | transitivePeerDependencies: 2018 | - '@types/node' 2019 | - less 2020 | - lightningcss 2021 | - sass 2022 | - sass-embedded 2023 | - stylus 2024 | - sugarss 2025 | - supports-color 2026 | - terser 2027 | 2028 | vite@5.4.11(@types/node@22.10.5): 2029 | dependencies: 2030 | esbuild: 0.21.5 2031 | postcss: 8.4.49 2032 | rollup: 4.30.1 2033 | optionalDependencies: 2034 | '@types/node': 22.10.5 2035 | fsevents: 2.3.3 2036 | 2037 | vitest@2.1.8(@types/node@22.10.5): 2038 | dependencies: 2039 | '@vitest/expect': 2.1.8 2040 | '@vitest/mocker': 2.1.8(vite@5.4.11(@types/node@22.10.5)) 2041 | '@vitest/pretty-format': 2.1.8 2042 | '@vitest/runner': 2.1.8 2043 | '@vitest/snapshot': 2.1.8 2044 | '@vitest/spy': 2.1.8 2045 | '@vitest/utils': 2.1.8 2046 | chai: 5.1.2 2047 | debug: 4.4.0 2048 | expect-type: 1.1.0 2049 | magic-string: 0.30.17 2050 | pathe: 1.1.2 2051 | std-env: 3.8.0 2052 | tinybench: 2.9.0 2053 | tinyexec: 0.3.2 2054 | tinypool: 1.0.2 2055 | tinyrainbow: 1.2.0 2056 | vite: 5.4.11(@types/node@22.10.5) 2057 | vite-node: 2.1.8(@types/node@22.10.5) 2058 | why-is-node-running: 2.3.0 2059 | optionalDependencies: 2060 | '@types/node': 22.10.5 2061 | transitivePeerDependencies: 2062 | - less 2063 | - lightningcss 2064 | - msw 2065 | - sass 2066 | - sass-embedded 2067 | - stylus 2068 | - sugarss 2069 | - supports-color 2070 | - terser 2071 | 2072 | which-module@2.0.0: {} 2073 | 2074 | why-is-node-running@2.3.0: 2075 | dependencies: 2076 | siginfo: 2.0.0 2077 | stackback: 0.0.2 2078 | 2079 | wrap-ansi@5.1.0: 2080 | dependencies: 2081 | ansi-styles: 3.2.1 2082 | string-width: 3.1.0 2083 | strip-ansi: 5.2.0 2084 | 2085 | y18n@4.0.3: {} 2086 | 2087 | yargs-parser@13.1.2: 2088 | dependencies: 2089 | camelcase: 5.3.1 2090 | decamelize: 1.2.0 2091 | 2092 | yargs@13.3.2: 2093 | dependencies: 2094 | cliui: 5.0.0 2095 | find-up: 3.0.0 2096 | get-caller-file: 2.0.5 2097 | require-directory: 2.1.1 2098 | require-main-filename: 2.0.0 2099 | set-blocking: 2.0.0 2100 | string-width: 3.1.0 2101 | which-module: 2.0.0 2102 | y18n: 4.0.3 2103 | yargs-parser: 13.1.2 2104 | 2105 | zimmerframe@1.1.2: {} 2106 | 2107 | zone.js@0.15.0: {} 2108 | --------------------------------------------------------------------------------