@dougongjs/reactive 0.8.3 → 0.10.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 +20 -78
- package/dist/abort.d.ts +4 -0
- package/dist/abort.d.ts.map +1 -0
- package/dist/async-result.d.ts +10 -0
- package/dist/async-result.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 +603 -383
- package/dist/observe.d.ts +9 -8
- package/dist/observe.d.ts.map +1 -1
- package/dist/protocol.d.ts +8 -5
- package/dist/protocol.d.ts.map +1 -1
- package/dist/signals.d.ts +4 -3
- package/dist/signals.d.ts.map +1 -1
- package/package.json +11 -4
- package/runtime/abort-listener.d.mts +2 -0
- package/runtime/abort-listener.mjs +27 -0
- package/dist/sync-result.d.ts +0 -3
- package/dist/sync-result.d.ts.map +0 -1
package/README.md
CHANGED
|
@@ -1,91 +1,33 @@
|
|
|
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
|
-
```
|
|
53
|
-
|
|
54
|
-
```ts
|
|
55
|
-
import { signal, computed, batch, observe } from "@dougongjs/reactive"
|
|
56
|
-
|
|
57
|
-
const count = signal(0)
|
|
58
|
-
const double = computed(() => count.get() * 2)
|
|
59
|
-
|
|
60
|
-
batch(() => count.set(21))
|
|
61
|
-
double.get()
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
`double.get()` returns `42`.
|
|
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
|
|
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).
|
|
69
6
|
|
|
70
7
|
```ts
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
8
|
+
import { batch, computed, disposeSymbol, 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[disposeSymbol]();
|
|
75
19
|
```
|
|
76
20
|
|
|
77
|
-
|
|
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`.
|
|
78
22
|
|
|
79
|
-
`
|
|
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.
|
|
80
24
|
|
|
81
|
-
`
|
|
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.
|
|
82
26
|
|
|
83
|
-
|
|
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.
|
|
84
28
|
|
|
85
|
-
|
|
86
|
-
- 仓库 / Repository: https://github.com/Tangerg/dougong
|
|
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.
|
|
87
30
|
|
|
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.
|
|
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 -->.
|
|
90
32
|
|
|
91
|
-
|
|
33
|
+
[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"}
|
|
@@ -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
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;AAGxE,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,KAAK,EAAE,CAAC,OAAO,EAAE,MAAM,OAAO,KAAK,eAAe,CAAC;CAC7D;AA4ID,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 { asyncDisposeSymbol, disposeSymbol, type AsyncDisposable, type Disposable, type 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,
|
|
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"}
|