flatsignals 0.2.3 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -76,24 +76,71 @@ const log = effect(() => console.log(double.val));
76
76
 
77
77
  ## With React
78
78
 
79
+ ### Bypass React's render cycle
80
+
79
81
  ```tsx
80
- import { useFlatSignal, useFlatReader } from "flatsignals/react";
82
+ import { useRef } from "react";
83
+ import {
84
+ useFlatRoot,
85
+ useFlatSignal,
86
+ useFlatEffect,
87
+ useFlatComputed,
88
+ } from "flatsignals/react";
89
+ import { useFrame } from "react-three-fiber";
90
+
91
+ // example zero rerenders
92
+ function FrameExample() {
93
+ const root = useFlatRoot(false);
94
+
95
+ const myRef = useRef(null);
96
+ const myCounter = useFlatSignal(1, root);
97
+ const myCounterDouble = useFlatComputed(() => myCounter.get() * 2, root);
98
+
99
+ useFlatEffect(() => {
100
+ const val = myCounterDouble.get();
101
+ if (myRef.current) {
102
+ myRef.current.style.setProperty("--clicks", val);
103
+ }
104
+ }, root);
105
+
106
+ useFrame((state) => {
107
+ // updates dirty effects every frame
108
+ root.flush();
109
+ });
110
+
111
+ return (
112
+ <div>
113
+ <div ref={myRef}></div>
114
+ <button onClick={() => myCounter.set((prev) => prev + 1)}></button>
115
+ </div>
116
+ );
117
+ }
118
+ ```
119
+
120
+ ### Sync with React's render cycle
121
+
122
+ ```tsx
123
+ import { useSyncFlatSignal, useSyncFlatReader } from "flatsignals/react";
81
124
  import { counter, double } from "./signals";
82
125
 
83
126
  function MyCounter() {
84
- const [val, setVal] = useFlatSignal(counter);
127
+ const [val, setVal] = useSyncFlatSignal(counter);
85
128
  return <button onClick={() => setVal(val + 1)}>Count: {val}</button>;
86
129
  }
87
130
 
88
131
  function ReadDouble() {
89
- const val = useFlatReader(double);
132
+ const val = useSyncFlatReader(double);
90
133
  return <div>{val}</div>;
91
134
  }
92
135
  ```
93
136
 
94
137
  ## Use Case
95
138
 
96
- > **Best suited for:** Scenarios with a small number of reactive signals driving many dependent computations.
139
+ **Best suited for:**
140
+
141
+ - High frequency updates that bypasses React's render cycle (e.g., animations, dragging).
142
+ - Granular state tracking to prevent unnecessary parent-to-child re-render cascades.
143
+ - Dynamic dependency graphs that require evaluation every frame (e.g., canvas rendering, node-based editors).
97
144
 
98
145
  **Limitations:**
99
146
 
@@ -1,21 +1,25 @@
1
1
  //#region src/index.d.ts
2
+ /** biome-ignore-all lint/style/noNonNullAssertion: guaranteed */
2
3
  declare class FlatRoot {
3
4
  #private;
5
+ autoFlush: boolean;
6
+ constructor(autoFlush?: boolean);
4
7
  dispose(): void;
8
+ flush(): void;
5
9
  }
6
- declare class FlatSignal<T = any> {
10
+ declare class FlatSignal<T = undefined> {
7
11
  #private;
8
12
  equals: typeof defaultEquality;
9
13
  constructor(val?: T);
10
- get val(): T;
14
+ get(): T;
11
15
  get peek(): T;
12
16
  get root(): FlatRoot;
13
- set val(val: T);
17
+ set(val: T): void;
14
18
  }
15
19
  declare class FlatCompute<T = unknown> {
16
20
  #private;
17
21
  constructor(compute?: () => T, val?: T, effect?: boolean);
18
- get val(): T;
22
+ get(): T;
19
23
  get peek(): T;
20
24
  get root(): FlatRoot;
21
25
  dispose(): void;
@@ -28,5 +32,5 @@ declare function signal<T = undefined>(): FlatSignal<T | undefined>;
28
32
  declare function computed<T>(val: () => T): FlatCompute<T>;
29
33
  declare function effect<T = unknown>(fn: () => T): () => void;
30
34
  //#endregion
31
- export { computed as a, signal as c, batch as i, FlatRoot as n, effect as o, FlatSignal as r, scoped as s, FlatCompute as t };
32
- //# sourceMappingURL=index-BUz21f61.d.ts.map
35
+ export { FlatCompute, FlatRoot, FlatSignal, batch, computed, effect, scoped, signal };
36
+ //# sourceMappingURL=index.d.mts.map
@@ -1,10 +1,15 @@
1
1
  //#region src/index.ts
2
+ /** biome-ignore-all lint/style/noNonNullAssertion: guaranteed */
2
3
  let ROOT = null, COMPUTED = null, BATCHING = false, ROOT_QUEUE = null;
3
4
  var FlatRoot = class {
5
+ autoFlush;
4
6
  _c = [];
5
7
  _x = [];
6
8
  _i = 0;
7
9
  #batch = 0;
10
+ constructor(autoFlush = true) {
11
+ this.autoFlush = autoFlush;
12
+ }
8
13
  dispose() {
9
14
  this._c = [];
10
15
  this._x = [];
@@ -26,13 +31,13 @@ var FlatRoot = class {
26
31
  _queue(mask) {
27
32
  if (!this.#batch) ROOT_QUEUE ??= this;
28
33
  this.#batch |= mask;
29
- if (!BATCHING) this._flush(true);
34
+ if (this.autoFlush && !BATCHING) this.flush();
30
35
  }
31
- _flush(force = false) {
32
- if (!this.#batch || !force) return;
36
+ flush() {
37
+ if (!this.#batch) return;
33
38
  for (const item of this._c) if (!item._dirty && (item._sources & this.#batch) !== 0) {
34
39
  item._dirty = true;
35
- if (item._effect) item.val;
40
+ if (item._effect) item.get();
36
41
  }
37
42
  this.#batch = 0;
38
43
  }
@@ -48,7 +53,7 @@ var FlatSignal = class {
48
53
  this.#val = val;
49
54
  this.#id |= 1 << this.#root._as();
50
55
  }
51
- get val() {
56
+ get() {
52
57
  if (COMPUTED) COMPUTED._sources |= this.#id;
53
58
  return this.#val;
54
59
  }
@@ -58,7 +63,7 @@ var FlatSignal = class {
58
63
  get root() {
59
64
  return this.#root;
60
65
  }
61
- set val(val) {
66
+ set(val) {
62
67
  if (this.equals(val, this.#val)) return;
63
68
  this.#val = val;
64
69
  this.#root._queue(this.#id);
@@ -73,19 +78,18 @@ var FlatCompute = class {
73
78
  _sources = 0;
74
79
  _dirty = true;
75
80
  _d = false;
76
- constructor(compute, val, effect$1) {
81
+ constructor(compute, val, effect) {
77
82
  if (!ROOT) ROOT = new FlatRoot();
78
83
  this.#root = ROOT;
79
84
  this.#fn = compute;
80
85
  this.#val = val;
81
86
  this.#id = this.#root._ac(this);
82
- if (effect$1) {
83
- this._effect = effect$1;
84
- this.val;
87
+ if (effect) {
88
+ this._effect = effect;
89
+ this.get();
85
90
  }
86
91
  }
87
- get val() {
88
- this.#root._flush();
92
+ get() {
89
93
  const prevCurrent = COMPUTED;
90
94
  if (this._dirty) {
91
95
  COMPUTED = this;
@@ -118,7 +122,7 @@ function batch(fn) {
118
122
  if (BATCHING) return fn();
119
123
  BATCHING = true;
120
124
  fn();
121
- ROOT_QUEUE?._flush(true);
125
+ if (ROOT_QUEUE?.autoFlush) ROOT_QUEUE?.flush();
122
126
  ROOT_QUEUE = null;
123
127
  BATCHING = false;
124
128
  }
@@ -139,7 +143,7 @@ function effect(fn) {
139
143
  const sig = new FlatCompute(fn, void 0, true);
140
144
  return sig.dispose.bind(sig);
141
145
  }
142
-
143
146
  //#endregion
144
- export { computed as a, signal as c, batch as i, FlatRoot as n, effect as o, FlatSignal as r, scoped as s, FlatCompute as t };
145
- //# sourceMappingURL=src-CJj34M6F.js.map
147
+ export { FlatCompute, FlatRoot, FlatSignal, batch, computed, effect, scoped, signal };
148
+
149
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":["#batch","#root","#val","#id","#fn"],"sources":["../src/index.ts"],"sourcesContent":["/** biome-ignore-all lint/style/noNonNullAssertion: guaranteed */\n\nlet ROOT: FlatRoot | null = null,\n\tCOMPUTED: FlatCompute | null = null,\n\tBATCHING = false,\n\tROOT_QUEUE: FlatRoot | null = null;\n\nexport class FlatRoot {\n\t/* @internal computeds */\n\t_c: Array<FlatCompute> = [];\n\t/* @internal disposed computeds */\n\t_x: Array<number> = [];\n\t/* @internal id generator */\n\t_i = 0;\n\t/* @internal batch mask */\n\t#batch: number = 0;\n\n\tconstructor(public autoFlush = true) {}\n\n\tdispose() {\n\t\tthis._c = [];\n\t\tthis._x = [];\n\t\tthis._i = 0;\n\t}\n\n\t/* @internal Add source */\n\t_as() {\n\t\treturn this._i++ % 32;\n\t}\n\n\t/* @internal Add computed */\n\t_ac(c: FlatCompute) {\n\t\tif (this._x.length) {\n\t\t\tconst u = this._x.pop()!;\n\t\t\tthis._c[u] = c;\n\t\t\treturn u;\n\t\t} else {\n\t\t\treturn this._c.push(c) - 1;\n\t\t}\n\t}\n\n\t/* @internal Destroy computed */\n\t_dc(idx: number) {\n\t\tthis._x.push(idx);\n\t}\n\n\t/* @internal */\n\t_queue(mask: number) {\n\t\tif (!this.#batch) {\n\t\t\tROOT_QUEUE ??= this;\n\t\t}\n\t\tthis.#batch |= mask;\n\t\tif (this.autoFlush && !BATCHING) this.flush();\n\t}\n\n\tflush() {\n\t\tif (!this.#batch) return;\n\t\tfor (const item of this._c) {\n\t\t\tif (!item._dirty && (item._sources & this.#batch) !== 0) {\n\t\t\t\titem._dirty = true;\n\t\t\t\tif (item._effect) item.get();\n\t\t\t}\n\t\t}\n\t\tthis.#batch = 0;\n\t}\n}\n\nexport class FlatSignal<T = undefined> {\n\t#root: FlatRoot;\n\t#val: T;\n\t#id = 0;\n\tequals = defaultEquality;\n\n\tconstructor(val?: T) {\n\t\tif (!ROOT) ROOT = new FlatRoot();\n\t\tthis.#root = ROOT;\n\t\tthis.#val = val as T;\n\t\tthis.#id |= 1 << this.#root._as();\n\t}\n\n\tget(): T {\n\t\tif (COMPUTED) {\n\t\t\tCOMPUTED._sources |= this.#id;\n\t\t}\n\t\treturn this.#val as T;\n\t}\n\n\tget peek() {\n\t\treturn this.#val;\n\t}\n\n\tget root() {\n\t\treturn this.#root;\n\t}\n\n\tset(val: T) {\n\t\tif (this.equals(val, this.#val)) return;\n\t\tthis.#val = val as T;\n\t\tthis.#root._queue(this.#id);\n\t}\n}\n\nexport class FlatCompute<T = unknown> {\n\t#root: FlatRoot;\n\t#id: number;\n\t#val: T;\n\t#fn: (() => T) | undefined;\n\t/* @internal */\n\t_effect: boolean = false;\n\t/* @internal */\n\t_sources: number = 0;\n\t/* @internal */\n\t_dirty = true;\n\t/* @internal destroyed */\n\t_d = false;\n\n\tconstructor(compute?: () => T, val?: T, effect?: boolean) {\n\t\tif (!ROOT) ROOT = new FlatRoot();\n\t\tthis.#root = ROOT;\n\t\tthis.#fn = compute;\n\t\tthis.#val = val!;\n\t\tthis.#id = this.#root._ac(this as FlatCompute<unknown>);\n\t\tif (effect) {\n\t\t\tthis._effect = effect;\n\t\t\tthis.get();\n\t\t}\n\t}\n\n\tget(): T {\n\t\tconst prevCurrent = COMPUTED;\n\t\tif (this._dirty) {\n\t\t\tCOMPUTED = this as FlatCompute<unknown>;\n\t\t\tthis._sources = 0;\n\t\t\tthis.#val = this.#fn!();\n\t\t\tthis._dirty = false;\n\t\t\tCOMPUTED = prevCurrent;\n\t\t}\n\t\tif (prevCurrent) {\n\t\t\tprevCurrent._sources |= this._sources;\n\t\t}\n\t\treturn this.#val!;\n\t}\n\n\tget peek() {\n\t\treturn this.#val;\n\t}\n\n\tget root() {\n\t\treturn this.#root;\n\t}\n\n\tdispose() {\n\t\tif (this._d) return;\n\t\tthis._sources = 0;\n\t\tthis._dirty = false;\n\t\tthis._d = true;\n\t\tthis.#root._dc(this.#id);\n\t}\n}\n\nfunction defaultEquality(a: unknown, b: unknown) {\n\treturn a === b;\n}\n\nexport function batch(fn: () => void) {\n\tif (BATCHING) return fn();\n\tBATCHING = true;\n\tfn();\n\tif (ROOT_QUEUE?.autoFlush) ROOT_QUEUE?.flush();\n\tROOT_QUEUE = null;\n\tBATCHING = false;\n}\n\nexport function scoped<T>(fn: () => T, scope?: FlatRoot): T {\n\tconst prevRoot = ROOT;\n\tROOT = scope ?? new FlatRoot();\n\tconst result = fn();\n\tROOT = prevRoot;\n\treturn result;\n}\n\nexport function signal<T>(value: T): FlatSignal<T>;\nexport function signal<T = undefined>(): FlatSignal<T | undefined>;\nexport function signal<T>(value?: T): FlatSignal<T> {\n\treturn new FlatSignal(value);\n}\n\nexport function computed<T>(val: () => T): FlatCompute<T> {\n\treturn new FlatCompute(val);\n}\n\nexport function effect<T = unknown>(fn: () => T) {\n\tconst sig = new FlatCompute(fn, undefined, true);\n\treturn sig.dispose.bind(sig);\n}\n"],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import { FlatCompute, FlatRoot, FlatSignal } from "./index.mjs";
2
+
3
+ //#region src/react/index.d.ts
4
+ declare function useSyncFlatReader<T>(signal: FlatSignal<T> | FlatCompute<T>, isEqual?: (a: T, b: T) => boolean): T;
5
+ declare function useSyncFlatSelector<T, R>(signal: FlatSignal<T> | FlatCompute<T>, selector: (value: T) => R, isEqual?: (a: R, b: R) => boolean): R;
6
+ declare function useSyncFlatWriter<T>(signal: FlatSignal<T>): (val: T | ((oldVal: T) => T)) => void;
7
+ declare function useSyncFlatSignal<T>(signal: FlatSignal<T>): readonly [T, (val: T | ((oldVal: T) => T)) => void];
8
+ declare function useFlatScope<T>(callback: () => T, scope?: FlatRoot): T;
9
+ declare function useFlatRoot(autoFlush?: boolean): FlatRoot;
10
+ declare function useFlatEffect(fn: () => (() => void) | undefined, root: FlatRoot): void;
11
+ declare function useFlatComputed<T>(fn: () => T, root: FlatRoot): FlatCompute<T>;
12
+ declare function useFlatSignal<T>(value: T, root: FlatRoot): FlatSignal<T>;
13
+ //#endregion
14
+ export { useFlatComputed, useFlatEffect, useFlatRoot, useFlatScope, useFlatSignal, useSyncFlatReader, useSyncFlatSelector, useSyncFlatSignal, useSyncFlatWriter };
15
+ //# sourceMappingURL=react.d.mts.map
package/dist/react.mjs ADDED
@@ -0,0 +1,74 @@
1
+ import { FlatRoot, computed, effect, scoped, signal } from "./index.mjs";
2
+ import { useCallback, useEffect, useEffectEvent, useMemo, useRef, useState, useSyncExternalStore } from "react";
3
+ //#region src/react/index.ts
4
+ function useSyncFlatReader(signal, isEqual = Object.is) {
5
+ const lastValueRef = useRef(signal.peek);
6
+ return useSyncExternalStore(useCallback((onStoreChange) => scoped(() => effect(() => {
7
+ const newValue = signal.get();
8
+ if (!isEqual(lastValueRef.current, newValue)) {
9
+ lastValueRef.current = newValue;
10
+ onStoreChange();
11
+ }
12
+ }), signal.root), [signal, isEqual]), () => signal.get(), () => signal.peek);
13
+ }
14
+ function useSyncFlatSelector(signal, selector, isEqual = Object.is) {
15
+ const lastSelectedRef = useRef(selector(signal.peek));
16
+ return useSyncExternalStore(useCallback((onStoreChange) => scoped(() => effect(() => {
17
+ const newSelected = selector(signal.get());
18
+ if (!isEqual(lastSelectedRef.current, newSelected)) {
19
+ lastSelectedRef.current = newSelected;
20
+ onStoreChange();
21
+ }
22
+ }), signal.root), [
23
+ signal,
24
+ selector,
25
+ isEqual
26
+ ]), () => selector(signal.get()), () => selector(signal.peek));
27
+ }
28
+ function useSyncFlatWriter(signal) {
29
+ return useCallback((val) => {
30
+ if (typeof val === "function") signal.set(val(signal.peek));
31
+ else signal.set(val);
32
+ }, [signal]);
33
+ }
34
+ function useSyncFlatSignal(signal) {
35
+ return [useSyncFlatReader(signal), useSyncFlatWriter(signal)];
36
+ }
37
+ function useFlatScope(callback, scope) {
38
+ return useMemo(() => scoped(callback, scope), [callback, scope]);
39
+ }
40
+ function useFlatRoot(autoFlush) {
41
+ const [root] = useState(() => new FlatRoot(autoFlush));
42
+ return root;
43
+ }
44
+ function useFlatEffect(fn, root) {
45
+ const onEffect = useEffectEvent(fn);
46
+ useEffect(() => {
47
+ let cleanup;
48
+ const stop = scoped(() => effect(() => {
49
+ if (cleanup) cleanup();
50
+ cleanup = onEffect();
51
+ }), root);
52
+ return () => {
53
+ if (cleanup) cleanup();
54
+ stop();
55
+ };
56
+ }, [root]);
57
+ }
58
+ function useFlatComputed(fn, root) {
59
+ const fnRef = useRef(fn);
60
+ fnRef.current = fn;
61
+ const [s] = useState(() => scoped(() => computed(() => fnRef.current()), root));
62
+ useEffect(() => {
63
+ return () => s.dispose();
64
+ }, [s]);
65
+ return s;
66
+ }
67
+ function useFlatSignal(value, root) {
68
+ const [s] = useState(() => scoped(() => signal(value), root));
69
+ return s;
70
+ }
71
+ //#endregion
72
+ export { useFlatComputed, useFlatEffect, useFlatRoot, useFlatScope, useFlatSignal, useSyncFlatReader, useSyncFlatSelector, useSyncFlatSignal, useSyncFlatWriter };
73
+
74
+ //# sourceMappingURL=react.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"react.mjs","names":[],"sources":["../src/react/index.ts"],"sourcesContent":["import {\n\tuseCallback,\n\tuseEffect,\n\tuseEffectEvent,\n\tuseMemo,\n\tuseRef,\n\tuseState,\n\tuseSyncExternalStore,\n} from \"react\";\nimport {\n\tcomputed,\n\teffect,\n\ttype FlatCompute,\n\tFlatRoot,\n\ttype FlatSignal,\n\tscoped,\n\tsignal,\n} from \"../index.js\";\n\nexport function useSyncFlatReader<T>(\n\tsignal: FlatSignal<T> | FlatCompute<T>,\n\tisEqual: (a: T, b: T) => boolean = Object.is,\n): T {\n\tconst lastValueRef = useRef<T>(signal.peek);\n\n\treturn useSyncExternalStore(\n\t\tuseCallback(\n\t\t\t(onStoreChange) =>\n\t\t\t\tscoped(\n\t\t\t\t\t() =>\n\t\t\t\t\t\teffect(() => {\n\t\t\t\t\t\t\tconst newValue = signal.get();\n\t\t\t\t\t\t\tif (!isEqual(lastValueRef.current, newValue)) {\n\t\t\t\t\t\t\t\tlastValueRef.current = newValue;\n\t\t\t\t\t\t\t\tonStoreChange();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}),\n\t\t\t\t\tsignal.root,\n\t\t\t\t),\n\t\t\t[signal, isEqual],\n\t\t),\n\t\t() => signal.get(),\n\t\t() => signal.peek,\n\t);\n}\n\nexport function useSyncFlatSelector<T, R>(\n\tsignal: FlatSignal<T> | FlatCompute<T>,\n\tselector: (value: T) => R,\n\tisEqual: (a: R, b: R) => boolean = Object.is,\n): R {\n\tconst lastSelectedRef = useRef<R>(selector(signal.peek));\n\n\treturn useSyncExternalStore(\n\t\tuseCallback(\n\t\t\t(onStoreChange) =>\n\t\t\t\tscoped(\n\t\t\t\t\t() =>\n\t\t\t\t\t\teffect(() => {\n\t\t\t\t\t\t\tconst newSelected = selector(signal.get());\n\t\t\t\t\t\t\tif (!isEqual(lastSelectedRef.current, newSelected)) {\n\t\t\t\t\t\t\t\tlastSelectedRef.current = newSelected;\n\t\t\t\t\t\t\t\tonStoreChange();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}),\n\t\t\t\t\tsignal.root,\n\t\t\t\t),\n\t\t\t[signal, selector, isEqual],\n\t\t),\n\t\t() => selector(signal.get()),\n\t\t() => selector(signal.peek),\n\t);\n}\n\nexport function useSyncFlatWriter<T>(\n\tsignal: FlatSignal<T>,\n): (val: T | ((oldVal: T) => T)) => void {\n\treturn useCallback(\n\t\t(val) => {\n\t\t\tif (typeof val === \"function\") {\n\t\t\t\tsignal.set((val as (oldVal: T) => T)(signal.peek));\n\t\t\t} else {\n\t\t\t\tsignal.set(val);\n\t\t\t}\n\t\t},\n\t\t[signal],\n\t);\n}\n\nexport function useSyncFlatSignal<T>(signal: FlatSignal<T>) {\n\tconst reader = useSyncFlatReader(signal);\n\tconst writer = useSyncFlatWriter(signal);\n\treturn [reader, writer] as const;\n}\n\nexport function useFlatScope<T>(callback: () => T, scope?: FlatRoot): T {\n\treturn useMemo(() => scoped(callback, scope), [callback, scope]);\n}\n\nexport function useFlatRoot(autoFlush?: boolean) {\n\tconst [root] = useState(() => new FlatRoot(autoFlush));\n\treturn root;\n}\n\nexport function useFlatEffect(\n\tfn: () => (() => void) | undefined,\n\troot: FlatRoot,\n): void {\n\tconst onEffect = useEffectEvent(fn);\n\n\tuseEffect(() => {\n\t\tlet cleanup: (() => void) | undefined;\n\t\tconst stop = scoped(\n\t\t\t() =>\n\t\t\t\teffect(() => {\n\t\t\t\t\tif (cleanup) cleanup();\n\t\t\t\t\tcleanup = onEffect();\n\t\t\t\t}),\n\t\t\troot,\n\t\t);\n\n\t\treturn () => {\n\t\t\tif (cleanup) cleanup();\n\t\t\tstop();\n\t\t};\n\t}, [root]);\n}\n\nexport function useFlatComputed<T>(\n\tfn: () => T,\n\troot: FlatRoot,\n): FlatCompute<T> {\n\tconst fnRef = useRef(fn);\n\tfnRef.current = fn;\n\n\tconst [s] = useState(() =>\n\t\tscoped(() => computed(() => fnRef.current()), root),\n\t);\n\n\tuseEffect(() => {\n\t\treturn () => s.dispose();\n\t}, [s]);\n\n\treturn s;\n}\n\nexport function useFlatSignal<T>(value: T, root: FlatRoot) {\n\tconst [s] = useState(() => scoped(() => signal(value), root));\n\treturn s;\n}\n"],"mappings":";;;AAmBA,SAAgB,kBACf,QACA,UAAmC,OAAO,IACtC;CACJ,MAAM,eAAe,OAAU,OAAO,IAAI;CAE1C,OAAO,qBACN,aACE,kBACA,aAEE,aAAa;EACZ,MAAM,WAAW,OAAO,IAAI;EAC5B,IAAI,CAAC,QAAQ,aAAa,SAAS,QAAQ,GAAG;GAC7C,aAAa,UAAU;GACvB,cAAc;EACf;CACD,CAAC,GACF,OAAO,IACR,GACD,CAAC,QAAQ,OAAO,CACjB,SACM,OAAO,IAAI,SACX,OAAO,IACd;AACD;AAEA,SAAgB,oBACf,QACA,UACA,UAAmC,OAAO,IACtC;CACJ,MAAM,kBAAkB,OAAU,SAAS,OAAO,IAAI,CAAC;CAEvD,OAAO,qBACN,aACE,kBACA,aAEE,aAAa;EACZ,MAAM,cAAc,SAAS,OAAO,IAAI,CAAC;EACzC,IAAI,CAAC,QAAQ,gBAAgB,SAAS,WAAW,GAAG;GACnD,gBAAgB,UAAU;GAC1B,cAAc;EACf;CACD,CAAC,GACF,OAAO,IACR,GACD;EAAC;EAAQ;EAAU;CAAO,CAC3B,SACM,SAAS,OAAO,IAAI,CAAC,SACrB,SAAS,OAAO,IAAI,CAC3B;AACD;AAEA,SAAgB,kBACf,QACwC;CACxC,OAAO,aACL,QAAQ;EACR,IAAI,OAAO,QAAQ,YAClB,OAAO,IAAK,IAAyB,OAAO,IAAI,CAAC;OAEjD,OAAO,IAAI,GAAG;CAEhB,GACA,CAAC,MAAM,CACR;AACD;AAEA,SAAgB,kBAAqB,QAAuB;CAG3D,OAAO,CAFQ,kBAAkB,MAEpB,GADE,kBAAkB,MACZ,CAAC;AACvB;AAEA,SAAgB,aAAgB,UAAmB,OAAqB;CACvE,OAAO,cAAc,OAAO,UAAU,KAAK,GAAG,CAAC,UAAU,KAAK,CAAC;AAChE;AAEA,SAAgB,YAAY,WAAqB;CAChD,MAAM,CAAC,QAAQ,eAAe,IAAI,SAAS,SAAS,CAAC;CACrD,OAAO;AACR;AAEA,SAAgB,cACf,IACA,MACO;CACP,MAAM,WAAW,eAAe,EAAE;CAElC,gBAAgB;EACf,IAAI;EACJ,MAAM,OAAO,aAEX,aAAa;GACZ,IAAI,SAAS,QAAQ;GACrB,UAAU,SAAS;EACpB,CAAC,GACF,IACD;EAEA,aAAa;GACZ,IAAI,SAAS,QAAQ;GACrB,KAAK;EACN;CACD,GAAG,CAAC,IAAI,CAAC;AACV;AAEA,SAAgB,gBACf,IACA,MACiB;CACjB,MAAM,QAAQ,OAAO,EAAE;CACvB,MAAM,UAAU;CAEhB,MAAM,CAAC,KAAK,eACX,aAAa,eAAe,MAAM,QAAQ,CAAC,GAAG,IAAI,CACnD;CAEA,gBAAgB;EACf,aAAa,EAAE,QAAQ;CACxB,GAAG,CAAC,CAAC,CAAC;CAEN,OAAO;AACR;AAEA,SAAgB,cAAiB,OAAU,MAAgB;CAC1D,MAAM,CAAC,KAAK,eAAe,aAAa,OAAO,KAAK,GAAG,IAAI,CAAC;CAC5D,OAAO;AACR"}
package/package.json CHANGED
@@ -1,64 +1,73 @@
1
1
  {
2
- "name": "flatsignals",
3
- "version": "0.2.3",
4
- "description": "FlatSignals is an extremely fast reactivity library (~0.5kb)",
5
- "main": "dist/index.cjs",
6
- "module": "dist/index.js",
7
- "types": "dist/index.d.ts",
8
- "files": [
9
- "dist"
10
- ],
11
- "exports": {
12
- ".": {
13
- "import": "./dist/index.js",
14
- "types": "./dist/index.d.ts"
15
- },
16
- "./react": {
17
- "import": "./dist/react.js",
18
- "types": "./dist/react.d.ts"
19
- }
20
- },
21
- "type": "module",
22
- "scripts": {
23
- "build": "tsdown",
24
- "dev": "tsdown --watch",
25
- "test": "vitest run",
26
- "bench": "vitest bench",
27
- "test:watch": "vitest",
28
- "size": "size-limit"
29
- },
30
- "license": "MIT",
31
- "devDependencies": {
32
- "@angular/core": "^20.3.7",
33
- "@biomejs/biome": "2.2.6",
34
- "@maverick-js/signals": "^6.0.0",
35
- "@preact/signals-core": "^1.12.1",
36
- "@reactively/core": "^0.0.8",
37
- "@size-limit/preset-small-lib": "^11.2.0",
38
- "@solidjs/signals": "^0.7.5",
39
- "@types/node": "^22.18.12",
40
- "@types/react": "^19.2.2",
41
- "@vue/reactivity": "^3.5.22",
42
- "alien-signals": "^3.0.3",
43
- "react": "^19.2.0",
44
- "size-limit": "^11.2.0",
45
- "solid-js": "^1.9.9",
46
- "tsdown": "^0.15.9",
47
- "typescript": "^5.9.3",
48
- "vitest": "^2.1.9"
49
- },
50
- "peerDependencies": {
51
- "react": "*"
52
- },
53
- "peerDependenciesMeta": {
54
- "react": {
55
- "optional": true
56
- }
57
- },
58
- "size-limit": [
59
- {
60
- "path": "dist/index.js",
61
- "limit": "1 kB"
62
- }
63
- ]
64
- }
2
+ "name": "flatsignals",
3
+ "version": "0.3.0",
4
+ "description": "FlatSignals is an extremely fast reactivity library (~0.5kb)",
5
+ "main": "dist/index.cjs",
6
+ "module": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "files": [
9
+ "dist"
10
+ ],
11
+ "exports": {
12
+ ".": {
13
+ "import": "./dist/index.js",
14
+ "types": "./dist/index.d.ts"
15
+ },
16
+ "./react": {
17
+ "import": "./dist/react.js",
18
+ "types": "./dist/react.d.ts"
19
+ }
20
+ },
21
+ "type": "module",
22
+ "license": "MIT",
23
+ "devDependencies": {
24
+ "@angular/core": "^22.0.4",
25
+ "@biomejs/biome": "2.5.1",
26
+ "@maverick-js/signals": "^6.0.0",
27
+ "@preact/signals-core": "^1.14.3",
28
+ "@reactively/core": "^0.0.8",
29
+ "@size-limit/preset-small-lib": "^12.1.0",
30
+ "@solidjs/signals": "^0.13.13",
31
+ "@testing-library/dom": "^10.4.1",
32
+ "@testing-library/react": "^16.3.2",
33
+ "@types/node": "^26.0.1",
34
+ "@types/react": "^19.2.17",
35
+ "@types/react-dom": "^19.2.3",
36
+ "@vitest/coverage-v8": "^4.1.9",
37
+ "@vue/reactivity": "^3.5.39",
38
+ "alien-signals": "^3.2.1",
39
+ "jsdom": "^29.1.1",
40
+ "react": "^19.2.7",
41
+ "size-limit": "^12.1.0",
42
+ "solid-js": "^1.9.13",
43
+ "tsdown": "^0.22.3",
44
+ "typescript": "^6.0.3",
45
+ "vitest": "^4.1.9"
46
+ },
47
+ "peerDependencies": {
48
+ "react": "^19.2.0"
49
+ },
50
+ "peerDependenciesMeta": {
51
+ "react": {
52
+ "optional": true
53
+ }
54
+ },
55
+ "size-limit": [
56
+ {
57
+ "path": "dist/index.js",
58
+ "limit": "1 kB"
59
+ }
60
+ ],
61
+ "dependencies": {
62
+ "vite": "^8.1.2"
63
+ },
64
+ "scripts": {
65
+ "build": "tsdown",
66
+ "dev": "tsdown --watch",
67
+ "test": "vitest run",
68
+ "bench": "vitest bench",
69
+ "test:watch": "vitest",
70
+ "coverage": "vitest run --coverage",
71
+ "size": "size-limit"
72
+ }
73
+ }
package/dist/index.d.ts DELETED
@@ -1,2 +0,0 @@
1
- import { a as computed, c as signal, i as batch, n as FlatRoot, o as effect, r as FlatSignal, s as scoped, t as FlatCompute } from "./index-BUz21f61.js";
2
- export { FlatCompute, FlatRoot, FlatSignal, batch, computed, effect, scoped, signal };
package/dist/index.js DELETED
@@ -1,3 +0,0 @@
1
- import { a as computed, c as signal, i as batch, n as FlatRoot, o as effect, r as FlatSignal, s as scoped, t as FlatCompute } from "./src-CJj34M6F.js";
2
-
3
- export { FlatCompute, FlatRoot, FlatSignal, batch, computed, effect, scoped, signal };
package/dist/react.d.ts DELETED
@@ -1,12 +0,0 @@
1
- import { n as FlatRoot, r as FlatSignal, t as FlatCompute } from "./index-BUz21f61.js";
2
-
3
- //#region src/react/index.d.ts
4
- declare function useFlatReader<T>(signal: FlatSignal<T> | FlatCompute<T>, isEqual?: (a: T, b: T) => boolean): T;
5
- declare function useFlatSelector<T, R>(signal: FlatSignal<T> | FlatCompute<T>, selector: (value: T) => R, isEqual?: (a: R, b: R) => boolean): R;
6
- declare function useFlatWriter<T>(signal: FlatSignal<T>): (val: T | ((oldVal: T) => T)) => void;
7
- declare function useFlatSignal<T>(signal: FlatSignal<T>): readonly [T, (val: T | ((oldVal: T) => T)) => void];
8
- declare function useFlatEffect(fn: () => undefined | (() => void)): void;
9
- declare function useFlatScope<T>(callback: () => T, scope?: FlatRoot): T;
10
- //#endregion
11
- export { useFlatEffect, useFlatReader, useFlatScope, useFlatSelector, useFlatSignal, useFlatWriter };
12
- //# sourceMappingURL=react.d.ts.map
package/dist/react.js DELETED
@@ -1,57 +0,0 @@
1
- import { o as effect, s as scoped } from "./src-CJj34M6F.js";
2
- import { useCallback, useEffect, useMemo, useRef, useSyncExternalStore } from "react";
3
-
4
- //#region src/react/index.ts
5
- function useFlatReader(signal, isEqual = Object.is) {
6
- const lastValueRef = useRef(signal.peek);
7
- return useSyncExternalStore(useCallback((onStoreChange) => scoped(() => effect(() => {
8
- const newValue = signal.val;
9
- if (!isEqual(lastValueRef.current, newValue)) {
10
- lastValueRef.current = newValue;
11
- onStoreChange();
12
- }
13
- }), signal.root), [signal, isEqual]), () => signal.val, () => signal.peek);
14
- }
15
- function useFlatSelector(signal, selector, isEqual = Object.is) {
16
- const lastSelectedRef = useRef(selector(signal.peek));
17
- return useSyncExternalStore(useCallback((onStoreChange) => scoped(() => effect(() => {
18
- const newSelected = selector(signal.val);
19
- if (!isEqual(lastSelectedRef.current, newSelected)) {
20
- lastSelectedRef.current = newSelected;
21
- onStoreChange();
22
- }
23
- }), signal.root), [
24
- signal,
25
- selector,
26
- isEqual
27
- ]), () => selector(signal.val), () => selector(signal.peek));
28
- }
29
- function useFlatWriter(signal) {
30
- return useCallback((val) => {
31
- if (typeof val === "function") signal.val = val(signal.peek);
32
- else signal.val = val;
33
- }, [signal]);
34
- }
35
- function useFlatSignal(signal) {
36
- return [useFlatReader(signal), useFlatWriter(signal)];
37
- }
38
- function useFlatEffect(fn) {
39
- useEffect(() => {
40
- let cleanup;
41
- const stop = effect(() => {
42
- if (cleanup) cleanup();
43
- cleanup = fn();
44
- });
45
- return () => {
46
- if (cleanup) cleanup();
47
- stop();
48
- };
49
- }, [fn]);
50
- }
51
- function useFlatScope(callback, scope) {
52
- return useMemo(() => scoped(callback, scope), [callback, scope]);
53
- }
54
-
55
- //#endregion
56
- export { useFlatEffect, useFlatReader, useFlatScope, useFlatSelector, useFlatSignal, useFlatWriter };
57
- //# sourceMappingURL=react.js.map
package/dist/react.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"react.js","names":["cleanup: undefined | (() => void)"],"sources":["../src/react/index.ts"],"sourcesContent":["import {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useSyncExternalStore,\n} from \"react\";\nimport {\n effect,\n type FlatCompute,\n type FlatRoot,\n type FlatSignal,\n scoped,\n} from \"../index.js\";\n\nexport function useFlatReader<T>(\n signal: FlatSignal<T> | FlatCompute<T>,\n isEqual: (a: T, b: T) => boolean = Object.is\n): T {\n const lastValueRef = useRef<T>(signal.peek);\n\n return useSyncExternalStore(\n useCallback(\n (onStoreChange) =>\n scoped(\n () =>\n effect(() => {\n const newValue = signal.val;\n if (!isEqual(lastValueRef.current, newValue)) {\n lastValueRef.current = newValue;\n onStoreChange();\n }\n }),\n signal.root\n ),\n [signal, isEqual]\n ),\n () => signal.val,\n () => signal.peek\n );\n}\n\nexport function useFlatSelector<T, R>(\n signal: FlatSignal<T> | FlatCompute<T>,\n selector: (value: T) => R,\n isEqual: (a: R, b: R) => boolean = Object.is\n): R {\n const lastSelectedRef = useRef<R>(selector(signal.peek));\n\n return useSyncExternalStore(\n useCallback(\n (onStoreChange) =>\n scoped(\n () =>\n effect(() => {\n const newSelected = selector(signal.val);\n if (!isEqual(lastSelectedRef.current, newSelected)) {\n lastSelectedRef.current = newSelected;\n onStoreChange();\n }\n }),\n signal.root\n ),\n [signal, selector, isEqual]\n ),\n () => selector(signal.val),\n () => selector(signal.peek)\n );\n}\n\nexport function useFlatWriter<T>(\n signal: FlatSignal<T>\n): (val: T | ((oldVal: T) => T)) => void {\n return useCallback(\n (val) => {\n if (typeof val === \"function\") {\n signal.val = (val as (oldVal: T) => T)(signal.peek);\n } else {\n signal.val = val;\n }\n },\n [signal]\n );\n}\n\nexport function useFlatSignal<T>(signal: FlatSignal<T>) {\n const reader = useFlatReader(signal);\n const writer = useFlatWriter(signal);\n return [reader, writer] as const;\n}\n\nexport function useFlatEffect(fn: () => undefined | (() => void)): void {\n useEffect(() => {\n let cleanup: undefined | (() => void);\n const stop = effect(() => {\n if (cleanup) cleanup();\n cleanup = fn();\n });\n\n return () => {\n if (cleanup) cleanup();\n stop();\n };\n }, [fn]);\n}\n\nexport function useFlatScope<T>(callback: () => T, scope?: FlatRoot): T {\n return useMemo(() => scoped(callback, scope), [callback, scope]);\n}\n"],"mappings":";;;;AAeA,SAAgB,cACd,QACA,UAAmC,OAAO,IACvC;CACH,MAAM,eAAe,OAAU,OAAO,KAAK;AAE3C,QAAO,qBACL,aACG,kBACC,aAEI,aAAa;EACX,MAAM,WAAW,OAAO;AACxB,MAAI,CAAC,QAAQ,aAAa,SAAS,SAAS,EAAE;AAC5C,gBAAa,UAAU;AACvB,kBAAe;;GAEjB,EACJ,OAAO,KACR,EACH,CAAC,QAAQ,QAAQ,CAClB,QACK,OAAO,WACP,OAAO,KACd;;AAGH,SAAgB,gBACd,QACA,UACA,UAAmC,OAAO,IACvC;CACH,MAAM,kBAAkB,OAAU,SAAS,OAAO,KAAK,CAAC;AAExD,QAAO,qBACL,aACG,kBACC,aAEI,aAAa;EACX,MAAM,cAAc,SAAS,OAAO,IAAI;AACxC,MAAI,CAAC,QAAQ,gBAAgB,SAAS,YAAY,EAAE;AAClD,mBAAgB,UAAU;AAC1B,kBAAe;;GAEjB,EACJ,OAAO,KACR,EACH;EAAC;EAAQ;EAAU;EAAQ,CAC5B,QACK,SAAS,OAAO,IAAI,QACpB,SAAS,OAAO,KAAK,CAC5B;;AAGH,SAAgB,cACd,QACuC;AACvC,QAAO,aACJ,QAAQ;AACP,MAAI,OAAO,QAAQ,WACjB,QAAO,MAAO,IAAyB,OAAO,KAAK;MAEnD,QAAO,MAAM;IAGjB,CAAC,OAAO,CACT;;AAGH,SAAgB,cAAiB,QAAuB;AAGtD,QAAO,CAFQ,cAAc,OAAO,EACrB,cAAc,OAAO,CACb;;AAGzB,SAAgB,cAAc,IAA0C;AACtE,iBAAgB;EACd,IAAIA;EACJ,MAAM,OAAO,aAAa;AACxB,OAAI,QAAS,UAAS;AACtB,aAAU,IAAI;IACd;AAEF,eAAa;AACX,OAAI,QAAS,UAAS;AACtB,SAAM;;IAEP,CAAC,GAAG,CAAC;;AAGV,SAAgB,aAAgB,UAAmB,OAAqB;AACtE,QAAO,cAAc,OAAO,UAAU,MAAM,EAAE,CAAC,UAAU,MAAM,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"src-CJj34M6F.js","names":["ROOT: FlatRoot | null","COMPUTED: FlatCompute | null","ROOT_QUEUE: FlatRoot | null","#batch","#root","#val","#id","#fn","effect"],"sources":["../src/index.ts"],"sourcesContent":["let ROOT: FlatRoot | null = null,\n\tCOMPUTED: FlatCompute | null = null,\n\tBATCHING = false,\n\tROOT_QUEUE: FlatRoot | null = null;\n\nexport class FlatRoot {\n\t/* @internal computeds */\n\t_c: Array<FlatCompute> = [];\n\t/* @internal disposed computeds */\n\t_x: Array<number> = [];\n\t/* @internal id generator */\n\t_i = 0;\n\t/* @internal batch mask */\n\t#batch: number = 0;\n\n\tdispose() {\n\t\tthis._c = [];\n\t\tthis._x = [];\n\t\tthis._i = 0;\n\t}\n\n\t/* @internal Add source */\n\t_as() {\n\t\treturn this._i++ % 32;\n\t}\n\n\t/* @internal Add computed */\n\t_ac(c: FlatCompute) {\n\t\tif (this._x.length) {\n\t\t\tconst u = this._x.pop()!;\n\t\t\tthis._c[u] = c;\n\t\t\treturn u;\n\t\t} else {\n\t\t\treturn this._c.push(c) - 1;\n\t\t}\n\t}\n\n\t/* @internal Destroy computed */\n\t_dc(idx: number) {\n\t\tthis._x.push(idx);\n\t}\n\n\t/* @internal */\n\t_queue(mask: number) {\n\t\tif (!this.#batch) {\n\t\t\tROOT_QUEUE ??= this;\n\t\t}\n\t\tthis.#batch |= mask;\n\t\tif (!BATCHING) this._flush(true);\n\t}\n\n\t/* @internal */\n\t_flush(force: boolean = false) {\n\t\tif (!this.#batch || !force) return;\n\t\tfor (const item of this._c) {\n\t\t\tif (!item._dirty && (item._sources & this.#batch) !== 0) {\n\t\t\t\titem._dirty = true;\n\t\t\t\tif (item._effect) item.val;\n\t\t\t}\n\t\t}\n\t\tthis.#batch = 0;\n\t}\n}\n\nexport class FlatSignal<T = any> {\n\t#root: FlatRoot;\n\t#val: T;\n\t#id = 0;\n\tequals = defaultEquality;\n\n\tconstructor(val?: T) {\n\t\tif (!ROOT) ROOT = new FlatRoot();\n\t\tthis.#root = ROOT;\n\t\tthis.#val = val as T;\n\t\tthis.#id |= 1 << this.#root._as();\n\t}\n\n\tget val(): T {\n\t\tif (COMPUTED) {\n\t\t\tCOMPUTED._sources |= this.#id;\n\t\t}\n\t\treturn this.#val as T;\n\t}\n\n\tget peek() {\n\t\treturn this.#val;\n\t}\n\n\tget root() {\n\t\treturn this.#root;\n\t}\n\n\tset val(val: T) {\n\t\tif (this.equals(val, this.#val)) return;\n\t\tthis.#val = val as T;\n\t\tthis.#root._queue(this.#id);\n\t}\n}\n\nexport class FlatCompute<T = unknown> {\n\t#root: FlatRoot;\n\t#id: number;\n\t#val: T;\n\t#fn: (() => T) | undefined;\n\t/* @internal */\n\t_effect: boolean = false;\n\t/* @internal */\n\t_sources: number = 0;\n\t/* @internal */\n\t_dirty = true;\n\t/* @internal destroyed */\n\t_d = false;\n\n\tconstructor(compute?: () => T, val?: T, effect?: boolean) {\n\t\tif (!ROOT) ROOT = new FlatRoot();\n\t\tthis.#root = ROOT;\n\t\tthis.#fn = compute;\n\t\tthis.#val = val!;\n\t\tthis.#id = this.#root._ac(this as FlatCompute<unknown>);\n\t\tif (effect) {\n\t\t\tthis._effect = effect;\n\t\t\tthis.val;\n\t\t}\n\t}\n\n\tget val(): T {\n\t\tthis.#root._flush();\n\t\tconst prevCurrent = COMPUTED;\n\t\tif (this._dirty) {\n\t\t\tCOMPUTED = this as FlatCompute<unknown>;\n\t\t\tthis._sources = 0;\n\t\t\tthis.#val = this.#fn!();\n\t\t\tthis._dirty = false;\n\t\t\tCOMPUTED = prevCurrent;\n\t\t}\n\t\tif (prevCurrent) {\n\t\t\tprevCurrent._sources |= this._sources;\n\t\t}\n\t\treturn this.#val!;\n\t}\n\n\tget peek() {\n\t\treturn this.#val;\n\t}\n\n\tget root() {\n\t\treturn this.#root;\n\t}\n\n\tdispose() {\n\t\tif (this._d) return;\n\t\tthis._sources = 0;\n\t\tthis._dirty = false;\n\t\tthis._d = true;\n\t\tthis.#root._dc(this.#id);\n\t}\n}\n\nfunction defaultEquality(a: unknown, b: unknown) {\n\treturn a === b;\n}\n\nexport function batch(fn: () => void) {\n\tif (BATCHING) return fn();\n\tBATCHING = true;\n\tfn();\n\tROOT_QUEUE?._flush(true);\n\tROOT_QUEUE = null;\n\tBATCHING = false;\n}\n\nexport function scoped<T>(fn: () => T, scope?: FlatRoot): T {\n\tconst prevRoot = ROOT;\n\tROOT = scope ?? new FlatRoot();\n\tconst result = fn();\n\tROOT = prevRoot;\n\treturn result;\n}\n\nexport function signal<T>(value: T): FlatSignal<T>;\nexport function signal<T = undefined>(): FlatSignal<T | undefined>;\nexport function signal<T>(value?: T): FlatSignal<T> {\n\treturn new FlatSignal(value);\n}\n\nexport function computed<T>(val: () => T): FlatCompute<T> {\n\treturn new FlatCompute(val);\n}\n\nexport function effect<T = unknown>(fn: () => T) {\n\tconst sig = new FlatCompute(fn, undefined, true);\n\treturn sig.dispose.bind(sig);\n}\n"],"mappings":"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"}