@dougongjs/reactive 0.0.2 → 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 +80 -5
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +219 -196
- package/dist/observe.d.ts +6 -6
- package/dist/observe.d.ts.map +1 -1
- package/dist/protocol.d.ts +8 -4
- package/dist/protocol.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,12 +1,87 @@
|
|
|
1
1
|
# @dougongjs/reactive
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[简体中文](#简体中文) · [English](#english)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
---
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
- 仓库:https://github.com/Tangerg/dougong
|
|
7
|
+
## 简体中文
|
|
9
8
|
|
|
10
|
-
|
|
9
|
+
**零依赖**的响应式原语:`signal` / `computed` / `batch` / `observe`。
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
npm install @dougongjs/reactive
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { signal, computed, batch, observe } from "@dougongjs/reactive"
|
|
17
|
+
|
|
18
|
+
const count = signal(0)
|
|
19
|
+
const double = computed(() => count.get() * 2)
|
|
20
|
+
|
|
21
|
+
batch(() => count.set(21))
|
|
22
|
+
double.get() // 42
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
`computed` 是惰性的,依赖动态追踪。`batch` 按订阅身份合并回调内的重复通知。三者都拒绝异步回调——同步追踪和批次边界不能跨越 `await`,与其产生静默的错误结果,不如立刻抛错。
|
|
26
|
+
|
|
27
|
+
### observe:把值的变化编译成资源的重建
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
observe(owner, source, (value, lifetime) => {
|
|
31
|
+
const socket = new WebSocket(value)
|
|
32
|
+
lifetime.cleanup(() => socket.close())
|
|
33
|
+
})
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
每次 `source` 变化:先释放上一个 `lifetime`,再用新值建一个新的。
|
|
37
|
+
|
|
38
|
+
`observe()` 与 `@dougongjs/core` **互不依赖**——它是一个作用在结构化 `ObservationOwner`(提供 `cleanup` / `lifetime` / `spawn`)上的自由函数,所以能驱动 Core 的 Lifetime,而 Core 不需要知道它存在。插件的 `ctx` 恰好满足这个形状。
|
|
39
|
+
|
|
40
|
+
`source` 只需满足结构化的 `Readable<T>`(`get()` + `subscribe()`)——Signal、Core 的 `ContributionView`、诊断视图,甚至你自己写的对象都可以。
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## English
|
|
45
|
+
|
|
46
|
+
**Zero-dependency** reactive primitives: `signal` / `computed` / `batch` / `observe`.
|
|
47
|
+
|
|
48
|
+
```sh
|
|
49
|
+
npm install @dougongjs/reactive
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
import { signal, computed, batch, observe } from "@dougongjs/reactive"
|
|
54
|
+
|
|
55
|
+
const count = signal(0)
|
|
56
|
+
const double = computed(() => count.get() * 2)
|
|
57
|
+
|
|
58
|
+
batch(() => count.set(21))
|
|
59
|
+
double.get() // 42
|
|
60
|
+
```
|
|
61
|
+
|
|
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
|
+
|
|
64
|
+
### observe: compiling value change into resource rebuild
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
observe(owner, source, (value, lifetime) => {
|
|
68
|
+
const socket = new WebSocket(value)
|
|
69
|
+
lifetime.cleanup(() => socket.close())
|
|
70
|
+
})
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
On each change to `source`: release the previous `lifetime`, then build a new one from the new value.
|
|
74
|
+
|
|
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
|
+
|
|
77
|
+
`source` need only satisfy the structural `Readable<T>` (`get()` + `subscribe()`) — a signal, Core's `ContributionView`, a diagnostics view, or your own object.
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
- 文档站 / Documentation: https://tangerg.github.io/dougong/
|
|
82
|
+
- 仓库 / Repository: https://github.com/Tangerg/dougong
|
|
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 runtime.
|
|
11
86
|
|
|
12
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,EAAc,QAAQ,EAAE,MAAM,YAAY,CAAC;AAGvD,YAAY,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;
|
|
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(
|
|
2
|
-
if (b(
|
|
3
|
-
throw Promise.resolve(
|
|
4
|
-
}), new TypeError(
|
|
1
|
+
function d(t, e) {
|
|
2
|
+
if (b(t))
|
|
3
|
+
throw Promise.resolve(t).catch(() => {
|
|
4
|
+
}), new TypeError(e);
|
|
5
5
|
}
|
|
6
|
-
function b(
|
|
7
|
-
return
|
|
6
|
+
function b(t) {
|
|
7
|
+
return t === null || typeof t != "object" && typeof t != "function" ? !1 : typeof t.then == "function";
|
|
8
8
|
}
|
|
9
|
-
class
|
|
9
|
+
class k {
|
|
10
|
+
#e;
|
|
11
|
+
#s;
|
|
10
12
|
#i;
|
|
13
|
+
#n = { present: !1 };
|
|
14
|
+
#r = !1;
|
|
11
15
|
#t;
|
|
12
|
-
#
|
|
13
|
-
#h;
|
|
14
|
-
#n;
|
|
15
|
-
#e = { present: !1 };
|
|
16
|
-
#o = !1;
|
|
17
|
-
#s = "active";
|
|
18
|
-
#a;
|
|
16
|
+
#o;
|
|
19
17
|
#c;
|
|
20
|
-
|
|
21
|
-
|
|
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
|
|
26
|
-
l(
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
() => this.#v(
|
|
32
|
-
() => this.#v(
|
|
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.#
|
|
37
|
-
const
|
|
38
|
-
return this.#
|
|
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
|
|
42
|
-
if (await
|
|
43
|
-
if (
|
|
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
|
-
#
|
|
46
|
-
this.#
|
|
42
|
+
#p() {
|
|
43
|
+
this.#e.phase === "active" && (this.#r = !0, this.#o?.());
|
|
47
44
|
}
|
|
48
|
-
async #
|
|
45
|
+
async #h(e) {
|
|
49
46
|
try {
|
|
50
|
-
for (; this.#
|
|
51
|
-
for (await this.#
|
|
52
|
-
this.#
|
|
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.#
|
|
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
|
-
#
|
|
59
|
-
return this.#
|
|
55
|
+
#a(e) {
|
|
56
|
+
return this.#r || this.#e.phase !== "active" || e.aborted ? Promise.resolve() : new Promise((s) => {
|
|
60
57
|
const i = () => {
|
|
61
|
-
|
|
58
|
+
e.removeEventListener("abort", i), this.#o === i && (this.#o = void 0), s();
|
|
62
59
|
};
|
|
63
|
-
this.#
|
|
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.#
|
|
68
|
-
const
|
|
69
|
-
if (this.#
|
|
70
|
-
const s = this.#
|
|
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 (
|
|
71
|
+
} catch (r) {
|
|
72
|
+
if (this.#e.phase !== "active") throw r;
|
|
75
73
|
return this.#d(
|
|
76
|
-
[
|
|
74
|
+
[r],
|
|
77
75
|
"Observation stopped because the previous lifetime could not be disposed"
|
|
78
76
|
);
|
|
79
77
|
}
|
|
80
|
-
if (this.#
|
|
81
|
-
const i = this.#
|
|
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(
|
|
85
|
-
} catch (
|
|
82
|
+
this.#l(e, i);
|
|
83
|
+
} catch (r) {
|
|
84
|
+
const n = this.#u();
|
|
86
85
|
try {
|
|
87
|
-
await
|
|
88
|
-
} catch (
|
|
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
|
-
[
|
|
94
|
+
[r, o],
|
|
91
95
|
"Observation callback failed and its resources could not be cleaned up"
|
|
92
96
|
);
|
|
93
97
|
}
|
|
94
|
-
throw
|
|
98
|
+
throw r;
|
|
95
99
|
}
|
|
96
|
-
this.#
|
|
100
|
+
this.#e.phase === "active" && (this.#n = { present: !0, value: e });
|
|
97
101
|
}
|
|
98
|
-
#l(
|
|
99
|
-
const i = this.#
|
|
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
|
|
104
|
-
return this.#
|
|
107
|
+
const e = this.#s;
|
|
108
|
+
return this.#s = void 0, e;
|
|
105
109
|
}
|
|
106
|
-
#
|
|
107
|
-
const
|
|
108
|
-
return this.#
|
|
110
|
+
#u() {
|
|
111
|
+
const e = this.#i;
|
|
112
|
+
return this.#i = void 0, e;
|
|
109
113
|
}
|
|
110
|
-
#
|
|
111
|
-
const
|
|
112
|
-
return this.#
|
|
114
|
+
#m() {
|
|
115
|
+
const e = this.#t;
|
|
116
|
+
return this.#t = void 0, e;
|
|
113
117
|
}
|
|
114
|
-
#v(
|
|
115
|
-
this.#
|
|
118
|
+
#v(e) {
|
|
119
|
+
this.#t === e && (this.#t = void 0);
|
|
116
120
|
}
|
|
117
|
-
async #d(
|
|
118
|
-
throw this.#
|
|
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
|
|
122
|
-
if (!
|
|
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 (!
|
|
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
|
|
128
|
-
|
|
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(),
|
|
131
|
-
} catch (
|
|
142
|
+
return i.start(), r;
|
|
143
|
+
} catch (n) {
|
|
132
144
|
throw i.dispose().catch(() => {
|
|
133
|
-
}),
|
|
145
|
+
}), n;
|
|
134
146
|
}
|
|
135
147
|
}
|
|
136
|
-
function
|
|
137
|
-
if (!
|
|
138
|
-
throw new TypeError(`${
|
|
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
|
|
141
|
-
if (!
|
|
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
|
|
145
|
-
if (!
|
|
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
|
|
149
|
-
if (
|
|
164
|
+
async function a(t, e) {
|
|
165
|
+
if (t)
|
|
150
166
|
try {
|
|
151
|
-
await
|
|
167
|
+
await t.dispose();
|
|
152
168
|
} catch (s) {
|
|
153
|
-
|
|
169
|
+
e.push(s);
|
|
154
170
|
}
|
|
155
171
|
}
|
|
156
|
-
let u = 0,
|
|
172
|
+
let u = 0, h, f;
|
|
157
173
|
const p = /* @__PURE__ */ new WeakMap();
|
|
158
|
-
function
|
|
159
|
-
return new
|
|
174
|
+
function v(t) {
|
|
175
|
+
return new D(t);
|
|
160
176
|
}
|
|
161
|
-
class
|
|
162
|
-
#
|
|
163
|
-
constructor(
|
|
164
|
-
this.#
|
|
177
|
+
class D {
|
|
178
|
+
#e;
|
|
179
|
+
constructor(e) {
|
|
180
|
+
this.#e = e, Object.freeze(this);
|
|
165
181
|
}
|
|
166
182
|
dispose() {
|
|
167
|
-
const
|
|
168
|
-
this.#
|
|
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
|
|
176
|
-
for (;
|
|
177
|
-
const
|
|
178
|
-
|
|
191
|
+
const t = [];
|
|
192
|
+
for (; h?.size; ) {
|
|
193
|
+
const e = h;
|
|
194
|
+
h = /* @__PURE__ */ new Set(), y(e, t);
|
|
179
195
|
}
|
|
180
|
-
|
|
196
|
+
h = void 0, g(t);
|
|
181
197
|
}
|
|
182
|
-
function
|
|
198
|
+
function w(t) {
|
|
183
199
|
if (u) {
|
|
184
|
-
const s =
|
|
185
|
-
for (const i of
|
|
200
|
+
const s = h ??= /* @__PURE__ */ new Set();
|
|
201
|
+
for (const i of t) s.add(i);
|
|
186
202
|
return;
|
|
187
203
|
}
|
|
188
|
-
const
|
|
189
|
-
|
|
204
|
+
const e = [];
|
|
205
|
+
y(t, e), g(e);
|
|
190
206
|
}
|
|
191
|
-
function
|
|
192
|
-
for (const s of
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
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(
|
|
200
|
-
if (
|
|
201
|
-
if (
|
|
202
|
-
throw new AggregateError(
|
|
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
|
|
205
|
-
f?.(
|
|
223
|
+
function m(t) {
|
|
224
|
+
f?.(t);
|
|
206
225
|
}
|
|
207
|
-
function C(
|
|
208
|
-
if (typeof
|
|
226
|
+
function C(t) {
|
|
227
|
+
if (typeof t != "function") throw new TypeError("batch() expects a function");
|
|
209
228
|
u++;
|
|
210
|
-
let
|
|
229
|
+
let e;
|
|
211
230
|
try {
|
|
212
|
-
const s =
|
|
213
|
-
d(s, "Reactive batches must be synchronous"),
|
|
231
|
+
const s = t();
|
|
232
|
+
d(s, "Reactive batches must be synchronous"), e = { ok: !0, value: s };
|
|
214
233
|
} catch (s) {
|
|
215
|
-
|
|
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
|
-
|
|
242
|
+
e = e.ok ? { ok: !1, error: s } : { ok: !1, error: new AggregateError([e.error, s], "Reactive batch failed") };
|
|
224
243
|
}
|
|
225
|
-
if (!
|
|
226
|
-
return
|
|
244
|
+
if (!e.ok) throw e.error;
|
|
245
|
+
return e.value;
|
|
227
246
|
}
|
|
228
|
-
function
|
|
229
|
-
let
|
|
230
|
-
const i = /* @__PURE__ */ new Set(),
|
|
247
|
+
function L(t) {
|
|
248
|
+
let e = t, s = 0;
|
|
249
|
+
const i = /* @__PURE__ */ new Set(), r = {
|
|
231
250
|
get() {
|
|
232
|
-
return
|
|
251
|
+
return m(r), e;
|
|
233
252
|
},
|
|
234
|
-
set(
|
|
235
|
-
Object.is(
|
|
253
|
+
set(n) {
|
|
254
|
+
Object.is(e, n) || (e = n, s++, w(i));
|
|
236
255
|
},
|
|
237
|
-
subscribe(
|
|
238
|
-
|
|
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(
|
|
264
|
+
return p.set(r, {
|
|
242
265
|
get version() {
|
|
243
266
|
return s;
|
|
244
267
|
},
|
|
245
268
|
refresh() {
|
|
246
269
|
}
|
|
247
|
-
}), Object.freeze(
|
|
270
|
+
}), Object.freeze(r);
|
|
248
271
|
}
|
|
249
|
-
function R(
|
|
250
|
-
if (typeof
|
|
251
|
-
return new
|
|
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
|
|
254
|
-
#
|
|
255
|
-
#
|
|
256
|
-
#
|
|
257
|
-
#
|
|
258
|
-
#
|
|
259
|
-
#
|
|
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(
|
|
263
|
-
this.#
|
|
264
|
-
get: () => this.#
|
|
265
|
-
subscribe: (s) => this.#
|
|
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.#
|
|
295
|
+
this.#a();
|
|
273
296
|
}
|
|
274
|
-
#
|
|
275
|
-
return
|
|
297
|
+
#c() {
|
|
298
|
+
return m(this.view), this.#a();
|
|
276
299
|
}
|
|
277
|
-
#
|
|
278
|
-
|
|
279
|
-
const s = this.#
|
|
280
|
-
if (this.#
|
|
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.#
|
|
283
|
-
} catch (
|
|
284
|
-
throw this.#
|
|
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
|
|
287
|
-
this.#
|
|
309
|
+
return v(() => {
|
|
310
|
+
s.listener = void 0, this.#s.delete(s), this.#s.size || this.#h();
|
|
288
311
|
});
|
|
289
312
|
}
|
|
290
|
-
#
|
|
291
|
-
this.#
|
|
313
|
+
#p = () => {
|
|
314
|
+
this.#t || (this.#t = !0, w(this.#s));
|
|
292
315
|
};
|
|
293
|
-
#
|
|
294
|
-
for (const
|
|
295
|
-
|
|
316
|
+
#h() {
|
|
317
|
+
for (const e of this.#i.values())
|
|
318
|
+
e.subscription?.dispose(), e.subscription = void 0;
|
|
296
319
|
}
|
|
297
|
-
#
|
|
298
|
-
const
|
|
299
|
-
if (
|
|
300
|
-
for (const
|
|
301
|
-
if (
|
|
302
|
-
this.#
|
|
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.#
|
|
328
|
+
if (!this.#t) return e.value;
|
|
306
329
|
}
|
|
307
|
-
if (this.#
|
|
330
|
+
if (this.#r) throw new TypeError("Circular computed signal");
|
|
308
331
|
const s = /* @__PURE__ */ new Set(), i = f;
|
|
309
|
-
this.#
|
|
310
|
-
let
|
|
332
|
+
this.#r = !0, f = (n) => s.add(n);
|
|
333
|
+
let r;
|
|
311
334
|
try {
|
|
312
|
-
|
|
335
|
+
r = this.#e(), d(r, "Computed signal calculations must be synchronous");
|
|
313
336
|
} finally {
|
|
314
|
-
f = i, this.#
|
|
337
|
+
f = i, this.#r = !1;
|
|
315
338
|
}
|
|
316
|
-
for (const [
|
|
317
|
-
s.has(
|
|
318
|
-
for (const
|
|
319
|
-
const o = p.get(
|
|
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
|
|
344
|
+
const c = this.#i.get(n) ?? {
|
|
322
345
|
node: o,
|
|
323
346
|
version: o.version,
|
|
324
347
|
subscription: void 0
|
|
325
348
|
};
|
|
326
|
-
|
|
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 (
|
|
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
|
|
332
|
-
if (typeof
|
|
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
|
-
|
|
339
|
-
|
|
361
|
+
A as observe,
|
|
362
|
+
L as signal
|
|
340
363
|
};
|
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,EAAE,
|
|
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"}
|
package/dist/protocol.d.ts
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
export interface Disposable {
|
|
2
|
-
dispose(): void
|
|
3
|
-
[Symbol.dispose]
|
|
4
|
-
[Symbol.asyncDispose]?(): Promise<void>;
|
|
2
|
+
dispose(): void;
|
|
3
|
+
[Symbol.dispose](): void;
|
|
5
4
|
}
|
|
6
|
-
|
|
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;
|
package/dist/protocol.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,UAAU;IACzB,OAAO,IAAI,IAAI,
|
|
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"}
|