@dougongjs/reactive 0.0.3 → 0.2.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 +8 -8
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +241 -203
- package/dist/observe.d.ts +6 -6
- package/dist/observe.d.ts.map +1 -1
- package/dist/protocol.d.ts +14 -4
- package/dist/protocol.d.ts.map +1 -1
- package/package.json +7 -1
package/README.md
CHANGED
|
@@ -22,12 +22,12 @@ batch(() => count.set(21))
|
|
|
22
22
|
double.get() // 42
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
-
`computed` 是惰性的,依赖动态追踪。`batch`
|
|
25
|
+
`computed` 是惰性的,依赖动态追踪。`batch` 按订阅身份合并回调内的重复通知。三者都拒绝异步回调——同步追踪和批次边界不能跨越 `await`,与其产生静默的错误结果,不如立刻抛错。
|
|
26
26
|
|
|
27
27
|
### observe:把值的变化编译成资源的重建
|
|
28
28
|
|
|
29
29
|
```ts
|
|
30
|
-
observe(
|
|
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 的 `
|
|
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
|
|
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(
|
|
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 `
|
|
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
|
|
85
|
-
> Early development (0.0.x); no backward-compatibility promises yet. Requires Node.js ≥ 22 or
|
|
84
|
+
> 早期开发阶段(0.0.x),当前不承诺向后兼容。需要 Node.js ≥ 22,或 Chrome / Edge 119、Firefox 121、Safari 17.4 及以上浏览器 / WebView。
|
|
85
|
+
> Early development (0.0.x); no backward-compatibility promises yet. Requires Node.js ≥ 22, or Chrome / Edge 119, Firefox 121 or Safari 17.4 and later.
|
|
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> {
|
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,KAAK,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkC,KAAK,QAAQ,EAAE,MAAM,YAAY,CAAC;AAG3E,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,340 +1,378 @@
|
|
|
1
|
-
function
|
|
2
|
-
if (
|
|
3
|
-
throw Promise.
|
|
4
|
-
}), new TypeError(t);
|
|
1
|
+
function T(t) {
|
|
2
|
+
if (typeof t != "function")
|
|
3
|
+
throw new Error("Unsupported JavaScript runtime: Promise.withResolvers is required");
|
|
5
4
|
}
|
|
6
|
-
function
|
|
7
|
-
return
|
|
5
|
+
function v(t, e) {
|
|
6
|
+
return t ?? Symbol.for(e);
|
|
8
7
|
}
|
|
9
|
-
|
|
8
|
+
T(Promise.withResolvers);
|
|
9
|
+
const w = v(
|
|
10
|
+
Symbol.dispose,
|
|
11
|
+
"Symbol.dispose"
|
|
12
|
+
), d = v(
|
|
13
|
+
Symbol.asyncDispose,
|
|
14
|
+
"Symbol.asyncDispose"
|
|
15
|
+
);
|
|
16
|
+
function l(t, e) {
|
|
17
|
+
if (y(t))
|
|
18
|
+
throw Promise.resolve(t).catch(() => {
|
|
19
|
+
}), new TypeError(e);
|
|
20
|
+
}
|
|
21
|
+
function y(t) {
|
|
22
|
+
return t === null || typeof t != "object" && typeof t != "function" ? !1 : typeof t.then == "function";
|
|
23
|
+
}
|
|
24
|
+
class D {
|
|
25
|
+
#e;
|
|
26
|
+
#s;
|
|
10
27
|
#i;
|
|
28
|
+
#n = { present: !1 };
|
|
29
|
+
#r = !1;
|
|
11
30
|
#t;
|
|
12
|
-
#
|
|
13
|
-
#h;
|
|
14
|
-
#n;
|
|
15
|
-
#e = { present: !1 };
|
|
16
|
-
#o = !1;
|
|
17
|
-
#s = "active";
|
|
18
|
-
#a;
|
|
31
|
+
#o;
|
|
19
32
|
#c;
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
this.#i = t, this.#t = s, this.#r = i;
|
|
33
|
+
constructor(e, s, i) {
|
|
34
|
+
this.#e = { phase: "active", binding: { owner: e, source: s, observer: i } };
|
|
23
35
|
}
|
|
24
36
|
start() {
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
() => this.#v(
|
|
32
|
-
() => this.#v(
|
|
37
|
+
const { owner: e, source: s } = this.#w(), i = s.get(), r = e.lifetime("observation");
|
|
38
|
+
b(r), this.#i = r;
|
|
39
|
+
const n = s.subscribe(() => this.#p());
|
|
40
|
+
z(n, "Readable.subscribe()"), this.#s = n, this.#l(i, r), this.#n = { present: !0, value: i };
|
|
41
|
+
const o = e.spawn((c) => this.#h(c));
|
|
42
|
+
P(o), this.#t = o, o.result.then(
|
|
43
|
+
() => this.#v(o),
|
|
44
|
+
() => this.#v(o)
|
|
33
45
|
);
|
|
34
46
|
}
|
|
35
47
|
dispose() {
|
|
36
|
-
if (this.#
|
|
37
|
-
const
|
|
38
|
-
return this.#
|
|
48
|
+
if (this.#c) return this.#c;
|
|
49
|
+
const e = Promise.withResolvers();
|
|
50
|
+
return this.#c = e.promise, this.#y("disposed"), this.#o?.(), this.#f().then(e.resolve, e.reject), e.promise;
|
|
39
51
|
}
|
|
40
52
|
async #f() {
|
|
41
|
-
const
|
|
42
|
-
if (await
|
|
43
|
-
if (
|
|
53
|
+
const e = [];
|
|
54
|
+
if (await a(this.#b(), e), await a(this.#g(), e), await a(this.#u(), e), e.length === 1) throw e[0];
|
|
55
|
+
if (e.length > 1) throw new AggregateError(e, "Observation cleanup failed");
|
|
44
56
|
}
|
|
45
|
-
#
|
|
46
|
-
this.#
|
|
57
|
+
#p() {
|
|
58
|
+
this.#e.phase === "active" && (this.#r = !0, this.#o?.());
|
|
47
59
|
}
|
|
48
|
-
async #
|
|
60
|
+
async #h(e) {
|
|
49
61
|
try {
|
|
50
|
-
for (; this.#
|
|
51
|
-
for (await this.#
|
|
52
|
-
this.#
|
|
62
|
+
for (; this.#e.phase === "active" && !e.aborted; )
|
|
63
|
+
for (await this.#a(e); this.#e.phase === "active" && !e.aborted && this.#r; )
|
|
64
|
+
this.#r = !1, await this.#m();
|
|
53
65
|
} catch (s) {
|
|
54
|
-
if (this.#
|
|
66
|
+
if (this.#e.phase !== "active") throw s;
|
|
55
67
|
await this.#d([s], "Observation stopped after a replacement failed");
|
|
56
68
|
}
|
|
57
69
|
}
|
|
58
|
-
#
|
|
59
|
-
return this.#
|
|
70
|
+
#a(e) {
|
|
71
|
+
return this.#r || this.#e.phase !== "active" || e.aborted ? Promise.resolve() : new Promise((s) => {
|
|
60
72
|
const i = () => {
|
|
61
|
-
|
|
73
|
+
e.removeEventListener("abort", i), this.#o === i && (this.#o = void 0), s();
|
|
62
74
|
};
|
|
63
|
-
this.#
|
|
75
|
+
this.#o = i, e.addEventListener("abort", i, { once: !0 }), (this.#r || this.#e.phase !== "active" || e.aborted) && i();
|
|
64
76
|
});
|
|
65
77
|
}
|
|
66
|
-
async #
|
|
67
|
-
if (this.#
|
|
68
|
-
const
|
|
69
|
-
if (this.#
|
|
70
|
-
const s = this.#
|
|
78
|
+
async #m() {
|
|
79
|
+
if (this.#e.phase !== "active") return;
|
|
80
|
+
const e = this.#e.binding.source.get();
|
|
81
|
+
if (this.#n.present && Object.is(e, this.#n.value)) return;
|
|
82
|
+
const s = this.#u();
|
|
71
83
|
if (s)
|
|
72
84
|
try {
|
|
73
85
|
await s.dispose();
|
|
74
|
-
} catch (
|
|
86
|
+
} catch (r) {
|
|
87
|
+
if (this.#e.phase !== "active") throw r;
|
|
75
88
|
return this.#d(
|
|
76
|
-
[
|
|
89
|
+
[r],
|
|
77
90
|
"Observation stopped because the previous lifetime could not be disposed"
|
|
78
91
|
);
|
|
79
92
|
}
|
|
80
|
-
if (this.#
|
|
81
|
-
const i = this.#
|
|
82
|
-
|
|
93
|
+
if (this.#n = { present: !1 }, this.#e.phase !== "active") return;
|
|
94
|
+
const i = this.#e.binding.owner.lifetime("observation");
|
|
95
|
+
b(i), this.#i = i;
|
|
83
96
|
try {
|
|
84
|
-
this.#l(
|
|
85
|
-
} catch (
|
|
97
|
+
this.#l(e, i);
|
|
98
|
+
} catch (r) {
|
|
99
|
+
const n = this.#u();
|
|
86
100
|
try {
|
|
87
|
-
await
|
|
88
|
-
} catch (
|
|
101
|
+
n && await n.dispose();
|
|
102
|
+
} catch (o) {
|
|
103
|
+
if (this.#e.phase !== "active")
|
|
104
|
+
throw new AggregateError(
|
|
105
|
+
[r, o],
|
|
106
|
+
"Observation callback failed and its resources could not be cleaned up"
|
|
107
|
+
);
|
|
89
108
|
return this.#d(
|
|
90
|
-
[
|
|
109
|
+
[r, o],
|
|
91
110
|
"Observation callback failed and its resources could not be cleaned up"
|
|
92
111
|
);
|
|
93
112
|
}
|
|
94
|
-
throw
|
|
113
|
+
throw r;
|
|
95
114
|
}
|
|
96
|
-
this.#
|
|
115
|
+
this.#e.phase === "active" && (this.#n = { present: !0, value: e });
|
|
97
116
|
}
|
|
98
|
-
#l(
|
|
99
|
-
const i = this.#
|
|
100
|
-
|
|
117
|
+
#l(e, s) {
|
|
118
|
+
const i = this.#w().observer(e, s);
|
|
119
|
+
l(i, "Observers must be synchronous; use lifetime.spawn() for async work");
|
|
101
120
|
}
|
|
102
121
|
#b() {
|
|
103
|
-
const
|
|
104
|
-
return this.#
|
|
122
|
+
const e = this.#s;
|
|
123
|
+
return this.#s = void 0, e;
|
|
105
124
|
}
|
|
106
|
-
#
|
|
107
|
-
const
|
|
108
|
-
return this.#
|
|
125
|
+
#u() {
|
|
126
|
+
const e = this.#i;
|
|
127
|
+
return this.#i = void 0, e;
|
|
128
|
+
}
|
|
129
|
+
#g() {
|
|
130
|
+
const e = this.#t;
|
|
131
|
+
return this.#t = void 0, e;
|
|
132
|
+
}
|
|
133
|
+
#v(e) {
|
|
134
|
+
this.#t === e && (this.#t = void 0);
|
|
109
135
|
}
|
|
110
|
-
#
|
|
111
|
-
|
|
112
|
-
return this.#a = void 0, t;
|
|
136
|
+
async #d(e, s) {
|
|
137
|
+
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);
|
|
113
138
|
}
|
|
114
|
-
#
|
|
115
|
-
|
|
139
|
+
#w() {
|
|
140
|
+
const e = this.#e;
|
|
141
|
+
if (e.phase !== "active") throw new Error("Observation is not active");
|
|
142
|
+
return e.binding;
|
|
116
143
|
}
|
|
117
|
-
|
|
118
|
-
|
|
144
|
+
#y(e) {
|
|
145
|
+
this.#e = { phase: e }, this.#n = { present: !1 }, this.#r = !1;
|
|
119
146
|
}
|
|
120
147
|
}
|
|
121
|
-
function
|
|
122
|
-
if (!
|
|
148
|
+
function L(t, e, s) {
|
|
149
|
+
if (!e || typeof e.get != "function" || typeof e.subscribe != "function")
|
|
123
150
|
throw new TypeError("observe() expects a readable source");
|
|
124
151
|
if (typeof s != "function") throw new TypeError("Observer must be a function");
|
|
125
|
-
if (!
|
|
152
|
+
if (!t || typeof t.cleanup != "function" || typeof t.lifetime != "function" || typeof t.spawn != "function")
|
|
126
153
|
throw new TypeError("observe() expects an observation owner");
|
|
127
|
-
const i = new
|
|
128
|
-
|
|
154
|
+
const i = new D(t, e, s), r = t.cleanup(() => i.dispose());
|
|
155
|
+
R(r, "ObservationOwner.cleanup()");
|
|
129
156
|
try {
|
|
130
|
-
return i.start(),
|
|
131
|
-
} catch (
|
|
157
|
+
return i.start(), r;
|
|
158
|
+
} catch (n) {
|
|
132
159
|
throw i.dispose().catch(() => {
|
|
133
|
-
}),
|
|
160
|
+
}), n;
|
|
134
161
|
}
|
|
135
162
|
}
|
|
136
|
-
function
|
|
137
|
-
if (!
|
|
138
|
-
throw new TypeError(`${
|
|
163
|
+
function z(t, e) {
|
|
164
|
+
if (!t || typeof t.dispose != "function" || typeof t[w] != "function")
|
|
165
|
+
throw new TypeError(`${e} must return a Disposable`);
|
|
166
|
+
}
|
|
167
|
+
function R(t, e) {
|
|
168
|
+
if (!t || typeof t.dispose != "function" || typeof t[d] != "function")
|
|
169
|
+
throw new TypeError(`${e} must return an AsyncDisposable`);
|
|
139
170
|
}
|
|
140
|
-
function
|
|
141
|
-
if (!
|
|
171
|
+
function b(t) {
|
|
172
|
+
if (!t || typeof t.dispose != "function" || typeof t[d] != "function" || typeof t.cleanup != "function" || typeof t.lifetime != "function" || typeof t.spawn != "function")
|
|
142
173
|
throw new TypeError("ObservationOwner.lifetime() must return an ObservationLifetime");
|
|
143
174
|
}
|
|
144
|
-
function
|
|
145
|
-
if (!
|
|
175
|
+
function P(t) {
|
|
176
|
+
if (!t || typeof t.dispose != "function" || typeof t[d] != "function" || !y(t.result))
|
|
146
177
|
throw new TypeError("ObservationOwner.spawn() must return an ObservationTask");
|
|
147
178
|
}
|
|
148
|
-
async function
|
|
149
|
-
if (
|
|
179
|
+
async function a(t, e) {
|
|
180
|
+
if (t)
|
|
150
181
|
try {
|
|
151
|
-
await
|
|
182
|
+
await t.dispose();
|
|
152
183
|
} catch (s) {
|
|
153
|
-
|
|
184
|
+
e.push(s);
|
|
154
185
|
}
|
|
155
186
|
}
|
|
156
|
-
let u = 0,
|
|
187
|
+
let u = 0, h, f;
|
|
157
188
|
const p = /* @__PURE__ */ new WeakMap();
|
|
158
|
-
function
|
|
159
|
-
return new
|
|
189
|
+
function m(t) {
|
|
190
|
+
return new j(t);
|
|
160
191
|
}
|
|
161
|
-
class
|
|
162
|
-
#
|
|
163
|
-
constructor(
|
|
164
|
-
this.#
|
|
192
|
+
class j {
|
|
193
|
+
#e;
|
|
194
|
+
constructor(e) {
|
|
195
|
+
this.#e = e, Object.freeze(this);
|
|
165
196
|
}
|
|
166
197
|
dispose() {
|
|
167
|
-
const
|
|
168
|
-
this.#
|
|
198
|
+
const e = this.#e;
|
|
199
|
+
this.#e = void 0, e?.();
|
|
169
200
|
}
|
|
170
|
-
[
|
|
201
|
+
[w]() {
|
|
171
202
|
this.dispose();
|
|
172
203
|
}
|
|
173
204
|
}
|
|
174
|
-
function
|
|
175
|
-
const
|
|
176
|
-
for (;
|
|
177
|
-
const
|
|
178
|
-
|
|
205
|
+
function A() {
|
|
206
|
+
const t = [];
|
|
207
|
+
for (; h?.size; ) {
|
|
208
|
+
const e = h;
|
|
209
|
+
h = /* @__PURE__ */ new Set(), O(e, t);
|
|
179
210
|
}
|
|
180
|
-
|
|
211
|
+
h = void 0, k(t);
|
|
181
212
|
}
|
|
182
|
-
function
|
|
213
|
+
function g(t) {
|
|
183
214
|
if (u) {
|
|
184
|
-
const s =
|
|
185
|
-
for (const i of
|
|
215
|
+
const s = h ??= /* @__PURE__ */ new Set();
|
|
216
|
+
for (const i of t) s.add(i);
|
|
186
217
|
return;
|
|
187
218
|
}
|
|
188
|
-
const
|
|
189
|
-
|
|
219
|
+
const e = [];
|
|
220
|
+
O(t, e), k(e);
|
|
190
221
|
}
|
|
191
|
-
function
|
|
192
|
-
for (const s of
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
222
|
+
function O(t, e) {
|
|
223
|
+
for (const s of t) {
|
|
224
|
+
const i = s.listener;
|
|
225
|
+
if (i)
|
|
226
|
+
try {
|
|
227
|
+
i();
|
|
228
|
+
} catch (r) {
|
|
229
|
+
e.push(r);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
198
232
|
}
|
|
199
|
-
function
|
|
200
|
-
if (
|
|
201
|
-
if (
|
|
202
|
-
throw new AggregateError(
|
|
233
|
+
function k(t) {
|
|
234
|
+
if (t.length === 1) throw t[0];
|
|
235
|
+
if (t.length > 1)
|
|
236
|
+
throw new AggregateError(t, "Reactive subscribers failed");
|
|
203
237
|
}
|
|
204
|
-
function
|
|
205
|
-
f?.(
|
|
238
|
+
function E(t) {
|
|
239
|
+
f?.(t);
|
|
206
240
|
}
|
|
207
|
-
function
|
|
208
|
-
if (typeof
|
|
241
|
+
function x(t) {
|
|
242
|
+
if (typeof t != "function") throw new TypeError("batch() expects a function");
|
|
209
243
|
u++;
|
|
210
|
-
let
|
|
244
|
+
let e;
|
|
211
245
|
try {
|
|
212
|
-
const s =
|
|
213
|
-
|
|
246
|
+
const s = t();
|
|
247
|
+
l(s, "Reactive batches must be synchronous"), e = { ok: !0, value: s };
|
|
214
248
|
} catch (s) {
|
|
215
|
-
|
|
249
|
+
e = { ok: !1, error: s };
|
|
216
250
|
} finally {
|
|
217
251
|
u--;
|
|
218
252
|
}
|
|
219
253
|
if (!u)
|
|
220
254
|
try {
|
|
221
|
-
|
|
255
|
+
A();
|
|
222
256
|
} catch (s) {
|
|
223
|
-
|
|
257
|
+
e = e.ok ? { ok: !1, error: s } : { ok: !1, error: new AggregateError([e.error, s], "Reactive batch failed") };
|
|
224
258
|
}
|
|
225
|
-
if (!
|
|
226
|
-
return
|
|
259
|
+
if (!e.ok) throw e.error;
|
|
260
|
+
return e.value;
|
|
227
261
|
}
|
|
228
|
-
function
|
|
229
|
-
let
|
|
230
|
-
const i = /* @__PURE__ */ new Set(),
|
|
262
|
+
function q(t) {
|
|
263
|
+
let e = t, s = 0;
|
|
264
|
+
const i = /* @__PURE__ */ new Set(), r = {
|
|
231
265
|
get() {
|
|
232
|
-
return
|
|
266
|
+
return E(r), e;
|
|
233
267
|
},
|
|
234
|
-
set(
|
|
235
|
-
Object.is(
|
|
268
|
+
set(n) {
|
|
269
|
+
Object.is(e, n) || (e = n, s++, g(i));
|
|
236
270
|
},
|
|
237
|
-
subscribe(
|
|
238
|
-
|
|
271
|
+
subscribe(n) {
|
|
272
|
+
S(n);
|
|
273
|
+
const o = { listener: n };
|
|
274
|
+
return i.add(o), m(() => {
|
|
275
|
+
o.listener = void 0, i.delete(o);
|
|
276
|
+
});
|
|
239
277
|
}
|
|
240
278
|
};
|
|
241
|
-
return p.set(
|
|
279
|
+
return p.set(r, {
|
|
242
280
|
get version() {
|
|
243
281
|
return s;
|
|
244
282
|
},
|
|
245
283
|
refresh() {
|
|
246
284
|
}
|
|
247
|
-
}), Object.freeze(
|
|
285
|
+
}), Object.freeze(r);
|
|
248
286
|
}
|
|
249
|
-
function
|
|
250
|
-
if (typeof
|
|
251
|
-
return new
|
|
287
|
+
function B(t) {
|
|
288
|
+
if (typeof t != "function") throw new TypeError("computed() expects a function");
|
|
289
|
+
return new C(t).view;
|
|
252
290
|
}
|
|
253
|
-
class
|
|
254
|
-
#
|
|
255
|
-
#
|
|
256
|
-
#
|
|
257
|
-
#
|
|
258
|
-
#
|
|
259
|
-
#
|
|
291
|
+
class C {
|
|
292
|
+
#e;
|
|
293
|
+
#s = /* @__PURE__ */ new Set();
|
|
294
|
+
#i = /* @__PURE__ */ new Map();
|
|
295
|
+
#n = { phase: "uninitialized" };
|
|
296
|
+
#r = !1;
|
|
297
|
+
#t = !0;
|
|
260
298
|
#o = 0;
|
|
261
299
|
view;
|
|
262
|
-
constructor(
|
|
263
|
-
this.#
|
|
264
|
-
get: () => this.#
|
|
265
|
-
subscribe: (s) => this.#
|
|
300
|
+
constructor(e) {
|
|
301
|
+
this.#e = e, this.view = Object.freeze({
|
|
302
|
+
get: () => this.#c(),
|
|
303
|
+
subscribe: (s) => this.#f(s)
|
|
266
304
|
}), p.set(this.view, this);
|
|
267
305
|
}
|
|
268
306
|
get version() {
|
|
269
307
|
return this.#o;
|
|
270
308
|
}
|
|
271
309
|
refresh() {
|
|
272
|
-
this.#
|
|
310
|
+
this.#a();
|
|
273
311
|
}
|
|
274
|
-
#
|
|
275
|
-
return
|
|
312
|
+
#c() {
|
|
313
|
+
return E(this.view), this.#a();
|
|
276
314
|
}
|
|
277
|
-
#
|
|
278
|
-
|
|
279
|
-
const s = this.#
|
|
280
|
-
if (this.#
|
|
315
|
+
#f(e) {
|
|
316
|
+
S(e);
|
|
317
|
+
const s = { listener: e }, i = this.#s.size === 0;
|
|
318
|
+
if (this.#s.add(s), i)
|
|
281
319
|
try {
|
|
282
|
-
this.#
|
|
283
|
-
} catch (
|
|
284
|
-
throw this.#
|
|
320
|
+
this.#t = !0, this.#a();
|
|
321
|
+
} catch (r) {
|
|
322
|
+
throw this.#s.delete(s), this.#s.size || this.#h(), r;
|
|
285
323
|
}
|
|
286
|
-
return
|
|
287
|
-
this.#
|
|
324
|
+
return m(() => {
|
|
325
|
+
s.listener = void 0, this.#s.delete(s), this.#s.size || this.#h();
|
|
288
326
|
});
|
|
289
327
|
}
|
|
290
|
-
#
|
|
291
|
-
this.#
|
|
328
|
+
#p = () => {
|
|
329
|
+
this.#t || (this.#t = !0, g(this.#s));
|
|
292
330
|
};
|
|
293
|
-
#
|
|
294
|
-
for (const
|
|
295
|
-
|
|
331
|
+
#h() {
|
|
332
|
+
for (const e of this.#i.values())
|
|
333
|
+
e.subscription?.dispose(), e.subscription = void 0;
|
|
296
334
|
}
|
|
297
|
-
#
|
|
298
|
-
const
|
|
299
|
-
if (
|
|
300
|
-
for (const
|
|
301
|
-
if (
|
|
302
|
-
this.#
|
|
335
|
+
#a() {
|
|
336
|
+
const e = this.#n;
|
|
337
|
+
if (e.phase === "cached" && !this.#t) {
|
|
338
|
+
for (const n of this.#i.values())
|
|
339
|
+
if (n.node.refresh(), n.version !== n.node.version) {
|
|
340
|
+
this.#t = !0;
|
|
303
341
|
break;
|
|
304
342
|
}
|
|
305
|
-
if (!this.#
|
|
343
|
+
if (!this.#t) return e.value;
|
|
306
344
|
}
|
|
307
|
-
if (this.#
|
|
345
|
+
if (this.#r) throw new TypeError("Circular computed signal");
|
|
308
346
|
const s = /* @__PURE__ */ new Set(), i = f;
|
|
309
|
-
this.#
|
|
310
|
-
let
|
|
347
|
+
this.#r = !0, f = (n) => s.add(n);
|
|
348
|
+
let r;
|
|
311
349
|
try {
|
|
312
|
-
|
|
350
|
+
r = this.#e(), l(r, "Computed signal calculations must be synchronous");
|
|
313
351
|
} finally {
|
|
314
|
-
f = i, this.#
|
|
352
|
+
f = i, this.#r = !1;
|
|
315
353
|
}
|
|
316
|
-
for (const [
|
|
317
|
-
s.has(
|
|
318
|
-
for (const
|
|
319
|
-
const o = p.get(
|
|
354
|
+
for (const [n, o] of this.#i)
|
|
355
|
+
s.has(n) || (o.subscription?.dispose(), this.#i.delete(n));
|
|
356
|
+
for (const n of s) {
|
|
357
|
+
const o = p.get(n);
|
|
320
358
|
if (!o) continue;
|
|
321
|
-
const
|
|
359
|
+
const c = this.#i.get(n) ?? {
|
|
322
360
|
node: o,
|
|
323
361
|
version: o.version,
|
|
324
362
|
subscription: void 0
|
|
325
363
|
};
|
|
326
|
-
|
|
364
|
+
c.version = o.version, this.#s.size && !c.subscription && (c.subscription = n.subscribe(this.#p)), this.#i.set(n, c);
|
|
327
365
|
}
|
|
328
|
-
return (
|
|
366
|
+
return (e.phase === "uninitialized" || !Object.is(e.value, r)) && this.#o++, this.#n = { phase: "cached", value: r }, this.#t = !1, r;
|
|
329
367
|
}
|
|
330
368
|
}
|
|
331
|
-
function
|
|
332
|
-
if (typeof
|
|
369
|
+
function S(t) {
|
|
370
|
+
if (typeof t != "function")
|
|
333
371
|
throw new TypeError("Signal subscriber must be a function");
|
|
334
372
|
}
|
|
335
373
|
export {
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
374
|
+
x as batch,
|
|
375
|
+
B as computed,
|
|
376
|
+
L as observe,
|
|
377
|
+
q as signal
|
|
340
378
|
};
|
package/dist/observe.d.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export interface ObservationTask<T = void> extends
|
|
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
|
|
6
|
-
cleanup(dispose: () => unknown):
|
|
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):
|
|
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>):
|
|
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
|
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,KAAK,
|
|
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;AAGpB,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"}
|
package/dist/protocol.d.ts
CHANGED
|
@@ -1,9 +1,19 @@
|
|
|
1
|
+
/** Fails at module load before an unavailable Promise primitive reaches an observation. */
|
|
2
|
+
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
|
+
export declare function resolveDisposalSymbol(native: symbol | undefined, name: "Symbol.dispose" | "Symbol.asyncDispose"): symbol;
|
|
5
|
+
export declare const disposeSymbol: typeof Symbol.dispose;
|
|
6
|
+
export declare const asyncDisposeSymbol: typeof Symbol.asyncDispose;
|
|
1
7
|
export interface Disposable {
|
|
2
|
-
dispose(): void
|
|
3
|
-
[Symbol.dispose]
|
|
4
|
-
[Symbol.asyncDispose]?(): Promise<void>;
|
|
8
|
+
dispose(): void;
|
|
9
|
+
[Symbol.dispose](): void;
|
|
5
10
|
}
|
|
6
|
-
|
|
11
|
+
export interface AsyncDisposable {
|
|
12
|
+
dispose(): Promise<void>;
|
|
13
|
+
[Symbol.asyncDispose](): Promise<void>;
|
|
14
|
+
}
|
|
15
|
+
export type Resource = Disposable | AsyncDisposable;
|
|
16
|
+
/** Structural observable protocol shared by signals, ContributionViews and diagnostics. */
|
|
7
17
|
export interface Readable<T> {
|
|
8
18
|
get(): T;
|
|
9
19
|
subscribe(listener: () => void): Disposable;
|
package/dist/protocol.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAAA,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"}
|
package/package.json
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dougongjs/reactive",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=22"
|
|
7
7
|
},
|
|
8
|
+
"browserslist": [
|
|
9
|
+
"Chrome >= 119",
|
|
10
|
+
"Edge >= 119",
|
|
11
|
+
"Firefox >= 121",
|
|
12
|
+
"Safari >= 17.4"
|
|
13
|
+
],
|
|
8
14
|
"sideEffects": false,
|
|
9
15
|
"files": [
|
|
10
16
|
"dist",
|