doxum 0.1.23 → 0.1.26
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 +208 -102
- package/dist/advanced.cjs +237 -2
- package/dist/advanced.cjs.map +1 -0
- package/dist/advanced.d.cts +90 -2
- package/dist/advanced.d.ts +90 -2
- package/dist/advanced.js +236 -1
- package/dist/advanced.js.map +1 -0
- package/dist/{contract-ULPQmiEs.d.ts → contract-CNR6BS75.d.cts} +216 -30
- package/dist/{contract-SYJFP0lM.d.cts → contract-COgxO5sv.d.ts} +216 -30
- package/dist/definition-CDOuvb5C.d.cts +111 -0
- package/dist/definition-xgk3YNBp.d.ts +111 -0
- package/dist/{driver-yFfvDMX5.js → driver-B5vdPohL.js} +59 -3
- package/dist/driver-B5vdPohL.js.map +1 -0
- package/dist/{driver-BD3DQOW6.cjs → driver-BL91gn7_.cjs} +107 -9
- package/dist/driver-BL91gn7_.cjs.map +1 -0
- package/dist/index.cjs +1637 -703
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +65 -33
- package/dist/index.d.ts +65 -33
- package/dist/index.js +1432 -497
- package/dist/index.js.map +1 -1
- package/dist/local-sync.cjs +69 -103
- package/dist/local-sync.cjs.map +1 -1
- package/dist/local-sync.d.cts +14 -26
- package/dist/local-sync.d.ts +14 -26
- package/dist/local-sync.js +68 -96
- package/dist/local-sync.js.map +1 -1
- package/dist/react.cjs +4 -5
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +6 -8
- package/dist/react.d.ts +6 -8
- package/dist/react.js +4 -5
- package/dist/react.js.map +1 -1
- package/dist/record-B2yRhYsc.js +28 -0
- package/dist/record-B2yRhYsc.js.map +1 -0
- package/dist/record-ChYUi8PP.cjs +45 -0
- package/dist/record-ChYUi8PP.cjs.map +1 -0
- package/dist/scheduler--137L3j-.js +500 -0
- package/dist/scheduler--137L3j-.js.map +1 -0
- package/dist/scheduler-D4VthaaK.cjs +589 -0
- package/dist/scheduler-D4VthaaK.cjs.map +1 -0
- package/package.json +6 -3
- package/skills/doxum-runtime/SKILL.md +32 -7
- package/skills/doxum-runtime/agents/openai.yaml +1 -1
- package/skills/doxum-runtime/references/api.en.md +249 -0
- package/skills/doxum-runtime/references/api.zh-CN.md +242 -0
- package/skills/doxum-runtime/references/guide.en.md +18 -8
- package/skills/doxum-runtime/references/guide.zh-CN.md +14 -6
- package/skills/doxum-runtime/references/invariants.en.md +29 -5
- package/skills/doxum-runtime/references/invariants.zh-CN.md +24 -6
- package/skills/doxum-runtime/references/patterns.en.md +78 -18
- package/skills/doxum-runtime/references/patterns.zh-CN.md +76 -18
- package/skills/doxum-runtime/references/projections.en.md +209 -119
- package/skills/doxum-runtime/references/projections.zh-CN.md +200 -101
- package/dist/advanced-CLJCWkHa.d.cts +0 -176
- package/dist/advanced-CRNHfJBu.js +0 -1224
- package/dist/advanced-CRNHfJBu.js.map +0 -1
- package/dist/advanced-Cle5g3B4.cjs +0 -1517
- package/dist/advanced-Cle5g3B4.cjs.map +0 -1
- package/dist/advanced-d6J61er9.d.ts +0 -176
- package/dist/context-BcLKGfq8.cjs +0 -149
- package/dist/context-BcLKGfq8.cjs.map +0 -1
- package/dist/context-DFEaDUv5.js +0 -84
- package/dist/context-DFEaDUv5.js.map +0 -1
- package/dist/driver-BD3DQOW6.cjs.map +0 -1
- package/dist/driver-yFfvDMX5.js.map +0 -1
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# Doxum 使用指南
|
|
2
2
|
|
|
3
|
+
`doxum`、`doxum/advanced`、`doxum/react`、`doxum/local-sync` 的精确公开签名见
|
|
4
|
+
[API 参考](api.zh-CN.md)。本指南只保留选择原则与生命周期语义。
|
|
5
|
+
|
|
3
6
|
## 定义、修改与读取
|
|
4
7
|
|
|
5
8
|
```ts
|
|
@@ -16,7 +19,6 @@ document.update(draft => {
|
|
|
16
19
|
const task = draft.tasks.get('a');
|
|
17
20
|
if (task) task.done = !task.done;
|
|
18
21
|
draft.tasks.put('b', { title: 'Review', done: false });
|
|
19
|
-
return { warnings: [] };
|
|
20
22
|
});
|
|
21
23
|
const done = read(document, state => state.tasks.get('a')?.done);
|
|
22
24
|
const tasks = read(document, state => snapshot(state.tasks));
|
|
@@ -62,8 +64,9 @@ tree 可以为空。`tree(field(...))` 要求每个已存在节点都拥有自
|
|
|
62
64
|
`tree(optional(field(...)))` 才允许节点缺失 payload,而 `optional(tree(...))` 独立控制整棵
|
|
63
65
|
tree 成员是否可以缺失。
|
|
64
66
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
+
函数 validator 是同步 predicate/assertion:返回 `true` 或 `undefined` 成功,
|
|
68
|
+
返回 `false` 拒绝,assertion 也可以 throw。Standard Schema v1 成功时必须按 identity
|
|
69
|
+
返回原输入;transform output 会被拒绝。validator 不得修改输入。数据转换在进入 Doxum 前完成。
|
|
67
70
|
parse(model, unknown) 复制校验后的结构并共享只读 payload;严格解析要求原子字段具备校验器。品牌键贯穿 map/table 方法、
|
|
68
71
|
符号路径和 impact。路径回调描述地址,包括缺失键,订阅注册时解析。
|
|
69
72
|
Core 的 `select(document, selector, equality?)` 追踪实际读取,在选择分支改变时更新
|
|
@@ -84,9 +87,14 @@ apply(changes, { expectedRevision }) 拒绝缺失或不匹配的本地基线;
|
|
|
84
87
|
observerErrors 属于已提交结果。
|
|
85
88
|
|
|
86
89
|
集合边界使用 `observe(document, path => path.tasks)`,纯派生值使用
|
|
87
|
-
`derive(
|
|
88
|
-
`createProjectionRuntime({ onError })` owner
|
|
89
|
-
keyed
|
|
90
|
+
`derive({ tasks, filter }, ({ tasks, filter }) => ...)`。定义是惰性的,由一个
|
|
91
|
+
`createProjectionRuntime({ onError })` owner 物化和管理。当一个 keyed source 决定
|
|
92
|
+
output key/order 时,用 `derive.keyed(entries, entry => entry.label)` 独立映射每个
|
|
93
|
+
entry;跨 collection 的动态 key lookup 继续在同一个 derivation 中用命名依赖对象声明:
|
|
94
|
+
普通 Projection 成员是全局依赖,`{ source, key }` 成员是逐 output key lookup,selector
|
|
95
|
+
接收 `(entry, key, dependencies)`。Runtime 拥有 binding/reverse index,producer DAG
|
|
96
|
+
仍保持显式。只有 `derive.keyed` 无法表达的 retained state、自定义跨 key index 或
|
|
97
|
+
keyed patch 才放到隔离的 `doxum/advanced` incremental 入口。生命周期、draft、
|
|
90
98
|
selector 追踪、batch 与故障恢复见 [Projection 参考](projections.zh-CN.md)。
|
|
91
99
|
tree 结构复用同一套 projection source 协议:`path.tree.rootId` 是 scalar,
|
|
92
100
|
`path.tree.nodes` 是 keyed collection,`path.tree.nodes.item(id)` 是单节点 value。
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# Runtime Invariants
|
|
2
2
|
|
|
3
|
+
Public call shapes belong in [api.en.md](api.en.md); keep this file limited to
|
|
4
|
+
implementation invariants that affect design and review decisions.
|
|
5
|
+
|
|
3
6
|
1. createDocument owns canonical state; Draft, apply and replace share MutationSession.
|
|
4
7
|
runtime owns one execution/rollback/seal/publish boundary, also used by history.
|
|
5
8
|
mutation/operations groups table/list/order/tree/replay commands by domain; session
|
|
@@ -21,23 +24,44 @@
|
|
|
21
24
|
5. Atomic equality is Object.is; canonical structure retains atomic references under
|
|
22
25
|
ownership contract. Snapshots copy structure and share readonly payloads, as do
|
|
23
26
|
commits and history. No payload cloning or publication freezing. Validators receive
|
|
24
|
-
original inputs and must be pure;
|
|
27
|
+
original inputs and must be pure; function validators are predicates/assertions and
|
|
28
|
+
Standard Schema success may not transform the canonical value.
|
|
25
29
|
6. List identity is a stable key; replacement retains it. Anchor owns ordering.
|
|
26
30
|
Tree owns reciprocal, connected, acyclic, empty-or-single-root topology.
|
|
27
|
-
7.
|
|
28
|
-
|
|
31
|
+
7. Public `Schema` / `ObjectSchema` handles own schema identity while concrete nodes stay
|
|
32
|
+
internal. Schema model, path compilation, compiled layout and value validation/copy
|
|
33
|
+
have separate internal owners; foundation model/layout modules do not depend on
|
|
34
|
+
document/projection Runtime code. One RuntimeContext owns instance identity for the runtime, history and
|
|
35
|
+
`document.readonly()` aliases. The shared path compiler and Core impact target
|
|
29
36
|
algorithms own addressing/identity/equality/bucketing and exact matching. React sees
|
|
30
37
|
only Readable/select contracts. Notification matches grouped changes directly without
|
|
31
38
|
building commit impact indexes.
|
|
32
39
|
8. Apply requires expectedRevision and captures actual local old state. Local reset is
|
|
33
40
|
reversible; remote commits invalidate history. Groups travel in one session.
|
|
34
|
-
9. Projections declare sources and settle before listeners.
|
|
35
|
-
|
|
41
|
+
9. Projections declare producer sources and settle before listeners. The producer DAG
|
|
42
|
+
remains static. `derive.keyed` declares extra sources in one named dependency object;
|
|
43
|
+
`{ source, key }` members may bind each output key to a key in an explicitly declared
|
|
44
|
+
keyed source. The materialized Runtime owns those bindings and reverse indexes,
|
|
45
|
+
including bindings to currently missing source entries. Processors do not create graph
|
|
46
|
+
dependencies through imperative Runtime reads. A scope adds lifecycle ownership only
|
|
47
|
+
through `scope.own`; scoped definitions may depend on root definitions, while root and
|
|
48
|
+
sibling scopes cannot depend on scoped definitions. Notification failures leave commits
|
|
49
|
+
accepted. Batch defers projection publication, not document commits/listeners.
|
|
50
|
+
Lazy definition metadata does not materialize sources. Input, observe and source
|
|
51
|
+
materialization remain separate boundaries. One output object owns staged/published
|
|
52
|
+
state, revision, listeners and graph-facing consumer capability; scheduler alone
|
|
53
|
+
attaches/detaches dependency edges. Collection-input callback/equality failure installs
|
|
54
|
+
no partial Runtime-local state. Cleanup is exhaustive after ownership is detached;
|
|
55
|
+
initialization failure releases already-created projection resources and preserves the
|
|
56
|
+
original error. Document dirty/pending state has one write owner; structural
|
|
57
|
+
materialization only reads it.
|
|
36
58
|
10. Local sync uses Web Lock leadership and contiguous durable sequence. Writes precede
|
|
37
59
|
asynchronous persistence. Version 5 / format 3 rejects old databases without editing
|
|
38
60
|
them. External replace and remote apply are prohibited while attached.
|
|
39
61
|
11. Core is framework-neutral. Public exports are deliberate; root dist is generated.
|
|
40
62
|
12. Test malformed input, partial rollback, history, impact, disposal and bounded work.
|
|
63
|
+
Dependency de-duplication uses target-owned canonical bucketing plus exact equality;
|
|
64
|
+
do not fall back to a linear all-target scan or an ad-hoc string path grammar.
|
|
41
65
|
Delete obsolete APIs and parallel protocols.
|
|
42
66
|
|
|
43
67
|
Network intent, authorization, collaborative undo and persist-before-visible acceptance
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Runtime 不变量
|
|
2
2
|
|
|
3
|
+
公开调用形态统一放在 [api.zh-CN.md](api.zh-CN.md);本文只保留影响实现和 review 的不变量。
|
|
4
|
+
|
|
3
5
|
1. createDocument 唯一拥有 canonical state;Draft、apply、replace 共用 MutationSession。
|
|
4
6
|
runtime 统一执行、回滚、seal、发布边界,history 同样复用。
|
|
5
7
|
mutation/operations 按 table/list/order/tree/replay 分类完整操作,session 持有写入内核。
|
|
@@ -15,21 +17,37 @@
|
|
|
15
17
|
capture、restore 和按域 seal 分离;当前顺序确实变化后才复制最终 order,净变化为零也保留执行期间的回滚基线。
|
|
16
18
|
5. 原子值按 Object.is 比较,canonical 结构依所有权契约保留原子引用;
|
|
17
19
|
快照只复制结构,payload 与 commit、history 共享且只读,不做深复制或发布冻结。
|
|
18
|
-
|
|
20
|
+
validator 直接读取原始输入且必须纯同步;函数形式是 predicate/assertion,
|
|
21
|
+
Standard Schema 成功时不能 transform canonical value。
|
|
19
22
|
6. list 以稳定键标记身份,替换值必须保留键。anchor 拥有排序语义;
|
|
20
23
|
tree 拥有双向一致、连通、无环、空树或单根的拓扑约束。
|
|
21
|
-
7.
|
|
22
|
-
|
|
24
|
+
7. 公开 `Schema` / `ObjectSchema` handle 拥有定义身份,concrete node 留在内部;
|
|
25
|
+
schema model、path compiler、compiled layout、value validation/copy 各有唯一内部 owner,
|
|
26
|
+
foundation model/layout 不依赖 document/projection Runtime。runtime、history 和
|
|
27
|
+
`document.readonly()` alias 共享唯一 RuntimeContext 实例身份。
|
|
28
|
+
共享路径 compiler 与 Core impact target 算法拥有寻址、身份、相等、
|
|
23
29
|
分桶和精确匹配;React 只消费 Readable/select 契约。通知直接匹配分组变化,
|
|
24
30
|
不构建 commit impact 索引。
|
|
25
31
|
8. apply 要求 expectedRevision,记录本地真实旧状态。本地 reset 可撤销,
|
|
26
32
|
remote commit 使 history 失效;group 在单个 session 中旅行。
|
|
27
|
-
9. projection 显式声明 source
|
|
28
|
-
|
|
33
|
+
9. projection 显式声明 producer source,先于监听结算,producer DAG 保持静态。
|
|
34
|
+
`derive.keyed` 用一个命名依赖对象声明额外 source;其中 `{ source, key }` 成员可以
|
|
35
|
+
把每个 output key 绑定到已显式声明 keyed source 的某个 key。物化后的 Runtime
|
|
36
|
+
拥有这些 binding 和 reverse index,包括当前尚不存在的 source entry。processor
|
|
37
|
+
不通过 imperative Runtime read 创建图依赖。scope 只通过 `scope.own` 增加 lifecycle
|
|
38
|
+
ownership;scoped definition 可以依赖 root definition,root 与 sibling scope 不能依赖
|
|
39
|
+
scoped definition。通知失败不撤销提交;batch 推迟投影发布,不推迟文档提交与文档监听。
|
|
40
|
+
lazy definition metadata 不负责物化 source;input、observe、source materialization 分层。
|
|
41
|
+
一个 output object 同时拥有 staged/published state、revision、listener 和 graph-facing
|
|
42
|
+
consumer capability,只有 scheduler 连接/断开 dependency edge。collection input 的 callback
|
|
43
|
+
或 entry equality 抛错时不得安装部分 Runtime-local state。cleanup 先摘除 ownership,再穷尽
|
|
44
|
+
释放;初始化失败释放已创建的 projection resource,同时保留原始异常。document dirty/pending
|
|
45
|
+
state 只有一个写 owner,structural materialization 只读该状态。
|
|
29
46
|
10. local-sync 使用 Web Lock 领导权和连续 durable seq,先可见后异步持久化。
|
|
30
47
|
版本 5 / 格式 3 拒绝旧数据库并保留原数据,附着期间禁止外部 replace 和 remote apply。
|
|
31
48
|
11. core 框架无关,公开导出需明确用途;根 dist 为构建产物。
|
|
32
49
|
12. 测试 malformed 输入、部分失败回滚、history、impact、dispose 和大集合工作量。
|
|
33
|
-
|
|
50
|
+
dependency 去重使用 impact target owner 提供的 canonical bucket,再做精确 equality;
|
|
51
|
+
不得退回全量线性扫描或新增字符串 path grammar。删除旧 API 和平行协议。
|
|
34
52
|
|
|
35
53
|
网络意图、鉴权、协作撤销、先持久化后可见属于独立需求,不是隐藏 runtime 能力。
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Doxum Patterns
|
|
2
2
|
|
|
3
|
+
Exact call shapes and callback fields are listed in the [API reference](api.en.md).
|
|
4
|
+
|
|
3
5
|
## Payload Or Structure
|
|
4
6
|
|
|
5
7
|
```ts
|
|
@@ -19,15 +21,27 @@ Edit position.x, replace stroke whole, and use rows.replace(key, value) for keye
|
|
|
19
21
|
const model = object({
|
|
20
22
|
entries: map(object({ rows: table(object({ title: field<string>() })) })),
|
|
21
23
|
});
|
|
22
|
-
const document = createDocument({
|
|
24
|
+
const document = createDocument({
|
|
25
|
+
schema: model,
|
|
26
|
+
initial: {
|
|
27
|
+
entries: {
|
|
28
|
+
a: { rows: { ids: ['x'], byId: { x: { title: 'First' } } } },
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
});
|
|
23
32
|
document.update(draft => {
|
|
24
|
-
draft.entries.
|
|
25
|
-
|
|
33
|
+
const entry = draft.entries.get('a')!;
|
|
34
|
+
replace(entry, 'rows', {
|
|
35
|
+
ids: ['y'],
|
|
36
|
+
byId: { y: { title: 'Replacement' } },
|
|
37
|
+
});
|
|
26
38
|
});
|
|
27
39
|
```
|
|
28
40
|
|
|
29
|
-
|
|
30
|
-
type contains collection tools
|
|
41
|
+
Use top-level `replace(parent, key, value)` for object/variant members whose Draft
|
|
42
|
+
type contains collection tools. Map membership still uses `put`/`remove`; a
|
|
43
|
+
collection's own `replace(next)` handles replacement when the collection itself is
|
|
44
|
+
the mutation target.
|
|
31
45
|
|
|
32
46
|
## Domain Keys
|
|
33
47
|
|
|
@@ -45,7 +59,7 @@ const model = object({ people: map(object({ name: field(text) }), { key: personI
|
|
|
45
59
|
const initial = parse(model, { people: { 'person:1': { name: 'Ada' } } });
|
|
46
60
|
```
|
|
47
61
|
|
|
48
|
-
|
|
62
|
+
Branded key types flow through map/table methods, symbolic paths and collection impact.
|
|
49
63
|
|
|
50
64
|
## Ordered Edits, History And Replay
|
|
51
65
|
|
|
@@ -85,18 +99,64 @@ envelopes or treat its container address as whole-container invalidation.
|
|
|
85
99
|
## Projection And React
|
|
86
100
|
|
|
87
101
|
```ts
|
|
88
|
-
const
|
|
89
|
-
const
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
const
|
|
102
|
+
const rows = observe(document, path => path.rows);
|
|
103
|
+
const metadata = observe(document, path => path.metadata);
|
|
104
|
+
const density = input<'compact' | 'comfortable'>('comfortable');
|
|
105
|
+
|
|
106
|
+
const labels = derive.keyed(rows, row => row.label);
|
|
107
|
+
const decorated = derive.keyed(
|
|
108
|
+
rows,
|
|
109
|
+
{
|
|
110
|
+
meta: { source: metadata, key: row => row.metadataId },
|
|
111
|
+
density,
|
|
112
|
+
},
|
|
113
|
+
(row, _rowId, { meta, density }) => formatRow(row, meta, density)
|
|
114
|
+
);
|
|
115
|
+
const count = derive({ labels }, ({ labels }) => labels.size);
|
|
116
|
+
const selection = input.collection<RowId, boolean>();
|
|
93
117
|
const runtime = createProjectionRuntime({ onError: console.error });
|
|
94
|
-
runtime.
|
|
95
|
-
|
|
118
|
+
runtime.read(decorated);
|
|
119
|
+
runtime.read(count);
|
|
120
|
+
runtime.update(density, 'compact');
|
|
121
|
+
runtime.update(selection, draft => draft.set(rowId, true));
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Use named-object `derive` for aggregate values. Use `derive.keyed` when one keyed
|
|
125
|
+
driver owns output keys/order. Its named dependency form handles dynamic keyed lookups;
|
|
126
|
+
the Runtime owns the binding/reverse index. Use `input.collection` for Runtime-local
|
|
127
|
+
keyed UI/application state rather than canonical document state.
|
|
128
|
+
|
|
129
|
+
Provide the Runtime or a scope through `ProjectionProvider`. `useProjection` reads
|
|
130
|
+
projections, including `useProjection(projection, selector, equality?)`; `useInput`
|
|
131
|
+
handles both scalar and collection inputs. Document selectors use
|
|
132
|
+
`useDocumentSelector(document, selector, equality?)` independently of the provider.
|
|
133
|
+
|
|
134
|
+
## Multi-output incremental processor
|
|
135
|
+
|
|
136
|
+
Use this only when outputs share retained state or cross-key work that pure `derive`
|
|
137
|
+
and `derive.keyed` cannot express:
|
|
138
|
+
|
|
139
|
+
```ts
|
|
140
|
+
const render = incremental.group(
|
|
141
|
+
{ scene },
|
|
142
|
+
{
|
|
143
|
+
output: define => ({
|
|
144
|
+
cards: define.collection<string, Card>(),
|
|
145
|
+
count: define.value<number>(),
|
|
146
|
+
}),
|
|
147
|
+
state: () => ({ runs: 0 }),
|
|
148
|
+
process: ({ values, output, state }) => {
|
|
149
|
+
state.runs++;
|
|
150
|
+
const cards = buildCards(values.scene);
|
|
151
|
+
for (const [id, card] of cards) output.cards.set(id, card);
|
|
152
|
+
output.cards.order([...cards.keys()]);
|
|
153
|
+
output.count.set(cards.size);
|
|
154
|
+
},
|
|
155
|
+
}
|
|
156
|
+
);
|
|
96
157
|
```
|
|
97
158
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
dependencies remain explicit even though React selectors track actual reads.
|
|
159
|
+
`define.value` / `define.collection` exist only inside the `output` declaration.
|
|
160
|
+
The returned nested object mirrors ordinary Projection leaves from one processor.
|
|
161
|
+
Dependencies are named; declare `state()` only when retained state is needed. Runtime
|
|
162
|
+
recovery recreates declared state after a processor fault.
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Doxum 常见模式
|
|
2
2
|
|
|
3
|
+
精确调用形态和 callback 字段见 [API 参考](api.zh-CN.md)。
|
|
4
|
+
|
|
3
5
|
## 原子值与结构
|
|
4
6
|
|
|
5
7
|
```ts
|
|
@@ -19,15 +21,26 @@ position.x 细粒度赋值;stroke 整体替换;rows.replace(key, value) 按
|
|
|
19
21
|
const model = object({
|
|
20
22
|
entries: map(object({ rows: table(object({ title: field<string>() })) })),
|
|
21
23
|
});
|
|
22
|
-
const document = createDocument({
|
|
24
|
+
const document = createDocument({
|
|
25
|
+
schema: model,
|
|
26
|
+
initial: {
|
|
27
|
+
entries: {
|
|
28
|
+
a: { rows: { ids: ['x'], byId: { x: { title: 'First' } } } },
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
});
|
|
23
32
|
document.update(draft => {
|
|
24
|
-
draft.entries.
|
|
25
|
-
|
|
33
|
+
const entry = draft.entries.get('a')!;
|
|
34
|
+
replace(entry, 'rows', {
|
|
35
|
+
ids: ['y'],
|
|
36
|
+
byId: { y: { title: 'Replacement' } },
|
|
37
|
+
});
|
|
26
38
|
});
|
|
27
39
|
```
|
|
28
40
|
|
|
29
|
-
|
|
30
|
-
|
|
41
|
+
Draft 类型含集合方法的 object/variant 成员使用顶层 `replace(parent, key, value)`;
|
|
42
|
+
map membership 仍使用 `put`/`remove`。当集合本身就是 mutation target 时,整体替换
|
|
43
|
+
使用集合自身的 `replace(next)`。
|
|
31
44
|
|
|
32
45
|
## 领域键
|
|
33
46
|
|
|
@@ -45,7 +58,7 @@ const model = object({ people: map(object({ name: field(text) }), { key: personI
|
|
|
45
58
|
const initial = parse(model, { people: { 'person:1': { name: 'Ada' } } });
|
|
46
59
|
```
|
|
47
60
|
|
|
48
|
-
|
|
61
|
+
品牌键类型贯穿 map/table 方法、符号路径和 collection impact。
|
|
49
62
|
|
|
50
63
|
## 顺序、History 与重放
|
|
51
64
|
|
|
@@ -84,18 +97,63 @@ document.apply(
|
|
|
84
97
|
## 投影与 React
|
|
85
98
|
|
|
86
99
|
```ts
|
|
87
|
-
const
|
|
88
|
-
const
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
const
|
|
100
|
+
const rows = observe(document, path => path.rows);
|
|
101
|
+
const metadata = observe(document, path => path.metadata);
|
|
102
|
+
const density = input<'compact' | 'comfortable'>('comfortable');
|
|
103
|
+
|
|
104
|
+
const labels = derive.keyed(rows, row => row.label);
|
|
105
|
+
const decorated = derive.keyed(
|
|
106
|
+
rows,
|
|
107
|
+
{
|
|
108
|
+
meta: { source: metadata, key: row => row.metadataId },
|
|
109
|
+
density,
|
|
110
|
+
},
|
|
111
|
+
(row, _rowId, { meta, density }) => formatRow(row, meta, density)
|
|
112
|
+
);
|
|
113
|
+
const count = derive({ labels }, ({ labels }) => labels.size);
|
|
114
|
+
const selection = input.collection<RowId, boolean>();
|
|
92
115
|
const runtime = createProjectionRuntime({ onError: console.error });
|
|
93
|
-
runtime.
|
|
94
|
-
|
|
116
|
+
runtime.read(decorated);
|
|
117
|
+
runtime.read(count);
|
|
118
|
+
runtime.update(density, 'compact');
|
|
119
|
+
runtime.update(selection, draft => draft.set(rowId, true));
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
聚合值使用 named-object `derive`。一个 keyed driver 拥有 output key/order 时使用
|
|
123
|
+
`derive.keyed`;named dependency form 负责 dynamic keyed lookup,binding/reverse
|
|
124
|
+
index 由 Runtime 拥有。`input.collection` 用于 Runtime-local 的 keyed UI/application
|
|
125
|
+
state,不进入 canonical document state。
|
|
126
|
+
|
|
127
|
+
React 用 `ProjectionProvider` 提供 Runtime 或 scope;`useProjection` 支持
|
|
128
|
+
`useProjection(projection, selector, equality?)`,`useInput` 同时支持 scalar 与
|
|
129
|
+
collection input。Document selector 独立使用
|
|
130
|
+
`useDocumentSelector(document, selector, equality?)`。
|
|
131
|
+
|
|
132
|
+
## 多输出 incremental processor
|
|
133
|
+
|
|
134
|
+
只有 pure `derive` / `derive.keyed` 无法表达共享 retained state 或 cross-key work 时使用:
|
|
135
|
+
|
|
136
|
+
```ts
|
|
137
|
+
const render = incremental.group(
|
|
138
|
+
{ scene },
|
|
139
|
+
{
|
|
140
|
+
output: define => ({
|
|
141
|
+
cards: define.collection<string, Card>(),
|
|
142
|
+
count: define.value<number>(),
|
|
143
|
+
}),
|
|
144
|
+
state: () => ({ runs: 0 }),
|
|
145
|
+
process: ({ values, output, state }) => {
|
|
146
|
+
state.runs++;
|
|
147
|
+
const cards = buildCards(values.scene);
|
|
148
|
+
for (const [id, card] of cards) output.cards.set(id, card);
|
|
149
|
+
output.cards.order([...cards.keys()]);
|
|
150
|
+
output.count.set(cards.size);
|
|
151
|
+
},
|
|
152
|
+
}
|
|
153
|
+
);
|
|
95
154
|
```
|
|
96
155
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
React selector 追踪只属于消费端。
|
|
156
|
+
`define.value` / `define.collection` 只存在于 `output` 声明 callback 内,返回的嵌套
|
|
157
|
+
object 会映射为同一 processor 的普通 Projection leaves。依赖按名称读取;只有需要
|
|
158
|
+
retained state 时才声明 `state()`。processor fault 后由 Runtime 重新创建已声明的 state
|
|
159
|
+
并恢复。
|