@dougongjs/reactive 0.9.0 → 0.11.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 +10 -4
- package/dist/abort.d.ts +2 -1
- package/dist/abort.d.ts.map +1 -1
- package/dist/async-result.d.ts +10 -0
- package/dist/async-result.d.ts.map +1 -0
- package/dist/bridge.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +210 -178
- package/dist/observe.d.ts +6 -5
- package/dist/observe.d.ts.map +1 -1
- package/dist/protocol.d.ts +2 -1
- package/dist/protocol.d.ts.map +1 -1
- package/dist/signals.d.ts +3 -2
- package/dist/signals.d.ts.map +1 -1
- package/package.json +3 -4
- package/runtime/abort-listener.d.mts +1 -0
- package/runtime/abort-listener.mjs +45 -0
- package/dist/synchronous-result.d.ts +0 -3
- package/dist/synchronous-result.d.ts.map +0 -1
- package/runtime/abort-listener.node.mjs +0 -26
- package/runtime/abort-listener.web.mjs +0 -7
package/README.md
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
Independent synchronous signals and structurally owned reactive generations. No external runtime dependencies.
|
|
4
4
|
|
|
5
|
-
Upgrading from 0.
|
|
5
|
+
Upgrading from 0.9? Read the [migration guide](https://tangerg.github.io/dougong/en/guide/migrating-to-0.10) and [0.10.0 release notes](https://tangerg.github.io/dougong/en/reference/release-0.10.0). From 0.8.3, complete [Migrating to 0.9.0](https://tangerg.github.io/dougong/en/guide/migrating-to-0.9) first.
|
|
6
6
|
|
|
7
7
|
```ts
|
|
8
|
-
import { batch, computed, signal } from "@dougongjs/reactive";
|
|
8
|
+
import { batch, computed, disposeSymbol, signal } from "@dougongjs/reactive";
|
|
9
9
|
|
|
10
10
|
const first = signal("Dougong");
|
|
11
11
|
const last = signal("Plugins");
|
|
@@ -15,13 +15,19 @@ batch(() => {
|
|
|
15
15
|
first.set("Typed");
|
|
16
16
|
last.set("Composition");
|
|
17
17
|
});
|
|
18
|
-
subscription[
|
|
18
|
+
subscription[disposeSymbol]();
|
|
19
19
|
```
|
|
20
20
|
|
|
21
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`.
|
|
22
22
|
|
|
23
|
+
`batch`, `computed`, `subscribe` and `observe` require synchronous callbacks in both their types and runtime checks. Direct Promise results, PromiseLike results and mixed asynchronous branches are rejected, including when a caller explicitly supplies `void`. Ordinary synchronous return values remain inferred; `Observer<T, Child, Result>` can describe an explicit non-void result. Values whose type was already erased still receive the runtime check.
|
|
24
|
+
|
|
23
25
|
`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.
|
|
24
26
|
|
|
25
|
-
|
|
27
|
+
`ObservationTask` describes completion only: it is not generic, and its `result` is `PromiseLike<unknown>`. The required `spawn` accepts a task returning `void | PromiseLike<void>`. Owners may provide a richer generic task API without coupling observation to their result payloads.
|
|
28
|
+
|
|
29
|
+
Results inheriting this module's realm's `Promise.prototype` select the intrinsic native protocol, including decorated local Promises and subclasses. Other results use one captured structural `then`; display tags do not select a protocol. Foreign Promises must expose a conforming public `then`. Adapt decorated foreign results in their origin realm, and box payloads there when another assimilation would change them. See the guide for the precise boundary and disposal guarantees.
|
|
30
|
+
|
|
31
|
+
ESM; <!-- runtime-baseline:start -->Node.js `^24.22.0 || >=26.10.0`, Chrome ≥ 119, Edge ≥ 119, Firefox ≥ 124, Safari ≥ 17.4<!-- runtime-baseline:end -->.
|
|
26
32
|
|
|
27
33
|
[Guide](https://tangerg.github.io/dougong/en/guide/reactive)
|
package/dist/abort.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
+
import { SynchronousResult } from './async-result.js';
|
|
1
2
|
export declare function isAborted(signal: AbortSignal): boolean;
|
|
2
3
|
export declare function abortReason(signal: AbortSignal): unknown;
|
|
3
|
-
export declare function onAbort(signal: AbortSignal, callback: (reason: unknown) =>
|
|
4
|
+
export declare function onAbort<Result>(signal: AbortSignal, callback: (reason: unknown) => SynchronousResult<Result>): () => void;
|
|
4
5
|
//# sourceMappingURL=abort.d.ts.map
|
package/dist/abort.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"abort.d.ts","sourceRoot":"","sources":["../src/abort.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"abort.d.ts","sourceRoot":"","sources":["../src/abort.ts"],"names":[],"mappings":"AAEA,OAAO,EAAqB,KAAK,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAG9E,wBAAgB,SAAS,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAEtD;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAExD;AAED,wBAAgB,OAAO,CAAC,MAAM,EAC5B,MAAM,EAAE,WAAW,EACnB,QAAQ,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,iBAAiB,CAAC,MAAM,CAAC,GACvD,MAAM,IAAI,CA6BZ"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
type WithoutCallableThen<T> = T extends object ? "then" extends keyof T ? Extract<T["then"], (...arguments_: never[]) => unknown> extends never ? T : never : T : T;
|
|
2
|
+
export type SynchronousResult<T> = (T & NoInfer<WithoutCallableThen<T>>) | (T & undefined);
|
|
3
|
+
export interface ResolvedValue<T> {
|
|
4
|
+
readonly value: T;
|
|
5
|
+
}
|
|
6
|
+
export declare function resolveValue<T>(value: T | PromiseLike<T>): Promise<ResolvedValue<Awaited<T>>>;
|
|
7
|
+
export declare function assertSynchronous(value: unknown, message: string): void;
|
|
8
|
+
export declare function captureThenable(value: unknown): Promise<ResolvedValue<unknown>> | undefined;
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=async-result.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"async-result.d.ts","sourceRoot":"","sources":["../src/async-result.ts"],"names":[],"mappings":"AAIA,KAAK,mBAAmB,CAAC,CAAC,IAAI,CAAC,SAAS,MAAM,GAC1C,MAAM,SAAS,MAAM,CAAC,GACpB,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,GAAG,UAAU,EAAE,KAAK,EAAE,KAAK,OAAO,CAAC,SAAS,KAAK,GACnE,CAAC,GACD,KAAK,GACP,CAAC,GACH,CAAC,CAAC;AAIN,MAAM,MAAM,iBAAiB,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC;AAE3F,MAAM,WAAW,aAAa,CAAC,CAAC;IAC9B,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC;CACnB;AAED,wBAAsB,YAAY,CAAC,CAAC,EAClC,KAAK,EAAE,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,GACxB,OAAO,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAGpC;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAUvE;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,GAAG,SAAS,CA0B3F"}
|
package/dist/bridge.d.ts.map
CHANGED
|
@@ -1 +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,
|
|
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,EAAiB,KAAK,cAAc,EAAe,MAAM,cAAc,CAAC;AAG/E,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,KAAK,EAAE,CAAC,OAAO,EAAE,MAAM,OAAO,KAAK,eAAe,CAAC;CAC7D;AAyJD,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,5 +1,5 @@
|
|
|
1
1
|
export { batch, computed, signal, type ReadonlySignal, type Signal } from './signals.js';
|
|
2
2
|
export { bridge, type BridgeOwner } from './bridge.js';
|
|
3
|
-
export type
|
|
3
|
+
export { asyncDisposeSymbol, disposeSymbol, type AsyncDisposable, type Disposable, type Readable, } from './protocol.js';
|
|
4
4
|
export { observe, type ObservationOwner, type ObservationTask, type Observer } from './observe.js';
|
|
5
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,cAAc,CAAC;AACzF,OAAO,EAAE,MAAM,EAAE,KAAK,WAAW,EAAE,MAAM,aAAa,CAAC;AACvD,
|
|
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,OAAO,EACL,kBAAkB,EAClB,aAAa,EACb,KAAK,eAAe,EACpB,KAAK,UAAU,EACf,KAAK,QAAQ,GACd,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,OAAO,EAAE,KAAK,gBAAgB,EAAE,KAAK,eAAe,EAAE,KAAK,QAAQ,EAAE,MAAM,cAAc,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -7,14 +7,20 @@ function n(e, t) {
|
|
|
7
7
|
return e ?? Symbol.for(t);
|
|
8
8
|
}
|
|
9
9
|
t(Promise.withResolvers);
|
|
10
|
-
var r = n(Symbol.dispose, "Symbol.dispose"), i = n(Symbol.asyncDispose, "Symbol.asyncDispose");
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
var r = n(Symbol.dispose, "Symbol.dispose"), i = n(Symbol.asyncDispose, "Symbol.asyncDispose"), a = Promise.prototype, o = a.then;
|
|
11
|
+
async function s(e) {
|
|
12
|
+
return l(e) ?? Object.freeze({ value: e });
|
|
13
|
+
}
|
|
14
|
+
function c(e, t) {
|
|
15
|
+
let n = l(e);
|
|
14
16
|
if (n) throw n.then(() => void 0, () => void 0), TypeError(t);
|
|
15
17
|
}
|
|
16
|
-
function
|
|
18
|
+
function l(e) {
|
|
17
19
|
if (e === null || typeof e != "object" && typeof e != "function") return;
|
|
20
|
+
if (u(e)) {
|
|
21
|
+
let t = Promise.withResolvers();
|
|
22
|
+
return Reflect.apply(o, e, [(e) => t.resolve(Object.freeze({ value: e })), t.reject]), t.promise;
|
|
23
|
+
}
|
|
18
24
|
let t = e.then;
|
|
19
25
|
if (typeof t != "function") return;
|
|
20
26
|
let n = Promise.withResolvers();
|
|
@@ -24,10 +30,19 @@ function o(e) {
|
|
|
24
30
|
} catch (e) {
|
|
25
31
|
n.reject(e);
|
|
26
32
|
}
|
|
27
|
-
}), n.promise;
|
|
33
|
+
}), n.promise.then((e) => Object.freeze({ value: e }));
|
|
34
|
+
}
|
|
35
|
+
function u(e) {
|
|
36
|
+
let t = /* @__PURE__ */ new Set();
|
|
37
|
+
for (let n = Object.getPrototypeOf(e); n !== null; n = Object.getPrototypeOf(n)) {
|
|
38
|
+
if (n === a) return !0;
|
|
39
|
+
if (t.has(n)) throw TypeError("Result has a cyclic prototype chain");
|
|
40
|
+
t.add(n);
|
|
41
|
+
}
|
|
42
|
+
return !1;
|
|
28
43
|
}
|
|
29
44
|
// src/protocol.ts
|
|
30
|
-
function
|
|
45
|
+
function d(e, t) {
|
|
31
46
|
let n = e?.get, r = e?.subscribe;
|
|
32
47
|
if (typeof n != "function" || typeof r != "function") throw TypeError(`${t} expects a readable source`);
|
|
33
48
|
return Object.freeze({
|
|
@@ -35,22 +50,22 @@ function s(e, t) {
|
|
|
35
50
|
subscribe: (t) => Reflect.apply(r, e, [t])
|
|
36
51
|
});
|
|
37
52
|
}
|
|
38
|
-
function
|
|
53
|
+
function f(e) {
|
|
39
54
|
let t = e?.[r];
|
|
40
|
-
return typeof t != "function" &&
|
|
55
|
+
return typeof t != "function" && m(e, "Readable.subscribe() must return a Disposable"), () => c(Reflect.apply(t, e, []), "Readable subscriptions must dispose synchronously");
|
|
41
56
|
}
|
|
42
|
-
function
|
|
57
|
+
function p(e, t) {
|
|
43
58
|
let n = e?.[i];
|
|
44
|
-
return typeof n != "function" &&
|
|
59
|
+
return typeof n != "function" && m(e, t), () => Reflect.apply(n, e, []);
|
|
45
60
|
}
|
|
46
|
-
function
|
|
47
|
-
throw
|
|
61
|
+
function m(e, t) {
|
|
62
|
+
throw c(e, t), TypeError(t);
|
|
48
63
|
}
|
|
49
|
-
function
|
|
64
|
+
function h(e) {
|
|
50
65
|
if (typeof e != "function") throw TypeError("Signal subscriber must be a function");
|
|
51
66
|
}
|
|
52
67
|
// src/signals.ts
|
|
53
|
-
var
|
|
68
|
+
var g = 0, _ = 0, v = !1, y = !1, b = /* @__PURE__ */ new Set(), x = /* @__PURE__ */ new Set(), S, C = [], w = class {
|
|
54
69
|
#e;
|
|
55
70
|
constructor(e) {
|
|
56
71
|
this.#e = e, Object.freeze(this);
|
|
@@ -60,64 +75,64 @@ var f = 0, p = 0, m = !1, h = !1, g = /* @__PURE__ */ new Set(), _ = /* @__PURE_
|
|
|
60
75
|
this.#e = void 0, e?.();
|
|
61
76
|
}
|
|
62
77
|
};
|
|
63
|
-
function
|
|
64
|
-
if (
|
|
78
|
+
function T() {
|
|
79
|
+
if (v) return;
|
|
65
80
|
let e = [];
|
|
66
|
-
|
|
81
|
+
v = !0;
|
|
67
82
|
try {
|
|
68
|
-
for (;
|
|
69
|
-
let t =
|
|
70
|
-
|
|
83
|
+
for (; b.size;) {
|
|
84
|
+
let t = b.values().next().value;
|
|
85
|
+
b.delete(t);
|
|
71
86
|
let n = t.listener;
|
|
72
87
|
if (n) try {
|
|
73
|
-
|
|
88
|
+
c(n(), "Signal subscribers must be synchronous");
|
|
74
89
|
} catch (t) {
|
|
75
90
|
e.push(t);
|
|
76
91
|
}
|
|
77
92
|
}
|
|
78
93
|
} finally {
|
|
79
|
-
|
|
94
|
+
b.clear(), v = !1;
|
|
80
95
|
}
|
|
81
|
-
|
|
96
|
+
O(e);
|
|
82
97
|
}
|
|
83
|
-
function
|
|
84
|
-
for (let t of e) (t.kind === "dependency" ?
|
|
85
|
-
if (!
|
|
86
|
-
|
|
98
|
+
function E(e) {
|
|
99
|
+
for (let t of e) (t.kind === "dependency" ? x : b).add(t);
|
|
100
|
+
if (!y) {
|
|
101
|
+
y = !0;
|
|
87
102
|
try {
|
|
88
|
-
for (;
|
|
89
|
-
let e =
|
|
90
|
-
|
|
103
|
+
for (; x.size;) {
|
|
104
|
+
let e = x.values().next().value;
|
|
105
|
+
x.delete(e);
|
|
91
106
|
let t = e.listener;
|
|
92
107
|
t?.();
|
|
93
108
|
}
|
|
94
109
|
} finally {
|
|
95
|
-
|
|
110
|
+
y = !1, x.clear();
|
|
96
111
|
}
|
|
97
|
-
|
|
112
|
+
_ || T();
|
|
98
113
|
}
|
|
99
114
|
}
|
|
100
|
-
function
|
|
101
|
-
|
|
115
|
+
function D(e, t, n) {
|
|
116
|
+
h(t);
|
|
102
117
|
let r = {
|
|
103
118
|
kind: n,
|
|
104
119
|
listener: t
|
|
105
120
|
};
|
|
106
|
-
return e.add(r), new
|
|
121
|
+
return e.add(r), new w(() => {
|
|
107
122
|
r.listener = void 0, e.delete(r);
|
|
108
123
|
});
|
|
109
124
|
}
|
|
110
|
-
function
|
|
125
|
+
function O(e) {
|
|
111
126
|
if (e.length === 1) throw e[0];
|
|
112
127
|
if (e.length > 1) throw AggregateError(e, "Reactive subscribers failed");
|
|
113
128
|
}
|
|
114
|
-
function
|
|
129
|
+
function k(e) {
|
|
115
130
|
if (typeof e != "function") throw TypeError("batch() expects a function");
|
|
116
|
-
|
|
131
|
+
_++;
|
|
117
132
|
let t;
|
|
118
133
|
try {
|
|
119
134
|
let n = e();
|
|
120
|
-
|
|
135
|
+
c(n, "Reactive batches must be synchronous"), t = {
|
|
121
136
|
succeeded: !0,
|
|
122
137
|
value: n
|
|
123
138
|
};
|
|
@@ -127,10 +142,10 @@ function T(e) {
|
|
|
127
142
|
error: e
|
|
128
143
|
};
|
|
129
144
|
} finally {
|
|
130
|
-
|
|
145
|
+
_--;
|
|
131
146
|
}
|
|
132
|
-
if (!
|
|
133
|
-
|
|
147
|
+
if (!_) try {
|
|
148
|
+
T();
|
|
134
149
|
} catch (e) {
|
|
135
150
|
t = t.succeeded ? {
|
|
136
151
|
succeeded: !1,
|
|
@@ -143,23 +158,23 @@ function T(e) {
|
|
|
143
158
|
if (!t.succeeded) throw t.error;
|
|
144
159
|
return t.value;
|
|
145
160
|
}
|
|
146
|
-
function
|
|
147
|
-
return new
|
|
161
|
+
function A(e) {
|
|
162
|
+
return new M(e).view;
|
|
148
163
|
}
|
|
149
|
-
function
|
|
164
|
+
function j(e) {
|
|
150
165
|
if (typeof e != "function") throw TypeError("computed() expects a function");
|
|
151
|
-
return new
|
|
166
|
+
return new N(e).view;
|
|
152
167
|
}
|
|
153
|
-
var
|
|
168
|
+
var M = class {
|
|
154
169
|
#e = /* @__PURE__ */ new Set();
|
|
155
170
|
#t;
|
|
156
171
|
#n = 0;
|
|
157
172
|
view;
|
|
158
173
|
constructor(e) {
|
|
159
174
|
this.#t = e, this.view = Object.freeze({
|
|
160
|
-
get: () => (
|
|
175
|
+
get: () => (S?.add(this), this.#t),
|
|
161
176
|
set: (e) => this.#r(e),
|
|
162
|
-
subscribe: (e) =>
|
|
177
|
+
subscribe: (e) => D(this.#e, e, "observer")
|
|
163
178
|
});
|
|
164
179
|
}
|
|
165
180
|
get version() {
|
|
@@ -167,13 +182,13 @@ var O = class {
|
|
|
167
182
|
}
|
|
168
183
|
refresh() {}
|
|
169
184
|
subscribeInvalidation(e) {
|
|
170
|
-
return
|
|
185
|
+
return D(this.#e, e, "dependency");
|
|
171
186
|
}
|
|
172
187
|
#r(e) {
|
|
173
|
-
if (
|
|
174
|
-
Object.is(this.#t, e) || (this.#t = e,
|
|
188
|
+
if (S) throw TypeError("Computed signal calculations cannot write signals");
|
|
189
|
+
Object.is(this.#t, e) || (this.#t = e, g++, this.#n++, E(this.#e));
|
|
175
190
|
}
|
|
176
|
-
},
|
|
191
|
+
}, N = class e {
|
|
177
192
|
#e;
|
|
178
193
|
#t = /* @__PURE__ */ new Set();
|
|
179
194
|
#n = /* @__PURE__ */ new Map();
|
|
@@ -181,7 +196,7 @@ var O = class {
|
|
|
181
196
|
#i = -1;
|
|
182
197
|
#a;
|
|
183
198
|
#o;
|
|
184
|
-
#s =
|
|
199
|
+
#s = -1;
|
|
185
200
|
#c = 0;
|
|
186
201
|
view;
|
|
187
202
|
constructor(e) {
|
|
@@ -194,33 +209,33 @@ var O = class {
|
|
|
194
209
|
return this.#c;
|
|
195
210
|
}
|
|
196
211
|
refresh() {
|
|
197
|
-
if (this.#a !== void 0) throw
|
|
198
|
-
this.#a =
|
|
212
|
+
if (this.#a !== void 0) throw S && this.#l(this.#a, S), TypeError("Circular computed signal");
|
|
213
|
+
this.#a = C.length, C.push(this);
|
|
199
214
|
try {
|
|
200
215
|
this.#g();
|
|
201
216
|
} finally {
|
|
202
|
-
|
|
217
|
+
C.pop(), this.#a = void 0;
|
|
203
218
|
}
|
|
204
219
|
}
|
|
205
220
|
#l(t, n) {
|
|
206
221
|
let r = /* @__PURE__ */ new Set();
|
|
207
|
-
for (let e of
|
|
222
|
+
for (let e of C.slice(t)) for (let t of e.#o ?? e.#n.keys()) r.add(t);
|
|
208
223
|
for (let t of r) if (t instanceof e) for (let e of t.#n.keys()) r.add(e);
|
|
209
224
|
else n.add(t);
|
|
210
225
|
}
|
|
211
226
|
#u() {
|
|
212
|
-
return this.refresh(),
|
|
227
|
+
return this.refresh(), S?.add(this), this.#d();
|
|
213
228
|
}
|
|
214
229
|
#d() {
|
|
215
230
|
let e = this.#r;
|
|
216
|
-
if (e.
|
|
231
|
+
if (e.outcome === "error") throw e.error;
|
|
217
232
|
return e.value;
|
|
218
233
|
}
|
|
219
234
|
subscribeInvalidation(e) {
|
|
220
235
|
return this.#f(e, "dependency");
|
|
221
236
|
}
|
|
222
237
|
#f(e, t = "observer") {
|
|
223
|
-
|
|
238
|
+
h(e);
|
|
224
239
|
let n = {
|
|
225
240
|
kind: t,
|
|
226
241
|
listener: e
|
|
@@ -230,12 +245,12 @@ var O = class {
|
|
|
230
245
|
} catch (e) {
|
|
231
246
|
throw this.#t.delete(n), this.#t.size || this.#m(), e;
|
|
232
247
|
}
|
|
233
|
-
return new
|
|
248
|
+
return new w(() => {
|
|
234
249
|
n.listener = void 0, this.#t.delete(n), this.#t.size || this.#m();
|
|
235
250
|
});
|
|
236
251
|
}
|
|
237
252
|
#p = () => {
|
|
238
|
-
this.#s
|
|
253
|
+
this.#s !== g && (this.#s = g, E(this.#t));
|
|
239
254
|
};
|
|
240
255
|
#m() {
|
|
241
256
|
for (let e of this.#n.values()) e.subscription?.[r](), e.subscription = void 0;
|
|
@@ -245,34 +260,33 @@ var O = class {
|
|
|
245
260
|
}
|
|
246
261
|
#g() {
|
|
247
262
|
let e = this.#r;
|
|
248
|
-
if (e &&
|
|
249
|
-
this.#i =
|
|
263
|
+
if (e && this.#s <= this.#i && (this.#i === g || !this.#_())) {
|
|
264
|
+
this.#i = g;
|
|
250
265
|
return;
|
|
251
266
|
}
|
|
252
|
-
this.#s = !0;
|
|
253
267
|
let t = /* @__PURE__ */ new Set(), n = this.#v(t);
|
|
254
|
-
this.#y(t), (!e || (n.
|
|
268
|
+
this.#y(t), (!e || (n.outcome === "value" ? e.outcome !== "value" || !Object.is(e.value, n.value) : e.outcome !== "error" || !Object.is(e.error, n.error))) && this.#c++, this.#r = n, this.#i = g, this.#t.size && this.#h();
|
|
255
269
|
}
|
|
256
270
|
#_() {
|
|
257
271
|
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;
|
|
272
|
+
return this.#s > this.#i;
|
|
259
273
|
}
|
|
260
274
|
#v(e) {
|
|
261
|
-
let t =
|
|
262
|
-
this.#o = e,
|
|
275
|
+
let t = S;
|
|
276
|
+
this.#o = e, S = e;
|
|
263
277
|
try {
|
|
264
278
|
let e = this.#e, t = e();
|
|
265
|
-
return
|
|
266
|
-
|
|
279
|
+
return c(t, "Computed signal calculations must be synchronous"), {
|
|
280
|
+
outcome: "value",
|
|
267
281
|
value: t
|
|
268
282
|
};
|
|
269
283
|
} catch (e) {
|
|
270
284
|
return {
|
|
271
|
-
|
|
285
|
+
outcome: "error",
|
|
272
286
|
error: e
|
|
273
287
|
};
|
|
274
288
|
} finally {
|
|
275
|
-
|
|
289
|
+
S = t, this.#o = void 0;
|
|
276
290
|
}
|
|
277
291
|
}
|
|
278
292
|
#y(e) {
|
|
@@ -285,22 +299,22 @@ var O = class {
|
|
|
285
299
|
});
|
|
286
300
|
}
|
|
287
301
|
}
|
|
288
|
-
},
|
|
302
|
+
}, P = "Readable bridge has been disposed", F = class {
|
|
289
303
|
#e;
|
|
290
304
|
#t;
|
|
291
305
|
#n;
|
|
292
306
|
#r;
|
|
293
307
|
view;
|
|
294
308
|
constructor(e) {
|
|
295
|
-
this.#e =
|
|
296
|
-
|
|
309
|
+
this.#e = A({
|
|
310
|
+
outcome: "value",
|
|
297
311
|
source: e,
|
|
298
312
|
value: e.get()
|
|
299
313
|
}), this.view = Object.freeze({
|
|
300
314
|
get: () => {
|
|
301
315
|
let e = this.#e?.get();
|
|
302
|
-
if (!e) throw Error(
|
|
303
|
-
if (e.
|
|
316
|
+
if (!e) throw Error(P);
|
|
317
|
+
if (e.outcome === "error") throw e.error;
|
|
304
318
|
return e.value;
|
|
305
319
|
},
|
|
306
320
|
subscribe: (e) => this.#o(e)
|
|
@@ -309,21 +323,22 @@ var O = class {
|
|
|
309
323
|
start(e) {
|
|
310
324
|
let t = !0, n;
|
|
311
325
|
try {
|
|
312
|
-
n =
|
|
326
|
+
n = p(e.defer(() => (this[r](), t ? Promise.resolve().then(() => this.#i()) : void 0)), "BridgeOwner.defer() must return an AsyncDisposable");
|
|
313
327
|
let i = this.#e?.get();
|
|
314
|
-
if (!i) throw Error(
|
|
315
|
-
let a =
|
|
328
|
+
if (!i) throw Error(P);
|
|
329
|
+
let a = f(i.source.subscribe(() => this.#a()));
|
|
316
330
|
if (this.#t = a, !this.#e?.get()) {
|
|
317
331
|
try {
|
|
318
332
|
a(), this.#t = void 0;
|
|
319
333
|
} catch (e) {
|
|
320
334
|
this.#r = { value: e };
|
|
321
335
|
}
|
|
322
|
-
throw Error(
|
|
336
|
+
throw Error(P);
|
|
323
337
|
}
|
|
324
338
|
this.#a(), this.view.get();
|
|
325
339
|
} catch (e) {
|
|
326
|
-
|
|
340
|
+
let t = n;
|
|
341
|
+
throw t && Promise.resolve().then(() => s(t())).catch(() => void 0), e;
|
|
327
342
|
} finally {
|
|
328
343
|
t = !1;
|
|
329
344
|
}
|
|
@@ -332,35 +347,37 @@ var O = class {
|
|
|
332
347
|
if (this.#r) throw this.#r.value;
|
|
333
348
|
}
|
|
334
349
|
#a() {
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
let { source: n } = e, r;
|
|
350
|
+
k(() => {
|
|
351
|
+
let e = this.#e?.get();
|
|
352
|
+
if (!e) return;
|
|
353
|
+
let t = Symbol();
|
|
354
|
+
this.#n = t;
|
|
341
355
|
try {
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
356
|
+
let { source: n } = e, r;
|
|
357
|
+
try {
|
|
358
|
+
r = {
|
|
359
|
+
outcome: "value",
|
|
360
|
+
source: n,
|
|
361
|
+
value: n.get()
|
|
362
|
+
};
|
|
363
|
+
} catch (e) {
|
|
364
|
+
r = {
|
|
365
|
+
outcome: "error",
|
|
366
|
+
source: n,
|
|
367
|
+
error: e
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
if (this.#n !== t || this.#e?.get() !== e || e.outcome === "value" && r.outcome === "value" && Object.is(e.value, r.value) || e.outcome === "error" && r.outcome === "error" && Object.is(e.error, r.error)) return;
|
|
371
|
+
this.#e?.set(r);
|
|
372
|
+
} finally {
|
|
373
|
+
this.#n = void 0;
|
|
353
374
|
}
|
|
354
|
-
|
|
355
|
-
this.#e?.set(r);
|
|
356
|
-
} finally {
|
|
357
|
-
this.#n = void 0;
|
|
358
|
-
}
|
|
375
|
+
});
|
|
359
376
|
}
|
|
360
377
|
#o(e) {
|
|
361
378
|
let t = this.#e;
|
|
362
|
-
if (!t?.get()) throw Error(
|
|
363
|
-
|
|
379
|
+
if (!t?.get()) throw Error(P);
|
|
380
|
+
h(e);
|
|
364
381
|
let n = t.subscribe(() => (this.#e?.get() || n[r](), e()));
|
|
365
382
|
return n;
|
|
366
383
|
}
|
|
@@ -386,10 +403,10 @@ var O = class {
|
|
|
386
403
|
}
|
|
387
404
|
}
|
|
388
405
|
};
|
|
389
|
-
function
|
|
406
|
+
function I(e, t) {
|
|
390
407
|
let n = e?.defer;
|
|
391
408
|
if (typeof n != "function") throw TypeError("bridge() expects a resource owner");
|
|
392
|
-
let i =
|
|
409
|
+
let i = d(t, "bridge()"), a = { defer: (t) => Reflect.apply(n, e, [t]) }, o = new F(i);
|
|
393
410
|
try {
|
|
394
411
|
return o.start(a), o.view;
|
|
395
412
|
} catch (e) {
|
|
@@ -402,15 +419,15 @@ function j(e, t) {
|
|
|
402
419
|
}
|
|
403
420
|
}
|
|
404
421
|
// src/abort.ts
|
|
405
|
-
function
|
|
422
|
+
function L(e) {
|
|
406
423
|
return Reflect.get(AbortSignal.prototype, "aborted", e);
|
|
407
424
|
}
|
|
408
|
-
function
|
|
425
|
+
function R(e) {
|
|
409
426
|
return Reflect.get(AbortSignal.prototype, "reason", e);
|
|
410
427
|
}
|
|
411
|
-
function
|
|
428
|
+
function z(t, n) {
|
|
412
429
|
if (typeof n != "function") throw TypeError("Abort listener must be a function");
|
|
413
|
-
if (
|
|
430
|
+
if (L(t)) return B(n, R(t)), () => {};
|
|
414
431
|
let r = {
|
|
415
432
|
signal: t,
|
|
416
433
|
callback: n
|
|
@@ -419,14 +436,17 @@ function P(t, n) {
|
|
|
419
436
|
r = void 0, i?.(), i = void 0;
|
|
420
437
|
}
|
|
421
438
|
function o() {
|
|
422
|
-
if (!r || !
|
|
423
|
-
let { signal: e, callback: t } = r, n =
|
|
424
|
-
a(), t
|
|
439
|
+
if (!r || !L(r.signal)) return;
|
|
440
|
+
let { signal: e, callback: t } = r, n = R(e);
|
|
441
|
+
a(), B(t, n);
|
|
425
442
|
}
|
|
426
443
|
return i = e(t, o), r ? o() : a(), a;
|
|
427
444
|
}
|
|
445
|
+
function B(e, t) {
|
|
446
|
+
c(e(t), "Abort listeners must be synchronous");
|
|
447
|
+
}
|
|
428
448
|
// src/observe.ts
|
|
429
|
-
var
|
|
449
|
+
var V = "observation", H = class {
|
|
430
450
|
#e;
|
|
431
451
|
#t;
|
|
432
452
|
#n;
|
|
@@ -438,6 +458,7 @@ var F = class {
|
|
|
438
458
|
#c;
|
|
439
459
|
#l;
|
|
440
460
|
#u;
|
|
461
|
+
#d;
|
|
441
462
|
constructor(e, t, n) {
|
|
442
463
|
this.#e = {
|
|
443
464
|
phase: "active",
|
|
@@ -451,61 +472,75 @@ var F = class {
|
|
|
451
472
|
start() {
|
|
452
473
|
this.#i = !0;
|
|
453
474
|
try {
|
|
454
|
-
this.#
|
|
475
|
+
this.#f();
|
|
455
476
|
} finally {
|
|
456
477
|
this.#i = !1;
|
|
457
478
|
}
|
|
458
479
|
}
|
|
459
|
-
#
|
|
460
|
-
let {
|
|
461
|
-
this.#t =
|
|
462
|
-
let n =
|
|
480
|
+
#f() {
|
|
481
|
+
let { source: e } = this.#w();
|
|
482
|
+
this.#t = f(e.subscribe(() => this.#h())), this.#v();
|
|
483
|
+
let { owner: t } = this.#w(), n = t.spawn((e) => this.#g(e)), r = p(n, "ObservationOwner.spawn() must return an ObservationTask");
|
|
463
484
|
this.#s = r;
|
|
464
|
-
let i =
|
|
485
|
+
let i = l(n.result);
|
|
465
486
|
if (!i) throw TypeError("ObservationOwner.spawn() must return an ObservationTask");
|
|
466
|
-
i.then(() => this.#
|
|
487
|
+
i.then(() => this.#S(r), () => this.#S(r));
|
|
467
488
|
}
|
|
468
489
|
attachOwnerCleanup(e) {
|
|
469
|
-
this.#c = e;
|
|
490
|
+
this.#d || (this.#c = e);
|
|
491
|
+
}
|
|
492
|
+
handle = Object.freeze({ [i]: () => this.#p() });
|
|
493
|
+
#p() {
|
|
494
|
+
if (this.#l) return this.#l;
|
|
495
|
+
let e = this.#c;
|
|
496
|
+
if (!e) return this.dispose();
|
|
497
|
+
let t = Promise.withResolvers();
|
|
498
|
+
this.#l = t.promise;
|
|
499
|
+
try {
|
|
500
|
+
s(e()).then(() => t.resolve(), t.reject);
|
|
501
|
+
} catch (e) {
|
|
502
|
+
t.reject(e);
|
|
503
|
+
}
|
|
504
|
+
return t.promise;
|
|
470
505
|
}
|
|
471
506
|
dispose() {
|
|
472
|
-
if (this.#
|
|
507
|
+
if (this.#d) return this.#d;
|
|
473
508
|
let e = Promise.withResolvers();
|
|
474
|
-
return this.#
|
|
509
|
+
return this.#d = e.promise, this.#c = void 0, this.#T("disposed"), this.#u?.(), (this.#i ? Promise.resolve().then(() => this.#m()) : this.#m()).then(e.resolve, e.reject), e.promise;
|
|
475
510
|
}
|
|
476
|
-
async #
|
|
511
|
+
async #m() {
|
|
477
512
|
let e = [];
|
|
478
513
|
try {
|
|
479
514
|
this.#t?.(), this.#t = void 0;
|
|
480
515
|
} catch (t) {
|
|
481
516
|
e.push(t);
|
|
482
517
|
}
|
|
483
|
-
await
|
|
518
|
+
await W(this.#x(), e);
|
|
484
519
|
try {
|
|
485
|
-
await this.#
|
|
520
|
+
await this.#b();
|
|
486
521
|
} catch (t) {
|
|
487
522
|
e.push(t);
|
|
488
523
|
}
|
|
489
524
|
if (e.length === 1) throw e[0];
|
|
490
525
|
if (e.length > 1) throw AggregateError(e, "Observation cleanup failed");
|
|
491
526
|
}
|
|
492
|
-
#
|
|
493
|
-
this.#e.phase === "active" && (this.#o = !0, this.#
|
|
527
|
+
#h() {
|
|
528
|
+
this.#e.phase === "active" && (this.#o = !0, this.#u?.());
|
|
494
529
|
}
|
|
495
|
-
async #
|
|
496
|
-
let t =
|
|
530
|
+
async #g(e) {
|
|
531
|
+
let t = z(e, () => this.#u?.());
|
|
497
532
|
try {
|
|
498
|
-
for (; this.#e.phase === "active" && !
|
|
533
|
+
for (; this.#e.phase === "active" && !L(e);) {
|
|
499
534
|
if (!this.#o) {
|
|
500
535
|
let e = Promise.withResolvers();
|
|
501
|
-
this.#
|
|
536
|
+
this.#u = e.resolve, await e.promise, this.#u = void 0;
|
|
502
537
|
}
|
|
503
|
-
for (; this.#e.phase === "active" && !
|
|
538
|
+
for (; this.#e.phase === "active" && !L(e) && this.#o;) this.#o = !1, await this.#_(e);
|
|
504
539
|
}
|
|
505
540
|
} catch (e) {
|
|
506
541
|
if (this.#e.phase !== "active") throw e;
|
|
507
542
|
try {
|
|
508
|
-
await this.#
|
|
543
|
+
await this.#C();
|
|
509
544
|
} catch (t) {
|
|
510
545
|
throw AggregateError([e, t], "Observation stopped after a replacement failed");
|
|
511
546
|
}
|
|
@@ -514,85 +549,82 @@ var F = class {
|
|
|
514
549
|
t();
|
|
515
550
|
}
|
|
516
551
|
}
|
|
517
|
-
async #
|
|
518
|
-
if (this.#e.phase !== "active" ||
|
|
552
|
+
async #_(e) {
|
|
553
|
+
if (this.#e.phase !== "active" || L(e)) return;
|
|
519
554
|
let t = this.#e.binding.source.get();
|
|
520
555
|
if (!(this.#a.present && Object.is(t, this.#a.value))) {
|
|
521
556
|
try {
|
|
522
|
-
await this.#
|
|
557
|
+
await this.#b();
|
|
523
558
|
} catch (e) {
|
|
524
|
-
throw this.#e.phase === "active" && await this.#
|
|
559
|
+
throw this.#e.phase === "active" && await this.#C(), e;
|
|
525
560
|
}
|
|
526
|
-
this.#a = { present: !1 }, !(this.#e.phase !== "active" ||
|
|
561
|
+
this.#a = { present: !1 }, !(this.#e.phase !== "active" || L(e)) && this.#v(e);
|
|
527
562
|
}
|
|
528
563
|
}
|
|
529
|
-
#
|
|
530
|
-
let { owner: t } = this.#
|
|
531
|
-
if (this.#n =
|
|
532
|
-
let r = this.#
|
|
533
|
-
e &&
|
|
564
|
+
#v(e) {
|
|
565
|
+
let { owner: t } = this.#w(), n = t.child(V);
|
|
566
|
+
if (this.#n = p(n, "ObservationOwner.child() must return an AsyncDisposable"), e && L(e)) return;
|
|
567
|
+
let r = this.#w().source.get();
|
|
568
|
+
e && L(e) || (this.#y(r, n), this.#e.phase === "active" && (this.#a = {
|
|
534
569
|
present: !0,
|
|
535
570
|
value: r
|
|
536
571
|
}));
|
|
537
572
|
}
|
|
538
|
-
#
|
|
539
|
-
let { observer: n } = this.#
|
|
540
|
-
|
|
573
|
+
#y(e, t) {
|
|
574
|
+
let { observer: n } = this.#w();
|
|
575
|
+
c(n(e, t), "Observers must be synchronous; use owner.spawn() for async work");
|
|
541
576
|
}
|
|
542
|
-
#
|
|
577
|
+
#b() {
|
|
543
578
|
let e = this.#n;
|
|
544
|
-
if (e) return this.#r ??= Promise.resolve().then(e).then(() => {
|
|
579
|
+
if (e) return this.#r ??= Promise.resolve().then(() => s(e())).then(() => {
|
|
545
580
|
this.#n = void 0, this.#r = void 0;
|
|
546
581
|
});
|
|
547
582
|
}
|
|
548
|
-
#
|
|
583
|
+
#x() {
|
|
549
584
|
let e = this.#s;
|
|
550
585
|
return this.#s = void 0, e;
|
|
551
586
|
}
|
|
552
|
-
#
|
|
587
|
+
#S(e) {
|
|
553
588
|
this.#s === e && (this.#s = void 0);
|
|
554
589
|
}
|
|
555
|
-
async #
|
|
556
|
-
this.#
|
|
557
|
-
let e = this.#c;
|
|
558
|
-
e ? await e() : await this.dispose();
|
|
590
|
+
async #C() {
|
|
591
|
+
this.#T("stopped"), this.#s = void 0, await this.#p();
|
|
559
592
|
}
|
|
560
|
-
#
|
|
593
|
+
#w() {
|
|
561
594
|
let e = this.#e;
|
|
562
595
|
if (e.phase !== "active") throw Error("Observation is not active");
|
|
563
596
|
return e.binding;
|
|
564
597
|
}
|
|
565
|
-
#
|
|
598
|
+
#T(e) {
|
|
566
599
|
this.#e = { phase: e }, this.#a = { present: !1 }, this.#o = !1;
|
|
567
600
|
}
|
|
568
601
|
};
|
|
569
|
-
function
|
|
570
|
-
let r =
|
|
602
|
+
function U(e, t, n) {
|
|
603
|
+
let r = d(t, "observe()");
|
|
571
604
|
if (typeof n != "function") throw TypeError("Observer must be a function");
|
|
572
|
-
let
|
|
573
|
-
if (typeof
|
|
574
|
-
let
|
|
575
|
-
defer: (t) => Reflect.apply(
|
|
576
|
-
child: (t) => Reflect.apply(
|
|
577
|
-
spawn: (t) => Reflect.apply(
|
|
578
|
-
},
|
|
579
|
-
d.attachOwnerCleanup(f);
|
|
605
|
+
let i = e?.defer, a = e?.child, o = e?.spawn;
|
|
606
|
+
if (typeof i != "function" || typeof a != "function" || typeof o != "function") throw TypeError("observe() expects an observation owner");
|
|
607
|
+
let c = {
|
|
608
|
+
defer: (t) => Reflect.apply(i, e, [t]),
|
|
609
|
+
child: (t) => Reflect.apply(a, e, [t]),
|
|
610
|
+
spawn: (t) => Reflect.apply(o, e, [t])
|
|
611
|
+
}, l = new H(c, r, n), u = l.dispose.bind(l);
|
|
580
612
|
try {
|
|
581
|
-
return
|
|
613
|
+
return u = p(c.defer(u), "ObservationOwner.defer() must return an AsyncDisposable"), l.attachOwnerCleanup(u), l.start(), l.handle;
|
|
582
614
|
} catch (e) {
|
|
583
615
|
try {
|
|
584
|
-
|
|
616
|
+
s(u()).catch(() => void 0);
|
|
585
617
|
} catch (t) {
|
|
586
618
|
throw AggregateError([e, t], "Observation construction failed");
|
|
587
619
|
}
|
|
588
620
|
throw e;
|
|
589
621
|
}
|
|
590
622
|
}
|
|
591
|
-
async function
|
|
623
|
+
async function W(e, t) {
|
|
592
624
|
if (e) try {
|
|
593
|
-
await e();
|
|
625
|
+
await s(e());
|
|
594
626
|
} catch (e) {
|
|
595
627
|
t.push(e);
|
|
596
628
|
}
|
|
597
629
|
}
|
|
598
|
-
export {
|
|
630
|
+
export { i as asyncDisposeSymbol, k as batch, I as bridge, j as computed, r as disposeSymbol, U as observe, A as signal };
|
package/dist/observe.d.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { AsyncDisposable, Readable } from './protocol.js';
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
import { SynchronousResult } from './async-result.js';
|
|
3
|
+
export interface ObservationTask extends AsyncDisposable {
|
|
4
|
+
readonly result: PromiseLike<unknown>;
|
|
4
5
|
}
|
|
5
6
|
export interface ObservationOwner<Child extends AsyncDisposable = AsyncDisposable> {
|
|
6
7
|
readonly defer: (dispose: () => unknown) => AsyncDisposable;
|
|
7
8
|
readonly child: (label: string) => Child;
|
|
8
|
-
readonly spawn:
|
|
9
|
+
readonly spawn: (task: (signal: AbortSignal) => void | PromiseLike<void>) => ObservationTask;
|
|
9
10
|
}
|
|
10
|
-
export type Observer<T, Child extends AsyncDisposable> = (value: T, lifetime: Child) =>
|
|
11
|
-
export declare function observe<T, Child extends AsyncDisposable>(owner: ObservationOwner<Child>, source: Readable<T>, observer: Observer<T, Child>): AsyncDisposable;
|
|
11
|
+
export type Observer<T, Child extends AsyncDisposable, Result = void> = (value: T, lifetime: Child) => SynchronousResult<Result>;
|
|
12
|
+
export declare function observe<T, Child extends AsyncDisposable, Result = void>(owner: ObservationOwner<Child>, source: Readable<T>, observer: Observer<T, Child, Result>): AsyncDisposable;
|
|
12
13
|
//# 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":"AACA,OAAO,EAKL,KAAK,eAAe,EACpB,KAAK,QAAQ,EACd,MAAM,eAAe,CAAC;
|
|
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;AACvB,OAAO,EAIL,KAAK,iBAAiB,EACvB,MAAM,mBAAmB,CAAC;AAI3B,MAAM,WAAW,eAAgB,SAAQ,eAAe;IACtD,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC;CACvC;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,IAAI,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC,KAAK,eAAe,CAAC;CAC9F;AAED,MAAM,MAAM,QAAQ,CAAC,CAAC,EAAE,KAAK,SAAS,eAAe,EAAE,MAAM,GAAG,IAAI,IAAI,CACtE,KAAK,EAAE,CAAC,EACR,QAAQ,EAAE,KAAK,KACZ,iBAAiB,CAAC,MAAM,CAAC,CAAC;AA2P/B,wBAAgB,OAAO,CAAC,CAAC,EAAE,KAAK,SAAS,eAAe,EAAE,MAAM,GAAG,IAAI,EACrE,KAAK,EAAE,gBAAgB,CAAC,KAAK,CAAC,EAC9B,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,EACnB,QAAQ,EAAE,QAAQ,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,GACnC,eAAe,CAkCjB"}
|
package/dist/protocol.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { SynchronousResult } from './async-result.js';
|
|
1
2
|
export { assertPromiseRuntime, resolveDisposalSymbol, asyncDisposeSymbol, disposeSymbol, } from './disposal-runtime.js';
|
|
2
3
|
export interface Disposable {
|
|
3
4
|
[Symbol.dispose](): void;
|
|
@@ -7,7 +8,7 @@ export interface AsyncDisposable {
|
|
|
7
8
|
}
|
|
8
9
|
export interface Readable<T> {
|
|
9
10
|
get(): T;
|
|
10
|
-
subscribe(listener: () =>
|
|
11
|
+
subscribe<Result>(listener: () => SynchronousResult<Result>): Disposable;
|
|
11
12
|
}
|
|
12
13
|
export declare function captureReadable<T>(source: Readable<T>, operation: string): Readable<T>;
|
|
13
14
|
export declare function captureUnsubscribe(subscription: Disposable): () => void;
|
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":"AACA,OAAO,EAAqB,KAAK,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAE9E,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,MAAM,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC,MAAM,CAAC,GAAG,UAAU,CAAC;CAC1E;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
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Readable } from './protocol.js';
|
|
2
|
+
import { SynchronousResult } from './async-result.js';
|
|
2
3
|
declare const dougongSignal: unique symbol;
|
|
3
4
|
export interface ReadonlySignal<T> extends Readable<T> {
|
|
4
5
|
readonly [dougongSignal]: true;
|
|
@@ -6,8 +7,8 @@ export interface ReadonlySignal<T> extends Readable<T> {
|
|
|
6
7
|
export interface Signal<T> extends ReadonlySignal<T> {
|
|
7
8
|
readonly set: (value: T) => void;
|
|
8
9
|
}
|
|
9
|
-
export declare function batch<T>(callback: () => T): T;
|
|
10
|
+
export declare function batch<T>(callback: () => SynchronousResult<T>): T;
|
|
10
11
|
export declare function signal<T>(initialValue: T): Signal<T>;
|
|
11
|
-
export declare function computed<T>(calculate: () => T): ReadonlySignal<T>;
|
|
12
|
+
export declare function computed<T>(calculate: () => SynchronousResult<T>): ReadonlySignal<T>;
|
|
12
13
|
export {};
|
|
13
14
|
//# sourceMappingURL=signals.d.ts.map
|
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,EAAkD,KAAK,QAAQ,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"signals.d.ts","sourceRoot":"","sources":["../src/signals.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkD,KAAK,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC9F,OAAO,EAAqB,KAAK,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAE9E,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,iBAAiB,CAAC,CAAC,CAAC,GAAG,CAAC,CAgChE;AAED,wBAAgB,MAAM,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAEpD;AAED,wBAAgB,QAAQ,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC,CAAC,CAAC,GAAG,cAAc,CAAC,CAAC,CAAC,CAGpF"}
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dougongjs/reactive",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"engines": {
|
|
6
|
-
"node": ">=
|
|
6
|
+
"node": "^24.22.0 || >=26.10.0"
|
|
7
7
|
},
|
|
8
8
|
"browserslist": [
|
|
9
9
|
"Chrome >= 119",
|
|
@@ -27,8 +27,7 @@
|
|
|
27
27
|
"imports": {
|
|
28
28
|
"#abort-listener": {
|
|
29
29
|
"types": "./runtime/abort-listener.d.mts",
|
|
30
|
-
"
|
|
31
|
-
"default": "./runtime/abort-listener.web.mjs"
|
|
30
|
+
"default": "./runtime/abort-listener.mjs"
|
|
32
31
|
}
|
|
33
32
|
},
|
|
34
33
|
"description": "Zero-dependency signal, computed, batch and observe primitives.",
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// Generated from tooling/internal/abort-listener.mjs. Run pnpm generate:internal.
|
|
2
|
+
// The private dependent must take its state from the source's controller. A runtime whose
|
|
3
|
+
// AbortSignal.any reads the public, shadowable properties instead would create a dependent
|
|
4
|
+
// already aborted with a forged reason, which never reports the real cancellation.
|
|
5
|
+
export function assertAbortRuntime(any) {
|
|
6
|
+
if (typeof any !== "function") {
|
|
7
|
+
throw new Error("Unsupported JavaScript runtime: AbortSignal.any is required");
|
|
8
|
+
}
|
|
9
|
+
const source = new AbortController().signal;
|
|
10
|
+
Object.defineProperty(source, "aborted", { value: true });
|
|
11
|
+
if (Reflect.get(AbortSignal.prototype, "aborted", any.call(AbortSignal, [source]))) {
|
|
12
|
+
throw new Error(
|
|
13
|
+
"Unsupported JavaScript runtime: AbortSignal.any must read native signal state",
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
assertAbortRuntime(AbortSignal.any);
|
|
19
|
+
|
|
20
|
+
// A separate closure prevents the dependent from retaining the source through its registration.
|
|
21
|
+
function weakAbortListener(reference) {
|
|
22
|
+
return () => reference.deref()?.();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function listenForAbort(signal, notify) {
|
|
26
|
+
const dependent = AbortSignal.any([signal]);
|
|
27
|
+
const listener = weakAbortListener(new WeakRef(notify));
|
|
28
|
+
let registration = { signal, notify, dependent, listener };
|
|
29
|
+
function detach() {
|
|
30
|
+
if (!registration) return;
|
|
31
|
+
const attached = registration;
|
|
32
|
+
registration = undefined;
|
|
33
|
+
EventTarget.prototype.removeEventListener.call(attached.signal, "abort", attached.notify);
|
|
34
|
+
attached.dependent.removeEventListener("abort", attached.listener);
|
|
35
|
+
}
|
|
36
|
+
try {
|
|
37
|
+
// Source listeners preserve native composite lifetimes; the private dependent bypasses stopped events.
|
|
38
|
+
EventTarget.prototype.addEventListener.call(signal, "abort", notify);
|
|
39
|
+
dependent.addEventListener("abort", listener);
|
|
40
|
+
} catch (error) {
|
|
41
|
+
detach();
|
|
42
|
+
throw error;
|
|
43
|
+
}
|
|
44
|
+
return detach;
|
|
45
|
+
}
|
|
@@ -1 +0,0 @@
|
|
|
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"}
|
|
@@ -1,26 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
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
|
-
}
|