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 +51 -4
- package/dist/{index-BUz21f61.d.ts → index.d.mts} +10 -6
- package/dist/{src-CJj34M6F.js → index.mjs} +20 -16
- package/dist/index.mjs.map +1 -0
- package/dist/react.d.mts +15 -0
- package/dist/react.mjs +74 -0
- package/dist/react.mjs.map +1 -0
- package/package.json +72 -63
- package/dist/index.d.ts +0 -2
- package/dist/index.js +0 -3
- package/dist/react.d.ts +0 -12
- package/dist/react.js +0 -57
- package/dist/react.js.map +0 -1
- package/dist/src-CJj34M6F.js.map +0 -1
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 {
|
|
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] =
|
|
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 =
|
|
132
|
+
const val = useSyncFlatReader(double);
|
|
90
133
|
return <div>{val}</div>;
|
|
91
134
|
}
|
|
92
135
|
```
|
|
93
136
|
|
|
94
137
|
## Use Case
|
|
95
138
|
|
|
96
|
-
|
|
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 =
|
|
10
|
+
declare class FlatSignal<T = undefined> {
|
|
7
11
|
#private;
|
|
8
12
|
equals: typeof defaultEquality;
|
|
9
13
|
constructor(val?: T);
|
|
10
|
-
get
|
|
14
|
+
get(): T;
|
|
11
15
|
get peek(): T;
|
|
12
16
|
get root(): FlatRoot;
|
|
13
|
-
set
|
|
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
|
|
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 {
|
|
32
|
-
//# sourceMappingURL=index
|
|
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.
|
|
34
|
+
if (this.autoFlush && !BATCHING) this.flush();
|
|
30
35
|
}
|
|
31
|
-
|
|
32
|
-
if (!this.#batch
|
|
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.
|
|
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
|
|
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
|
|
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
|
|
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
|
|
83
|
-
this._effect = effect
|
|
84
|
-
this.
|
|
87
|
+
if (effect) {
|
|
88
|
+
this._effect = effect;
|
|
89
|
+
this.get();
|
|
85
90
|
}
|
|
86
91
|
}
|
|
87
|
-
get
|
|
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?.
|
|
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 {
|
|
145
|
-
|
|
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":";;AAEA,IAAI,OAAwB,MAC3B,WAA+B,MAC/B,WAAW,OACX,aAA8B;AAE/B,IAAa,WAAb,MAAsB;CAUF;CARnB,KAAyB,CAAC;CAE1B,KAAoB,CAAC;CAErB,KAAK;CAEL,SAAiB;CAEjB,YAAY,YAAmB,MAAM;EAAlB,KAAA,YAAA;CAAmB;CAEtC,UAAU;EACT,KAAK,KAAK,CAAC;EACX,KAAK,KAAK,CAAC;EACX,KAAK,KAAK;CACX;CAGA,MAAM;EACL,OAAO,KAAK,OAAO;CACpB;CAGA,IAAI,GAAgB;EACnB,IAAI,KAAK,GAAG,QAAQ;GACnB,MAAM,IAAI,KAAK,GAAG,IAAI;GACtB,KAAK,GAAG,KAAK;GACb,OAAO;EACR,OACC,OAAO,KAAK,GAAG,KAAK,CAAC,IAAI;CAE3B;CAGA,IAAI,KAAa;EAChB,KAAK,GAAG,KAAK,GAAG;CACjB;CAGA,OAAO,MAAc;EACpB,IAAI,CAAC,KAAKA,QACT,eAAe;EAEhB,KAAKA,UAAU;EACf,IAAI,KAAK,aAAa,CAAC,UAAU,KAAK,MAAM;CAC7C;CAEA,QAAQ;EACP,IAAI,CAAC,KAAKA,QAAQ;EAClB,KAAK,MAAM,QAAQ,KAAK,IACvB,IAAI,CAAC,KAAK,WAAW,KAAK,WAAW,KAAKA,YAAY,GAAG;GACxD,KAAK,SAAS;GACd,IAAI,KAAK,SAAS,KAAK,IAAI;EAC5B;EAED,KAAKA,SAAS;CACf;AACD;AAEA,IAAa,aAAb,MAAuC;CACtC;CACA;CACA,MAAM;CACN,SAAS;CAET,YAAY,KAAS;EACpB,IAAI,CAAC,MAAM,OAAO,IAAI,SAAS;EAC/B,KAAKC,QAAQ;EACb,KAAKC,OAAO;EACZ,KAAKC,OAAO,KAAK,KAAKF,MAAM,IAAI;CACjC;CAEA,MAAS;EACR,IAAI,UACH,SAAS,YAAY,KAAKE;EAE3B,OAAO,KAAKD;CACb;CAEA,IAAI,OAAO;EACV,OAAO,KAAKA;CACb;CAEA,IAAI,OAAO;EACV,OAAO,KAAKD;CACb;CAEA,IAAI,KAAQ;EACX,IAAI,KAAK,OAAO,KAAK,KAAKC,IAAI,GAAG;EACjC,KAAKA,OAAO;EACZ,KAAKD,MAAM,OAAO,KAAKE,GAAG;CAC3B;AACD;AAEA,IAAa,cAAb,MAAsC;CACrC;CACA;CACA;CACA;CAEA,UAAmB;CAEnB,WAAmB;CAEnB,SAAS;CAET,KAAK;CAEL,YAAY,SAAmB,KAAS,QAAkB;EACzD,IAAI,CAAC,MAAM,OAAO,IAAI,SAAS;EAC/B,KAAKF,QAAQ;EACb,KAAKG,MAAM;EACX,KAAKF,OAAO;EACZ,KAAKC,MAAM,KAAKF,MAAM,IAAI,IAA4B;EACtD,IAAI,QAAQ;GACX,KAAK,UAAU;GACf,KAAK,IAAI;EACV;CACD;CAEA,MAAS;EACR,MAAM,cAAc;EACpB,IAAI,KAAK,QAAQ;GAChB,WAAW;GACX,KAAK,WAAW;GAChB,KAAKC,OAAO,KAAKE,IAAK;GACtB,KAAK,SAAS;GACd,WAAW;EACZ;EACA,IAAI,aACH,YAAY,YAAY,KAAK;EAE9B,OAAO,KAAKF;CACb;CAEA,IAAI,OAAO;EACV,OAAO,KAAKA;CACb;CAEA,IAAI,OAAO;EACV,OAAO,KAAKD;CACb;CAEA,UAAU;EACT,IAAI,KAAK,IAAI;EACb,KAAK,WAAW;EAChB,KAAK,SAAS;EACd,KAAK,KAAK;EACV,KAAKA,MAAM,IAAI,KAAKE,GAAG;CACxB;AACD;AAEA,SAAS,gBAAgB,GAAY,GAAY;CAChD,OAAO,MAAM;AACd;AAEA,SAAgB,MAAM,IAAgB;CACrC,IAAI,UAAU,OAAO,GAAG;CACxB,WAAW;CACX,GAAG;CACH,IAAI,YAAY,WAAW,YAAY,MAAM;CAC7C,aAAa;CACb,WAAW;AACZ;AAEA,SAAgB,OAAU,IAAa,OAAqB;CAC3D,MAAM,WAAW;CACjB,OAAO,SAAS,IAAI,SAAS;CAC7B,MAAM,SAAS,GAAG;CAClB,OAAO;CACP,OAAO;AACR;AAIA,SAAgB,OAAU,OAA0B;CACnD,OAAO,IAAI,WAAW,KAAK;AAC5B;AAEA,SAAgB,SAAY,KAA8B;CACzD,OAAO,IAAI,YAAY,GAAG;AAC3B;AAEA,SAAgB,OAAoB,IAAa;CAChD,MAAM,MAAM,IAAI,YAAY,IAAI,KAAA,GAAW,IAAI;CAC/C,OAAO,IAAI,QAAQ,KAAK,GAAG;AAC5B"}
|
package/dist/react.d.mts
ADDED
|
@@ -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
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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
package/dist/index.js
DELETED
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"}
|
package/dist/src-CJj34M6F.js.map
DELETED
|
@@ -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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"}
|