@dougongjs/reactive 0.0.3 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -22,12 +22,12 @@ batch(() => count.set(21))
22
22
  double.get() // 42
23
23
  ```
24
24
 
25
- `computed` 是惰性的,依赖动态追踪。`batch` 合并回调内的全部通知。三者都拒绝异步回调——同步追踪和批次边界不能跨越 `await`,与其产生静默的错误结果,不如立刻抛错。
25
+ `computed` 是惰性的,依赖动态追踪。`batch` 按订阅身份合并回调内的重复通知。三者都拒绝异步回调——同步追踪和批次边界不能跨越 `await`,与其产生静默的错误结果,不如立刻抛错。
26
26
 
27
27
  ### observe:把值的变化编译成资源的重建
28
28
 
29
29
  ```ts
30
- observe(source, owner, (value, lifetime) => {
30
+ observe(owner, source, (value, lifetime) => {
31
31
  const socket = new WebSocket(value)
32
32
  lifetime.cleanup(() => socket.close())
33
33
  })
@@ -37,7 +37,7 @@ observe(source, owner, (value, lifetime) => {
37
37
 
38
38
  `observe()` 与 `@dougongjs/core` **互不依赖**——它是一个作用在结构化 `ObservationOwner`(提供 `cleanup` / `lifetime` / `spawn`)上的自由函数,所以能驱动 Core 的 Lifetime,而 Core 不需要知道它存在。插件的 `ctx` 恰好满足这个形状。
39
39
 
40
- `source` 只需满足结构化的 `Readable<T>`(`get()` + `subscribe()`)——Signal、Core 的 `ExtensionView`、诊断视图,甚至你自己写的对象都可以。
40
+ `source` 只需满足结构化的 `Readable<T>`(`get()` + `subscribe()`)——Signal、Core 的 `ContributionView`、诊断视图,甚至你自己写的对象都可以。
41
41
 
42
42
  ---
43
43
 
@@ -59,12 +59,12 @@ batch(() => count.set(21))
59
59
  double.get() // 42
60
60
  ```
61
61
 
62
- `computed` is lazy with dynamic dependency tracking. `batch` coalesces every notification inside the callback. All three reject asynchronous callbacks — synchronous tracking and batch boundaries cannot survive an `await`, and throwing immediately beats producing a silently wrong result.
62
+ `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
63
 
64
64
  ### observe: compiling value change into resource rebuild
65
65
 
66
66
  ```ts
67
- observe(source, owner, (value, lifetime) => {
67
+ observe(owner, source, (value, lifetime) => {
68
68
  const socket = new WebSocket(value)
69
69
  lifetime.cleanup(() => socket.close())
70
70
  })
@@ -74,14 +74,14 @@ On each change to `source`: release the previous `lifetime`, then build a new on
74
74
 
75
75
  `observe()` and `@dougongjs/core` are **mutually independent** — it is a free function over a structural `ObservationOwner` (anything providing `cleanup` / `lifetime` / `spawn`), so it can drive Core Lifetimes without Core knowing it exists. A plugin's `ctx` happens to satisfy that shape.
76
76
 
77
- `source` need only satisfy the structural `Readable<T>` (`get()` + `subscribe()`) — a signal, Core's `ExtensionView`, a diagnostics view, or your own object.
77
+ `source` need only satisfy the structural `Readable<T>` (`get()` + `subscribe()`) — a signal, Core's `ContributionView`, a diagnostics view, or your own object.
78
78
 
79
79
  ---
80
80
 
81
81
  - 文档站 / Documentation: https://tangerg.github.io/dougong/
82
82
  - 仓库 / Repository: https://github.com/Tangerg/dougong
83
83
 
84
- > 早期开发阶段(0.0.x),当前不承诺向后兼容。需要 Node.js ≥ 22 或等价的 ES2024 宿主。
85
- > Early development (0.0.x); no backward-compatibility promises yet. Requires Node.js ≥ 22 or an equivalent ES2024 host.
84
+ > 早期开发阶段(0.0.x),当前不承诺向后兼容。需要 Node.js ≥ 22 或等价的 ES2024 运行时。
85
+ > Early development (0.0.x); no backward-compatibility promises yet. Requires Node.js ≥ 22 or an equivalent ES2024 runtime.
86
86
 
87
87
  MIT
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Readable } from './protocol';
2
- export type { Disposable, Readable } from './protocol';
2
+ export type { AsyncDisposable, Disposable, Readable } from './protocol';
3
3
  declare const dougongSignal: unique symbol;
4
4
  /** A reactive node created by Dougong and safe to compose with computed(). */
5
5
  export interface ReadonlySignal<T> extends Readable<T> {
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAc,QAAQ,EAAE,MAAM,YAAY,CAAC;AAGvD,YAAY,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEvD,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,GAAG,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC;CACrB;AAED,OAAO,EACL,OAAO,EACP,KAAK,mBAAmB,EACxB,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,KAAK,QAAQ,GACd,MAAM,WAAW,CAAC;AAyFnB,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,CAkCpD;AAED,wBAAgB,QAAQ,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,CAAC,GAAG,cAAc,CAAC,CAAC,CAAC,CAGjE"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAc,QAAQ,EAAE,MAAM,YAAY,CAAC;AAGvD,YAAY,EAAE,eAAe,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAExE,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,GAAG,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC;CACrB;AAED,OAAO,EACL,OAAO,EACP,KAAK,mBAAmB,EACxB,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,KAAK,QAAQ,GACd,MAAM,WAAW,CAAC;AA8FnB,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,CAsCpD;AAED,wBAAgB,QAAQ,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,CAAC,GAAG,cAAc,CAAC,CAAC,CAAC,CAGjE"}
package/dist/index.js CHANGED
@@ -1,218 +1,237 @@
1
- function d(e, t) {
2
- if (b(e))
3
- throw Promise.resolve(e).catch(() => {
4
- }), new TypeError(t);
1
+ function d(t, e) {
2
+ if (b(t))
3
+ throw Promise.resolve(t).catch(() => {
4
+ }), new TypeError(e);
5
5
  }
6
- function b(e) {
7
- return e === null || typeof e != "object" && typeof e != "function" ? !1 : typeof e.then == "function";
6
+ function b(t) {
7
+ return t === null || typeof t != "object" && typeof t != "function" ? !1 : typeof t.then == "function";
8
8
  }
9
- class E {
9
+ class k {
10
+ #e;
11
+ #s;
10
12
  #i;
13
+ #n = { present: !1 };
14
+ #r = !1;
11
15
  #t;
12
- #r;
13
- #h;
14
- #n;
15
- #e = { present: !1 };
16
- #o = !1;
17
- #s = "active";
18
- #a;
16
+ #o;
19
17
  #c;
20
- #u;
21
- constructor(t, s, i) {
22
- this.#i = t, this.#t = s, this.#r = i;
18
+ constructor(e, s, i) {
19
+ this.#e = { phase: "active", binding: { owner: e, source: s, observer: i } };
23
20
  }
24
21
  start() {
25
- const t = this.#t.get(), s = this.#i.lifetime("observation");
26
- l(s), this.#n = s;
27
- const i = this.#t.subscribe(() => this.#w());
28
- v(i, "Readable.subscribe()"), this.#h = i, this.#l(t, s), this.#e = { present: !0, value: t };
29
- const n = this.#i.spawn((r) => this.#y(r));
30
- T(n), this.#a = n, n.result.then(
31
- () => this.#v(n),
32
- () => this.#v(n)
22
+ const { owner: e, source: s } = this.#w(), i = s.get(), r = e.lifetime("observation");
23
+ l(r), this.#i = r;
24
+ const n = s.subscribe(() => this.#p());
25
+ E(n, "Readable.subscribe()"), this.#s = n, this.#l(i, r), this.#n = { present: !0, value: i };
26
+ const o = e.spawn((c) => this.#h(c));
27
+ S(o), this.#t = o, o.result.then(
28
+ () => this.#v(o),
29
+ () => this.#v(o)
33
30
  );
34
31
  }
35
32
  dispose() {
36
- if (this.#u) return this.#u;
37
- const t = Promise.withResolvers();
38
- return this.#u = t.promise, this.#s = "disposed", this.#c?.(), this.#f().then(t.resolve, t.reject), t.promise;
33
+ if (this.#c) return this.#c;
34
+ const e = Promise.withResolvers();
35
+ return this.#c = e.promise, this.#y("disposed"), this.#o?.(), this.#f().then(e.resolve, e.reject), e.promise;
39
36
  }
40
37
  async #f() {
41
- const t = [];
42
- if (await c(this.#b(), t), await c(this.#O(), t), await c(this.#p(), t), t.length === 1) throw t[0];
43
- if (t.length > 1) throw new AggregateError(t, "Observation cleanup failed");
38
+ const e = [];
39
+ if (await a(this.#b(), e), await a(this.#m(), e), await a(this.#u(), e), e.length === 1) throw e[0];
40
+ if (e.length > 1) throw new AggregateError(e, "Observation cleanup failed");
44
41
  }
45
- #w() {
46
- this.#s === "active" && (this.#o = !0, this.#c?.());
42
+ #p() {
43
+ this.#e.phase === "active" && (this.#r = !0, this.#o?.());
47
44
  }
48
- async #y(t) {
45
+ async #h(e) {
49
46
  try {
50
- for (; this.#s === "active" && !t.aborted; )
51
- for (await this.#m(t); this.#s === "active" && !t.aborted && this.#o; )
52
- this.#o = !1, await this.#g();
47
+ for (; this.#e.phase === "active" && !e.aborted; )
48
+ for (await this.#a(e); this.#e.phase === "active" && !e.aborted && this.#r; )
49
+ this.#r = !1, await this.#g();
53
50
  } catch (s) {
54
- if (this.#s === "stopped") throw s;
51
+ if (this.#e.phase !== "active") throw s;
55
52
  await this.#d([s], "Observation stopped after a replacement failed");
56
53
  }
57
54
  }
58
- #m(t) {
59
- return this.#o || this.#s !== "active" || t.aborted ? Promise.resolve() : new Promise((s) => {
55
+ #a(e) {
56
+ return this.#r || this.#e.phase !== "active" || e.aborted ? Promise.resolve() : new Promise((s) => {
60
57
  const i = () => {
61
- t.removeEventListener("abort", i), this.#c === i && (this.#c = void 0), s();
58
+ e.removeEventListener("abort", i), this.#o === i && (this.#o = void 0), s();
62
59
  };
63
- this.#c = i, t.addEventListener("abort", i, { once: !0 }), (this.#o || this.#s !== "active" || t.aborted) && i();
60
+ this.#o = i, e.addEventListener("abort", i, { once: !0 }), (this.#r || this.#e.phase !== "active" || e.aborted) && i();
64
61
  });
65
62
  }
66
63
  async #g() {
67
- if (this.#s !== "active") return;
68
- const t = this.#t.get();
69
- if (this.#e.present && Object.is(t, this.#e.value)) return;
70
- const s = this.#p();
64
+ if (this.#e.phase !== "active") return;
65
+ const e = this.#e.binding.source.get();
66
+ if (this.#n.present && Object.is(e, this.#n.value)) return;
67
+ const s = this.#u();
71
68
  if (s)
72
69
  try {
73
70
  await s.dispose();
74
- } catch (n) {
71
+ } catch (r) {
72
+ if (this.#e.phase !== "active") throw r;
75
73
  return this.#d(
76
- [n],
74
+ [r],
77
75
  "Observation stopped because the previous lifetime could not be disposed"
78
76
  );
79
77
  }
80
- if (this.#e = { present: !1 }, this.#s !== "active") return;
81
- const i = this.#i.lifetime("observation");
82
- l(i);
78
+ if (this.#n = { present: !1 }, this.#e.phase !== "active") return;
79
+ const i = this.#e.binding.owner.lifetime("observation");
80
+ l(i), this.#i = i;
83
81
  try {
84
- this.#l(t, i);
85
- } catch (n) {
82
+ this.#l(e, i);
83
+ } catch (r) {
84
+ const n = this.#u();
86
85
  try {
87
- await i.dispose();
88
- } catch (r) {
86
+ n && await n.dispose();
87
+ } catch (o) {
88
+ if (this.#e.phase !== "active")
89
+ throw new AggregateError(
90
+ [r, o],
91
+ "Observation callback failed and its resources could not be cleaned up"
92
+ );
89
93
  return this.#d(
90
- [n, r],
94
+ [r, o],
91
95
  "Observation callback failed and its resources could not be cleaned up"
92
96
  );
93
97
  }
94
- throw n;
98
+ throw r;
95
99
  }
96
- this.#n = i, this.#e = { present: !0, value: t };
100
+ this.#e.phase === "active" && (this.#n = { present: !0, value: e });
97
101
  }
98
- #l(t, s) {
99
- const i = this.#r(t, s);
102
+ #l(e, s) {
103
+ const i = this.#w().observer(e, s);
100
104
  d(i, "Observers must be synchronous; use lifetime.spawn() for async work");
101
105
  }
102
106
  #b() {
103
- const t = this.#h;
104
- return this.#h = void 0, t;
107
+ const e = this.#s;
108
+ return this.#s = void 0, e;
105
109
  }
106
- #p() {
107
- const t = this.#n;
108
- return this.#n = void 0, t;
110
+ #u() {
111
+ const e = this.#i;
112
+ return this.#i = void 0, e;
109
113
  }
110
- #O() {
111
- const t = this.#a;
112
- return this.#a = void 0, t;
114
+ #m() {
115
+ const e = this.#t;
116
+ return this.#t = void 0, e;
113
117
  }
114
- #v(t) {
115
- this.#a === t && (this.#a = void 0);
118
+ #v(e) {
119
+ this.#t === e && (this.#t = void 0);
116
120
  }
117
- async #d(t, s) {
118
- throw this.#s = "stopped", this.#c?.(), await c(this.#b(), t), await c(this.#p(), t), t.length === 1 ? t[0] : new AggregateError(t, s);
121
+ async #d(e, s) {
122
+ throw this.#y("stopped"), this.#o?.(), await a(this.#b(), e), await a(this.#u(), e), e.length === 1 ? e[0] : new AggregateError(e, s);
123
+ }
124
+ #w() {
125
+ const e = this.#e;
126
+ if (e.phase !== "active") throw new Error("Observation is not active");
127
+ return e.binding;
128
+ }
129
+ #y(e) {
130
+ this.#e = { phase: e }, this.#n = { present: !1 }, this.#r = !1;
119
131
  }
120
132
  }
121
- function j(e, t, s) {
122
- if (!t || typeof t.get != "function" || typeof t.subscribe != "function")
133
+ function A(t, e, s) {
134
+ if (!e || typeof e.get != "function" || typeof e.subscribe != "function")
123
135
  throw new TypeError("observe() expects a readable source");
124
136
  if (typeof s != "function") throw new TypeError("Observer must be a function");
125
- if (!e || typeof e.cleanup != "function" || typeof e.lifetime != "function" || typeof e.spawn != "function")
137
+ if (!t || typeof t.cleanup != "function" || typeof t.lifetime != "function" || typeof t.spawn != "function")
126
138
  throw new TypeError("observe() expects an observation owner");
127
- const i = new E(e, t, s), n = e.cleanup(() => i.dispose());
128
- v(n, "ObservationOwner.cleanup()");
139
+ const i = new k(t, e, s), r = t.cleanup(() => i.dispose());
140
+ T(r, "ObservationOwner.cleanup()");
129
141
  try {
130
- return i.start(), n;
131
- } catch (r) {
142
+ return i.start(), r;
143
+ } catch (n) {
132
144
  throw i.dispose().catch(() => {
133
- }), r;
145
+ }), n;
134
146
  }
135
147
  }
136
- function v(e, t) {
137
- if (!e || typeof e.dispose != "function")
138
- throw new TypeError(`${t} must return a Disposable`);
148
+ function E(t, e) {
149
+ if (!t || typeof t.dispose != "function" || typeof t[Symbol.dispose] != "function")
150
+ throw new TypeError(`${e} must return a Disposable`);
139
151
  }
140
- function l(e) {
141
- if (!e || typeof e.dispose != "function" || typeof e.cleanup != "function" || typeof e.lifetime != "function" || typeof e.spawn != "function")
152
+ function T(t, e) {
153
+ if (!t || typeof t.dispose != "function" || typeof t[Symbol.asyncDispose] != "function")
154
+ throw new TypeError(`${e} must return an AsyncDisposable`);
155
+ }
156
+ function l(t) {
157
+ if (!t || typeof t.dispose != "function" || typeof t[Symbol.asyncDispose] != "function" || typeof t.cleanup != "function" || typeof t.lifetime != "function" || typeof t.spawn != "function")
142
158
  throw new TypeError("ObservationOwner.lifetime() must return an ObservationLifetime");
143
159
  }
144
- function T(e) {
145
- if (!e || typeof e.dispose != "function" || !b(e.result))
160
+ function S(t) {
161
+ if (!t || typeof t.dispose != "function" || typeof t[Symbol.asyncDispose] != "function" || !b(t.result))
146
162
  throw new TypeError("ObservationOwner.spawn() must return an ObservationTask");
147
163
  }
148
- async function c(e, t) {
149
- if (e)
164
+ async function a(t, e) {
165
+ if (t)
150
166
  try {
151
- await e.dispose();
167
+ await t.dispose();
152
168
  } catch (s) {
153
- t.push(s);
169
+ e.push(s);
154
170
  }
155
171
  }
156
- let u = 0, a, f;
172
+ let u = 0, h, f;
157
173
  const p = /* @__PURE__ */ new WeakMap();
158
- function w(e) {
159
- return new S(e);
174
+ function v(t) {
175
+ return new D(t);
160
176
  }
161
- class S {
162
- #i;
163
- constructor(t) {
164
- this.#i = t, Object.freeze(this);
177
+ class D {
178
+ #e;
179
+ constructor(e) {
180
+ this.#e = e, Object.freeze(this);
165
181
  }
166
182
  dispose() {
167
- const t = this.#i;
168
- this.#i = void 0, t?.();
183
+ const e = this.#e;
184
+ this.#e = void 0, e?.();
169
185
  }
170
186
  [Symbol.dispose]() {
171
187
  this.dispose();
172
188
  }
173
189
  }
174
190
  function z() {
175
- const e = [];
176
- for (; a?.size; ) {
177
- const t = a;
178
- a = /* @__PURE__ */ new Set(), m(t, e);
191
+ const t = [];
192
+ for (; h?.size; ) {
193
+ const e = h;
194
+ h = /* @__PURE__ */ new Set(), y(e, t);
179
195
  }
180
- a = void 0, g(e);
196
+ h = void 0, g(t);
181
197
  }
182
- function y(e) {
198
+ function w(t) {
183
199
  if (u) {
184
- const s = a ??= /* @__PURE__ */ new Set();
185
- for (const i of e) s.add(i);
200
+ const s = h ??= /* @__PURE__ */ new Set();
201
+ for (const i of t) s.add(i);
186
202
  return;
187
203
  }
188
- const t = [];
189
- m([...e], t), g(t);
204
+ const e = [];
205
+ y(t, e), g(e);
190
206
  }
191
- function m(e, t) {
192
- for (const s of e)
193
- try {
194
- s();
195
- } catch (i) {
196
- t.push(i);
197
- }
207
+ function y(t, e) {
208
+ for (const s of t) {
209
+ const i = s.listener;
210
+ if (i)
211
+ try {
212
+ i();
213
+ } catch (r) {
214
+ e.push(r);
215
+ }
216
+ }
198
217
  }
199
- function g(e) {
200
- if (e.length === 1) throw e[0];
201
- if (e.length > 1)
202
- throw new AggregateError(e, "Reactive subscribers failed");
218
+ function g(t) {
219
+ if (t.length === 1) throw t[0];
220
+ if (t.length > 1)
221
+ throw new AggregateError(t, "Reactive subscribers failed");
203
222
  }
204
- function O(e) {
205
- f?.(e);
223
+ function m(t) {
224
+ f?.(t);
206
225
  }
207
- function C(e) {
208
- if (typeof e != "function") throw new TypeError("batch() expects a function");
226
+ function C(t) {
227
+ if (typeof t != "function") throw new TypeError("batch() expects a function");
209
228
  u++;
210
- let t;
229
+ let e;
211
230
  try {
212
- const s = e();
213
- d(s, "Reactive batches must be synchronous"), t = { ok: !0, value: s };
231
+ const s = t();
232
+ d(s, "Reactive batches must be synchronous"), e = { ok: !0, value: s };
214
233
  } catch (s) {
215
- t = { ok: !1, error: s };
234
+ e = { ok: !1, error: s };
216
235
  } finally {
217
236
  u--;
218
237
  }
@@ -220,121 +239,125 @@ function C(e) {
220
239
  try {
221
240
  z();
222
241
  } catch (s) {
223
- t = t.ok ? { ok: !1, error: s } : { ok: !1, error: new AggregateError([t.error, s], "Reactive batch failed") };
242
+ e = e.ok ? { ok: !1, error: s } : { ok: !1, error: new AggregateError([e.error, s], "Reactive batch failed") };
224
243
  }
225
- if (!t.ok) throw t.error;
226
- return t.value;
244
+ if (!e.ok) throw e.error;
245
+ return e.value;
227
246
  }
228
- function D(e) {
229
- let t = e, s = 0;
230
- const i = /* @__PURE__ */ new Set(), n = {
247
+ function L(t) {
248
+ let e = t, s = 0;
249
+ const i = /* @__PURE__ */ new Set(), r = {
231
250
  get() {
232
- return O(n), t;
251
+ return m(r), e;
233
252
  },
234
- set(r) {
235
- Object.is(t, r) || (t = r, s++, y(i));
253
+ set(n) {
254
+ Object.is(e, n) || (e = n, s++, w(i));
236
255
  },
237
- subscribe(r) {
238
- return k(r), i.add(r), w(() => i.delete(r));
256
+ subscribe(n) {
257
+ O(n);
258
+ const o = { listener: n };
259
+ return i.add(o), v(() => {
260
+ o.listener = void 0, i.delete(o);
261
+ });
239
262
  }
240
263
  };
241
- return p.set(n, {
264
+ return p.set(r, {
242
265
  get version() {
243
266
  return s;
244
267
  },
245
268
  refresh() {
246
269
  }
247
- }), Object.freeze(n);
270
+ }), Object.freeze(r);
248
271
  }
249
- function R(e) {
250
- if (typeof e != "function") throw new TypeError("computed() expects a function");
251
- return new L(e).view;
272
+ function R(t) {
273
+ if (typeof t != "function") throw new TypeError("computed() expects a function");
274
+ return new j(t).view;
252
275
  }
253
- class L {
254
- #i;
255
- #t = /* @__PURE__ */ new Set();
256
- #r = /* @__PURE__ */ new Map();
257
- #h = { phase: "uninitialized" };
258
- #n = !1;
259
- #e = !0;
276
+ class j {
277
+ #e;
278
+ #s = /* @__PURE__ */ new Set();
279
+ #i = /* @__PURE__ */ new Map();
280
+ #n = { phase: "uninitialized" };
281
+ #r = !1;
282
+ #t = !0;
260
283
  #o = 0;
261
284
  view;
262
- constructor(t) {
263
- this.#i = t, this.view = Object.freeze({
264
- get: () => this.#s(),
265
- subscribe: (s) => this.#a(s)
285
+ constructor(e) {
286
+ this.#e = e, this.view = Object.freeze({
287
+ get: () => this.#c(),
288
+ subscribe: (s) => this.#f(s)
266
289
  }), p.set(this.view, this);
267
290
  }
268
291
  get version() {
269
292
  return this.#o;
270
293
  }
271
294
  refresh() {
272
- this.#f();
295
+ this.#a();
273
296
  }
274
- #s() {
275
- return O(this.view), this.#f();
297
+ #c() {
298
+ return m(this.view), this.#a();
276
299
  }
277
- #a(t) {
278
- k(t);
279
- const s = this.#t.size === 0;
280
- if (this.#t.add(t), s)
300
+ #f(e) {
301
+ O(e);
302
+ const s = { listener: e }, i = this.#s.size === 0;
303
+ if (this.#s.add(s), i)
281
304
  try {
282
- this.#e = !0, this.#f();
283
- } catch (i) {
284
- throw this.#t.delete(t), this.#t.size || this.#u(), i;
305
+ this.#t = !0, this.#a();
306
+ } catch (r) {
307
+ throw this.#s.delete(s), this.#s.size || this.#h(), r;
285
308
  }
286
- return w(() => {
287
- this.#t.delete(t), this.#t.size || this.#u();
309
+ return v(() => {
310
+ s.listener = void 0, this.#s.delete(s), this.#s.size || this.#h();
288
311
  });
289
312
  }
290
- #c = () => {
291
- this.#e || (this.#e = !0, y(this.#t));
313
+ #p = () => {
314
+ this.#t || (this.#t = !0, w(this.#s));
292
315
  };
293
- #u() {
294
- for (const t of this.#r.values())
295
- t.subscription?.dispose(), t.subscription = void 0;
316
+ #h() {
317
+ for (const e of this.#i.values())
318
+ e.subscription?.dispose(), e.subscription = void 0;
296
319
  }
297
- #f() {
298
- const t = this.#h;
299
- if (t.phase === "cached" && !this.#e) {
300
- for (const r of this.#r.values())
301
- if (r.node.refresh(), r.version !== r.node.version) {
302
- this.#e = !0;
320
+ #a() {
321
+ const e = this.#n;
322
+ if (e.phase === "cached" && !this.#t) {
323
+ for (const n of this.#i.values())
324
+ if (n.node.refresh(), n.version !== n.node.version) {
325
+ this.#t = !0;
303
326
  break;
304
327
  }
305
- if (!this.#e) return t.value;
328
+ if (!this.#t) return e.value;
306
329
  }
307
- if (this.#n) throw new TypeError("Circular computed signal");
330
+ if (this.#r) throw new TypeError("Circular computed signal");
308
331
  const s = /* @__PURE__ */ new Set(), i = f;
309
- this.#n = !0, f = (r) => s.add(r);
310
- let n;
332
+ this.#r = !0, f = (n) => s.add(n);
333
+ let r;
311
334
  try {
312
- n = this.#i(), d(n, "Computed signal calculations must be synchronous");
335
+ r = this.#e(), d(r, "Computed signal calculations must be synchronous");
313
336
  } finally {
314
- f = i, this.#n = !1;
337
+ f = i, this.#r = !1;
315
338
  }
316
- for (const [r, o] of this.#r)
317
- s.has(r) || (o.subscription?.dispose(), this.#r.delete(r));
318
- for (const r of s) {
319
- const o = p.get(r);
339
+ for (const [n, o] of this.#i)
340
+ s.has(n) || (o.subscription?.dispose(), this.#i.delete(n));
341
+ for (const n of s) {
342
+ const o = p.get(n);
320
343
  if (!o) continue;
321
- const h = this.#r.get(r) ?? {
344
+ const c = this.#i.get(n) ?? {
322
345
  node: o,
323
346
  version: o.version,
324
347
  subscription: void 0
325
348
  };
326
- h.version = o.version, this.#t.size && !h.subscription && (h.subscription = r.subscribe(this.#c)), this.#r.set(r, h);
349
+ c.version = o.version, this.#s.size && !c.subscription && (c.subscription = n.subscribe(this.#p)), this.#i.set(n, c);
327
350
  }
328
- return (t.phase === "uninitialized" || !Object.is(t.value, n)) && this.#o++, this.#h = { phase: "cached", value: n }, this.#e = !1, n;
351
+ return (e.phase === "uninitialized" || !Object.is(e.value, r)) && this.#o++, this.#n = { phase: "cached", value: r }, this.#t = !1, r;
329
352
  }
330
353
  }
331
- function k(e) {
332
- if (typeof e != "function")
354
+ function O(t) {
355
+ if (typeof t != "function")
333
356
  throw new TypeError("Signal subscriber must be a function");
334
357
  }
335
358
  export {
336
359
  C as batch,
337
360
  R as computed,
338
- j as observe,
339
- D as signal
361
+ A as observe,
362
+ L as signal
340
363
  };
package/dist/observe.d.ts CHANGED
@@ -1,14 +1,14 @@
1
- import { Disposable, Readable } from './protocol';
2
- export interface ObservationTask<T = void> extends Disposable {
1
+ import { AsyncDisposable, Readable } from './protocol';
2
+ export interface ObservationTask<T = void> extends AsyncDisposable {
3
3
  readonly result: Promise<T>;
4
4
  }
5
- export interface ObservationLifetime extends Disposable {
6
- cleanup(dispose: () => unknown): Disposable;
5
+ export interface ObservationLifetime extends AsyncDisposable {
6
+ cleanup(dispose: () => unknown): AsyncDisposable;
7
7
  lifetime(label: string): ObservationLifetime;
8
8
  spawn<T>(task: (signal: AbortSignal) => T | PromiseLike<T>): ObservationTask<T>;
9
9
  }
10
10
  export interface ObservationOwner<Child extends ObservationLifetime = ObservationLifetime> {
11
- cleanup(dispose: () => unknown): Disposable;
11
+ cleanup(dispose: () => unknown): AsyncDisposable;
12
12
  lifetime(label: string): Child;
13
13
  spawn<T>(task: (signal: AbortSignal) => T | PromiseLike<T>): ObservationTask<T>;
14
14
  }
@@ -17,5 +17,5 @@ export type Observer<T, Child extends ObservationLifetime> = (value: T, lifetime
17
17
  * Lifetime-aware synchronization built only from the public source and
18
18
  * Lifetime protocols. It is a reactive-layer combinator, not a Core hook.
19
19
  */
20
- export declare function observe<T, Child extends ObservationLifetime>(owner: ObservationOwner<Child>, source: Readable<T>, observer: Observer<T, Child>): Disposable;
20
+ export declare function observe<T, Child extends ObservationLifetime>(owner: ObservationOwner<Child>, source: Readable<T>, observer: Observer<T, Child>): AsyncDisposable;
21
21
  //# sourceMappingURL=observe.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"observe.d.ts","sourceRoot":"","sources":["../src/observe.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAGvD,MAAM,WAAW,eAAe,CAAC,CAAC,GAAG,IAAI,CAAE,SAAQ,UAAU;IAC3D,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;CAC7B;AAED,MAAM,WAAW,mBAAoB,SAAQ,UAAU;IACrD,OAAO,CAAC,OAAO,EAAE,MAAM,OAAO,GAAG,UAAU,CAAC;IAC5C,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,mBAAmB,CAAC;IAC7C,KAAK,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,GAAG,eAAe,CAAC,CAAC,CAAC,CAAC;CACjF;AAED,MAAM,WAAW,gBAAgB,CAAC,KAAK,SAAS,mBAAmB,GAAG,mBAAmB;IACvF,OAAO,CAAC,OAAO,EAAE,MAAM,OAAO,GAAG,UAAU,CAAC;IAC5C,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,CAAC;IAC/B,KAAK,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,GAAG,eAAe,CAAC,CAAC,CAAC,CAAC;CACjF;AAED,MAAM,MAAM,QAAQ,CAAC,CAAC,EAAE,KAAK,SAAS,mBAAmB,IAAI,CAAC,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,KAAK,KAAK,IAAI,CAAC;AA+KjG;;;GAGG;AACH,wBAAgB,OAAO,CAAC,CAAC,EAAE,KAAK,SAAS,mBAAmB,EAC1D,KAAK,EAAE,gBAAgB,CAAC,KAAK,CAAC,EAC9B,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,EACnB,QAAQ,EAAE,QAAQ,CAAC,CAAC,EAAE,KAAK,CAAC,GAC3B,UAAU,CA0BZ"}
1
+ {"version":3,"file":"observe.d.ts","sourceRoot":"","sources":["../src/observe.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAc,QAAQ,EAAY,MAAM,YAAY,CAAC;AAGlF,MAAM,WAAW,eAAe,CAAC,CAAC,GAAG,IAAI,CAAE,SAAQ,eAAe;IAChE,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;CAC7B;AAED,MAAM,WAAW,mBAAoB,SAAQ,eAAe;IAC1D,OAAO,CAAC,OAAO,EAAE,MAAM,OAAO,GAAG,eAAe,CAAC;IACjD,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,mBAAmB,CAAC;IAC7C,KAAK,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,GAAG,eAAe,CAAC,CAAC,CAAC,CAAC;CACjF;AAED,MAAM,WAAW,gBAAgB,CAAC,KAAK,SAAS,mBAAmB,GAAG,mBAAmB;IACvF,OAAO,CAAC,OAAO,EAAE,MAAM,OAAO,GAAG,eAAe,CAAC;IACjD,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,CAAC;IAC/B,KAAK,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,GAAG,eAAe,CAAC,CAAC,CAAC,CAAC;CACjF;AAED,MAAM,MAAM,QAAQ,CAAC,CAAC,EAAE,KAAK,SAAS,mBAAmB,IAAI,CAAC,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,KAAK,KAAK,IAAI,CAAC;AA+MjG;;;GAGG;AACH,wBAAgB,OAAO,CAAC,CAAC,EAAE,KAAK,SAAS,mBAAmB,EAC1D,KAAK,EAAE,gBAAgB,CAAC,KAAK,CAAC,EAC9B,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,EACnB,QAAQ,EAAE,QAAQ,CAAC,CAAC,EAAE,KAAK,CAAC,GAC3B,eAAe,CA0BjB"}
@@ -1,9 +1,13 @@
1
1
  export interface Disposable {
2
- dispose(): void | Promise<void>;
3
- [Symbol.dispose]?(): void;
4
- [Symbol.asyncDispose]?(): Promise<void>;
2
+ dispose(): void;
3
+ [Symbol.dispose](): void;
5
4
  }
6
- /** Structural observable protocol shared by signals, extensions and diagnostics. */
5
+ export interface AsyncDisposable {
6
+ dispose(): Promise<void>;
7
+ [Symbol.asyncDispose](): Promise<void>;
8
+ }
9
+ export type Resource = Disposable | AsyncDisposable;
10
+ /** Structural observable protocol shared by signals, ContributionViews and diagnostics. */
7
11
  export interface Readable<T> {
8
12
  get(): T;
9
13
  subscribe(listener: () => void): Disposable;
@@ -1 +1 @@
1
- {"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,UAAU;IACzB,OAAO,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAChC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,IAAI,CAAC;IAC1B,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACzC;AAED,oFAAoF;AACpF,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,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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dougongjs/reactive",
3
- "version": "0.0.3",
3
+ "version": "0.1.0",
4
4
  "type": "module",
5
5
  "engines": {
6
6
  "node": ">=22"