@dougongjs/reactive 0.8.2 → 0.9.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 +17 -81
- package/dist/abort.d.ts +4 -0
- package/dist/abort.d.ts.map +1 -0
- package/dist/bridge.d.ts +7 -0
- package/dist/bridge.d.ts.map +1 -0
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +576 -384
- package/dist/observe.d.ts +3 -3
- package/dist/observe.d.ts.map +1 -1
- package/dist/protocol.d.ts +6 -4
- package/dist/protocol.d.ts.map +1 -1
- package/dist/signals.d.ts +1 -1
- package/dist/signals.d.ts.map +1 -1
- package/dist/{sync-result.d.ts → synchronous-result.d.ts} +1 -1
- package/dist/synchronous-result.d.ts.map +1 -0
- package/package.json +11 -3
- package/runtime/abort-listener.d.mts +2 -0
- package/runtime/abort-listener.node.mjs +26 -0
- package/runtime/abort-listener.web.mjs +7 -0
- package/dist/sync-result.d.ts.map +0 -1
package/README.md
CHANGED
|
@@ -1,91 +1,27 @@
|
|
|
1
1
|
# @dougongjs/reactive
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Independent synchronous signals and structurally owned reactive generations. No external runtime dependencies.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
## 简体中文
|
|
8
|
-
|
|
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()
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
`double.get()` 返回 `42`。
|
|
26
|
-
|
|
27
|
-
`computed` 是惰性的,依赖动态追踪。`batch` 按订阅身份合并回调内的重复通知。三者都拒绝异步回调——同步追踪和批次边界不能跨越 `await`,与其产生静默的错误结果,不如立刻抛错。
|
|
28
|
-
|
|
29
|
-
### observe:把值的变化编译成资源的重建
|
|
30
|
-
|
|
31
|
-
```ts
|
|
32
|
-
observe(owner, source, (value, lifetime) => {
|
|
33
|
-
const socket = new WebSocket(value)
|
|
34
|
-
lifetime.cleanup(() => socket.close())
|
|
35
|
-
})
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
每次 `source` 变化:先释放上一个 `lifetime`,再用新值建一个新的。
|
|
39
|
-
|
|
40
|
-
`observe()` 与 `@dougongjs/core` **互不依赖**——它是一个作用在结构化 `ObservationOwner`(提供 `cleanup` / `lifetime` / `spawn`)上的自由函数,所以能驱动 Core 的 Lifetime,而 Core 不需要知道它存在。插件的 `ctx` 恰好满足这个形状。
|
|
41
|
-
|
|
42
|
-
`source` 只需满足结构化的 `Readable<T>`(`get()` + `subscribe()`)——Signal、Core 的 `ContributionView`、诊断视图,甚至你自己写的对象都可以。
|
|
43
|
-
|
|
44
|
-
---
|
|
45
|
-
|
|
46
|
-
## English
|
|
47
|
-
|
|
48
|
-
**Zero-dependency** reactive primitives: `signal` / `computed` / `batch` / `observe`.
|
|
49
|
-
|
|
50
|
-
```sh
|
|
51
|
-
npm install @dougongjs/reactive
|
|
52
|
-
```
|
|
5
|
+
Upgrading from 0.8.3? Read the [migration guide](https://tangerg.github.io/dougong/en/guide/migrating-to-0.9) and [0.9.0 release notes](https://tangerg.github.io/dougong/en/reference/release-0.9.0).
|
|
53
6
|
|
|
54
7
|
```ts
|
|
55
|
-
import {
|
|
56
|
-
|
|
57
|
-
const
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
8
|
+
import { batch, computed, signal } from "@dougongjs/reactive";
|
|
9
|
+
|
|
10
|
+
const first = signal("Dougong");
|
|
11
|
+
const last = signal("Plugins");
|
|
12
|
+
const title = computed(() => `${first.get()} ${last.get()}`);
|
|
13
|
+
const subscription = title.subscribe(() => render(title.get()));
|
|
14
|
+
batch(() => {
|
|
15
|
+
first.set("Typed");
|
|
16
|
+
last.set("Composition");
|
|
17
|
+
});
|
|
18
|
+
subscription[Symbol.dispose]();
|
|
62
19
|
```
|
|
63
20
|
|
|
64
|
-
`
|
|
65
|
-
|
|
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.
|
|
67
|
-
|
|
68
|
-
### observe: compiling value change into resource rebuild
|
|
69
|
-
|
|
70
|
-
```ts
|
|
71
|
-
observe(owner, source, (value, lifetime) => {
|
|
72
|
-
const socket = new WebSocket(value)
|
|
73
|
-
lifetime.cleanup(() => socket.close())
|
|
74
|
-
})
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
On each change to `source`: release the previous `lifetime`, then build a new one from the new value.
|
|
78
|
-
|
|
79
|
-
`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.
|
|
80
|
-
|
|
81
|
-
`source` need only satisfy the structural `Readable<T>` (`get()` + `subscribe()`) — a signal, Core's `ContributionView`, a diagnostics view, or your own object.
|
|
82
|
-
|
|
83
|
-
---
|
|
21
|
+
`Readable<T>` exposes `get()` and `subscribe()`. The subscription implements standard `Symbol.dispose`; callbacks notify changes without replay. `bridge(owner, readable)` adapts another module copy or implementation into the local dependency graph. Its subscription and terminal detachment belong to the supplied owner through `defer`.
|
|
84
22
|
|
|
85
|
-
|
|
86
|
-
- 仓库 / Repository: https://github.com/Tangerg/dougong
|
|
23
|
+
`observe(owner, readable, observer)` disposes the previous child before building the next one. The structural owner supplies `child`, `defer` and `spawn`; only the returned child needs `Symbol.asyncDispose`; no framework base class is required. Use `generation.spawn` for asynchronous observer work.
|
|
87
24
|
|
|
88
|
-
|
|
89
|
-
> Early 0.x development; no backward-compatibility promises yet. Requires Node.js ≥ 22, or Chrome / Edge 119, Firefox 121 or Safari 17.4 and later.
|
|
25
|
+
ESM; Node.js 22+, Chrome/Edge 119+, Firefox 124+, Safari 17.4+.
|
|
90
26
|
|
|
91
|
-
|
|
27
|
+
[Guide](https://tangerg.github.io/dougong/en/guide/reactive)
|
package/dist/abort.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare function isAborted(signal: AbortSignal): boolean;
|
|
2
|
+
export declare function abortReason(signal: AbortSignal): unknown;
|
|
3
|
+
export declare function onAbort(signal: AbortSignal, callback: (reason: unknown) => void): () => void;
|
|
4
|
+
//# sourceMappingURL=abort.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"abort.d.ts","sourceRoot":"","sources":["../src/abort.ts"],"names":[],"mappings":"AAIA,wBAAgB,SAAS,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAEtD;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAExD;AAED,wBAAgB,OAAO,CAAC,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,GAAG,MAAM,IAAI,CA6B5F"}
|
package/dist/bridge.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { AsyncDisposable, Readable } from './protocol.js';
|
|
2
|
+
import { ReadonlySignal } from './signals.js';
|
|
3
|
+
export interface BridgeOwner {
|
|
4
|
+
readonly defer: (dispose: () => unknown) => AsyncDisposable;
|
|
5
|
+
}
|
|
6
|
+
export declare function bridge<T>(owner: BridgeOwner, source: Readable<T>): ReadonlySignal<T>;
|
|
7
|
+
//# sourceMappingURL=bridge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,eAAe,EAEpB,KAAK,QAAQ,EACd,MAAM,eAAe,CAAC;AACvB,OAAO,EAAU,KAAK,cAAc,EAAe,MAAM,cAAc,CAAC;AAExE,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,KAAK,EAAE,CAAC,OAAO,EAAE,MAAM,OAAO,KAAK,eAAe,CAAC;CAC7D;AA2ID,wBAAgB,MAAM,CAAC,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,cAAc,CAAC,CAAC,CAAC,CAmBpF"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
export { batch, computed, signal, type ReadonlySignal, type Signal } from './signals';
|
|
2
|
-
export
|
|
3
|
-
export {
|
|
1
|
+
export { batch, computed, signal, type ReadonlySignal, type Signal } from './signals.js';
|
|
2
|
+
export { bridge, type BridgeOwner } from './bridge.js';
|
|
3
|
+
export type { AsyncDisposable, Disposable, Readable } from './protocol.js';
|
|
4
|
+
export { observe, type ObservationOwner, type ObservationTask, type Observer } from './observe.js';
|
|
4
5
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,cAAc,EAAE,KAAK,MAAM,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,cAAc,EAAE,KAAK,MAAM,EAAE,MAAM,cAAc,CAAC;AACzF,OAAO,EAAE,MAAM,EAAE,KAAK,WAAW,EAAE,MAAM,aAAa,CAAC;AACvD,YAAY,EAAE,eAAe,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC3E,OAAO,EAAE,OAAO,EAAE,KAAK,gBAAgB,EAAE,KAAK,eAAe,EAAE,KAAK,QAAQ,EAAE,MAAM,cAAc,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,406 +1,598 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { listenForAbort as e } from "#abort-listener";
|
|
2
|
+
// src/disposal-runtime.ts
|
|
3
|
+
function t(e) {
|
|
4
|
+
if (typeof e != "function") throw Error("Unsupported JavaScript runtime: Promise.withResolvers is required");
|
|
4
5
|
}
|
|
5
|
-
function
|
|
6
|
-
|
|
6
|
+
function n(e, t) {
|
|
7
|
+
return e ?? Symbol.for(t);
|
|
7
8
|
}
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
"Symbol.asyncDispose"
|
|
15
|
-
);
|
|
16
|
-
function a(s, e) {
|
|
17
|
-
const t = m(s);
|
|
18
|
-
if (t)
|
|
19
|
-
throw t.catch(() => {
|
|
20
|
-
}), new TypeError(e);
|
|
9
|
+
t(Promise.withResolvers);
|
|
10
|
+
var r = n(Symbol.dispose, "Symbol.dispose"), i = n(Symbol.asyncDispose, "Symbol.asyncDispose");
|
|
11
|
+
// src/synchronous-result.ts
|
|
12
|
+
function a(e, t) {
|
|
13
|
+
let n = o(e);
|
|
14
|
+
if (n) throw n.then(() => void 0, () => void 0), TypeError(t);
|
|
21
15
|
}
|
|
22
|
-
function
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
16
|
+
function o(e) {
|
|
17
|
+
if (e === null || typeof e != "object" && typeof e != "function") return;
|
|
18
|
+
let t = e.then;
|
|
19
|
+
if (typeof t != "function") return;
|
|
20
|
+
let n = Promise.withResolvers();
|
|
21
|
+
return queueMicrotask(() => {
|
|
22
|
+
try {
|
|
23
|
+
Reflect.apply(t, e, [n.resolve, n.reject]);
|
|
24
|
+
} catch (e) {
|
|
25
|
+
n.reject(e);
|
|
26
|
+
}
|
|
27
|
+
}), n.promise;
|
|
29
28
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
let
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
}
|
|
39
|
-
dispose() {
|
|
40
|
-
const e = this.#e;
|
|
41
|
-
this.#e = void 0, e?.();
|
|
42
|
-
}
|
|
43
|
-
[w]() {
|
|
44
|
-
this.dispose();
|
|
45
|
-
}
|
|
29
|
+
// src/protocol.ts
|
|
30
|
+
function s(e, t) {
|
|
31
|
+
let n = e?.get, r = e?.subscribe;
|
|
32
|
+
if (typeof n != "function" || typeof r != "function") throw TypeError(`${t} expects a readable source`);
|
|
33
|
+
return Object.freeze({
|
|
34
|
+
get: () => Reflect.apply(n, e, []),
|
|
35
|
+
subscribe: (t) => Reflect.apply(r, e, [t])
|
|
36
|
+
});
|
|
46
37
|
}
|
|
47
|
-
function
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
p = !0;
|
|
51
|
-
try {
|
|
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;
|
|
56
|
-
if (i)
|
|
57
|
-
try {
|
|
58
|
-
a(i(), "Signal subscribers must be synchronous");
|
|
59
|
-
} catch (r) {
|
|
60
|
-
s.push(r);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
} finally {
|
|
64
|
-
f.clear(), o.clear(), p = !1;
|
|
65
|
-
}
|
|
66
|
-
R(s);
|
|
38
|
+
function c(e) {
|
|
39
|
+
let t = e?.[r];
|
|
40
|
+
return typeof t != "function" && u(e, "Readable.subscribe() must return a Disposable"), () => a(Reflect.apply(t, e, []), "Readable subscriptions must dispose synchronously");
|
|
67
41
|
}
|
|
68
|
-
function
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
u || O();
|
|
42
|
+
function l(e, t) {
|
|
43
|
+
let n = e?.[i];
|
|
44
|
+
return typeof n != "function" && u(e, t), () => Reflect.apply(n, e, []);
|
|
72
45
|
}
|
|
73
|
-
function
|
|
74
|
-
|
|
75
|
-
const i = { kind: t, listener: e };
|
|
76
|
-
return s.add(i), new g(() => {
|
|
77
|
-
i.listener = void 0, s.delete(i);
|
|
78
|
-
});
|
|
46
|
+
function u(e, t) {
|
|
47
|
+
throw a(e, t), TypeError(t);
|
|
79
48
|
}
|
|
80
|
-
function
|
|
81
|
-
|
|
82
|
-
if (s.length > 1)
|
|
83
|
-
throw new AggregateError(s, "Reactive subscribers failed");
|
|
49
|
+
function d(e) {
|
|
50
|
+
if (typeof e != "function") throw TypeError("Signal subscriber must be a function");
|
|
84
51
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
52
|
+
// src/signals.ts
|
|
53
|
+
var f = 0, p = 0, m = !1, h = !1, g = /* @__PURE__ */ new Set(), _ = /* @__PURE__ */ new Set(), v, y = [], b = class {
|
|
54
|
+
#e;
|
|
55
|
+
constructor(e) {
|
|
56
|
+
this.#e = e, Object.freeze(this);
|
|
57
|
+
}
|
|
58
|
+
[r]() {
|
|
59
|
+
let e = this.#e;
|
|
60
|
+
this.#e = void 0, e?.();
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
function x() {
|
|
64
|
+
if (m) return;
|
|
65
|
+
let e = [];
|
|
66
|
+
m = !0;
|
|
67
|
+
try {
|
|
68
|
+
for (; g.size;) {
|
|
69
|
+
let t = g.values().next().value;
|
|
70
|
+
g.delete(t);
|
|
71
|
+
let n = t.listener;
|
|
72
|
+
if (n) try {
|
|
73
|
+
a(n(), "Signal subscribers must be synchronous");
|
|
74
|
+
} catch (t) {
|
|
75
|
+
e.push(t);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
} finally {
|
|
79
|
+
g.clear(), m = !1;
|
|
80
|
+
}
|
|
81
|
+
w(e);
|
|
108
82
|
}
|
|
109
|
-
function
|
|
110
|
-
|
|
83
|
+
function S(e) {
|
|
84
|
+
for (let t of e) (t.kind === "dependency" ? _ : g).add(t);
|
|
85
|
+
if (!h) {
|
|
86
|
+
h = !0;
|
|
87
|
+
try {
|
|
88
|
+
for (; _.size;) {
|
|
89
|
+
let e = _.values().next().value;
|
|
90
|
+
_.delete(e);
|
|
91
|
+
let t = e.listener;
|
|
92
|
+
t?.();
|
|
93
|
+
}
|
|
94
|
+
} finally {
|
|
95
|
+
h = !1, _.clear();
|
|
96
|
+
}
|
|
97
|
+
p || x();
|
|
98
|
+
}
|
|
111
99
|
}
|
|
112
|
-
function
|
|
113
|
-
|
|
114
|
-
|
|
100
|
+
function C(e, t, n) {
|
|
101
|
+
d(t);
|
|
102
|
+
let r = {
|
|
103
|
+
kind: n,
|
|
104
|
+
listener: t
|
|
105
|
+
};
|
|
106
|
+
return e.add(r), new b(() => {
|
|
107
|
+
r.listener = void 0, e.delete(r);
|
|
108
|
+
});
|
|
115
109
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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
|
-
}
|
|
110
|
+
function w(e) {
|
|
111
|
+
if (e.length === 1) throw e[0];
|
|
112
|
+
if (e.length > 1) throw AggregateError(e, "Reactive subscribers failed");
|
|
141
113
|
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
const e = this.#r;
|
|
174
|
-
if (e.phase === "error") throw e.error;
|
|
175
|
-
return e.value;
|
|
176
|
-
}
|
|
177
|
-
subscribeInvalidation(e) {
|
|
178
|
-
return this.#h(e, "dependency");
|
|
179
|
-
}
|
|
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)
|
|
184
|
-
try {
|
|
185
|
-
this.refresh(), t === "observer" && this.#a(), this.#f();
|
|
186
|
-
} catch (n) {
|
|
187
|
-
throw this.#t.delete(i), this.#t.size || this.#u(), n;
|
|
188
|
-
}
|
|
189
|
-
return new g(() => {
|
|
190
|
-
i.listener = void 0, this.#t.delete(i), this.#t.size || this.#u();
|
|
191
|
-
});
|
|
192
|
-
}
|
|
193
|
-
#d = () => {
|
|
194
|
-
this.#i || (this.#i = !0, S(this.#t));
|
|
195
|
-
};
|
|
196
|
-
#u() {
|
|
197
|
-
for (const e of this.#s.values())
|
|
198
|
-
e.subscription?.dispose(), e.subscription = void 0;
|
|
199
|
-
}
|
|
200
|
-
#f() {
|
|
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);
|
|
219
|
-
try {
|
|
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 };
|
|
224
|
-
} finally {
|
|
225
|
-
c = t;
|
|
226
|
-
}
|
|
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,
|
|
237
|
-
subscription: void 0
|
|
238
|
-
};
|
|
239
|
-
r.version = i.version, this.#s.set(t, r);
|
|
240
|
-
}
|
|
241
|
-
}
|
|
114
|
+
function T(e) {
|
|
115
|
+
if (typeof e != "function") throw TypeError("batch() expects a function");
|
|
116
|
+
p++;
|
|
117
|
+
let t;
|
|
118
|
+
try {
|
|
119
|
+
let n = e();
|
|
120
|
+
a(n, "Reactive batches must be synchronous"), t = {
|
|
121
|
+
succeeded: !0,
|
|
122
|
+
value: n
|
|
123
|
+
};
|
|
124
|
+
} catch (e) {
|
|
125
|
+
t = {
|
|
126
|
+
succeeded: !1,
|
|
127
|
+
error: e
|
|
128
|
+
};
|
|
129
|
+
} finally {
|
|
130
|
+
p--;
|
|
131
|
+
}
|
|
132
|
+
if (!p) try {
|
|
133
|
+
x();
|
|
134
|
+
} catch (e) {
|
|
135
|
+
t = t.succeeded ? {
|
|
136
|
+
succeeded: !1,
|
|
137
|
+
error: e
|
|
138
|
+
} : {
|
|
139
|
+
succeeded: !1,
|
|
140
|
+
error: AggregateError([t.error, e], "Reactive batch failed")
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
if (!t.succeeded) throw t.error;
|
|
144
|
+
return t.value;
|
|
242
145
|
}
|
|
243
|
-
function
|
|
244
|
-
|
|
245
|
-
throw new TypeError("Signal subscriber must be a function");
|
|
146
|
+
function E(e) {
|
|
147
|
+
return new O(e).view;
|
|
246
148
|
}
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
#s;
|
|
251
|
-
#r = { present: !1 };
|
|
252
|
-
#n = !1;
|
|
253
|
-
#i;
|
|
254
|
-
#c;
|
|
255
|
-
#o;
|
|
256
|
-
#a;
|
|
257
|
-
constructor(e, t, i) {
|
|
258
|
-
this.#e = { phase: "active", binding: { owner: e, source: t, observer: i } };
|
|
259
|
-
}
|
|
260
|
-
start() {
|
|
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)
|
|
269
|
-
);
|
|
270
|
-
}
|
|
271
|
-
attachOwnerCleanup(e) {
|
|
272
|
-
this.#c = e;
|
|
273
|
-
}
|
|
274
|
-
dispose() {
|
|
275
|
-
if (this.#a) return this.#a;
|
|
276
|
-
const e = Promise.withResolvers();
|
|
277
|
-
return this.#a = e.promise, this.#c = void 0, this.#g("disposed"), this.#o?.(), this.#h().then(e.resolve, e.reject), e.promise;
|
|
278
|
-
}
|
|
279
|
-
async #h() {
|
|
280
|
-
const e = [];
|
|
281
|
-
if (await this.#l(e), await h(this.#O(), e), await h(this.#w(), e), e.length === 1) throw e[0];
|
|
282
|
-
if (e.length > 1) throw new AggregateError(e, "Observation cleanup failed");
|
|
283
|
-
}
|
|
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.
|
|
288
|
-
async #u(e) {
|
|
289
|
-
try {
|
|
290
|
-
for (; this.#e.phase === "active" && !e.aborted; )
|
|
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);
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
#f(e) {
|
|
299
|
-
return this.#n || this.#e.phase !== "active" || e.aborted ? Promise.resolve() : new Promise((t) => {
|
|
300
|
-
const i = () => {
|
|
301
|
-
e.removeEventListener("abort", i), this.#o === i && (this.#o = void 0), t();
|
|
302
|
-
};
|
|
303
|
-
this.#o = i, e.addEventListener("abort", i, { once: !0 }), (this.#n || this.#e.phase !== "active" || e.aborted) && i();
|
|
304
|
-
});
|
|
305
|
-
}
|
|
306
|
-
// Clear the observed marker during replacement so interruption cannot leave a value without its owner.
|
|
307
|
-
async #v(e) {
|
|
308
|
-
if (this.#e.phase !== "active" || e.aborted) return;
|
|
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();
|
|
313
|
-
}
|
|
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)
|
|
327
|
-
try {
|
|
328
|
-
v(t);
|
|
329
|
-
} catch (i) {
|
|
330
|
-
e.push(i);
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
#w() {
|
|
334
|
-
const e = this.#s;
|
|
335
|
-
return this.#s = void 0, e;
|
|
336
|
-
}
|
|
337
|
-
#O() {
|
|
338
|
-
const e = this.#i;
|
|
339
|
-
return this.#i = void 0, e;
|
|
340
|
-
}
|
|
341
|
-
#S() {
|
|
342
|
-
const e = this.#c;
|
|
343
|
-
return this.#c = void 0, e;
|
|
344
|
-
}
|
|
345
|
-
#m(e) {
|
|
346
|
-
this.#i === e && (this.#i = void 0);
|
|
347
|
-
}
|
|
348
|
-
async #E(e) {
|
|
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");
|
|
351
|
-
}
|
|
352
|
-
#y() {
|
|
353
|
-
const e = this.#e;
|
|
354
|
-
if (e.phase !== "active") throw new Error("Observation is not active");
|
|
355
|
-
return e.binding;
|
|
356
|
-
}
|
|
357
|
-
#g(e) {
|
|
358
|
-
this.#e = { phase: e }, this.#r = { present: !1 }, this.#n = !1;
|
|
359
|
-
}
|
|
149
|
+
function D(e) {
|
|
150
|
+
if (typeof e != "function") throw TypeError("computed() expects a function");
|
|
151
|
+
return new k(e).view;
|
|
360
152
|
}
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
153
|
+
var O = class {
|
|
154
|
+
#e = /* @__PURE__ */ new Set();
|
|
155
|
+
#t;
|
|
156
|
+
#n = 0;
|
|
157
|
+
view;
|
|
158
|
+
constructor(e) {
|
|
159
|
+
this.#t = e, this.view = Object.freeze({
|
|
160
|
+
get: () => (v?.add(this), this.#t),
|
|
161
|
+
set: (e) => this.#r(e),
|
|
162
|
+
subscribe: (e) => C(this.#e, e, "observer")
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
get version() {
|
|
166
|
+
return this.#n;
|
|
167
|
+
}
|
|
168
|
+
refresh() {}
|
|
169
|
+
subscribeInvalidation(e) {
|
|
170
|
+
return C(this.#e, e, "dependency");
|
|
171
|
+
}
|
|
172
|
+
#r(e) {
|
|
173
|
+
if (v) throw TypeError("Computed signal calculations cannot write signals");
|
|
174
|
+
Object.is(this.#t, e) || (this.#t = e, f++, this.#n++, S(this.#e));
|
|
175
|
+
}
|
|
176
|
+
}, k = class e {
|
|
177
|
+
#e;
|
|
178
|
+
#t = /* @__PURE__ */ new Set();
|
|
179
|
+
#n = /* @__PURE__ */ new Map();
|
|
180
|
+
#r;
|
|
181
|
+
#i = -1;
|
|
182
|
+
#a;
|
|
183
|
+
#o;
|
|
184
|
+
#s = !0;
|
|
185
|
+
#c = 0;
|
|
186
|
+
view;
|
|
187
|
+
constructor(e) {
|
|
188
|
+
this.#e = e, this.view = Object.freeze({
|
|
189
|
+
get: () => this.#u(),
|
|
190
|
+
subscribe: (e) => this.#f(e)
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
get version() {
|
|
194
|
+
return this.#c;
|
|
195
|
+
}
|
|
196
|
+
refresh() {
|
|
197
|
+
if (this.#a !== void 0) throw v && this.#l(this.#a, v), TypeError("Circular computed signal");
|
|
198
|
+
this.#a = y.length, y.push(this);
|
|
199
|
+
try {
|
|
200
|
+
this.#g();
|
|
201
|
+
} finally {
|
|
202
|
+
y.pop(), this.#a = void 0;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
#l(t, n) {
|
|
206
|
+
let r = /* @__PURE__ */ new Set();
|
|
207
|
+
for (let e of y.slice(t)) for (let t of e.#o ?? e.#n.keys()) r.add(t);
|
|
208
|
+
for (let t of r) if (t instanceof e) for (let e of t.#n.keys()) r.add(e);
|
|
209
|
+
else n.add(t);
|
|
210
|
+
}
|
|
211
|
+
#u() {
|
|
212
|
+
return this.refresh(), v?.add(this), this.#d();
|
|
213
|
+
}
|
|
214
|
+
#d() {
|
|
215
|
+
let e = this.#r;
|
|
216
|
+
if (e.phase === "error") throw e.error;
|
|
217
|
+
return e.value;
|
|
218
|
+
}
|
|
219
|
+
subscribeInvalidation(e) {
|
|
220
|
+
return this.#f(e, "dependency");
|
|
221
|
+
}
|
|
222
|
+
#f(e, t = "observer") {
|
|
223
|
+
d(e);
|
|
224
|
+
let n = {
|
|
225
|
+
kind: t,
|
|
226
|
+
listener: e
|
|
227
|
+
}, r = this.#t.size === 0;
|
|
228
|
+
if (this.#t.add(n), r || t === "observer") try {
|
|
229
|
+
this.refresh(), t === "observer" && this.#d(), r && this.#h();
|
|
230
|
+
} catch (e) {
|
|
231
|
+
throw this.#t.delete(n), this.#t.size || this.#m(), e;
|
|
232
|
+
}
|
|
233
|
+
return new b(() => {
|
|
234
|
+
n.listener = void 0, this.#t.delete(n), this.#t.size || this.#m();
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
#p = () => {
|
|
238
|
+
this.#s || (this.#s = !0, S(this.#t));
|
|
239
|
+
};
|
|
240
|
+
#m() {
|
|
241
|
+
for (let e of this.#n.values()) e.subscription?.[r](), e.subscription = void 0;
|
|
242
|
+
}
|
|
243
|
+
#h() {
|
|
244
|
+
for (let [e, t] of this.#n) t.subscription ??= e.subscribeInvalidation(this.#p);
|
|
245
|
+
}
|
|
246
|
+
#g() {
|
|
247
|
+
let e = this.#r;
|
|
248
|
+
if (e && !this.#s && (this.#i === f || !this.#_())) {
|
|
249
|
+
this.#i = f;
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
this.#s = !0;
|
|
253
|
+
let t = /* @__PURE__ */ new Set(), n = this.#v(t);
|
|
254
|
+
this.#y(t), (!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.#r = n, this.#i = f, this.#s = !1, this.#t.size && this.#h();
|
|
255
|
+
}
|
|
256
|
+
#_() {
|
|
257
|
+
for (let [t, n] of this.#n) if (t instanceof e && t.#a !== void 0 || (t.refresh(), n.version !== t.version)) return !0;
|
|
258
|
+
return this.#s;
|
|
259
|
+
}
|
|
260
|
+
#v(e) {
|
|
261
|
+
let t = v;
|
|
262
|
+
this.#o = e, v = e;
|
|
263
|
+
try {
|
|
264
|
+
let e = this.#e, t = e();
|
|
265
|
+
return a(t, "Computed signal calculations must be synchronous"), {
|
|
266
|
+
phase: "value",
|
|
267
|
+
value: t
|
|
268
|
+
};
|
|
269
|
+
} catch (e) {
|
|
270
|
+
return {
|
|
271
|
+
phase: "error",
|
|
272
|
+
error: e
|
|
273
|
+
};
|
|
274
|
+
} finally {
|
|
275
|
+
v = t, this.#o = void 0;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
#y(e) {
|
|
279
|
+
for (let [t, n] of this.#n) e.has(t) || (n.subscription?.[r](), this.#n.delete(t));
|
|
280
|
+
for (let t of e) {
|
|
281
|
+
let e = this.#n.get(t);
|
|
282
|
+
e ? e.version = t.version : this.#n.set(t, {
|
|
283
|
+
version: t.version,
|
|
284
|
+
subscription: void 0
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}, A = class {
|
|
289
|
+
#e;
|
|
290
|
+
#t;
|
|
291
|
+
#n;
|
|
292
|
+
#r;
|
|
293
|
+
view;
|
|
294
|
+
constructor(e) {
|
|
295
|
+
this.#e = E({
|
|
296
|
+
phase: "value",
|
|
297
|
+
source: e,
|
|
298
|
+
value: e.get()
|
|
299
|
+
}), this.view = Object.freeze({
|
|
300
|
+
get: () => {
|
|
301
|
+
let e = this.#e?.get();
|
|
302
|
+
if (!e) throw Error("Readable bridge has been disposed");
|
|
303
|
+
if (e.phase === "error") throw e.error;
|
|
304
|
+
return e.value;
|
|
305
|
+
},
|
|
306
|
+
subscribe: (e) => this.#o(e)
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
start(e) {
|
|
310
|
+
let t = !0, n;
|
|
311
|
+
try {
|
|
312
|
+
n = l(e.defer(() => (this[r](), t ? Promise.resolve().then(() => this.#i()) : void 0)), "BridgeOwner.defer() must return an AsyncDisposable");
|
|
313
|
+
let i = this.#e?.get();
|
|
314
|
+
if (!i) throw Error("Readable bridge has been disposed");
|
|
315
|
+
let a = c(i.source.subscribe(() => this.#a()));
|
|
316
|
+
if (this.#t = a, !this.#e?.get()) {
|
|
317
|
+
try {
|
|
318
|
+
a(), this.#t = void 0;
|
|
319
|
+
} catch (e) {
|
|
320
|
+
this.#r = { value: e };
|
|
321
|
+
}
|
|
322
|
+
throw Error("Readable bridge has been disposed");
|
|
323
|
+
}
|
|
324
|
+
this.#a(), this.view.get();
|
|
325
|
+
} catch (e) {
|
|
326
|
+
throw n && Promise.resolve().then(n).catch(() => void 0), e;
|
|
327
|
+
} finally {
|
|
328
|
+
t = !1;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
#i() {
|
|
332
|
+
if (this.#r) throw this.#r.value;
|
|
333
|
+
}
|
|
334
|
+
#a() {
|
|
335
|
+
let e = this.#e?.get();
|
|
336
|
+
if (!e) return;
|
|
337
|
+
let t = Symbol();
|
|
338
|
+
this.#n = t;
|
|
339
|
+
try {
|
|
340
|
+
let { source: n } = e, r;
|
|
341
|
+
try {
|
|
342
|
+
r = {
|
|
343
|
+
phase: "value",
|
|
344
|
+
source: n,
|
|
345
|
+
value: n.get()
|
|
346
|
+
};
|
|
347
|
+
} catch (e) {
|
|
348
|
+
r = {
|
|
349
|
+
phase: "error",
|
|
350
|
+
source: n,
|
|
351
|
+
error: e
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
if (this.#n !== t || this.#e?.get() !== e || e.phase === "value" && r.phase === "value" && Object.is(e.value, r.value) || e.phase === "error" && r.phase === "error" && Object.is(e.error, r.error)) return;
|
|
355
|
+
this.#e?.set(r);
|
|
356
|
+
} finally {
|
|
357
|
+
this.#n = void 0;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
#o(e) {
|
|
361
|
+
let t = this.#e;
|
|
362
|
+
if (!t?.get()) throw Error("Readable bridge has been disposed");
|
|
363
|
+
d(e);
|
|
364
|
+
let n = t.subscribe(() => (this.#e?.get() || n[r](), e()));
|
|
365
|
+
return n;
|
|
366
|
+
}
|
|
367
|
+
[r]() {
|
|
368
|
+
let e = this.#e;
|
|
369
|
+
if (!e?.get()) return this.#i();
|
|
370
|
+
let t = [];
|
|
371
|
+
try {
|
|
372
|
+
e.set(void 0);
|
|
373
|
+
} catch (n) {
|
|
374
|
+
if (e.get()) throw n;
|
|
375
|
+
t.push(n);
|
|
376
|
+
}
|
|
377
|
+
this.#e = void 0;
|
|
378
|
+
try {
|
|
379
|
+
this.#t?.(), this.#t = void 0;
|
|
380
|
+
} catch (e) {
|
|
381
|
+
t.push(e);
|
|
382
|
+
}
|
|
383
|
+
if (t.length) {
|
|
384
|
+
let e = t.length === 1 ? t[0] : AggregateError(t, "Readable bridge cleanup failed");
|
|
385
|
+
throw this.#r = { value: e }, e;
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
};
|
|
389
|
+
function j(e, t) {
|
|
390
|
+
let n = e?.defer;
|
|
391
|
+
if (typeof n != "function") throw TypeError("bridge() expects a resource owner");
|
|
392
|
+
let i = s(t, "bridge()"), a = { defer: (t) => Reflect.apply(n, e, [t]) }, o = new A(i);
|
|
393
|
+
try {
|
|
394
|
+
return o.start(a), o.view;
|
|
395
|
+
} catch (e) {
|
|
396
|
+
try {
|
|
397
|
+
o[r]();
|
|
398
|
+
} catch (t) {
|
|
399
|
+
throw AggregateError([e, t], "Readable bridge construction failed");
|
|
400
|
+
}
|
|
401
|
+
throw e;
|
|
402
|
+
}
|
|
375
403
|
}
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
404
|
+
// src/abort.ts
|
|
405
|
+
function M(e) {
|
|
406
|
+
return Reflect.get(AbortSignal.prototype, "aborted", e);
|
|
379
407
|
}
|
|
380
|
-
function
|
|
381
|
-
|
|
382
|
-
throw new TypeError(`${e} must return an AsyncDisposable`);
|
|
408
|
+
function N(e) {
|
|
409
|
+
return Reflect.get(AbortSignal.prototype, "reason", e);
|
|
383
410
|
}
|
|
384
|
-
function
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
411
|
+
function P(t, n) {
|
|
412
|
+
if (typeof n != "function") throw TypeError("Abort listener must be a function");
|
|
413
|
+
if (M(t)) return n(N(t)), () => {};
|
|
414
|
+
let r = {
|
|
415
|
+
signal: t,
|
|
416
|
+
callback: n
|
|
417
|
+
}, i;
|
|
418
|
+
function a() {
|
|
419
|
+
r = void 0, i?.(), i = void 0;
|
|
420
|
+
}
|
|
421
|
+
function o() {
|
|
422
|
+
if (!r || !M(r.signal)) return;
|
|
423
|
+
let { signal: e, callback: t } = r, n = N(e);
|
|
424
|
+
a(), t(n);
|
|
425
|
+
}
|
|
426
|
+
return i = e(t, o), r ? o() : a(), a;
|
|
389
427
|
}
|
|
390
|
-
|
|
391
|
-
|
|
428
|
+
// src/observe.ts
|
|
429
|
+
var F = class {
|
|
430
|
+
#e;
|
|
431
|
+
#t;
|
|
432
|
+
#n;
|
|
433
|
+
#r;
|
|
434
|
+
#i = !1;
|
|
435
|
+
#a = { present: !1 };
|
|
436
|
+
#o = !1;
|
|
437
|
+
#s;
|
|
438
|
+
#c;
|
|
439
|
+
#l;
|
|
440
|
+
#u;
|
|
441
|
+
constructor(e, t, n) {
|
|
442
|
+
this.#e = {
|
|
443
|
+
phase: "active",
|
|
444
|
+
binding: {
|
|
445
|
+
owner: e,
|
|
446
|
+
source: t,
|
|
447
|
+
observer: n
|
|
448
|
+
}
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
start() {
|
|
452
|
+
this.#i = !0;
|
|
453
|
+
try {
|
|
454
|
+
this.#d();
|
|
455
|
+
} finally {
|
|
456
|
+
this.#i = !1;
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
#d() {
|
|
460
|
+
let { owner: e, source: t } = this.#S();
|
|
461
|
+
this.#t = c(t.subscribe(() => this.#p())), this.#g();
|
|
462
|
+
let n = e.spawn((e) => this.#m(e)), r = l(n, "ObservationOwner.spawn() must return an ObservationTask");
|
|
463
|
+
this.#s = r;
|
|
464
|
+
let i = o(n.result);
|
|
465
|
+
if (!i) throw TypeError("ObservationOwner.spawn() must return an ObservationTask");
|
|
466
|
+
i.then(() => this.#b(r), () => this.#b(r));
|
|
467
|
+
}
|
|
468
|
+
attachOwnerCleanup(e) {
|
|
469
|
+
this.#c = e;
|
|
470
|
+
}
|
|
471
|
+
dispose() {
|
|
472
|
+
if (this.#u) return this.#u;
|
|
473
|
+
let e = Promise.withResolvers();
|
|
474
|
+
return this.#u = e.promise, this.#c = void 0, this.#C("disposed"), this.#l?.(), (this.#i ? Promise.resolve().then(() => this.#f()) : this.#f()).then(e.resolve, e.reject), e.promise;
|
|
475
|
+
}
|
|
476
|
+
async #f() {
|
|
477
|
+
let e = [];
|
|
478
|
+
try {
|
|
479
|
+
this.#t?.(), this.#t = void 0;
|
|
480
|
+
} catch (t) {
|
|
481
|
+
e.push(t);
|
|
482
|
+
}
|
|
483
|
+
await L(this.#y(), e);
|
|
484
|
+
try {
|
|
485
|
+
await this.#v();
|
|
486
|
+
} catch (t) {
|
|
487
|
+
e.push(t);
|
|
488
|
+
}
|
|
489
|
+
if (e.length === 1) throw e[0];
|
|
490
|
+
if (e.length > 1) throw AggregateError(e, "Observation cleanup failed");
|
|
491
|
+
}
|
|
492
|
+
#p() {
|
|
493
|
+
this.#e.phase === "active" && (this.#o = !0, this.#l?.());
|
|
494
|
+
}
|
|
495
|
+
async #m(e) {
|
|
496
|
+
let t = P(e, () => this.#l?.());
|
|
497
|
+
try {
|
|
498
|
+
for (; this.#e.phase === "active" && !M(e);) {
|
|
499
|
+
if (!this.#o) {
|
|
500
|
+
let e = Promise.withResolvers();
|
|
501
|
+
this.#l = e.resolve, await e.promise, this.#l = void 0;
|
|
502
|
+
}
|
|
503
|
+
for (; this.#e.phase === "active" && !M(e) && this.#o;) this.#o = !1, await this.#h(e);
|
|
504
|
+
}
|
|
505
|
+
} catch (e) {
|
|
506
|
+
if (this.#e.phase !== "active") throw e;
|
|
507
|
+
try {
|
|
508
|
+
await this.#x();
|
|
509
|
+
} catch (t) {
|
|
510
|
+
throw AggregateError([e, t], "Observation stopped after a replacement failed");
|
|
511
|
+
}
|
|
512
|
+
throw e;
|
|
513
|
+
} finally {
|
|
514
|
+
t();
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
async #h(e) {
|
|
518
|
+
if (this.#e.phase !== "active" || M(e)) return;
|
|
519
|
+
let t = this.#e.binding.source.get();
|
|
520
|
+
if (!(this.#a.present && Object.is(t, this.#a.value))) {
|
|
521
|
+
try {
|
|
522
|
+
await this.#v();
|
|
523
|
+
} catch (e) {
|
|
524
|
+
throw this.#e.phase === "active" && await this.#x(), e;
|
|
525
|
+
}
|
|
526
|
+
this.#a = { present: !1 }, !(this.#e.phase !== "active" || M(e)) && this.#g(e);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
#g(e) {
|
|
530
|
+
let { owner: t } = this.#S(), n = t.child("observation");
|
|
531
|
+
if (this.#n = l(n, "ObservationOwner.child() must return an AsyncDisposable"), e && M(e)) return;
|
|
532
|
+
let r = this.#S().source.get();
|
|
533
|
+
e && M(e) || (this.#_(r, n), this.#e.phase === "active" && (this.#a = {
|
|
534
|
+
present: !0,
|
|
535
|
+
value: r
|
|
536
|
+
}));
|
|
537
|
+
}
|
|
538
|
+
#_(e, t) {
|
|
539
|
+
let { observer: n } = this.#S();
|
|
540
|
+
a(n(e, t), "Observers must be synchronous; use owner.spawn() for async work");
|
|
541
|
+
}
|
|
542
|
+
#v() {
|
|
543
|
+
let e = this.#n;
|
|
544
|
+
if (e) return this.#r ??= Promise.resolve().then(e).then(() => {
|
|
545
|
+
this.#n = void 0, this.#r = void 0;
|
|
546
|
+
});
|
|
547
|
+
}
|
|
548
|
+
#y() {
|
|
549
|
+
let e = this.#s;
|
|
550
|
+
return this.#s = void 0, e;
|
|
551
|
+
}
|
|
552
|
+
#b(e) {
|
|
553
|
+
this.#s === e && (this.#s = void 0);
|
|
554
|
+
}
|
|
555
|
+
async #x() {
|
|
556
|
+
this.#C("stopped"), this.#s = void 0;
|
|
557
|
+
let e = this.#c;
|
|
558
|
+
e ? await e() : await this.dispose();
|
|
559
|
+
}
|
|
560
|
+
#S() {
|
|
561
|
+
let e = this.#e;
|
|
562
|
+
if (e.phase !== "active") throw Error("Observation is not active");
|
|
563
|
+
return e.binding;
|
|
564
|
+
}
|
|
565
|
+
#C(e) {
|
|
566
|
+
this.#e = { phase: e }, this.#a = { present: !1 }, this.#o = !1;
|
|
567
|
+
}
|
|
568
|
+
};
|
|
569
|
+
function I(e, t, n) {
|
|
570
|
+
let r = s(t, "observe()");
|
|
571
|
+
if (typeof n != "function") throw TypeError("Observer must be a function");
|
|
572
|
+
let a = e?.defer, o = e?.child, c = e?.spawn;
|
|
573
|
+
if (typeof a != "function" || typeof o != "function" || typeof c != "function") throw TypeError("observe() expects an observation owner");
|
|
574
|
+
let u = {
|
|
575
|
+
defer: (t) => Reflect.apply(a, e, [t]),
|
|
576
|
+
child: (t) => Reflect.apply(o, e, [t]),
|
|
577
|
+
spawn: (t) => Reflect.apply(c, e, [t])
|
|
578
|
+
}, d = new F(u, r, n), f = l(u.defer(() => d.dispose()), "ObservationOwner.defer() must return an AsyncDisposable");
|
|
579
|
+
d.attachOwnerCleanup(f);
|
|
580
|
+
try {
|
|
581
|
+
return d.start(), Object.freeze({ [i]: f });
|
|
582
|
+
} catch (e) {
|
|
583
|
+
try {
|
|
584
|
+
Promise.resolve(f()).catch(() => void 0);
|
|
585
|
+
} catch (t) {
|
|
586
|
+
throw AggregateError([e, t], "Observation construction failed");
|
|
587
|
+
}
|
|
588
|
+
throw e;
|
|
589
|
+
}
|
|
392
590
|
}
|
|
393
|
-
async function
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
}
|
|
591
|
+
async function L(e, t) {
|
|
592
|
+
if (e) try {
|
|
593
|
+
await e();
|
|
594
|
+
} catch (e) {
|
|
595
|
+
t.push(e);
|
|
596
|
+
}
|
|
400
597
|
}
|
|
401
|
-
export {
|
|
402
|
-
I as batch,
|
|
403
|
-
q as computed,
|
|
404
|
-
B as observe,
|
|
405
|
-
L as signal
|
|
406
|
-
};
|
|
598
|
+
export { T as batch, j as bridge, D as computed, I as observe, E as signal };
|
package/dist/observe.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { AsyncDisposable, Readable } from './protocol';
|
|
1
|
+
import { AsyncDisposable, Readable } from './protocol.js';
|
|
2
2
|
export interface ObservationTask<T = void> extends AsyncDisposable {
|
|
3
3
|
readonly result: Promise<T>;
|
|
4
4
|
}
|
|
5
5
|
export interface ObservationOwner<Child extends AsyncDisposable = AsyncDisposable> {
|
|
6
|
-
readonly
|
|
7
|
-
readonly
|
|
6
|
+
readonly defer: (dispose: () => unknown) => AsyncDisposable;
|
|
7
|
+
readonly child: (label: string) => Child;
|
|
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;
|
package/dist/observe.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"observe.d.ts","sourceRoot":"","sources":["../src/observe.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"observe.d.ts","sourceRoot":"","sources":["../src/observe.ts"],"names":[],"mappings":"AACA,OAAO,EAKL,KAAK,eAAe,EACpB,KAAK,QAAQ,EACd,MAAM,eAAe,CAAC;AAKvB,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,KAAK,EAAE,CAAC,OAAO,EAAE,MAAM,OAAO,KAAK,eAAe,CAAC;IAC5D,QAAQ,CAAC,KAAK,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,KAAK,CAAC;IACzC,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;AAoO7F,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,CAiCjB"}
|
package/dist/protocol.d.ts
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
export { assertPromiseRuntime, resolveDisposalSymbol, asyncDisposeSymbol, disposeSymbol, } from './disposal-runtime';
|
|
1
|
+
export { assertPromiseRuntime, resolveDisposalSymbol, asyncDisposeSymbol, disposeSymbol, } from './disposal-runtime.js';
|
|
2
2
|
export interface Disposable {
|
|
3
|
-
dispose(): void;
|
|
4
3
|
[Symbol.dispose](): void;
|
|
5
4
|
}
|
|
6
5
|
export interface AsyncDisposable {
|
|
7
|
-
|
|
8
|
-
[Symbol.asyncDispose](): Promise<void>;
|
|
6
|
+
[Symbol.asyncDispose](): PromiseLike<void>;
|
|
9
7
|
}
|
|
10
8
|
export interface Readable<T> {
|
|
11
9
|
get(): T;
|
|
12
10
|
subscribe(listener: () => void): Disposable;
|
|
13
11
|
}
|
|
12
|
+
export declare function captureReadable<T>(source: Readable<T>, operation: string): Readable<T>;
|
|
13
|
+
export declare function captureUnsubscribe(subscription: Disposable): () => void;
|
|
14
|
+
export declare function captureRelease(value: AsyncDisposable, message: string): () => PromiseLike<void>;
|
|
15
|
+
export declare function assertListener(listener: unknown): asserts listener is () => void;
|
|
14
16
|
//# sourceMappingURL=protocol.d.ts.map
|
package/dist/protocol.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,oBAAoB,EACpB,qBAAqB,EACrB,kBAAkB,EAClB,aAAa,GACd,MAAM,uBAAuB,CAAC;AAE/B,MAAM,WAAW,UAAU;IACzB,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC;CAC1B;AAED,MAAM,WAAW,eAAe;IAC9B,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,WAAW,CAAC,IAAI,CAAC,CAAC;CAC5C;AAED,MAAM,WAAW,QAAQ,CAAC,CAAC;IACzB,GAAG,IAAI,CAAC,CAAC;IACT,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,UAAU,CAAC;CAC7C;AAED,wBAAgB,eAAe,CAAC,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAC,CAStF;AAED,wBAAgB,kBAAkB,CAAC,YAAY,EAAE,UAAU,GAAG,MAAM,IAAI,CASvE;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,WAAW,CAAC,IAAI,CAAC,CAI/F;AAQD,wBAAgB,cAAc,CAAC,QAAQ,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,IAAI,MAAM,IAAI,CAIhF"}
|
package/dist/signals.d.ts
CHANGED
package/dist/signals.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"signals.d.ts","sourceRoot":"","sources":["../src/signals.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"signals.d.ts","sourceRoot":"","sources":["../src/signals.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkD,KAAK,QAAQ,EAAE,MAAM,eAAe,CAAC;AAG9F,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;AAgHD,wBAAgB,KAAK,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,GAAG,CAAC,CAgC7C;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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"synchronous-result.d.ts","sourceRoot":"","sources":["../src/synchronous-result.ts"],"names":[],"mappings":"AACA,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAUvE;AAGD,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,SAAS,CAa5E"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dougongjs/reactive",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=22"
|
|
@@ -8,12 +8,13 @@
|
|
|
8
8
|
"browserslist": [
|
|
9
9
|
"Chrome >= 119",
|
|
10
10
|
"Edge >= 119",
|
|
11
|
-
"Firefox >=
|
|
11
|
+
"Firefox >= 124",
|
|
12
12
|
"Safari >= 17.4"
|
|
13
13
|
],
|
|
14
14
|
"sideEffects": false,
|
|
15
15
|
"files": [
|
|
16
16
|
"dist",
|
|
17
|
+
"runtime",
|
|
17
18
|
"README.md",
|
|
18
19
|
"LICENSE"
|
|
19
20
|
],
|
|
@@ -23,6 +24,13 @@
|
|
|
23
24
|
"import": "./dist/index.js"
|
|
24
25
|
}
|
|
25
26
|
},
|
|
27
|
+
"imports": {
|
|
28
|
+
"#abort-listener": {
|
|
29
|
+
"types": "./runtime/abort-listener.d.mts",
|
|
30
|
+
"node": "./runtime/abort-listener.node.mjs",
|
|
31
|
+
"default": "./runtime/abort-listener.web.mjs"
|
|
32
|
+
}
|
|
33
|
+
},
|
|
26
34
|
"description": "Zero-dependency signal, computed, batch and observe primitives.",
|
|
27
35
|
"license": "MIT",
|
|
28
36
|
"author": "Dougong contributors",
|
|
@@ -47,6 +55,6 @@
|
|
|
47
55
|
"access": "public"
|
|
48
56
|
},
|
|
49
57
|
"scripts": {
|
|
50
|
-
"build": "node ../../
|
|
58
|
+
"build": "node ../../tooling/generate-internal.mjs && vite build"
|
|
51
59
|
}
|
|
52
60
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// Generated from tooling/internal/abort-listener.node.mjs. Run pnpm generate:internal.
|
|
2
|
+
import { addAbortListener } from "node:events";
|
|
3
|
+
|
|
4
|
+
export function listenForAbort(signal, notify) {
|
|
5
|
+
// Node supplies stop-resistant listener options to this private registration signal.
|
|
6
|
+
// The caller's genuine EventTarget owns the actual listener and its native GC bookkeeping.
|
|
7
|
+
const registration = new AbortController().signal;
|
|
8
|
+
let detach;
|
|
9
|
+
registration.addEventListener = (type, listener, options) => {
|
|
10
|
+
detach = () => EventTarget.prototype.removeEventListener.call(signal, type, listener);
|
|
11
|
+
try {
|
|
12
|
+
// Only native cancellation may consume the listener. Synthetic abort events can arrive first.
|
|
13
|
+
EventTarget.prototype.addEventListener.call(signal, type, listener, {
|
|
14
|
+
...options,
|
|
15
|
+
once: false,
|
|
16
|
+
});
|
|
17
|
+
} catch (error) {
|
|
18
|
+
// A native listener hook may throw before or after insertion.
|
|
19
|
+
detach();
|
|
20
|
+
throw error;
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
addAbortListener(registration, notify);
|
|
24
|
+
if (!detach) throw new Error("Node did not attach the protected abort listener");
|
|
25
|
+
return detach;
|
|
26
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// Generated from tooling/internal/abort-listener.web.mjs. Run pnpm generate:internal.
|
|
2
|
+
// Only the controller can abort this private dependent; other listeners can stop propagation on the source.
|
|
3
|
+
export function listenForAbort(signal, notify) {
|
|
4
|
+
const dependent = AbortSignal.any([signal]);
|
|
5
|
+
dependent.addEventListener("abort", notify);
|
|
6
|
+
return () => dependent.removeEventListener("abort", notify);
|
|
7
|
+
}
|
|
@@ -1 +0,0 @@
|
|
|
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"}
|