@dougongjs/reactive 0.8.1 → 0.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -19,9 +19,11 @@ const count = signal(0)
19
19
  const double = computed(() => count.get() * 2)
20
20
 
21
21
  batch(() => count.set(21))
22
- double.get() // 42
22
+ double.get()
23
23
  ```
24
24
 
25
+ `double.get()` 返回 `42`。
26
+
25
27
  `computed` 是惰性的,依赖动态追踪。`batch` 按订阅身份合并回调内的重复通知。三者都拒绝异步回调——同步追踪和批次边界不能跨越 `await`,与其产生静默的错误结果,不如立刻抛错。
26
28
 
27
29
  ### observe:把值的变化编译成资源的重建
@@ -56,9 +58,11 @@ const count = signal(0)
56
58
  const double = computed(() => count.get() * 2)
57
59
 
58
60
  batch(() => count.set(21))
59
- double.get() // 42
61
+ double.get()
60
62
  ```
61
63
 
64
+ `double.get()` returns `42`.
65
+
62
66
  `computed` is lazy with dynamic dependency tracking. `batch` coalesces repeated notifications per subscription identity. All three reject asynchronous callbacks — synchronous tracking and batch boundaries cannot survive an `await`, and throwing immediately beats producing a silently wrong result.
63
67
 
64
68
  ### observe: compiling value change into resource rebuild
@@ -1,9 +1,5 @@
1
- /** Fails at module load before an unavailable Promise primitive reaches a lifecycle transition. */
2
1
  export declare function assertPromiseRuntime(withResolvers: unknown): asserts withResolvers is typeof Promise.withResolvers;
3
- /** Resolves the conventional protocol key without mutating the ambient Symbol constructor. */
4
2
  export declare function resolveDisposalSymbol(native: symbol | undefined, name: "Symbol.dispose" | "Symbol.asyncDispose"): symbol;
5
- /** Canonical runtime key for Dougong's synchronous disposal protocol. */
6
3
  export declare const disposeSymbol: typeof Symbol.dispose;
7
- /** Canonical runtime key for Dougong's asynchronous disposal protocol. */
8
4
  export declare const asyncDisposeSymbol: typeof Symbol.asyncDispose;
9
5
  //# sourceMappingURL=disposal-runtime.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"disposal-runtime.d.ts","sourceRoot":"","sources":["../src/disposal-runtime.ts"],"names":[],"mappings":"AACA,mGAAmG;AACnG,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,yEAAyE;AACzE,eAAO,MAAM,aAAa,EAAE,OAAO,MAAM,CAAC,OAGhB,CAAC;AAC3B,0EAA0E;AAC1E,eAAO,MAAM,kBAAkB,EAAE,OAAO,MAAM,CAAC,YAGhB,CAAC"}
1
+ {"version":3,"file":"disposal-runtime.d.ts","sourceRoot":"","sources":["../src/disposal-runtime.ts"],"names":[],"mappings":"AACA,wBAAgB,oBAAoB,CAClC,aAAa,EAAE,OAAO,GACrB,OAAO,CAAC,aAAa,IAAI,OAAO,OAAO,CAAC,aAAa,CAIvD;AAED,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"}
package/dist/index.js CHANGED
@@ -1,40 +1,37 @@
1
- function R(t) {
2
- if (typeof t != "function")
1
+ function D(s) {
2
+ if (typeof s != "function")
3
3
  throw new Error("Unsupported JavaScript runtime: Promise.withResolvers is required");
4
4
  }
5
- function y(t, e) {
6
- return t ?? Symbol.for(e);
5
+ function b(s, e) {
6
+ return s ?? Symbol.for(e);
7
7
  }
8
- R(Promise.withResolvers);
9
- const m = y(
8
+ D(Promise.withResolvers);
9
+ const w = b(
10
10
  Symbol.dispose,
11
11
  "Symbol.dispose"
12
- ), g = y(
12
+ ), y = b(
13
13
  Symbol.asyncDispose,
14
14
  "Symbol.asyncDispose"
15
15
  );
16
- function h(t, e) {
17
- const s = O(t);
18
- if (s)
19
- throw s.catch(() => {
16
+ function a(s, e) {
17
+ const t = m(s);
18
+ if (t)
19
+ throw t.catch(() => {
20
20
  }), new TypeError(e);
21
21
  }
22
- function O(t) {
23
- if (t === null || typeof t != "object" && typeof t != "function") return;
24
- const e = t.then;
22
+ function m(s) {
23
+ if (s === null || typeof s != "object" && typeof s != "function") return;
24
+ const e = s.then;
25
25
  if (typeof e == "function")
26
26
  return Promise.resolve().then(
27
- () => new Promise((s, i) => Reflect.apply(e, t, [s, i]))
27
+ () => new Promise((t, i) => Reflect.apply(e, s, [t, i]))
28
28
  );
29
29
  }
30
- let f = 0, l = !1;
31
- const p = /* @__PURE__ */ new Set(), c = /* @__PURE__ */ new Set();
32
- let a;
33
- const d = /* @__PURE__ */ new WeakMap();
34
- function S(t) {
35
- return new z(t);
36
- }
37
- class z {
30
+ let u = 0, p = !1;
31
+ const f = /* @__PURE__ */ new Set(), o = /* @__PURE__ */ new Set();
32
+ let c;
33
+ const l = /* @__PURE__ */ new WeakMap();
34
+ class g {
38
35
  #e;
39
36
  constructor(e) {
40
37
  this.#e = e, Object.freeze(this);
@@ -43,224 +40,232 @@ class z {
43
40
  const e = this.#e;
44
41
  this.#e = void 0, e?.();
45
42
  }
46
- [m]() {
43
+ [w]() {
47
44
  this.dispose();
48
45
  }
49
46
  }
50
- function E() {
51
- if (l) return;
52
- const t = [];
53
- l = !0;
47
+ function O() {
48
+ if (p) return;
49
+ const s = [];
50
+ p = !0;
54
51
  try {
55
- for (; c.size || p.size; ) {
56
- const e = c.size ? c : p, s = e.values().next().value;
57
- e.delete(s);
58
- const i = s.listener;
52
+ for (; o.size || f.size; ) {
53
+ const e = o.size ? o : f, t = e.values().next().value;
54
+ e.delete(t);
55
+ const i = t.listener;
59
56
  if (i)
60
57
  try {
61
- h(i(), "Signal subscribers must be synchronous");
62
- } catch (n) {
63
- t.push(n);
58
+ a(i(), "Signal subscribers must be synchronous");
59
+ } catch (r) {
60
+ s.push(r);
64
61
  }
65
62
  }
66
63
  } finally {
67
- p.clear(), c.clear(), l = !1;
64
+ f.clear(), o.clear(), p = !1;
68
65
  }
69
- P(t);
66
+ R(s);
70
67
  }
71
- function k(t) {
72
- for (const e of t)
73
- (e.kind === "dependency" ? c : p).add(e);
74
- f || E();
68
+ function S(s) {
69
+ for (const e of s)
70
+ (e.kind === "dependency" ? o : f).add(e);
71
+ u || O();
75
72
  }
76
- function v(t, e, s) {
77
- C(e);
78
- const i = { kind: s, listener: e };
79
- return t.add(i), S(() => {
80
- i.listener = void 0, t.delete(i);
73
+ function d(s, e, t) {
74
+ k(e);
75
+ const i = { kind: t, listener: e };
76
+ return s.add(i), new g(() => {
77
+ i.listener = void 0, s.delete(i);
81
78
  });
82
79
  }
83
- function P(t) {
84
- if (t.length === 1) throw t[0];
85
- if (t.length > 1)
86
- throw new AggregateError(t, "Reactive subscribers failed");
80
+ function R(s) {
81
+ if (s.length === 1) throw s[0];
82
+ if (s.length > 1)
83
+ throw new AggregateError(s, "Reactive subscribers failed");
87
84
  }
88
- function T(t) {
89
- a?.(t);
85
+ function E(s) {
86
+ c?.(s);
90
87
  }
91
- function L(t) {
92
- if (typeof t != "function") throw new TypeError("batch() expects a function");
93
- f++;
88
+ function I(s) {
89
+ if (typeof s != "function") throw new TypeError("batch() expects a function");
90
+ u++;
94
91
  let e;
95
92
  try {
96
- const s = t();
97
- h(s, "Reactive batches must be synchronous"), e = { ok: !0, value: s };
98
- } catch (s) {
99
- e = { ok: !1, error: s };
93
+ const t = s();
94
+ a(t, "Reactive batches must be synchronous"), e = { ok: !0, value: t };
95
+ } catch (t) {
96
+ e = { ok: !1, error: t };
100
97
  } finally {
101
- f--;
98
+ u--;
102
99
  }
103
- if (!f)
100
+ if (!u)
104
101
  try {
105
- E();
106
- } catch (s) {
107
- e = e.ok ? { ok: !1, error: s } : { ok: !1, error: new AggregateError([e.error, s], "Reactive batch failed") };
102
+ O();
103
+ } catch (t) {
104
+ e = e.ok ? { ok: !1, error: t } : { ok: !1, error: new AggregateError([e.error, t], "Reactive batch failed") };
108
105
  }
109
106
  if (!e.ok) throw e.error;
110
107
  return e.value;
111
108
  }
112
- function q(t) {
113
- let e = t, s = 0;
114
- const i = /* @__PURE__ */ new Set(), n = {
115
- get() {
116
- return T(n), e;
117
- },
118
- set(r) {
119
- if (a)
120
- throw new TypeError("Computed signal calculations cannot write signals");
121
- Object.is(e, r) || (e = r, s++, k(i));
122
- },
123
- subscribe(r) {
124
- return v(i, r, "observer");
125
- }
126
- };
127
- return d.set(n, {
128
- get version() {
129
- return s;
130
- },
131
- refresh() {
132
- },
133
- subscribeInvalidation: (r) => v(i, r, "dependency")
134
- }), Object.freeze(n);
109
+ function L(s) {
110
+ return new z(s).view;
135
111
  }
136
- function B(t) {
137
- if (typeof t != "function") throw new TypeError("computed() expects a function");
138
- return new j(t).view;
112
+ function q(s) {
113
+ if (typeof s != "function") throw new TypeError("computed() expects a function");
114
+ return new P(s).view;
139
115
  }
140
- class j {
116
+ class z {
117
+ #e = /* @__PURE__ */ new Set();
118
+ #t;
119
+ #s = 0;
120
+ view;
121
+ constructor(e) {
122
+ this.#t = e, this.view = Object.freeze({
123
+ get: () => (E(this.view), this.#t),
124
+ set: (t) => this.#r(t),
125
+ subscribe: (t) => d(this.#e, t, "observer")
126
+ }), l.set(this.view, this);
127
+ }
128
+ get version() {
129
+ return this.#s;
130
+ }
131
+ refresh() {
132
+ }
133
+ subscribeInvalidation(e) {
134
+ return d(this.#e, e, "dependency");
135
+ }
136
+ #r(e) {
137
+ if (c)
138
+ throw new TypeError("Computed signal calculations cannot write signals");
139
+ Object.is(this.#t, e) || (this.#t = e, this.#s++, S(this.#e));
140
+ }
141
+ }
142
+ class P {
141
143
  #e;
142
- #s = /* @__PURE__ */ new Set();
143
- #i = /* @__PURE__ */ new Map();
144
- #n;
145
- #r = !1;
146
- #t = !0;
144
+ #t = /* @__PURE__ */ new Set();
145
+ #s = /* @__PURE__ */ new Map();
146
+ #r;
147
+ #n = !1;
148
+ #i = !0;
147
149
  #c = 0;
148
150
  view;
149
151
  constructor(e) {
150
152
  this.#e = e, this.view = Object.freeze({
151
153
  get: () => this.#o(),
152
- subscribe: (s) => this.#h(s)
153
- }), d.set(this.view, this);
154
+ subscribe: (t) => this.#h(t)
155
+ }), l.set(this.view, this);
154
156
  }
155
157
  get version() {
156
158
  return this.#c;
157
159
  }
158
160
  refresh() {
159
- if (this.#r) throw new TypeError("Circular computed signal");
160
- this.#r = !0;
161
+ if (this.#n) throw new TypeError("Circular computed signal");
162
+ this.#n = !0;
161
163
  try {
162
- this.#l();
164
+ this.#v();
163
165
  } finally {
164
- this.#r = !1;
166
+ this.#n = !1;
165
167
  }
166
168
  }
167
169
  #o() {
168
- return this.refresh(), T(this.view), this.#a();
170
+ return this.refresh(), E(this.view), this.#a();
169
171
  }
170
172
  #a() {
171
- const e = this.#n;
173
+ const e = this.#r;
172
174
  if (e.phase === "error") throw e.error;
173
175
  return e.value;
174
176
  }
175
177
  subscribeInvalidation(e) {
176
178
  return this.#h(e, "dependency");
177
179
  }
178
- #h(e, s = "observer") {
179
- C(e);
180
- const i = { kind: s, listener: e }, n = this.#s.size === 0;
181
- if (this.#s.add(i), n)
180
+ #h(e, t = "observer") {
181
+ k(e);
182
+ const i = { kind: t, listener: e }, r = this.#t.size === 0;
183
+ if (this.#t.add(i), r)
182
184
  try {
183
- this.refresh(), s === "observer" && this.#a(), this.#f();
184
- } catch (r) {
185
- throw this.#s.delete(i), this.#s.size || this.#u(), r;
185
+ this.refresh(), t === "observer" && this.#a(), this.#f();
186
+ } catch (n) {
187
+ throw this.#t.delete(i), this.#t.size || this.#u(), n;
186
188
  }
187
- return S(() => {
188
- i.listener = void 0, this.#s.delete(i), this.#s.size || this.#u();
189
+ return new g(() => {
190
+ i.listener = void 0, this.#t.delete(i), this.#t.size || this.#u();
189
191
  });
190
192
  }
191
- #p = () => {
192
- this.#t || (this.#t = !0, k(this.#s));
193
+ #d = () => {
194
+ this.#i || (this.#i = !0, S(this.#t));
193
195
  };
194
196
  #u() {
195
- for (const e of this.#i.values())
197
+ for (const e of this.#s.values())
196
198
  e.subscription?.dispose(), e.subscription = void 0;
197
199
  }
198
200
  #f() {
199
- for (const e of this.#i.values())
200
- e.subscription ??= e.node.subscribeInvalidation(this.#p);
201
- }
202
- #l() {
203
- const e = this.#n;
204
- if (e && !this.#t) {
205
- for (const r of this.#i.values())
206
- if (r.node.refresh(), r.version !== r.node.version) {
207
- this.#t = !0;
208
- break;
209
- }
210
- if (!this.#t) return;
211
- }
212
- const s = /* @__PURE__ */ new Set(), i = a;
213
- a = (r) => s.add(r);
214
- let n;
201
+ for (const e of this.#s.values())
202
+ e.subscription ??= e.node.subscribeInvalidation(this.#d);
203
+ }
204
+ #v() {
205
+ const e = this.#r;
206
+ if (e && !this.#i && !this.#p()) return;
207
+ this.#i = !0;
208
+ const t = /* @__PURE__ */ new Set(), i = this.#b(t);
209
+ this.#l(t), (!e || (i.phase === "value" ? e.phase !== "value" || !Object.is(e.value, i.value) : e.phase !== "error" || !Object.is(e.error, i.error))) && this.#c++, this.#r = i, this.#i = !1, this.#t.size && this.#f();
210
+ }
211
+ #p() {
212
+ for (const e of this.#s.values())
213
+ if (e.node.refresh(), e.version !== e.node.version) return !0;
214
+ return this.#i;
215
+ }
216
+ #b(e) {
217
+ const t = c;
218
+ c = (i) => e.add(i);
215
219
  try {
216
- const r = this.#e();
217
- h(r, "Computed signal calculations must be synchronous"), n = { phase: "value", value: r };
218
- } catch (r) {
219
- n = { phase: "error", error: r };
220
+ const i = this.#e();
221
+ return a(i, "Computed signal calculations must be synchronous"), { phase: "value", value: i };
222
+ } catch (i) {
223
+ return { phase: "error", error: i };
220
224
  } finally {
221
- a = i;
225
+ c = t;
222
226
  }
223
- for (const [r, o] of this.#i)
224
- s.has(r) || (o.subscription?.dispose(), this.#i.delete(r));
225
- for (const r of s) {
226
- const o = d.get(r);
227
- if (!o) continue;
228
- const b = this.#i.get(r) ?? {
229
- node: o,
230
- version: o.version,
227
+ }
228
+ #l(e) {
229
+ for (const [t, i] of this.#s)
230
+ e.has(t) || (i.subscription?.dispose(), this.#s.delete(t));
231
+ for (const t of e) {
232
+ const i = l.get(t);
233
+ if (!i) continue;
234
+ const r = this.#s.get(t) ?? {
235
+ node: i,
236
+ version: i.version,
231
237
  subscription: void 0
232
238
  };
233
- b.version = o.version, this.#i.set(r, b);
239
+ r.version = i.version, this.#s.set(t, r);
234
240
  }
235
- (!e || (n.phase === "value" ? e.phase !== "value" || !Object.is(e.value, n.value) : e.phase !== "error" || !Object.is(e.error, n.error))) && this.#c++, this.#n = n, this.#t = !1, this.#s.size && this.#f();
236
241
  }
237
242
  }
238
- function C(t) {
239
- if (typeof t != "function")
243
+ function k(s) {
244
+ if (typeof s != "function")
240
245
  throw new TypeError("Signal subscriber must be a function");
241
246
  }
242
- class x {
247
+ class j {
243
248
  #e;
249
+ #t;
244
250
  #s;
251
+ #r = { present: !1 };
252
+ #n = !1;
245
253
  #i;
246
- #n = { present: !1 };
247
- #r = !1;
248
- #t;
249
254
  #c;
250
255
  #o;
251
256
  #a;
252
- constructor(e, s, i) {
253
- this.#e = { phase: "active", binding: { owner: e, source: s, observer: i } };
257
+ constructor(e, t, i) {
258
+ this.#e = { phase: "active", binding: { owner: e, source: t, observer: i } };
254
259
  }
255
260
  start() {
256
- const { owner: e, source: s } = this.#b(), i = s.subscribe(() => this.#p());
257
- if (A(i, "Readable.subscribe()"), this.#e.phase !== "active")
258
- throw w(i), new Error("Observation is not active");
259
- this.#s = i, this.#v();
260
- const n = e.spawn((o) => this.#u(o)), r = I(n);
261
- this.#t = n, r.then(
262
- () => this.#y(n),
263
- () => this.#y(n)
261
+ const { owner: e, source: t } = this.#y(), i = t.subscribe(() => this.#d());
262
+ if (x(i, "Readable.subscribe()"), this.#e.phase !== "active")
263
+ throw v(i), new Error("Observation is not active");
264
+ this.#t = i, this.#p();
265
+ const r = e.spawn((C) => this.#u(C)), n = A(r);
266
+ this.#i = r, n.then(
267
+ () => this.#m(r),
268
+ () => this.#m(r)
264
269
  );
265
270
  }
266
271
  attachOwnerCleanup(e) {
@@ -269,151 +274,133 @@ class x {
269
274
  dispose() {
270
275
  if (this.#a) return this.#a;
271
276
  const e = Promise.withResolvers();
272
- return this.#a = e.promise, this.#c = void 0, this.#m("disposed"), this.#o?.(), this.#h().then(e.resolve, e.reject), e.promise;
277
+ return this.#a = e.promise, this.#c = void 0, this.#g("disposed"), this.#o?.(), this.#h().then(e.resolve, e.reject), e.promise;
273
278
  }
274
279
  async #h() {
275
280
  const e = [];
276
- if (await this.#w(e), await u(this.#O(), e), await u(this.#d(), e), e.length === 1) throw e[0];
281
+ if (await this.#l(e), await h(this.#O(), e), await h(this.#w(), e), e.length === 1) throw e[0];
277
282
  if (e.length > 1) throw new AggregateError(e, "Observation cleanup failed");
278
283
  }
279
- #p() {
280
- this.#e.phase === "active" && (this.#r = !0, this.#o?.());
281
- }
282
- /**
283
- * The serialization point. Sleeps until invalidated, then replaces the current
284
- * lifetime, repeating while the value keeps changing. The inner loop collapses
285
- * a burst of changes into one replacement per settled value rather than one per
286
- * notification.
287
- *
288
- * A failure here stops the observation and reports it. There is no caller to
289
- * return to — this runs in a spawned task — so continuing would mean silently
290
- * observing a value with a lifetime that failed to establish.
291
- */
284
+ #d() {
285
+ this.#e.phase === "active" && (this.#n = !0, this.#o?.());
286
+ }
287
+ // Stop on drain failure because continuing would observe a value whose resource ownership never established.
292
288
  async #u(e) {
293
289
  try {
294
290
  for (; this.#e.phase === "active" && !e.aborted; )
295
- for (await this.#f(e); this.#e.phase === "active" && !e.aborted && this.#r; )
296
- this.#r = !1, await this.#l(e);
297
- } catch (s) {
298
- if (this.#e.phase !== "active") throw s;
299
- await this.#E(s);
291
+ for (await this.#f(e); this.#e.phase === "active" && !e.aborted && this.#n; )
292
+ this.#n = !1, await this.#v(e);
293
+ } catch (t) {
294
+ if (this.#e.phase !== "active") throw t;
295
+ await this.#E(t);
300
296
  }
301
297
  }
302
298
  #f(e) {
303
- return this.#r || this.#e.phase !== "active" || e.aborted ? Promise.resolve() : new Promise((s) => {
299
+ return this.#n || this.#e.phase !== "active" || e.aborted ? Promise.resolve() : new Promise((t) => {
304
300
  const i = () => {
305
- e.removeEventListener("abort", i), this.#o === i && (this.#o = void 0), s();
301
+ e.removeEventListener("abort", i), this.#o === i && (this.#o = void 0), t();
306
302
  };
307
- this.#o = i, e.addEventListener("abort", i, { once: !0 }), (this.#r || this.#e.phase !== "active" || e.aborted) && i();
303
+ this.#o = i, e.addEventListener("abort", i, { once: !0 }), (this.#n || this.#e.phase !== "active" || e.aborted) && i();
308
304
  });
309
305
  }
310
- /**
311
- * Dispose the old lifetime, then create the new one, then call the observer.
312
- * Strictly in that order: overlapping them would run two owners of whatever the
313
- * observer set up, and a failed observer would leave the previous lifetime half
314
- * disposed.
315
- *
316
- * `#observed` is cleared while no lifetime is current, so an interrupted
317
- * replacement cannot leave a value marked as observed when nothing is
318
- * observing it.
319
- */
320
- async #l(e) {
306
+ // Clear the observed marker during replacement so interruption cannot leave a value without its owner.
307
+ async #v(e) {
321
308
  if (this.#e.phase !== "active" || e.aborted) return;
322
- const s = this.#e.binding.source.get();
323
- if (this.#n.present && Object.is(s, this.#n.value)) return;
324
- const i = this.#d();
325
- i && await i.dispose(), this.#n = { present: !1 }, !(this.#e.phase !== "active" || e.aborted) && this.#v();
309
+ const t = this.#e.binding.source.get();
310
+ if (this.#r.present && Object.is(t, this.#r.value)) return;
311
+ const i = this.#w();
312
+ i && await i.dispose(), this.#r = { present: !1 }, !(this.#e.phase !== "active" || e.aborted) && this.#p();
326
313
  }
327
- #v() {
328
- const { owner: e, source: s } = this.#b(), i = e.lifetime("observation");
329
- D(i, "ObservationOwner.lifetime()"), this.#i = i;
330
- const n = s.get();
331
- this.#g(n, i), this.#e.phase === "active" && (this.#n = { present: !0, value: n });
332
- }
333
- #g(e, s) {
334
- const i = this.#b().observer(e, s);
335
- h(i, "Observers must be synchronous; use owner.spawn() for async work");
336
- }
337
- async #w(e) {
338
- const s = this.#s;
339
- if (this.#s = void 0, !!s)
314
+ #p() {
315
+ const { owner: e, source: t } = this.#y(), i = e.lifetime("observation");
316
+ T(i, "ObservationOwner.lifetime()"), this.#s = i;
317
+ const r = t.get();
318
+ this.#b(r, i), this.#e.phase === "active" && (this.#r = { present: !0, value: r });
319
+ }
320
+ #b(e, t) {
321
+ const i = this.#y().observer(e, t);
322
+ a(i, "Observers must be synchronous; use owner.spawn() for async work");
323
+ }
324
+ async #l(e) {
325
+ const t = this.#t;
326
+ if (this.#t = void 0, !!t)
340
327
  try {
341
- w(s);
328
+ v(t);
342
329
  } catch (i) {
343
330
  e.push(i);
344
331
  }
345
332
  }
346
- #d() {
347
- const e = this.#i;
348
- return this.#i = void 0, e;
333
+ #w() {
334
+ const e = this.#s;
335
+ return this.#s = void 0, e;
349
336
  }
350
337
  #O() {
351
- const e = this.#t;
352
- return this.#t = void 0, e;
338
+ const e = this.#i;
339
+ return this.#i = void 0, e;
353
340
  }
354
341
  #S() {
355
342
  const e = this.#c;
356
343
  return this.#c = void 0, e;
357
344
  }
358
- #y(e) {
359
- this.#t === e && (this.#t = void 0);
345
+ #m(e) {
346
+ this.#i === e && (this.#i = void 0);
360
347
  }
361
348
  async #E(e) {
362
- const s = [e];
363
- throw this.#m("stopped"), this.#o?.(), await this.#w(s), await u(this.#d(), s), this.#t = void 0, await u(this.#S(), s), s.length === 1 ? s[0] : new AggregateError(s, "Observation stopped after a replacement failed");
349
+ const t = [e];
350
+ throw this.#g("stopped"), this.#o?.(), await this.#l(t), await h(this.#w(), t), this.#i = void 0, await h(this.#S(), t), t.length === 1 ? t[0] : new AggregateError(t, "Observation stopped after a replacement failed");
364
351
  }
365
- #b() {
352
+ #y() {
366
353
  const e = this.#e;
367
354
  if (e.phase !== "active") throw new Error("Observation is not active");
368
355
  return e.binding;
369
356
  }
370
- #m(e) {
371
- this.#e = { phase: e }, this.#n = { present: !1 }, this.#r = !1;
357
+ #g(e) {
358
+ this.#e = { phase: e }, this.#r = { present: !1 }, this.#n = !1;
372
359
  }
373
360
  }
374
- function F(t, e, s) {
361
+ function B(s, e, t) {
375
362
  if (!e || typeof e.get != "function" || typeof e.subscribe != "function")
376
363
  throw new TypeError("observe() expects a readable source");
377
- if (typeof s != "function") throw new TypeError("Observer must be a function");
378
- if (!t || typeof t.cleanup != "function" || typeof t.lifetime != "function" || typeof t.spawn != "function")
364
+ if (typeof t != "function") throw new TypeError("Observer must be a function");
365
+ if (!s || typeof s.cleanup != "function" || typeof s.lifetime != "function" || typeof s.spawn != "function")
379
366
  throw new TypeError("observe() expects an observation owner");
380
- const i = new x(t, e, s), n = t.cleanup(() => i.dispose());
381
- D(n, "ObservationOwner.cleanup()"), i.attachOwnerCleanup(n);
367
+ const i = new j(s, e, t), r = s.cleanup(() => i.dispose());
368
+ T(r, "ObservationOwner.cleanup()"), i.attachOwnerCleanup(r);
382
369
  try {
383
- return i.start(), n;
384
- } catch (r) {
370
+ return i.start(), r;
371
+ } catch (n) {
385
372
  throw i.dispose().catch(() => {
386
- }), r;
373
+ }), n;
387
374
  }
388
375
  }
389
- function A(t, e) {
390
- if (!t || typeof t.dispose != "function" || typeof t[m] != "function")
376
+ function x(s, e) {
377
+ if (!s || typeof s.dispose != "function" || typeof s[w] != "function")
391
378
  throw new TypeError(`${e} must return a Disposable`);
392
379
  }
393
- function D(t, e) {
394
- if (!t || typeof t.dispose != "function" || typeof t[g] != "function")
380
+ function T(s, e) {
381
+ if (!s || typeof s.dispose != "function" || typeof s[y] != "function")
395
382
  throw new TypeError(`${e} must return an AsyncDisposable`);
396
383
  }
397
- function I(t) {
398
- const e = t && typeof t.dispose == "function" && typeof t[g] == "function" ? O(t.result) : void 0;
384
+ function A(s) {
385
+ const e = s && typeof s.dispose == "function" && typeof s[y] == "function" ? m(s.result) : void 0;
399
386
  if (!e)
400
387
  throw new TypeError("ObservationOwner.spawn() must return an ObservationTask");
401
388
  return e;
402
389
  }
403
- function w(t) {
404
- h(t.dispose(), "Readable subscriptions must dispose synchronously");
390
+ function v(s) {
391
+ a(s.dispose(), "Readable subscriptions must dispose synchronously");
405
392
  }
406
- async function u(t, e) {
407
- if (t)
393
+ async function h(s, e) {
394
+ if (s)
408
395
  try {
409
- await t.dispose();
410
- } catch (s) {
411
- e.push(s);
396
+ await s.dispose();
397
+ } catch (t) {
398
+ e.push(t);
412
399
  }
413
400
  }
414
401
  export {
415
- L as batch,
416
- B as computed,
417
- F as observe,
418
- q as signal
402
+ I as batch,
403
+ q as computed,
404
+ B as observe,
405
+ L as signal
419
406
  };
package/dist/observe.d.ts CHANGED
@@ -8,9 +8,5 @@ export interface ObservationOwner<Child extends AsyncDisposable = AsyncDisposabl
8
8
  readonly spawn: <T>(task: (signal: AbortSignal) => T | PromiseLike<T>) => ObservationTask<T>;
9
9
  }
10
10
  export type Observer<T, Child extends AsyncDisposable> = (value: T, lifetime: Child) => void;
11
- /**
12
- * Lifetime-aware synchronization built only from the public source and
13
- * Lifetime protocols. It is a reactive-layer combinator, not a Core hook.
14
- */
15
11
  export declare function observe<T, Child extends AsyncDisposable>(owner: ObservationOwner<Child>, source: Readable<T>, observer: Observer<T, Child>): AsyncDisposable;
16
12
  //# sourceMappingURL=observe.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"observe.d.ts","sourceRoot":"","sources":["../src/observe.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,eAAe,EAEpB,KAAK,QAAQ,EACd,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;AAqO7F;;;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"}
1
+ {"version":3,"file":"observe.d.ts","sourceRoot":"","sources":["../src/observe.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,eAAe,EAEpB,KAAK,QAAQ,EACd,MAAM,YAAY,CAAC;AAKpB,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;AAgN7F,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,CAyBjB"}
@@ -7,7 +7,6 @@ export interface AsyncDisposable {
7
7
  dispose(): Promise<void>;
8
8
  [Symbol.asyncDispose](): Promise<void>;
9
9
  }
10
- /** Structural observable protocol shared by signals, ContributionViews and diagnostics. */
11
10
  export interface Readable<T> {
12
11
  get(): T;
13
12
  subscribe(listener: () => void): Disposable;
@@ -1 +1 @@
1
- {"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,oBAAoB,EACpB,qBAAqB,EACrB,kBAAkB,EAClB,aAAa,GACd,MAAM,oBAAoB,CAAC;AAE5B,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,2FAA2F;AAC3F,MAAM,WAAW,QAAQ,CAAC,CAAC;IACzB,GAAG,IAAI,CAAC,CAAC;IACT,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,UAAU,CAAC;CAC7C"}
1
+ {"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,oBAAoB,EACpB,qBAAqB,EACrB,kBAAkB,EAClB,aAAa,GACd,MAAM,oBAAoB,CAAC;AAE5B,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,WAAW,QAAQ,CAAC,CAAC;IACzB,GAAG,IAAI,CAAC,CAAC;IACT,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,UAAU,CAAC;CAC7C"}
package/dist/signals.d.ts CHANGED
@@ -1,29 +1,12 @@
1
1
  import { Readable } from './protocol';
2
2
  declare const dougongSignal: unique symbol;
3
- /** A reactive node created by Dougong and safe to compose with computed(). */
4
3
  export interface ReadonlySignal<T> extends Readable<T> {
5
4
  readonly [dougongSignal]: true;
6
5
  }
7
6
  export interface Signal<T> extends ReadonlySignal<T> {
8
7
  readonly set: (value: T) => void;
9
8
  }
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
9
  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
10
  export declare function signal<T>(initialValue: T): Signal<T>;
28
11
  export declare function computed<T>(calculate: () => T): ReadonlySignal<T>;
29
12
  export {};
@@ -1 +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
+ {"version":3,"file":"signals.d.ts","sourceRoot":"","sources":["../src/signals.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkC,KAAK,QAAQ,EAAE,MAAM,YAAY,CAAC;AAG3E,OAAO,CAAC,MAAM,aAAa,EAAE,OAAO,MAAM,CAAC;AAE3C,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;AA0GD,wBAAgB,KAAK,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,GAAG,CAAC,CA4B7C;AAED,wBAAgB,MAAM,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAEpD;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":"AASA,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CASvE;AAKD,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,SAAS,CAO5E"}
1
+ {"version":3,"file":"sync-result.d.ts","sourceRoot":"","sources":["../src/sync-result.ts"],"names":[],"mappings":"AACA,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAOvE;AAGD,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,SAAS,CAO5E"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dougongjs/reactive",
3
- "version": "0.8.1",
3
+ "version": "0.8.2",
4
4
  "type": "module",
5
5
  "engines": {
6
6
  "node": ">=22"