@dougongjs/reactive 0.4.0 → 0.5.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/dist/index.d.ts +2 -13
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +282 -267
- package/dist/observe.d.ts +3 -8
- package/dist/observe.d.ts.map +1 -1
- package/dist/protocol.d.ts.map +1 -1
- package/dist/signals.d.ts +30 -0
- package/dist/signals.d.ts.map +1 -0
- package/dist/sync-result.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,15 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
export { batch, computed, signal, type ReadonlySignal, type Signal } from './signals';
|
|
2
2
|
export type { AsyncDisposable, Disposable, Readable } from './protocol';
|
|
3
|
-
|
|
4
|
-
/** A reactive node created by Dougong and safe to compose with computed(). */
|
|
5
|
-
export interface ReadonlySignal<T> extends Readable<T> {
|
|
6
|
-
readonly [dougongSignal]: true;
|
|
7
|
-
}
|
|
8
|
-
export interface Signal<T> extends ReadonlySignal<T> {
|
|
9
|
-
readonly set: (value: T) => void;
|
|
10
|
-
}
|
|
11
|
-
export { observe, type ObservationLifetime, type ObservationOwner, type ObservationTask, type Observer, } from './observe';
|
|
12
|
-
export declare function batch<T>(callback: () => T): T;
|
|
13
|
-
export declare function signal<T>(initialValue: T): Signal<T>;
|
|
14
|
-
export declare function computed<T>(calculate: () => T): ReadonlySignal<T>;
|
|
3
|
+
export { observe, type ObservationOwner, type ObservationTask, type Observer } from './observe';
|
|
15
4
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,cAAc,EAAE,KAAK,MAAM,EAAE,MAAM,WAAW,CAAC;AACtF,YAAY,EAAE,eAAe,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACxE,OAAO,EAAE,OAAO,EAAE,KAAK,gBAAgB,EAAE,KAAK,eAAe,EAAE,KAAK,QAAQ,EAAE,MAAM,WAAW,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,203 +1,34 @@
|
|
|
1
|
-
function
|
|
1
|
+
function D(t) {
|
|
2
2
|
if (typeof t != "function")
|
|
3
3
|
throw new Error("Unsupported JavaScript runtime: Promise.withResolvers is required");
|
|
4
4
|
}
|
|
5
|
-
function
|
|
5
|
+
function w(t, e) {
|
|
6
6
|
return t ?? Symbol.for(e);
|
|
7
7
|
}
|
|
8
|
-
|
|
9
|
-
const
|
|
8
|
+
D(Promise.withResolvers);
|
|
9
|
+
const y = w(
|
|
10
10
|
Symbol.dispose,
|
|
11
11
|
"Symbol.dispose"
|
|
12
|
-
),
|
|
12
|
+
), m = w(
|
|
13
13
|
Symbol.asyncDispose,
|
|
14
14
|
"Symbol.asyncDispose"
|
|
15
15
|
);
|
|
16
16
|
function p(t, e) {
|
|
17
|
-
if (
|
|
17
|
+
if (g(t))
|
|
18
18
|
throw Promise.resolve(t).catch(() => {
|
|
19
19
|
}), new TypeError(e);
|
|
20
20
|
}
|
|
21
|
-
function
|
|
21
|
+
function g(t) {
|
|
22
22
|
return t === null || typeof t != "object" && typeof t != "function" ? !1 : typeof t.then == "function";
|
|
23
23
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
#t;
|
|
31
|
-
#c;
|
|
32
|
-
#o;
|
|
33
|
-
#a;
|
|
34
|
-
constructor(e, s, i) {
|
|
35
|
-
this.#e = { phase: "active", binding: { owner: e, source: s, observer: i } };
|
|
36
|
-
}
|
|
37
|
-
start() {
|
|
38
|
-
const { owner: e, source: s } = this.#w(), i = s.get(), r = e.lifetime("observation");
|
|
39
|
-
b(r), this.#i = r;
|
|
40
|
-
const n = s.subscribe(() => this.#u());
|
|
41
|
-
C(n, "Readable.subscribe()"), this.#s = n, this.#d(i, r), this.#n = { present: !0, value: i };
|
|
42
|
-
const o = e.spawn((c) => this.#h(c));
|
|
43
|
-
R(o), this.#t = o, o.result.then(
|
|
44
|
-
() => this.#v(o),
|
|
45
|
-
() => this.#v(o)
|
|
46
|
-
);
|
|
47
|
-
}
|
|
48
|
-
attachOwnerCleanup(e) {
|
|
49
|
-
this.#c = e;
|
|
50
|
-
}
|
|
51
|
-
dispose() {
|
|
52
|
-
if (this.#a) return this.#a;
|
|
53
|
-
const e = Promise.withResolvers();
|
|
54
|
-
return this.#a = e.promise, this.#c = void 0, this.#y("disposed"), this.#o?.(), this.#p().then(e.resolve, e.reject), e.promise;
|
|
55
|
-
}
|
|
56
|
-
async #p() {
|
|
57
|
-
const e = [];
|
|
58
|
-
if (await a(this.#b(), e), await a(this.#O(), e), await a(this.#f(), e), e.length === 1) throw e[0];
|
|
59
|
-
if (e.length > 1) throw new AggregateError(e, "Observation cleanup failed");
|
|
60
|
-
}
|
|
61
|
-
#u() {
|
|
62
|
-
this.#e.phase === "active" && (this.#r = !0, this.#o?.());
|
|
63
|
-
}
|
|
64
|
-
async #h(e) {
|
|
65
|
-
try {
|
|
66
|
-
for (; this.#e.phase === "active" && !e.aborted; )
|
|
67
|
-
for (await this.#m(e); this.#e.phase === "active" && !e.aborted && this.#r; )
|
|
68
|
-
this.#r = !1, await this.#g();
|
|
69
|
-
} catch (s) {
|
|
70
|
-
if (this.#e.phase !== "active") throw s;
|
|
71
|
-
await this.#l([s], "Observation stopped after a replacement failed");
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
#m(e) {
|
|
75
|
-
return this.#r || this.#e.phase !== "active" || e.aborted ? Promise.resolve() : new Promise((s) => {
|
|
76
|
-
const i = () => {
|
|
77
|
-
e.removeEventListener("abort", i), this.#o === i && (this.#o = void 0), s();
|
|
78
|
-
};
|
|
79
|
-
this.#o = i, e.addEventListener("abort", i, { once: !0 }), (this.#r || this.#e.phase !== "active" || e.aborted) && i();
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
async #g() {
|
|
83
|
-
if (this.#e.phase !== "active") return;
|
|
84
|
-
const e = this.#e.binding.source.get();
|
|
85
|
-
if (this.#n.present && Object.is(e, this.#n.value)) return;
|
|
86
|
-
const s = this.#f();
|
|
87
|
-
if (s)
|
|
88
|
-
try {
|
|
89
|
-
await s.dispose();
|
|
90
|
-
} catch (r) {
|
|
91
|
-
if (this.#e.phase !== "active") throw r;
|
|
92
|
-
return this.#l(
|
|
93
|
-
[r],
|
|
94
|
-
"Observation stopped because the previous lifetime could not be disposed"
|
|
95
|
-
);
|
|
96
|
-
}
|
|
97
|
-
if (this.#n = { present: !1 }, this.#e.phase !== "active") return;
|
|
98
|
-
const i = this.#e.binding.owner.lifetime("observation");
|
|
99
|
-
b(i), this.#i = i;
|
|
100
|
-
try {
|
|
101
|
-
this.#d(e, i);
|
|
102
|
-
} catch (r) {
|
|
103
|
-
const n = this.#f();
|
|
104
|
-
try {
|
|
105
|
-
n && await n.dispose();
|
|
106
|
-
} catch (o) {
|
|
107
|
-
if (this.#e.phase !== "active")
|
|
108
|
-
throw new AggregateError(
|
|
109
|
-
[r, o],
|
|
110
|
-
"Observation callback failed and its resources could not be cleaned up"
|
|
111
|
-
);
|
|
112
|
-
return this.#l(
|
|
113
|
-
[r, o],
|
|
114
|
-
"Observation callback failed and its resources could not be cleaned up"
|
|
115
|
-
);
|
|
116
|
-
}
|
|
117
|
-
throw r;
|
|
118
|
-
}
|
|
119
|
-
this.#e.phase === "active" && (this.#n = { present: !0, value: e });
|
|
120
|
-
}
|
|
121
|
-
#d(e, s) {
|
|
122
|
-
const i = this.#w().observer(e, s);
|
|
123
|
-
p(i, "Observers must be synchronous; use lifetime.spawn() for async work");
|
|
124
|
-
}
|
|
125
|
-
#b() {
|
|
126
|
-
const e = this.#s;
|
|
127
|
-
return this.#s = void 0, e;
|
|
128
|
-
}
|
|
129
|
-
#f() {
|
|
130
|
-
const e = this.#i;
|
|
131
|
-
return this.#i = void 0, e;
|
|
132
|
-
}
|
|
133
|
-
#O() {
|
|
134
|
-
const e = this.#t;
|
|
135
|
-
return this.#t = void 0, e;
|
|
136
|
-
}
|
|
137
|
-
#k() {
|
|
138
|
-
const e = this.#c;
|
|
139
|
-
return this.#c = void 0, e;
|
|
140
|
-
}
|
|
141
|
-
#v(e) {
|
|
142
|
-
this.#t === e && (this.#t = void 0);
|
|
143
|
-
}
|
|
144
|
-
async #l(e, s) {
|
|
145
|
-
throw this.#y("stopped"), this.#o?.(), await a(this.#b(), e), await a(this.#f(), e), this.#t = void 0, await a(this.#k(), e), e.length === 1 ? e[0] : new AggregateError(e, s);
|
|
146
|
-
}
|
|
147
|
-
#w() {
|
|
148
|
-
const e = this.#e;
|
|
149
|
-
if (e.phase !== "active") throw new Error("Observation is not active");
|
|
150
|
-
return e.binding;
|
|
151
|
-
}
|
|
152
|
-
#y(e) {
|
|
153
|
-
this.#e = { phase: e }, this.#n = { present: !1 }, this.#r = !1;
|
|
154
|
-
}
|
|
24
|
+
let u = 0, l = !1;
|
|
25
|
+
const f = /* @__PURE__ */ new Set(), c = /* @__PURE__ */ new Set();
|
|
26
|
+
let h;
|
|
27
|
+
const d = /* @__PURE__ */ new WeakMap();
|
|
28
|
+
function O(t) {
|
|
29
|
+
return new R(t);
|
|
155
30
|
}
|
|
156
|
-
|
|
157
|
-
if (!e || typeof e.get != "function" || typeof e.subscribe != "function")
|
|
158
|
-
throw new TypeError("observe() expects a readable source");
|
|
159
|
-
if (typeof s != "function") throw new TypeError("Observer must be a function");
|
|
160
|
-
if (!t || typeof t.cleanup != "function" || typeof t.lifetime != "function" || typeof t.spawn != "function")
|
|
161
|
-
throw new TypeError("observe() expects an observation owner");
|
|
162
|
-
const i = new D(t, e, s), r = t.cleanup(() => i.dispose());
|
|
163
|
-
z(r, "ObservationOwner.cleanup()"), i.attachOwnerCleanup(r);
|
|
164
|
-
try {
|
|
165
|
-
return i.start(), r;
|
|
166
|
-
} catch (n) {
|
|
167
|
-
throw i.dispose().catch(() => {
|
|
168
|
-
}), n;
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
function C(t, e) {
|
|
172
|
-
if (!t || typeof t.dispose != "function" || typeof t[w] != "function")
|
|
173
|
-
throw new TypeError(`${e} must return a Disposable`);
|
|
174
|
-
}
|
|
175
|
-
function z(t, e) {
|
|
176
|
-
if (!t || typeof t.dispose != "function" || typeof t[d] != "function")
|
|
177
|
-
throw new TypeError(`${e} must return an AsyncDisposable`);
|
|
178
|
-
}
|
|
179
|
-
function b(t) {
|
|
180
|
-
if (!t || typeof t.dispose != "function" || typeof t[d] != "function" || typeof t.cleanup != "function" || typeof t.lifetime != "function" || typeof t.spawn != "function")
|
|
181
|
-
throw new TypeError("ObservationOwner.lifetime() must return an ObservationLifetime");
|
|
182
|
-
}
|
|
183
|
-
function R(t) {
|
|
184
|
-
if (!t || typeof t.dispose != "function" || typeof t[d] != "function" || !y(t.result))
|
|
185
|
-
throw new TypeError("ObservationOwner.spawn() must return an ObservationTask");
|
|
186
|
-
}
|
|
187
|
-
async function a(t, e) {
|
|
188
|
-
if (t)
|
|
189
|
-
try {
|
|
190
|
-
await t.dispose();
|
|
191
|
-
} catch (s) {
|
|
192
|
-
e.push(s);
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
let u = 0, h, f;
|
|
196
|
-
const l = /* @__PURE__ */ new WeakMap();
|
|
197
|
-
function m(t) {
|
|
198
|
-
return new P(t);
|
|
199
|
-
}
|
|
200
|
-
class P {
|
|
31
|
+
class R {
|
|
201
32
|
#e;
|
|
202
33
|
constructor(e) {
|
|
203
34
|
this.#e = e, Object.freeze(this);
|
|
@@ -206,47 +37,52 @@ class P {
|
|
|
206
37
|
const e = this.#e;
|
|
207
38
|
this.#e = void 0, e?.();
|
|
208
39
|
}
|
|
209
|
-
[
|
|
40
|
+
[y]() {
|
|
210
41
|
this.dispose();
|
|
211
42
|
}
|
|
212
43
|
}
|
|
213
|
-
function
|
|
44
|
+
function S() {
|
|
45
|
+
if (l) return;
|
|
214
46
|
const t = [];
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
47
|
+
l = !0;
|
|
48
|
+
try {
|
|
49
|
+
for (; c.size || f.size; ) {
|
|
50
|
+
const e = c.size ? c : f, s = e.values().next().value;
|
|
51
|
+
e.delete(s);
|
|
52
|
+
const i = s.listener;
|
|
53
|
+
if (i)
|
|
54
|
+
try {
|
|
55
|
+
p(i(), "Signal subscribers must be synchronous");
|
|
56
|
+
} catch (n) {
|
|
57
|
+
t.push(n);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
} finally {
|
|
61
|
+
f.clear(), c.clear(), l = !1;
|
|
218
62
|
}
|
|
219
|
-
|
|
63
|
+
z(t);
|
|
220
64
|
}
|
|
221
|
-
function
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
return;
|
|
226
|
-
}
|
|
227
|
-
const e = [];
|
|
228
|
-
O(t, e), k(e);
|
|
65
|
+
function k(t) {
|
|
66
|
+
for (const e of t)
|
|
67
|
+
(e.kind === "dependency" ? c : f).add(e);
|
|
68
|
+
u || S();
|
|
229
69
|
}
|
|
230
|
-
function
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
} catch (r) {
|
|
237
|
-
e.push(r);
|
|
238
|
-
}
|
|
239
|
-
}
|
|
70
|
+
function v(t, e, s) {
|
|
71
|
+
T(e);
|
|
72
|
+
const i = { kind: s, listener: e };
|
|
73
|
+
return t.add(i), O(() => {
|
|
74
|
+
i.listener = void 0, t.delete(i);
|
|
75
|
+
});
|
|
240
76
|
}
|
|
241
|
-
function
|
|
77
|
+
function z(t) {
|
|
242
78
|
if (t.length === 1) throw t[0];
|
|
243
79
|
if (t.length > 1)
|
|
244
80
|
throw new AggregateError(t, "Reactive subscribers failed");
|
|
245
81
|
}
|
|
246
82
|
function E(t) {
|
|
247
|
-
|
|
83
|
+
h?.(t);
|
|
248
84
|
}
|
|
249
|
-
function
|
|
85
|
+
function I(t) {
|
|
250
86
|
if (typeof t != "function") throw new TypeError("batch() expects a function");
|
|
251
87
|
u++;
|
|
252
88
|
let e;
|
|
@@ -260,127 +96,306 @@ function x(t) {
|
|
|
260
96
|
}
|
|
261
97
|
if (!u)
|
|
262
98
|
try {
|
|
263
|
-
|
|
99
|
+
S();
|
|
264
100
|
} catch (s) {
|
|
265
101
|
e = e.ok ? { ok: !1, error: s } : { ok: !1, error: new AggregateError([e.error, s], "Reactive batch failed") };
|
|
266
102
|
}
|
|
267
103
|
if (!e.ok) throw e.error;
|
|
268
104
|
return e.value;
|
|
269
105
|
}
|
|
270
|
-
function
|
|
106
|
+
function L(t) {
|
|
271
107
|
let e = t, s = 0;
|
|
272
|
-
const i = /* @__PURE__ */ new Set(),
|
|
108
|
+
const i = /* @__PURE__ */ new Set(), n = {
|
|
273
109
|
get() {
|
|
274
|
-
return E(
|
|
110
|
+
return E(n), e;
|
|
275
111
|
},
|
|
276
|
-
set(
|
|
277
|
-
|
|
112
|
+
set(r) {
|
|
113
|
+
if (h)
|
|
114
|
+
throw new TypeError("Computed signal calculations cannot write signals");
|
|
115
|
+
Object.is(e, r) || (e = r, s++, k(i));
|
|
278
116
|
},
|
|
279
|
-
subscribe(
|
|
280
|
-
|
|
281
|
-
const o = { listener: n };
|
|
282
|
-
return i.add(o), m(() => {
|
|
283
|
-
o.listener = void 0, i.delete(o);
|
|
284
|
-
});
|
|
117
|
+
subscribe(r) {
|
|
118
|
+
return v(i, r, "observer");
|
|
285
119
|
}
|
|
286
120
|
};
|
|
287
|
-
return
|
|
121
|
+
return d.set(n, {
|
|
288
122
|
get version() {
|
|
289
123
|
return s;
|
|
290
124
|
},
|
|
291
125
|
refresh() {
|
|
292
|
-
}
|
|
293
|
-
|
|
126
|
+
},
|
|
127
|
+
subscribeInvalidation: (r) => v(i, r, "dependency")
|
|
128
|
+
}), Object.freeze(n);
|
|
294
129
|
}
|
|
295
|
-
function
|
|
130
|
+
function q(t) {
|
|
296
131
|
if (typeof t != "function") throw new TypeError("computed() expects a function");
|
|
297
|
-
return new
|
|
132
|
+
return new j(t).view;
|
|
298
133
|
}
|
|
299
|
-
class
|
|
134
|
+
class j {
|
|
300
135
|
#e;
|
|
301
136
|
#s = /* @__PURE__ */ new Set();
|
|
302
137
|
#i = /* @__PURE__ */ new Map();
|
|
303
|
-
#n
|
|
138
|
+
#n;
|
|
304
139
|
#r = !1;
|
|
305
140
|
#t = !0;
|
|
306
|
-
#
|
|
141
|
+
#a = 0;
|
|
307
142
|
view;
|
|
308
143
|
constructor(e) {
|
|
309
144
|
this.#e = e, this.view = Object.freeze({
|
|
310
145
|
get: () => this.#o(),
|
|
311
|
-
subscribe: (s) => this.#
|
|
312
|
-
}),
|
|
146
|
+
subscribe: (s) => this.#h(s)
|
|
147
|
+
}), d.set(this.view, this);
|
|
313
148
|
}
|
|
314
149
|
get version() {
|
|
315
|
-
return this.#
|
|
150
|
+
return this.#a;
|
|
316
151
|
}
|
|
317
152
|
refresh() {
|
|
318
|
-
this.#
|
|
153
|
+
if (this.#r) throw new TypeError("Circular computed signal");
|
|
154
|
+
this.#r = !0;
|
|
155
|
+
try {
|
|
156
|
+
this.#l();
|
|
157
|
+
} finally {
|
|
158
|
+
this.#r = !1;
|
|
159
|
+
}
|
|
319
160
|
}
|
|
320
161
|
#o() {
|
|
321
|
-
return E(this.view), this.#
|
|
162
|
+
return this.refresh(), E(this.view), this.#c();
|
|
322
163
|
}
|
|
323
|
-
#
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
164
|
+
#c() {
|
|
165
|
+
const e = this.#n;
|
|
166
|
+
if (e.phase === "error") throw e.error;
|
|
167
|
+
return e.value;
|
|
168
|
+
}
|
|
169
|
+
subscribeInvalidation(e) {
|
|
170
|
+
return this.#h(e, "dependency");
|
|
171
|
+
}
|
|
172
|
+
#h(e, s = "observer") {
|
|
173
|
+
T(e);
|
|
174
|
+
const i = { kind: s, listener: e }, n = this.#s.size === 0;
|
|
175
|
+
if (this.#s.add(i), n)
|
|
327
176
|
try {
|
|
328
|
-
this
|
|
177
|
+
this.refresh(), s === "observer" && this.#c(), this.#f();
|
|
329
178
|
} catch (r) {
|
|
330
|
-
throw this.#s.delete(
|
|
179
|
+
throw this.#s.delete(i), this.#s.size || this.#u(), r;
|
|
331
180
|
}
|
|
332
|
-
return
|
|
333
|
-
|
|
181
|
+
return O(() => {
|
|
182
|
+
i.listener = void 0, this.#s.delete(i), this.#s.size || this.#u();
|
|
334
183
|
});
|
|
335
184
|
}
|
|
336
185
|
#p = () => {
|
|
337
|
-
this.#t || (this.#t = !0,
|
|
186
|
+
this.#t || (this.#t = !0, k(this.#s));
|
|
338
187
|
};
|
|
339
188
|
#u() {
|
|
340
189
|
for (const e of this.#i.values())
|
|
341
190
|
e.subscription?.dispose(), e.subscription = void 0;
|
|
342
191
|
}
|
|
343
|
-
#
|
|
192
|
+
#f() {
|
|
193
|
+
for (const e of this.#i.values())
|
|
194
|
+
e.subscription ??= e.node.subscribeInvalidation(this.#p);
|
|
195
|
+
}
|
|
196
|
+
#l() {
|
|
344
197
|
const e = this.#n;
|
|
345
|
-
if (e
|
|
346
|
-
for (const
|
|
347
|
-
if (
|
|
198
|
+
if (e && !this.#t) {
|
|
199
|
+
for (const r of this.#i.values())
|
|
200
|
+
if (r.node.refresh(), r.version !== r.node.version) {
|
|
348
201
|
this.#t = !0;
|
|
349
202
|
break;
|
|
350
203
|
}
|
|
351
|
-
if (!this.#t) return
|
|
204
|
+
if (!this.#t) return;
|
|
352
205
|
}
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
let r;
|
|
206
|
+
const s = /* @__PURE__ */ new Set(), i = h;
|
|
207
|
+
h = (r) => s.add(r);
|
|
208
|
+
let n;
|
|
357
209
|
try {
|
|
358
|
-
r = this.#e()
|
|
210
|
+
const r = this.#e();
|
|
211
|
+
p(r, "Computed signal calculations must be synchronous"), n = { phase: "value", value: r };
|
|
212
|
+
} catch (r) {
|
|
213
|
+
n = { phase: "error", error: r };
|
|
359
214
|
} finally {
|
|
360
|
-
|
|
215
|
+
h = i;
|
|
361
216
|
}
|
|
362
|
-
for (const [
|
|
363
|
-
s.has(
|
|
364
|
-
for (const
|
|
365
|
-
const o =
|
|
217
|
+
for (const [r, o] of this.#i)
|
|
218
|
+
s.has(r) || (o.subscription?.dispose(), this.#i.delete(r));
|
|
219
|
+
for (const r of s) {
|
|
220
|
+
const o = d.get(r);
|
|
366
221
|
if (!o) continue;
|
|
367
|
-
const
|
|
222
|
+
const b = this.#i.get(r) ?? {
|
|
368
223
|
node: o,
|
|
369
224
|
version: o.version,
|
|
370
225
|
subscription: void 0
|
|
371
226
|
};
|
|
372
|
-
|
|
227
|
+
b.version = o.version, this.#i.set(r, b);
|
|
373
228
|
}
|
|
374
|
-
|
|
229
|
+
(!e || (n.phase === "value" ? e.phase !== "value" || !Object.is(e.value, n.value) : e.phase !== "error" || !Object.is(e.error, n.error))) && this.#a++, this.#n = n, this.#t = !1, this.#s.size && this.#f();
|
|
375
230
|
}
|
|
376
231
|
}
|
|
377
|
-
function
|
|
232
|
+
function T(t) {
|
|
378
233
|
if (typeof t != "function")
|
|
379
234
|
throw new TypeError("Signal subscriber must be a function");
|
|
380
235
|
}
|
|
236
|
+
class P {
|
|
237
|
+
#e;
|
|
238
|
+
#s;
|
|
239
|
+
#i;
|
|
240
|
+
#n = { present: !1 };
|
|
241
|
+
#r = !1;
|
|
242
|
+
#t;
|
|
243
|
+
#a;
|
|
244
|
+
#o;
|
|
245
|
+
#c;
|
|
246
|
+
constructor(e, s, i) {
|
|
247
|
+
this.#e = { phase: "active", binding: { owner: e, source: s, observer: i } };
|
|
248
|
+
}
|
|
249
|
+
start() {
|
|
250
|
+
const { owner: e, source: s } = this.#b(), i = s.subscribe(() => this.#p());
|
|
251
|
+
x(i, "Readable.subscribe()"), this.#s = i, this.#v();
|
|
252
|
+
const n = e.spawn((r) => this.#u(r));
|
|
253
|
+
A(n), this.#t = n, n.result.then(
|
|
254
|
+
() => this.#y(n),
|
|
255
|
+
() => this.#y(n)
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
attachOwnerCleanup(e) {
|
|
259
|
+
this.#a = e;
|
|
260
|
+
}
|
|
261
|
+
dispose() {
|
|
262
|
+
if (this.#c) return this.#c;
|
|
263
|
+
const e = Promise.withResolvers();
|
|
264
|
+
return this.#c = e.promise, this.#a = void 0, this.#m("disposed"), this.#o?.(), this.#h().then(e.resolve, e.reject), e.promise;
|
|
265
|
+
}
|
|
266
|
+
async #h() {
|
|
267
|
+
const e = [];
|
|
268
|
+
if (await a(this.#w(), e), await a(this.#O(), e), await a(this.#d(), e), e.length === 1) throw e[0];
|
|
269
|
+
if (e.length > 1) throw new AggregateError(e, "Observation cleanup failed");
|
|
270
|
+
}
|
|
271
|
+
#p() {
|
|
272
|
+
this.#e.phase === "active" && (this.#r = !0, this.#o?.());
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* The serialization point. Sleeps until invalidated, then replaces the current
|
|
276
|
+
* lifetime, repeating while the value keeps changing. The inner loop collapses
|
|
277
|
+
* a burst of changes into one replacement per settled value rather than one per
|
|
278
|
+
* notification.
|
|
279
|
+
*
|
|
280
|
+
* A failure here stops the observation and reports it. There is no caller to
|
|
281
|
+
* return to — this runs in a spawned task — so continuing would mean silently
|
|
282
|
+
* observing a value with a lifetime that failed to establish.
|
|
283
|
+
*/
|
|
284
|
+
async #u(e) {
|
|
285
|
+
try {
|
|
286
|
+
for (; this.#e.phase === "active" && !e.aborted; )
|
|
287
|
+
for (await this.#f(e); this.#e.phase === "active" && !e.aborted && this.#r; )
|
|
288
|
+
this.#r = !1, await this.#l();
|
|
289
|
+
} catch (s) {
|
|
290
|
+
if (this.#e.phase !== "active") throw s;
|
|
291
|
+
await this.#k(s);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
#f(e) {
|
|
295
|
+
return this.#r || this.#e.phase !== "active" || e.aborted ? Promise.resolve() : new Promise((s) => {
|
|
296
|
+
const i = () => {
|
|
297
|
+
e.removeEventListener("abort", i), this.#o === i && (this.#o = void 0), s();
|
|
298
|
+
};
|
|
299
|
+
this.#o = i, e.addEventListener("abort", i, { once: !0 }), (this.#r || this.#e.phase !== "active" || e.aborted) && i();
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* Dispose the old lifetime, then create the new one, then call the observer.
|
|
304
|
+
* Strictly in that order: overlapping them would run two owners of whatever the
|
|
305
|
+
* observer set up, and a failed observer would leave the previous lifetime half
|
|
306
|
+
* disposed.
|
|
307
|
+
*
|
|
308
|
+
* `#observed` is cleared while no lifetime is current, so an interrupted
|
|
309
|
+
* replacement cannot leave a value marked as observed when nothing is
|
|
310
|
+
* observing it.
|
|
311
|
+
*/
|
|
312
|
+
async #l() {
|
|
313
|
+
if (this.#e.phase !== "active") return;
|
|
314
|
+
const e = this.#e.binding.source.get();
|
|
315
|
+
if (this.#n.present && Object.is(e, this.#n.value)) return;
|
|
316
|
+
const s = this.#d();
|
|
317
|
+
s && await s.dispose(), this.#n = { present: !1 }, this.#e.phase === "active" && this.#v();
|
|
318
|
+
}
|
|
319
|
+
#v() {
|
|
320
|
+
const { owner: e, source: s } = this.#b(), i = e.lifetime("observation");
|
|
321
|
+
C(i, "ObservationOwner.lifetime()"), this.#i = i;
|
|
322
|
+
const n = s.get();
|
|
323
|
+
this.#g(n, i), this.#e.phase === "active" && (this.#n = { present: !0, value: n });
|
|
324
|
+
}
|
|
325
|
+
#g(e, s) {
|
|
326
|
+
const i = this.#b().observer(e, s);
|
|
327
|
+
p(i, "Observers must be synchronous; use owner.spawn() for async work");
|
|
328
|
+
}
|
|
329
|
+
#w() {
|
|
330
|
+
const e = this.#s;
|
|
331
|
+
return this.#s = void 0, e;
|
|
332
|
+
}
|
|
333
|
+
#d() {
|
|
334
|
+
const e = this.#i;
|
|
335
|
+
return this.#i = void 0, e;
|
|
336
|
+
}
|
|
337
|
+
#O() {
|
|
338
|
+
const e = this.#t;
|
|
339
|
+
return this.#t = void 0, e;
|
|
340
|
+
}
|
|
341
|
+
#S() {
|
|
342
|
+
const e = this.#a;
|
|
343
|
+
return this.#a = void 0, e;
|
|
344
|
+
}
|
|
345
|
+
#y(e) {
|
|
346
|
+
this.#t === e && (this.#t = void 0);
|
|
347
|
+
}
|
|
348
|
+
async #k(e) {
|
|
349
|
+
const s = [e];
|
|
350
|
+
throw this.#m("stopped"), this.#o?.(), await a(this.#w(), s), await a(this.#d(), s), this.#t = void 0, await a(this.#S(), s), s.length === 1 ? s[0] : new AggregateError(s, "Observation stopped after a replacement failed");
|
|
351
|
+
}
|
|
352
|
+
#b() {
|
|
353
|
+
const e = this.#e;
|
|
354
|
+
if (e.phase !== "active") throw new Error("Observation is not active");
|
|
355
|
+
return e.binding;
|
|
356
|
+
}
|
|
357
|
+
#m(e) {
|
|
358
|
+
this.#e = { phase: e }, this.#n = { present: !1 }, this.#r = !1;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
function B(t, e, s) {
|
|
362
|
+
if (!e || typeof e.get != "function" || typeof e.subscribe != "function")
|
|
363
|
+
throw new TypeError("observe() expects a readable source");
|
|
364
|
+
if (typeof s != "function") throw new TypeError("Observer must be a function");
|
|
365
|
+
if (!t || typeof t.cleanup != "function" || typeof t.lifetime != "function" || typeof t.spawn != "function")
|
|
366
|
+
throw new TypeError("observe() expects an observation owner");
|
|
367
|
+
const i = new P(t, e, s), n = t.cleanup(() => i.dispose());
|
|
368
|
+
C(n, "ObservationOwner.cleanup()"), i.attachOwnerCleanup(n);
|
|
369
|
+
try {
|
|
370
|
+
return i.start(), n;
|
|
371
|
+
} catch (r) {
|
|
372
|
+
throw i.dispose().catch(() => {
|
|
373
|
+
}), r;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
function x(t, e) {
|
|
377
|
+
if (!t || typeof t.dispose != "function" || typeof t[y] != "function")
|
|
378
|
+
throw new TypeError(`${e} must return a Disposable`);
|
|
379
|
+
}
|
|
380
|
+
function C(t, e) {
|
|
381
|
+
if (!t || typeof t.dispose != "function" || typeof t[m] != "function")
|
|
382
|
+
throw new TypeError(`${e} must return an AsyncDisposable`);
|
|
383
|
+
}
|
|
384
|
+
function A(t) {
|
|
385
|
+
if (!t || typeof t.dispose != "function" || typeof t[m] != "function" || !g(t.result))
|
|
386
|
+
throw new TypeError("ObservationOwner.spawn() must return an ObservationTask");
|
|
387
|
+
}
|
|
388
|
+
async function a(t, e) {
|
|
389
|
+
if (t)
|
|
390
|
+
try {
|
|
391
|
+
await t.dispose();
|
|
392
|
+
} catch (s) {
|
|
393
|
+
e.push(s);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
381
396
|
export {
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
397
|
+
I as batch,
|
|
398
|
+
q as computed,
|
|
399
|
+
B as observe,
|
|
400
|
+
L as signal
|
|
386
401
|
};
|
package/dist/observe.d.ts
CHANGED
|
@@ -2,20 +2,15 @@ import { AsyncDisposable, Readable } from './protocol';
|
|
|
2
2
|
export interface ObservationTask<T = void> extends AsyncDisposable {
|
|
3
3
|
readonly result: Promise<T>;
|
|
4
4
|
}
|
|
5
|
-
export interface
|
|
6
|
-
readonly cleanup: (dispose: () => unknown) => AsyncDisposable;
|
|
7
|
-
readonly lifetime: (label: string) => ObservationLifetime;
|
|
8
|
-
readonly spawn: <T>(task: (signal: AbortSignal) => T | PromiseLike<T>) => ObservationTask<T>;
|
|
9
|
-
}
|
|
10
|
-
export interface ObservationOwner<Child extends ObservationLifetime = ObservationLifetime> {
|
|
5
|
+
export interface ObservationOwner<Child extends AsyncDisposable = AsyncDisposable> {
|
|
11
6
|
readonly cleanup: (dispose: () => unknown) => AsyncDisposable;
|
|
12
7
|
readonly lifetime: (label: string) => Child;
|
|
13
8
|
readonly spawn: <T>(task: (signal: AbortSignal) => T | PromiseLike<T>) => ObservationTask<T>;
|
|
14
9
|
}
|
|
15
|
-
export type Observer<T, Child extends
|
|
10
|
+
export type Observer<T, Child extends AsyncDisposable> = (value: T, lifetime: Child) => void;
|
|
16
11
|
/**
|
|
17
12
|
* Lifetime-aware synchronization built only from the public source and
|
|
18
13
|
* Lifetime protocols. It is a reactive-layer combinator, not a Core hook.
|
|
19
14
|
*/
|
|
20
|
-
export declare function observe<T, Child extends
|
|
15
|
+
export declare function observe<T, Child extends AsyncDisposable>(owner: ObservationOwner<Child>, source: Readable<T>, observer: Observer<T, Child>): AsyncDisposable;
|
|
21
16
|
//# sourceMappingURL=observe.d.ts.map
|
package/dist/observe.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"observe.d.ts","sourceRoot":"","sources":["../src/observe.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,eAAe,EAEpB,KAAK,QAAQ,EAEd,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"observe.d.ts","sourceRoot":"","sources":["../src/observe.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,eAAe,EAEpB,KAAK,QAAQ,EAEd,MAAM,YAAY,CAAC;AAwBpB,MAAM,WAAW,eAAe,CAAC,CAAC,GAAG,IAAI,CAAE,SAAQ,eAAe;IAChE,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;CAC7B;AAED,MAAM,WAAW,gBAAgB,CAAC,KAAK,SAAS,eAAe,GAAG,eAAe;IAC/E,QAAQ,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE,MAAM,OAAO,KAAK,eAAe,CAAC;IAC9D,QAAQ,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,KAAK,CAAC;IAC5C,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,KAAK,eAAe,CAAC,CAAC,CAAC,CAAC;CAC9F;AAED,MAAM,MAAM,QAAQ,CAAC,CAAC,EAAE,KAAK,SAAS,eAAe,IAAI,CAAC,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,KAAK,KAAK,IAAI,CAAC;AA2N7F;;;GAGG;AACH,wBAAgB,OAAO,CAAC,CAAC,EAAE,KAAK,SAAS,eAAe,EACtD,KAAK,EAAE,gBAAgB,CAAC,KAAK,CAAC,EAC9B,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,EACnB,QAAQ,EAAE,QAAQ,CAAC,CAAC,EAAE,KAAK,CAAC,GAC3B,eAAe,CA2BjB"}
|
package/dist/protocol.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAMA,2FAA2F;AAC3F,wBAAgB,oBAAoB,CAClC,aAAa,EAAE,OAAO,GACrB,OAAO,CAAC,aAAa,IAAI,OAAO,OAAO,CAAC,aAAa,CAIvD;AAED,8FAA8F;AAC9F,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,IAAI,EAAE,gBAAgB,GAAG,qBAAqB,UAG/C;AAID,eAAO,MAAM,aAAa,EAAE,OAAO,MAAM,CAAC,OAGhB,CAAC;AAC3B,eAAO,MAAM,kBAAkB,EAAE,OAAO,MAAM,CAAC,YAGhB,CAAC;AAEhC,MAAM,WAAW,UAAU;IACzB,OAAO,IAAI,IAAI,CAAC;IAChB,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC;CAC1B;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACzB,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxC;AAED,MAAM,MAAM,QAAQ,GAAG,UAAU,GAAG,eAAe,CAAC;AAEpD,2FAA2F;AAC3F,MAAM,WAAW,QAAQ,CAAC,CAAC;IACzB,GAAG,IAAI,CAAC,CAAC;IACT,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,UAAU,CAAC;CAC7C"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Readable } from './protocol';
|
|
2
|
+
declare const dougongSignal: unique symbol;
|
|
3
|
+
/** A reactive node created by Dougong and safe to compose with computed(). */
|
|
4
|
+
export interface ReadonlySignal<T> extends Readable<T> {
|
|
5
|
+
readonly [dougongSignal]: true;
|
|
6
|
+
}
|
|
7
|
+
export interface Signal<T> extends ReadonlySignal<T> {
|
|
8
|
+
readonly set: (value: T) => void;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Defers notification until the outermost batch returns, so a group of writes is
|
|
12
|
+
* observed once. Nested calls join the outer batch.
|
|
13
|
+
*
|
|
14
|
+
* Notifications are queued by subscription, not by callback, so the same
|
|
15
|
+
* function subscribed to two signals is still called twice — deduplicating by
|
|
16
|
+
* callback would silently merge two independent subscriptions.
|
|
17
|
+
*
|
|
18
|
+
* If both the body and the flush fail, both errors are reported. A subscriber
|
|
19
|
+
* failing during flush must not hide why the batch itself failed.
|
|
20
|
+
*/
|
|
21
|
+
export declare function batch<T>(callback: () => T): T;
|
|
22
|
+
/**
|
|
23
|
+
* A value with subscribers. Writes are compared with `Object.is`, so setting the
|
|
24
|
+
* same value notifies nobody — which is what makes `computed` chains stable
|
|
25
|
+
* under idempotent updates.
|
|
26
|
+
*/
|
|
27
|
+
export declare function signal<T>(initialValue: T): Signal<T>;
|
|
28
|
+
export declare function computed<T>(calculate: () => T): ReadonlySignal<T>;
|
|
29
|
+
export {};
|
|
30
|
+
//# sourceMappingURL=signals.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signals.d.ts","sourceRoot":"","sources":["../src/signals.ts"],"names":[],"mappings":"AAiBA,OAAO,EAAkC,KAAK,QAAQ,EAAE,MAAM,YAAY,CAAC;AAG3E,OAAO,CAAC,MAAM,aAAa,EAAE,OAAO,MAAM,CAAC;AAE3C,8EAA8E;AAC9E,MAAM,WAAW,cAAc,CAAC,CAAC,CAAE,SAAQ,QAAQ,CAAC,CAAC,CAAC;IACpD,QAAQ,CAAC,CAAC,aAAa,CAAC,EAAE,IAAI,CAAC;CAChC;AAED,MAAM,WAAW,MAAM,CAAC,CAAC,CAAE,SAAQ,cAAc,CAAC,CAAC,CAAC;IAClD,QAAQ,CAAC,GAAG,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC;CAClC;AA4HD;;;;;;;;;;GAUG;AACH,wBAAgB,KAAK,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,GAAG,CAAC,CA4B7C;AAED;;;;GAIG;AACH,wBAAgB,MAAM,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAoCpD;AAED,wBAAgB,QAAQ,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,CAAC,GAAG,cAAc,CAAC,CAAC,CAAC,CAGjE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sync-result.d.ts","sourceRoot":"","sources":["../src/sync-result.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"sync-result.d.ts","sourceRoot":"","sources":["../src/sync-result.ts"],"names":[],"mappings":"AAaA,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAQvE;AAID,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,WAAW,CAAC,OAAO,CAAC,CAGxE"}
|