flatsignals 0.1.0 β 0.2.1
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 +93 -72
- package/dist/index-BUz21f61.d.ts +32 -0
- package/dist/index.d.ts +2 -32
- package/dist/index.js +2 -208
- package/dist/react.d.ts +11 -0
- package/dist/react.js +43 -0
- package/dist/react.js.map +1 -0
- package/dist/src-CJj34M6F.js +145 -0
- package/dist/src-CJj34M6F.js.map +1 -0
- package/package.json +62 -37
- package/dist/index.cjs +0 -222
- package/dist/index.d.cts +0 -32
package/README.md
CHANGED
|
@@ -1,81 +1,102 @@
|
|
|
1
1
|
# FlatSignals
|
|
2
2
|
|
|
3
|
-
FlatSignals is an
|
|
3
|
+
**FlatSignals** is an ultra-fast reactivity library (~0.5 KB) optimized for **high-frequency, few-to-many updates**.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
5
|
+
## π Why itβs fast:
|
|
6
|
+
|
|
7
|
+
- No graph traversals
|
|
8
|
+
- O(1) dynamic dependency management
|
|
9
|
+
- Lazy computations by default
|
|
10
10
|
|
|
11
11
|
## Benchmarks
|
|
12
12
|
|
|
13
|
-
You can
|
|
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
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
13
|
+
> **Note:** Benchmarks were run in a controlled environment. Results may vary based on hardware and JavaScript engine. You can reproduce these benchmarks by cloning the repo and running `pnpm bench`.
|
|
14
|
+
|
|
15
|
+
### 1-to-64 fanout
|
|
16
|
+
|
|
17
|
+
| Library | Operations/sec β‘ | vs flatsignals |
|
|
18
|
+
| -------------------- | ----------------- | -------------- |
|
|
19
|
+
| **flatsignals** π | **449,848** | **baseline** |
|
|
20
|
+
| alien-signals | 261,082 | 1.72x slower |
|
|
21
|
+
| @preact/signals | 230,899 | 1.95x slower |
|
|
22
|
+
| @reactively/core | 182,403 | 2.47x slower |
|
|
23
|
+
| @vue/reactivity | 152,709 | 2.95x slower |
|
|
24
|
+
| @maverick-js/signals | 137,712 | 3.27x slower |
|
|
25
|
+
| Angular Signals | 98,597 | 4.56x slower |
|
|
26
|
+
| @solidjs/signals | 78,849 | 5.71x slower |
|
|
27
|
+
|
|
28
|
+
### High-frequency updates
|
|
29
|
+
|
|
30
|
+
| Library | Operations/sec β‘ | vs flatsignals |
|
|
31
|
+
| -------------------- | ----------------- | -------------- |
|
|
32
|
+
| **flatsignals** π | **976,139** | **baseline** |
|
|
33
|
+
| alien-signals | 502,513 | 1.94x slower |
|
|
34
|
+
| @preact/signals | 492,543 | 1.98x slower |
|
|
35
|
+
| @reactively/core | 438,882 | 2.22x slower |
|
|
36
|
+
| @maverick-js/signals | 343,368 | 2.84x slower |
|
|
37
|
+
| Angular Signals | 241,189 | 4.05x slower |
|
|
38
|
+
| @vue/reactivity | 221,537 | 4.41x slower |
|
|
39
|
+
| @solidjs/signals | 202,492 | 4.82x slower |
|
|
40
|
+
|
|
41
|
+
### Diamond
|
|
42
|
+
|
|
43
|
+
| Library | Operations/sec β‘ | vs flatsignals |
|
|
44
|
+
| -------------------- | ----------------- | -------------- |
|
|
45
|
+
| **flatsignals** π | **4,556,987** | **baseline** |
|
|
46
|
+
| alien-signals | 3,028,320 | 1.50x slower |
|
|
47
|
+
| @preact/signals | 2,531,788 | 1.80x slower |
|
|
48
|
+
| @reactively/core | 1,688,053 | 2.70x slower |
|
|
49
|
+
| Angular Signals | 1,614,432 | 2.82x slower |
|
|
50
|
+
| @vue/reactivity | 1,563,865 | 2.91x slower |
|
|
51
|
+
| @maverick-js/signals | 1,407,975 | 3.24x slower |
|
|
52
|
+
| @solidjs/signals | 870,080 | 5.24x slower |
|
|
53
|
+
|
|
54
|
+
## Installation
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
# npm
|
|
58
|
+
npm install flatsignals
|
|
59
|
+
|
|
60
|
+
# pnpm
|
|
61
|
+
pnpm add flatsignals
|
|
62
|
+
|
|
63
|
+
# yarn
|
|
64
|
+
yarn add flatsignals
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Usage
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
import { signal, computed, effect } from "flatsignals";
|
|
71
|
+
|
|
72
|
+
const counter = signal(0);
|
|
73
|
+
const double = computed(() => counter.val * 2);
|
|
74
|
+
const log = effect(() => console.log(double.val));
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## With React
|
|
78
|
+
|
|
79
|
+
```tsx
|
|
80
|
+
import { useFlatSignal, useFlatReader } from "flatsignals/react";
|
|
81
|
+
import { counter, double } from "./signals";
|
|
82
|
+
|
|
83
|
+
function MyCounter() {
|
|
84
|
+
const [val, setVal] = useFlatSignal(counter);
|
|
85
|
+
return <button onClick={() => setVal(val + 1)}>Count: {val}</button>;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function ReadDouble() {
|
|
89
|
+
const val = useFlatReader(double);
|
|
90
|
+
return <div>{val}</div>;
|
|
91
|
+
}
|
|
75
92
|
```
|
|
76
93
|
|
|
77
|
-
##
|
|
94
|
+
## Use Case
|
|
95
|
+
|
|
96
|
+
> **Best suited for:** Scenarios with a small number of reactive signals driving many dependent computations.
|
|
97
|
+
|
|
98
|
+
**Limitations:**
|
|
78
99
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
100
|
+
- **Signal limit**: Maximum of 32 signals per root. Beyond this limit, effects may trigger even when their tracked signals haven't changed.
|
|
101
|
+
- **Set complexity**: O(N) time proportional to dependent computations (amortized through batching)
|
|
102
|
+
- **Eager propagation**: All downstream nodes marked dirty immediately, even when intermediate values unchanged
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
//#region src/index.d.ts
|
|
2
|
+
declare class FlatRoot {
|
|
3
|
+
#private;
|
|
4
|
+
dispose(): void;
|
|
5
|
+
}
|
|
6
|
+
declare class FlatSignal<T = any> {
|
|
7
|
+
#private;
|
|
8
|
+
equals: typeof defaultEquality;
|
|
9
|
+
constructor(val?: T);
|
|
10
|
+
get val(): T;
|
|
11
|
+
get peek(): T;
|
|
12
|
+
get root(): FlatRoot;
|
|
13
|
+
set val(val: T);
|
|
14
|
+
}
|
|
15
|
+
declare class FlatCompute<T = unknown> {
|
|
16
|
+
#private;
|
|
17
|
+
constructor(compute?: () => T, val?: T, effect?: boolean);
|
|
18
|
+
get val(): T;
|
|
19
|
+
get peek(): T;
|
|
20
|
+
get root(): FlatRoot;
|
|
21
|
+
dispose(): void;
|
|
22
|
+
}
|
|
23
|
+
declare function defaultEquality(a: unknown, b: unknown): boolean;
|
|
24
|
+
declare function batch(fn: () => void): void;
|
|
25
|
+
declare function scoped<T>(fn: () => T, scope?: FlatRoot): T;
|
|
26
|
+
declare function signal<T>(value: T): FlatSignal<T>;
|
|
27
|
+
declare function signal<T = undefined>(): FlatSignal<T | undefined>;
|
|
28
|
+
declare function computed<T>(val: () => T): FlatCompute<T>;
|
|
29
|
+
declare function effect<T = unknown>(fn: () => T): () => void;
|
|
30
|
+
//#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
|
package/dist/index.d.ts
CHANGED
|
@@ -1,32 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
}
|
|
4
|
-
declare class Root extends Scope {
|
|
5
|
-
#private;
|
|
6
|
-
}
|
|
7
|
-
declare class DataSignal<T = any> {
|
|
8
|
-
#private;
|
|
9
|
-
equals: typeof defaultEquality;
|
|
10
|
-
constructor(val?: T);
|
|
11
|
-
get val(): T;
|
|
12
|
-
set val(val: T);
|
|
13
|
-
}
|
|
14
|
-
declare class Computation<T = unknown> extends Scope {
|
|
15
|
-
#private;
|
|
16
|
-
constructor(compute?: () => T, val?: T, effect?: boolean);
|
|
17
|
-
get val(): T;
|
|
18
|
-
get peek(): T;
|
|
19
|
-
getRoot(): Root;
|
|
20
|
-
}
|
|
21
|
-
declare function defaultEquality(a: unknown, b: unknown): boolean;
|
|
22
|
-
declare function getScope(): Scope | null;
|
|
23
|
-
declare function withScope(scope: Scope, fn: () => void): void;
|
|
24
|
-
declare function onDispose(fn: () => void): void;
|
|
25
|
-
declare function batch(fn: () => void): void;
|
|
26
|
-
declare function root<T>(fn: (dispose: () => void) => T, existingRoot?: Root): T;
|
|
27
|
-
declare function signal<T>(value: T): DataSignal<T>;
|
|
28
|
-
declare function signal<T = undefined>(): DataSignal<T | undefined>;
|
|
29
|
-
declare function computed<T>(val: () => T): Computation<T>;
|
|
30
|
-
declare function effect<T = unknown>(fn: () => T): () => void;
|
|
31
|
-
|
|
32
|
-
export { Computation, DataSignal, Root, Scope, batch, computed, effect, getScope, onDispose, root, signal, withScope };
|
|
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
CHANGED
|
@@ -1,209 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
var ROOT = null;
|
|
3
|
-
var COMPUTED = null;
|
|
4
|
-
var BATCHING = false;
|
|
5
|
-
var ROOT_QUEUE = [];
|
|
6
|
-
var Scope = class {
|
|
7
|
-
/* @internal */
|
|
8
|
-
_disposing = false;
|
|
9
|
-
/* @internal */
|
|
10
|
-
_disposals = null;
|
|
11
|
-
constructor() {
|
|
12
|
-
getScope()?._onDispose(this._dispose.bind(this));
|
|
13
|
-
}
|
|
14
|
-
/* @internal */
|
|
15
|
-
_dispose() {
|
|
16
|
-
if (this._disposing) return;
|
|
17
|
-
this._disposing = true;
|
|
18
|
-
this._disposals?.forEach((fn) => fn());
|
|
19
|
-
this._disposing = false;
|
|
20
|
-
}
|
|
21
|
-
/* @internal */
|
|
22
|
-
_onDispose(fn) {
|
|
23
|
-
if (!this._disposals) this._disposals = [];
|
|
24
|
-
this._disposals.push(fn);
|
|
25
|
-
}
|
|
26
|
-
};
|
|
27
|
-
var Root = class extends Scope {
|
|
28
|
-
/* @internal computeds */
|
|
29
|
-
_c = [];
|
|
30
|
-
/* @internal disposed computeds */
|
|
31
|
-
_x = [];
|
|
32
|
-
/* @internal id generator */
|
|
33
|
-
_i = 0;
|
|
34
|
-
/* @internal batch mask */
|
|
35
|
-
#batch = 0;
|
|
36
|
-
/* @internal */
|
|
37
|
-
_dispose() {
|
|
38
|
-
super._dispose();
|
|
39
|
-
this._c = [];
|
|
40
|
-
this._x = [];
|
|
41
|
-
this._i = 0;
|
|
42
|
-
}
|
|
43
|
-
/* @internal Add source */
|
|
44
|
-
_as() {
|
|
45
|
-
return this._i++ % 32;
|
|
46
|
-
}
|
|
47
|
-
/* @internal Add computed */
|
|
48
|
-
_ac(c) {
|
|
49
|
-
if (this._x.length) {
|
|
50
|
-
const u = this._x.pop();
|
|
51
|
-
this._c[u] = c;
|
|
52
|
-
return u;
|
|
53
|
-
} else {
|
|
54
|
-
return this._c.push(c) - 1;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
/* @internal Destroy computed */
|
|
58
|
-
_dc(idx) {
|
|
59
|
-
this._x.push(idx);
|
|
60
|
-
}
|
|
61
|
-
/* @internal */
|
|
62
|
-
_queue(mask) {
|
|
63
|
-
if (!this.#batch) {
|
|
64
|
-
ROOT_QUEUE.push(this);
|
|
65
|
-
}
|
|
66
|
-
this.#batch |= mask;
|
|
67
|
-
if (!BATCHING) this._flush(true);
|
|
68
|
-
}
|
|
69
|
-
/* @internal */
|
|
70
|
-
_flush(force = false) {
|
|
71
|
-
if (!this.#batch || !force) return;
|
|
72
|
-
for (const item of this._c) {
|
|
73
|
-
if (!item._dirty && (item._sources & this.#batch) !== 0) {
|
|
74
|
-
item._dirty = true;
|
|
75
|
-
if (item._effect) item.val;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
this.#batch = 0;
|
|
79
|
-
}
|
|
80
|
-
};
|
|
81
|
-
var DataSignal = class {
|
|
82
|
-
#root;
|
|
83
|
-
#val;
|
|
84
|
-
#id = 0;
|
|
85
|
-
equals = defaultEquality;
|
|
86
|
-
constructor(val) {
|
|
87
|
-
if (!ROOT) ROOT = new Root();
|
|
88
|
-
this.#root = ROOT;
|
|
89
|
-
this.#val = val;
|
|
90
|
-
this.#id |= 1 << this.#root._as();
|
|
91
|
-
}
|
|
92
|
-
get val() {
|
|
93
|
-
if (COMPUTED) {
|
|
94
|
-
COMPUTED._sources |= this.#id;
|
|
95
|
-
}
|
|
96
|
-
return this.#val;
|
|
97
|
-
}
|
|
98
|
-
set val(val) {
|
|
99
|
-
if (this.equals(val, this.#val)) return;
|
|
100
|
-
this.#val = val;
|
|
101
|
-
this.#root._queue(this.#id);
|
|
102
|
-
}
|
|
103
|
-
};
|
|
104
|
-
var Computation = class extends Scope {
|
|
105
|
-
#root;
|
|
106
|
-
#id;
|
|
107
|
-
#val;
|
|
108
|
-
#fn;
|
|
109
|
-
/* @internal */
|
|
110
|
-
_effect = false;
|
|
111
|
-
/* @internal */
|
|
112
|
-
_sources = 0;
|
|
113
|
-
/* @internal */
|
|
114
|
-
_dirty = true;
|
|
115
|
-
/* @internal destroyed */
|
|
116
|
-
_d = false;
|
|
117
|
-
constructor(compute, val, effect2) {
|
|
118
|
-
if (!ROOT) ROOT = new Root();
|
|
119
|
-
super();
|
|
120
|
-
this.#root = ROOT;
|
|
121
|
-
this.#fn = compute;
|
|
122
|
-
this.#val = val;
|
|
123
|
-
this.#id = this.#root._ac(this);
|
|
124
|
-
if (effect2) {
|
|
125
|
-
this._effect = effect2;
|
|
126
|
-
this.val;
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
get val() {
|
|
130
|
-
this.#root._flush();
|
|
131
|
-
const prevCurrent = COMPUTED;
|
|
132
|
-
if (this._dirty) {
|
|
133
|
-
super._dispose();
|
|
134
|
-
COMPUTED = this;
|
|
135
|
-
this._sources = 0;
|
|
136
|
-
this.#val = this.#fn();
|
|
137
|
-
this._dirty = false;
|
|
138
|
-
COMPUTED = prevCurrent;
|
|
139
|
-
}
|
|
140
|
-
if (prevCurrent) {
|
|
141
|
-
prevCurrent._sources |= this._sources;
|
|
142
|
-
}
|
|
143
|
-
return this.#val;
|
|
144
|
-
}
|
|
145
|
-
get peek() {
|
|
146
|
-
return this.#val;
|
|
147
|
-
}
|
|
148
|
-
getRoot() {
|
|
149
|
-
return this.#root;
|
|
150
|
-
}
|
|
151
|
-
/* @internal */
|
|
152
|
-
_dispose() {
|
|
153
|
-
if (this._d) return;
|
|
154
|
-
super._dispose();
|
|
155
|
-
this._sources = 0;
|
|
156
|
-
this._dirty = false;
|
|
157
|
-
this._d = true;
|
|
158
|
-
this.#root._dc(this.#id);
|
|
159
|
-
}
|
|
160
|
-
};
|
|
161
|
-
function defaultEquality(a, b) {
|
|
162
|
-
return a === b;
|
|
163
|
-
}
|
|
164
|
-
function getScope() {
|
|
165
|
-
return COMPUTED || ROOT;
|
|
166
|
-
}
|
|
167
|
-
function withScope(scope, fn) {
|
|
168
|
-
const prevComputed = COMPUTED, prevRoot = ROOT;
|
|
169
|
-
if (scope instanceof Computation) COMPUTED = scope;
|
|
170
|
-
ROOT = COMPUTED?.getRoot() ?? ROOT;
|
|
171
|
-
const result = fn();
|
|
172
|
-
COMPUTED = prevComputed;
|
|
173
|
-
ROOT = prevRoot;
|
|
174
|
-
return result;
|
|
175
|
-
}
|
|
176
|
-
function onDispose(fn) {
|
|
177
|
-
getScope()?._onDispose(fn);
|
|
178
|
-
}
|
|
179
|
-
function batch(fn) {
|
|
180
|
-
if (BATCHING) return fn();
|
|
181
|
-
BATCHING = true;
|
|
182
|
-
fn();
|
|
183
|
-
if (ROOT_QUEUE.length) {
|
|
184
|
-
for (const el of ROOT_QUEUE) el._flush(true);
|
|
185
|
-
ROOT_QUEUE = [];
|
|
186
|
-
}
|
|
187
|
-
BATCHING = false;
|
|
188
|
-
}
|
|
189
|
-
function root(fn, existingRoot) {
|
|
190
|
-
const prevRoot = ROOT;
|
|
191
|
-
const prevScope = getScope();
|
|
192
|
-
ROOT = existingRoot ?? new Root();
|
|
193
|
-
prevScope?._onDispose(ROOT._dispose.bind(ROOT));
|
|
194
|
-
const result = fn(ROOT._dispose.bind(ROOT));
|
|
195
|
-
ROOT = prevRoot;
|
|
196
|
-
return result;
|
|
197
|
-
}
|
|
198
|
-
function signal(value) {
|
|
199
|
-
return new DataSignal(value);
|
|
200
|
-
}
|
|
201
|
-
function computed(val) {
|
|
202
|
-
return new Computation(val);
|
|
203
|
-
}
|
|
204
|
-
function effect(fn) {
|
|
205
|
-
const sig = new Computation(fn, void 0, true);
|
|
206
|
-
return sig._dispose.bind(sig);
|
|
207
|
-
}
|
|
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";
|
|
208
2
|
|
|
209
|
-
export {
|
|
3
|
+
export { FlatCompute, FlatRoot, FlatSignal, batch, computed, effect, scoped, signal };
|
package/dist/react.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
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 useFlatWriter<T>(signal: FlatSignal<T>): (val: T | ((oldVal: T) => T)) => void;
|
|
6
|
+
declare function useFlatSignal<T>(signal: FlatSignal<T>): readonly [T, (val: T | ((oldVal: T) => T)) => void];
|
|
7
|
+
declare function useFlatEffect(fn: () => undefined | (() => void)): void;
|
|
8
|
+
declare function useFlatScope<T>(callback: () => T, scope?: FlatRoot): T;
|
|
9
|
+
//#endregion
|
|
10
|
+
export { useFlatEffect, useFlatReader, useFlatScope, useFlatSignal, useFlatWriter };
|
|
11
|
+
//# sourceMappingURL=react.d.ts.map
|
package/dist/react.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
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 useFlatWriter(signal) {
|
|
16
|
+
return useCallback((val) => {
|
|
17
|
+
if (typeof val === "function") signal.val = val(signal.peek);
|
|
18
|
+
else signal.val = val;
|
|
19
|
+
}, [signal]);
|
|
20
|
+
}
|
|
21
|
+
function useFlatSignal(signal) {
|
|
22
|
+
return [useFlatReader(signal), useFlatWriter(signal)];
|
|
23
|
+
}
|
|
24
|
+
function useFlatEffect(fn) {
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
let cleanup;
|
|
27
|
+
const stop = effect(() => {
|
|
28
|
+
if (cleanup) cleanup();
|
|
29
|
+
cleanup = fn();
|
|
30
|
+
});
|
|
31
|
+
return () => {
|
|
32
|
+
if (cleanup) cleanup();
|
|
33
|
+
stop();
|
|
34
|
+
};
|
|
35
|
+
}, [fn]);
|
|
36
|
+
}
|
|
37
|
+
function useFlatScope(callback, scope) {
|
|
38
|
+
return useMemo(() => scoped(callback, scope), [callback, scope]);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
//#endregion
|
|
42
|
+
export { useFlatEffect, useFlatReader, useFlatScope, useFlatSignal, useFlatWriter };
|
|
43
|
+
//# sourceMappingURL=react.js.map
|
|
@@ -0,0 +1 @@
|
|
|
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 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,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"}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
//#region src/index.ts
|
|
2
|
+
let ROOT = null, COMPUTED = null, BATCHING = false, ROOT_QUEUE = null;
|
|
3
|
+
var FlatRoot = class {
|
|
4
|
+
_c = [];
|
|
5
|
+
_x = [];
|
|
6
|
+
_i = 0;
|
|
7
|
+
#batch = 0;
|
|
8
|
+
dispose() {
|
|
9
|
+
this._c = [];
|
|
10
|
+
this._x = [];
|
|
11
|
+
this._i = 0;
|
|
12
|
+
}
|
|
13
|
+
_as() {
|
|
14
|
+
return this._i++ % 32;
|
|
15
|
+
}
|
|
16
|
+
_ac(c) {
|
|
17
|
+
if (this._x.length) {
|
|
18
|
+
const u = this._x.pop();
|
|
19
|
+
this._c[u] = c;
|
|
20
|
+
return u;
|
|
21
|
+
} else return this._c.push(c) - 1;
|
|
22
|
+
}
|
|
23
|
+
_dc(idx) {
|
|
24
|
+
this._x.push(idx);
|
|
25
|
+
}
|
|
26
|
+
_queue(mask) {
|
|
27
|
+
if (!this.#batch) ROOT_QUEUE ??= this;
|
|
28
|
+
this.#batch |= mask;
|
|
29
|
+
if (!BATCHING) this._flush(true);
|
|
30
|
+
}
|
|
31
|
+
_flush(force = false) {
|
|
32
|
+
if (!this.#batch || !force) return;
|
|
33
|
+
for (const item of this._c) if (!item._dirty && (item._sources & this.#batch) !== 0) {
|
|
34
|
+
item._dirty = true;
|
|
35
|
+
if (item._effect) item.val;
|
|
36
|
+
}
|
|
37
|
+
this.#batch = 0;
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
var FlatSignal = class {
|
|
41
|
+
#root;
|
|
42
|
+
#val;
|
|
43
|
+
#id = 0;
|
|
44
|
+
equals = defaultEquality;
|
|
45
|
+
constructor(val) {
|
|
46
|
+
if (!ROOT) ROOT = new FlatRoot();
|
|
47
|
+
this.#root = ROOT;
|
|
48
|
+
this.#val = val;
|
|
49
|
+
this.#id |= 1 << this.#root._as();
|
|
50
|
+
}
|
|
51
|
+
get val() {
|
|
52
|
+
if (COMPUTED) COMPUTED._sources |= this.#id;
|
|
53
|
+
return this.#val;
|
|
54
|
+
}
|
|
55
|
+
get peek() {
|
|
56
|
+
return this.#val;
|
|
57
|
+
}
|
|
58
|
+
get root() {
|
|
59
|
+
return this.#root;
|
|
60
|
+
}
|
|
61
|
+
set val(val) {
|
|
62
|
+
if (this.equals(val, this.#val)) return;
|
|
63
|
+
this.#val = val;
|
|
64
|
+
this.#root._queue(this.#id);
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
var FlatCompute = class {
|
|
68
|
+
#root;
|
|
69
|
+
#id;
|
|
70
|
+
#val;
|
|
71
|
+
#fn;
|
|
72
|
+
_effect = false;
|
|
73
|
+
_sources = 0;
|
|
74
|
+
_dirty = true;
|
|
75
|
+
_d = false;
|
|
76
|
+
constructor(compute, val, effect$1) {
|
|
77
|
+
if (!ROOT) ROOT = new FlatRoot();
|
|
78
|
+
this.#root = ROOT;
|
|
79
|
+
this.#fn = compute;
|
|
80
|
+
this.#val = val;
|
|
81
|
+
this.#id = this.#root._ac(this);
|
|
82
|
+
if (effect$1) {
|
|
83
|
+
this._effect = effect$1;
|
|
84
|
+
this.val;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
get val() {
|
|
88
|
+
this.#root._flush();
|
|
89
|
+
const prevCurrent = COMPUTED;
|
|
90
|
+
if (this._dirty) {
|
|
91
|
+
COMPUTED = this;
|
|
92
|
+
this._sources = 0;
|
|
93
|
+
this.#val = this.#fn();
|
|
94
|
+
this._dirty = false;
|
|
95
|
+
COMPUTED = prevCurrent;
|
|
96
|
+
}
|
|
97
|
+
if (prevCurrent) prevCurrent._sources |= this._sources;
|
|
98
|
+
return this.#val;
|
|
99
|
+
}
|
|
100
|
+
get peek() {
|
|
101
|
+
return this.#val;
|
|
102
|
+
}
|
|
103
|
+
get root() {
|
|
104
|
+
return this.#root;
|
|
105
|
+
}
|
|
106
|
+
dispose() {
|
|
107
|
+
if (this._d) return;
|
|
108
|
+
this._sources = 0;
|
|
109
|
+
this._dirty = false;
|
|
110
|
+
this._d = true;
|
|
111
|
+
this.#root._dc(this.#id);
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
function defaultEquality(a, b) {
|
|
115
|
+
return a === b;
|
|
116
|
+
}
|
|
117
|
+
function batch(fn) {
|
|
118
|
+
if (BATCHING) return fn();
|
|
119
|
+
BATCHING = true;
|
|
120
|
+
fn();
|
|
121
|
+
ROOT_QUEUE?._flush(true);
|
|
122
|
+
ROOT_QUEUE = null;
|
|
123
|
+
BATCHING = false;
|
|
124
|
+
}
|
|
125
|
+
function scoped(fn, scope) {
|
|
126
|
+
const prevRoot = ROOT;
|
|
127
|
+
ROOT = scope ?? new FlatRoot();
|
|
128
|
+
const result = fn();
|
|
129
|
+
ROOT = prevRoot;
|
|
130
|
+
return result;
|
|
131
|
+
}
|
|
132
|
+
function signal(value) {
|
|
133
|
+
return new FlatSignal(value);
|
|
134
|
+
}
|
|
135
|
+
function computed(val) {
|
|
136
|
+
return new FlatCompute(val);
|
|
137
|
+
}
|
|
138
|
+
function effect(fn) {
|
|
139
|
+
const sig = new FlatCompute(fn, void 0, true);
|
|
140
|
+
return sig.dispose.bind(sig);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
//#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
|
|
@@ -0,0 +1 @@
|
|
|
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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"}
|
package/package.json
CHANGED
|
@@ -1,39 +1,64 @@
|
|
|
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
|
-
|
|
2
|
+
"name": "flatsignals",
|
|
3
|
+
"version": "0.2.1",
|
|
4
|
+
"description": "FlatSignals is an extremely fast reactivity library (~0.7kb)",
|
|
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
|
+
]
|
|
39
64
|
}
|
package/dist/index.cjs
DELETED
|
@@ -1,222 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
// src/index.ts
|
|
4
|
-
var ROOT = null;
|
|
5
|
-
var COMPUTED = null;
|
|
6
|
-
var BATCHING = false;
|
|
7
|
-
var ROOT_QUEUE = [];
|
|
8
|
-
var Scope = class {
|
|
9
|
-
/* @internal */
|
|
10
|
-
_disposing = false;
|
|
11
|
-
/* @internal */
|
|
12
|
-
_disposals = null;
|
|
13
|
-
constructor() {
|
|
14
|
-
getScope()?._onDispose(this._dispose.bind(this));
|
|
15
|
-
}
|
|
16
|
-
/* @internal */
|
|
17
|
-
_dispose() {
|
|
18
|
-
if (this._disposing) return;
|
|
19
|
-
this._disposing = true;
|
|
20
|
-
this._disposals?.forEach((fn) => fn());
|
|
21
|
-
this._disposing = false;
|
|
22
|
-
}
|
|
23
|
-
/* @internal */
|
|
24
|
-
_onDispose(fn) {
|
|
25
|
-
if (!this._disposals) this._disposals = [];
|
|
26
|
-
this._disposals.push(fn);
|
|
27
|
-
}
|
|
28
|
-
};
|
|
29
|
-
var Root = class extends Scope {
|
|
30
|
-
/* @internal computeds */
|
|
31
|
-
_c = [];
|
|
32
|
-
/* @internal disposed computeds */
|
|
33
|
-
_x = [];
|
|
34
|
-
/* @internal id generator */
|
|
35
|
-
_i = 0;
|
|
36
|
-
/* @internal batch mask */
|
|
37
|
-
#batch = 0;
|
|
38
|
-
/* @internal */
|
|
39
|
-
_dispose() {
|
|
40
|
-
super._dispose();
|
|
41
|
-
this._c = [];
|
|
42
|
-
this._x = [];
|
|
43
|
-
this._i = 0;
|
|
44
|
-
}
|
|
45
|
-
/* @internal Add source */
|
|
46
|
-
_as() {
|
|
47
|
-
return this._i++ % 32;
|
|
48
|
-
}
|
|
49
|
-
/* @internal Add computed */
|
|
50
|
-
_ac(c) {
|
|
51
|
-
if (this._x.length) {
|
|
52
|
-
const u = this._x.pop();
|
|
53
|
-
this._c[u] = c;
|
|
54
|
-
return u;
|
|
55
|
-
} else {
|
|
56
|
-
return this._c.push(c) - 1;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
/* @internal Destroy computed */
|
|
60
|
-
_dc(idx) {
|
|
61
|
-
this._x.push(idx);
|
|
62
|
-
}
|
|
63
|
-
/* @internal */
|
|
64
|
-
_queue(mask) {
|
|
65
|
-
if (!this.#batch) {
|
|
66
|
-
ROOT_QUEUE.push(this);
|
|
67
|
-
}
|
|
68
|
-
this.#batch |= mask;
|
|
69
|
-
if (!BATCHING) this._flush(true);
|
|
70
|
-
}
|
|
71
|
-
/* @internal */
|
|
72
|
-
_flush(force = false) {
|
|
73
|
-
if (!this.#batch || !force) return;
|
|
74
|
-
for (const item of this._c) {
|
|
75
|
-
if (!item._dirty && (item._sources & this.#batch) !== 0) {
|
|
76
|
-
item._dirty = true;
|
|
77
|
-
if (item._effect) item.val;
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
this.#batch = 0;
|
|
81
|
-
}
|
|
82
|
-
};
|
|
83
|
-
var DataSignal = class {
|
|
84
|
-
#root;
|
|
85
|
-
#val;
|
|
86
|
-
#id = 0;
|
|
87
|
-
equals = defaultEquality;
|
|
88
|
-
constructor(val) {
|
|
89
|
-
if (!ROOT) ROOT = new Root();
|
|
90
|
-
this.#root = ROOT;
|
|
91
|
-
this.#val = val;
|
|
92
|
-
this.#id |= 1 << this.#root._as();
|
|
93
|
-
}
|
|
94
|
-
get val() {
|
|
95
|
-
if (COMPUTED) {
|
|
96
|
-
COMPUTED._sources |= this.#id;
|
|
97
|
-
}
|
|
98
|
-
return this.#val;
|
|
99
|
-
}
|
|
100
|
-
set val(val) {
|
|
101
|
-
if (this.equals(val, this.#val)) return;
|
|
102
|
-
this.#val = val;
|
|
103
|
-
this.#root._queue(this.#id);
|
|
104
|
-
}
|
|
105
|
-
};
|
|
106
|
-
var Computation = class extends Scope {
|
|
107
|
-
#root;
|
|
108
|
-
#id;
|
|
109
|
-
#val;
|
|
110
|
-
#fn;
|
|
111
|
-
/* @internal */
|
|
112
|
-
_effect = false;
|
|
113
|
-
/* @internal */
|
|
114
|
-
_sources = 0;
|
|
115
|
-
/* @internal */
|
|
116
|
-
_dirty = true;
|
|
117
|
-
/* @internal destroyed */
|
|
118
|
-
_d = false;
|
|
119
|
-
constructor(compute, val, effect2) {
|
|
120
|
-
if (!ROOT) ROOT = new Root();
|
|
121
|
-
super();
|
|
122
|
-
this.#root = ROOT;
|
|
123
|
-
this.#fn = compute;
|
|
124
|
-
this.#val = val;
|
|
125
|
-
this.#id = this.#root._ac(this);
|
|
126
|
-
if (effect2) {
|
|
127
|
-
this._effect = effect2;
|
|
128
|
-
this.val;
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
get val() {
|
|
132
|
-
this.#root._flush();
|
|
133
|
-
const prevCurrent = COMPUTED;
|
|
134
|
-
if (this._dirty) {
|
|
135
|
-
super._dispose();
|
|
136
|
-
COMPUTED = this;
|
|
137
|
-
this._sources = 0;
|
|
138
|
-
this.#val = this.#fn();
|
|
139
|
-
this._dirty = false;
|
|
140
|
-
COMPUTED = prevCurrent;
|
|
141
|
-
}
|
|
142
|
-
if (prevCurrent) {
|
|
143
|
-
prevCurrent._sources |= this._sources;
|
|
144
|
-
}
|
|
145
|
-
return this.#val;
|
|
146
|
-
}
|
|
147
|
-
get peek() {
|
|
148
|
-
return this.#val;
|
|
149
|
-
}
|
|
150
|
-
getRoot() {
|
|
151
|
-
return this.#root;
|
|
152
|
-
}
|
|
153
|
-
/* @internal */
|
|
154
|
-
_dispose() {
|
|
155
|
-
if (this._d) return;
|
|
156
|
-
super._dispose();
|
|
157
|
-
this._sources = 0;
|
|
158
|
-
this._dirty = false;
|
|
159
|
-
this._d = true;
|
|
160
|
-
this.#root._dc(this.#id);
|
|
161
|
-
}
|
|
162
|
-
};
|
|
163
|
-
function defaultEquality(a, b) {
|
|
164
|
-
return a === b;
|
|
165
|
-
}
|
|
166
|
-
function getScope() {
|
|
167
|
-
return COMPUTED || ROOT;
|
|
168
|
-
}
|
|
169
|
-
function withScope(scope, fn) {
|
|
170
|
-
const prevComputed = COMPUTED, prevRoot = ROOT;
|
|
171
|
-
if (scope instanceof Computation) COMPUTED = scope;
|
|
172
|
-
ROOT = COMPUTED?.getRoot() ?? ROOT;
|
|
173
|
-
const result = fn();
|
|
174
|
-
COMPUTED = prevComputed;
|
|
175
|
-
ROOT = prevRoot;
|
|
176
|
-
return result;
|
|
177
|
-
}
|
|
178
|
-
function onDispose(fn) {
|
|
179
|
-
getScope()?._onDispose(fn);
|
|
180
|
-
}
|
|
181
|
-
function batch(fn) {
|
|
182
|
-
if (BATCHING) return fn();
|
|
183
|
-
BATCHING = true;
|
|
184
|
-
fn();
|
|
185
|
-
if (ROOT_QUEUE.length) {
|
|
186
|
-
for (const el of ROOT_QUEUE) el._flush(true);
|
|
187
|
-
ROOT_QUEUE = [];
|
|
188
|
-
}
|
|
189
|
-
BATCHING = false;
|
|
190
|
-
}
|
|
191
|
-
function root(fn, existingRoot) {
|
|
192
|
-
const prevRoot = ROOT;
|
|
193
|
-
const prevScope = getScope();
|
|
194
|
-
ROOT = existingRoot ?? new Root();
|
|
195
|
-
prevScope?._onDispose(ROOT._dispose.bind(ROOT));
|
|
196
|
-
const result = fn(ROOT._dispose.bind(ROOT));
|
|
197
|
-
ROOT = prevRoot;
|
|
198
|
-
return result;
|
|
199
|
-
}
|
|
200
|
-
function signal(value) {
|
|
201
|
-
return new DataSignal(value);
|
|
202
|
-
}
|
|
203
|
-
function computed(val) {
|
|
204
|
-
return new Computation(val);
|
|
205
|
-
}
|
|
206
|
-
function effect(fn) {
|
|
207
|
-
const sig = new Computation(fn, void 0, true);
|
|
208
|
-
return sig._dispose.bind(sig);
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
exports.Computation = Computation;
|
|
212
|
-
exports.DataSignal = DataSignal;
|
|
213
|
-
exports.Root = Root;
|
|
214
|
-
exports.Scope = Scope;
|
|
215
|
-
exports.batch = batch;
|
|
216
|
-
exports.computed = computed;
|
|
217
|
-
exports.effect = effect;
|
|
218
|
-
exports.getScope = getScope;
|
|
219
|
-
exports.onDispose = onDispose;
|
|
220
|
-
exports.root = root;
|
|
221
|
-
exports.signal = signal;
|
|
222
|
-
exports.withScope = withScope;
|
package/dist/index.d.cts
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
declare class Scope {
|
|
2
|
-
constructor();
|
|
3
|
-
}
|
|
4
|
-
declare class Root extends Scope {
|
|
5
|
-
#private;
|
|
6
|
-
}
|
|
7
|
-
declare class DataSignal<T = any> {
|
|
8
|
-
#private;
|
|
9
|
-
equals: typeof defaultEquality;
|
|
10
|
-
constructor(val?: T);
|
|
11
|
-
get val(): T;
|
|
12
|
-
set val(val: T);
|
|
13
|
-
}
|
|
14
|
-
declare class Computation<T = unknown> extends Scope {
|
|
15
|
-
#private;
|
|
16
|
-
constructor(compute?: () => T, val?: T, effect?: boolean);
|
|
17
|
-
get val(): T;
|
|
18
|
-
get peek(): T;
|
|
19
|
-
getRoot(): Root;
|
|
20
|
-
}
|
|
21
|
-
declare function defaultEquality(a: unknown, b: unknown): boolean;
|
|
22
|
-
declare function getScope(): Scope | null;
|
|
23
|
-
declare function withScope(scope: Scope, fn: () => void): void;
|
|
24
|
-
declare function onDispose(fn: () => void): void;
|
|
25
|
-
declare function batch(fn: () => void): void;
|
|
26
|
-
declare function root<T>(fn: (dispose: () => void) => T, existingRoot?: Root): T;
|
|
27
|
-
declare function signal<T>(value: T): DataSignal<T>;
|
|
28
|
-
declare function signal<T = undefined>(): DataSignal<T | undefined>;
|
|
29
|
-
declare function computed<T>(val: () => T): Computation<T>;
|
|
30
|
-
declare function effect<T = unknown>(fn: () => T): () => void;
|
|
31
|
-
|
|
32
|
-
export { Computation, DataSignal, Root, Scope, batch, computed, effect, getScope, onDispose, root, signal, withScope };
|