doxum 0.1.5 → 0.1.6
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 +46 -5
- package/dist/{contract-4E3moCbY.d.ts → contract-I99DUMbN.d.ts} +1 -1
- package/dist/index.cjs +73 -608
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -63
- package/dist/index.d.ts +3 -64
- package/dist/index.js +11 -547
- package/dist/index.js.map +1 -1
- package/dist/integration-Bs3pOk65.cjs +35 -0
- package/dist/integration-Bs3pOk65.cjs.map +1 -0
- package/dist/integration-xupBIKjU.js +30 -0
- package/dist/integration-xupBIKjU.js.map +1 -0
- package/dist/integration.cjs +4 -14
- package/dist/integration.d.cts +2 -1
- package/dist/integration.d.ts +3 -2
- package/dist/integration.js +3 -14
- package/dist/local-sync.cjs +1 -1
- package/dist/local-sync.d.ts +1 -1
- package/dist/local-sync.js +1 -1
- package/dist/{ownership-CU-9DKJQ.cjs → ownership-CY0nPXGF.cjs} +4 -1
- package/dist/ownership-CY0nPXGF.cjs.map +1 -0
- package/dist/{ownership-B-VAPcce.js → ownership-CduRygE7.js} +4 -1
- package/dist/ownership-CduRygE7.js.map +1 -0
- package/dist/react.cjs +1 -1
- package/dist/react.js +1 -1
- package/dist/runtime-BJRXF0gq.js +1735 -0
- package/dist/runtime-BJRXF0gq.js.map +1 -0
- package/dist/runtime-CRijXjYp.cjs +1991 -0
- package/dist/runtime-CRijXjYp.cjs.map +1 -0
- package/dist/runtime-CUcvhFNP.d.ts +152 -0
- package/dist/runtime-C_vKHmhy.d.cts +152 -0
- package/package.json +1 -1
- package/skills/doxum-runtime/references/guide.en.md +30 -18
- package/skills/doxum-runtime/references/guide.zh-CN.md +22 -14
- package/skills/doxum-runtime/references/invariants.en.md +12 -9
- package/skills/doxum-runtime/references/invariants.zh-CN.md +2 -2
- package/skills/doxum-runtime/references/patterns.en.md +29 -56
- package/skills/doxum-runtime/references/patterns.zh-CN.md +21 -45
- package/dist/dependency-C5_R1tvQ.js +0 -496
- package/dist/dependency-C5_R1tvQ.js.map +0 -1
- package/dist/dependency-Dfzs3khk.cjs +0 -722
- package/dist/dependency-Dfzs3khk.cjs.map +0 -1
- package/dist/integration.cjs.map +0 -1
- package/dist/integration.js.map +0 -1
- package/dist/ownership-B-VAPcce.js.map +0 -1
- package/dist/ownership-CU-9DKJQ.cjs.map +0 -1
package/package.json
CHANGED
|
@@ -24,8 +24,8 @@ resolution.
|
|
|
24
24
|
| Replace an entire trusted snapshot | `runtime.replace(document, options)` |
|
|
25
25
|
| Observe one schema location | `schema.value` plus `runtime.subscribe` |
|
|
26
26
|
| Observe a table or map | `schema.collection` plus `runtime.subscribe` |
|
|
27
|
-
| Maintain mapped collection data | `
|
|
28
|
-
| Maintain an aggregate or index | `
|
|
27
|
+
| Maintain mapped collection data | `projection.map` |
|
|
28
|
+
| Maintain an aggregate or index | `projection.value / projection.collection` |
|
|
29
29
|
| Read in React | `useDocumentSelector`, `useReadable`, or `useReadable` |
|
|
30
30
|
|
|
31
31
|
Do not write a second mutable copy of the document. `createDocument` is the
|
|
@@ -293,26 +293,38 @@ comparison helpers in application modules.
|
|
|
293
293
|
|
|
294
294
|
## Build derived read models
|
|
295
295
|
|
|
296
|
-
`createCollectionView` maps one table or map into stable ids, cached `item(id)`
|
|
297
|
-
readables, and a lazy `all` array. It updates only affected entries where possible.
|
|
298
|
-
|
|
299
296
|
```ts
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
const
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
297
|
+
const projection = createProjectionRuntime({ onError: error => console.error(error) });
|
|
298
|
+
const document = projection.document(runtime);
|
|
299
|
+
const notes = document.collection(path => path.notes);
|
|
300
|
+
const noteSummaries = projection.map(
|
|
301
|
+
notes,
|
|
302
|
+
(id, note) => ({ id, preview: note.body.get().slice(0, 80) }),
|
|
303
|
+
{ isEqual: (a, b) => a.id === b.id && a.preview === b.preview }
|
|
304
|
+
);
|
|
305
|
+
const noteCount = projection.value({
|
|
306
|
+
sources: { notes },
|
|
307
|
+
build: ({ notes }) => ({
|
|
308
|
+
value: notes.read.ids().length,
|
|
309
|
+
update: ({ notes }) => ({
|
|
310
|
+
kind: 'changed',
|
|
311
|
+
value: notes.read.ids().length,
|
|
312
|
+
}),
|
|
313
|
+
}),
|
|
306
314
|
});
|
|
307
|
-
|
|
308
|
-
taskTitles.item('write-guide').current();
|
|
309
|
-
taskTitles.all.current();
|
|
310
315
|
```
|
|
311
316
|
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
317
|
+
projection.map produces stable ids/item readables and lazy all. Declare a
|
|
318
|
+
DocumentCollectionSource directly in any processor's sources, or use
|
|
319
|
+
projection.value and projection.collection for explicit incremental logic.
|
|
320
|
+
There is no automatic keyed dependency tracking. All sources belong to the
|
|
321
|
+
same projection owner, but may refer to different document runtimes.
|
|
322
|
+
|
|
323
|
+
Use projection.input(initial, { isEqual }) for boundary values; give processors
|
|
324
|
+
its source and keep set at the application boundary. fromReadable attaches an
|
|
325
|
+
existing external source without taking ownership of it. Batch synchronous
|
|
326
|
+
multi-source changes before the first commit. Dispose the projection at service
|
|
327
|
+
shutdown; React unmount only unsubscribes. Disposed handles throw.
|
|
316
328
|
|
|
317
329
|
## React integration
|
|
318
330
|
|
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
| 整体替换可信快照 | `runtime.replace(document, options)` |
|
|
18
18
|
| 监听一个 schema 位置 | `schema.value` + `runtime.subscribe` |
|
|
19
19
|
| 监听 table 或 map | `schema.collection` + `runtime.subscribe` |
|
|
20
|
-
| 维护映射后的集合数据 | `
|
|
21
|
-
| 维护聚合或索引 | `
|
|
20
|
+
| 维护映射后的集合数据 | `projection.map` |
|
|
21
|
+
| 维护聚合或索引 | `projection.value / projection.collection` |
|
|
22
22
|
| 在 React 中读取 | `useDocumentSelector`、`useReadable` 或 `useReadable` |
|
|
23
23
|
|
|
24
24
|
不要再维护一份可写的文档副本。`createDocument` 是 canonical state 的唯一所有者。
|
|
@@ -243,22 +243,30 @@ value selector 使用 `commit.impact.affects(target)`;table 或 map selector
|
|
|
243
243
|
|
|
244
244
|
## 构建派生读模型
|
|
245
245
|
|
|
246
|
-
`createCollectionView` 将一个 table 或 map 映射为稳定的 ids、缓存的 `item(id)` readable 和惰性 `all` 数组;在可能的情况下只更新受影响的条目。
|
|
247
|
-
|
|
248
246
|
```ts
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
const
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
247
|
+
const projection = createProjectionRuntime({ onError: error => console.error(error) });
|
|
248
|
+
const document = projection.document(runtime);
|
|
249
|
+
const notes = document.collection(path => path.notes);
|
|
250
|
+
const noteSummaries = projection.map(
|
|
251
|
+
notes,
|
|
252
|
+
(id, note) => ({ id, preview: note.body.get().slice(0, 80) }),
|
|
253
|
+
{ isEqual: (a, b) => a.id === b.id && a.preview === b.preview }
|
|
254
|
+
);
|
|
255
|
+
const noteCount = projection.value({
|
|
256
|
+
sources: { notes },
|
|
257
|
+
build: ({ notes }) => ({
|
|
258
|
+
value: notes.read.ids().length,
|
|
259
|
+
update: ({ notes }) => ({
|
|
260
|
+
kind: 'changed',
|
|
261
|
+
value: notes.read.ids().length,
|
|
262
|
+
}),
|
|
263
|
+
}),
|
|
255
264
|
});
|
|
256
|
-
|
|
257
|
-
taskTitles.item('write-guide').current();
|
|
258
|
-
taskTitles.all.current();
|
|
259
265
|
```
|
|
260
266
|
|
|
261
|
-
|
|
267
|
+
projection.map 提供稳定的 ids/item readable 和惰性 all。DocumentCollectionSource 可直接用于自定义 processor 的 sources;projection.value 和 projection.collection 承载显式增量计算。不自动跟踪 keyed 依赖。sources 属于同一 projection owner,但可接入不同 document runtime。
|
|
268
|
+
|
|
269
|
+
projection.input(initial, { isEqual }) 用于边界值;应用保留 set,processor 只接收 source。fromReadable 接入已有外部 source,不取得其生命周期所有权。多 source 同步更新应在首次 commit 前进入 batch。projection 随 service 销毁,React unmount 只取消订阅,disposed handle 明确抛错。
|
|
262
270
|
|
|
263
271
|
## React 集成
|
|
264
272
|
|
|
@@ -117,15 +117,18 @@ and history stay settled.
|
|
|
117
117
|
|
|
118
118
|
## Derived state is declared, not synchronized by callers
|
|
119
119
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
120
|
+
ProjectionCollection and ProjectionValue share one explicit source graph and
|
|
121
|
+
publication mechanism. Only scoped processors write derived output. Application
|
|
122
|
+
code selects candidate keys from native impacts; Doxum owns final change,
|
|
123
|
+
equality, revision and notification. Never expose mutable private indexes or
|
|
124
|
+
retain scoped readers/writers. Updates must be synchronous.
|
|
125
|
+
|
|
126
|
+
All affected nodes settle before listeners; failures discard staged output and
|
|
127
|
+
attempt one fresh build before faulting. Faulted ancestors block descendants.
|
|
128
|
+
Listener exceptions are isolated. Source writes are forbidden during graph
|
|
129
|
+
processing and notification. Explicit batch defers graph publication, not
|
|
130
|
+
canonical commits or document listeners, and never promises source rollback.
|
|
131
|
+
Dispose the owner; do not dispose a node while it still has consumers.
|
|
129
132
|
|
|
130
133
|
## Framework and product boundaries
|
|
131
134
|
|
|
@@ -78,9 +78,9 @@ update 与 notification 窗口中禁止写入。processor、flush 和 listener e
|
|
|
78
78
|
|
|
79
79
|
## 派生状态应声明,而不是由调用方同步
|
|
80
80
|
|
|
81
|
-
|
|
81
|
+
ProjectionCollection 和 ProjectionValue 共用显式 source 图与发布机制。只有 scoped processor 写入派生输出。应用根据原生 impact 选择候选 key,Doxum 拥有最终 change、equality、revision 和 notification。不能泄漏可变私有索引,也不能保留 scoped reader/writer。update 必须同步。
|
|
82
82
|
|
|
83
|
-
|
|
83
|
+
受影响节点全部 settle 后再通知。失败丢弃候选输出,限一次全新 build 恢复,持续失败阻断下游;listener 错误逐个隔离。处理和通知期间禁止 source write。显式 batch 延迟派生图发布,不延迟 canonical commit 或 document listener,不承诺 source rollback。owner 负责 dispose,仍有消费者的节点不能单独释放。
|
|
84
84
|
|
|
85
85
|
## Framework 与产品边界
|
|
86
86
|
|
|
@@ -188,68 +188,41 @@ as values. Use `target.address`, `target.id`, `target.belongs`, and
|
|
|
188
188
|
`target.bucket` rather than duplicating their interpretation in a framework
|
|
189
189
|
adapter or local helper.
|
|
190
190
|
|
|
191
|
-
## Build
|
|
192
|
-
|
|
193
|
-
Use a collection view when a table or map needs a reusable read model. Its
|
|
194
|
-
mapping callback can read the entry's typed fields; `isEqual` lets the view
|
|
195
|
-
retain a previous mapped value when a remapped row is semantically unchanged.
|
|
191
|
+
## Build explicit projections
|
|
196
192
|
|
|
197
193
|
```ts
|
|
198
|
-
const
|
|
199
|
-
|
|
200
|
-
const
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
isEqual: (
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
## Build a materialized aggregate or index
|
|
216
|
-
|
|
217
|
-
Use a materialized view when you own an aggregate that can update from a
|
|
218
|
-
commit. During `update`, inspect the impact through the provided `impact`
|
|
219
|
-
object. That records the actual dependencies, so future unrelated commits can
|
|
220
|
-
skip this update.
|
|
221
|
-
|
|
222
|
-
```ts
|
|
223
|
-
const notes = documentSchema.collection(path => path.notes);
|
|
224
|
-
|
|
225
|
-
const noteCount = createMaterializedView(runtime, {
|
|
226
|
-
build: ({ read }) => ({
|
|
227
|
-
value: read.notes.ids().length,
|
|
228
|
-
update: ({ impact, read }) => {
|
|
229
|
-
const change = impact.collection(notes);
|
|
230
|
-
if (
|
|
231
|
-
change.kind === 'incremental' &&
|
|
232
|
-
change.added.size === 0 &&
|
|
233
|
-
change.removed.size === 0 &&
|
|
234
|
-
change.updated.size === 0 &&
|
|
235
|
-
!change.orderChanged
|
|
236
|
-
) {
|
|
237
|
-
return { kind: 'unchanged' };
|
|
238
|
-
}
|
|
239
|
-
return {
|
|
240
|
-
kind: 'changed',
|
|
241
|
-
value: read.notes.ids().length,
|
|
242
|
-
change,
|
|
243
|
-
};
|
|
244
|
-
},
|
|
194
|
+
const projection = createProjectionRuntime({ onError: error => console.error(error) });
|
|
195
|
+
const document = projection.document(runtime);
|
|
196
|
+
const notes = document.collection(path => path.notes);
|
|
197
|
+
const noteSummaries = projection.map(
|
|
198
|
+
notes,
|
|
199
|
+
(id, note) => ({ id, preview: note.body.get().slice(0, 80) }),
|
|
200
|
+
{ isEqual: (a, b) => a.id === b.id && a.preview === b.preview }
|
|
201
|
+
);
|
|
202
|
+
const noteCount = projection.value({
|
|
203
|
+
sources: { notes },
|
|
204
|
+
build: ({ notes }) => ({
|
|
205
|
+
value: notes.read.ids().length,
|
|
206
|
+
update: ({ notes }) => ({
|
|
207
|
+
kind: 'changed',
|
|
208
|
+
value: notes.read.ids().length,
|
|
209
|
+
}),
|
|
245
210
|
}),
|
|
246
211
|
});
|
|
247
212
|
```
|
|
248
213
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
commit.
|
|
214
|
+
Use map for one-to-one collection transforms. For custom indexes, use
|
|
215
|
+
projection.collection with declared sources and scoped writer.set/remove/order/replace.
|
|
216
|
+
Its previous and next readers separate published values from staged writes.
|
|
217
|
+
Inspect each document source commit.impact, or an upstream collection change,
|
|
218
|
+
to determine candidate keys. Dependencies are explicit and fixed, not learned
|
|
219
|
+
from reads. Doxum determines final changes through equality.
|
|
220
|
+
|
|
221
|
+
A failed update discards its instance and attempts one fresh build. Persistent
|
|
222
|
+
faults block descendants; independent branches continue. Call rebuild through
|
|
223
|
+
the node API, never manually emit. Dispose the projection with its owner.
|
|
224
|
+
Wrap document mutation and editor cleanup in projection.batch before the first
|
|
225
|
+
commit; reads inside the batch remain at the last published projection.
|
|
253
226
|
|
|
254
227
|
## Bind read models in React
|
|
255
228
|
|
|
@@ -157,58 +157,34 @@ const unsubscribe = runtime.subscribe([notes, body], commit => {
|
|
|
157
157
|
|
|
158
158
|
只有需要将两个 impact target 当作值比较时,使用 `target.same(left, right)`。应使用 `target.address`、`target.id`、`target.belongs` 与 `target.bucket`,不要在 framework adapter 或局部 helper 中复制它们的解释逻辑。
|
|
159
159
|
|
|
160
|
-
##
|
|
161
|
-
|
|
162
|
-
当一个 table 或 map 需要可复用 read model 时,使用 collection view。mapping callback 可以读取 entry 的类型化字段;`isEqual` 允许 view 在重新映射出的 row 语义不变时保留旧值。
|
|
160
|
+
## 构建显式派生图
|
|
163
161
|
|
|
164
162
|
```ts
|
|
165
|
-
const
|
|
166
|
-
|
|
167
|
-
const
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
isEqual: (
|
|
163
|
+
const projection = createProjectionRuntime({ onError: error => console.error(error) });
|
|
164
|
+
const document = projection.document(runtime);
|
|
165
|
+
const notes = document.collection(path => path.notes);
|
|
166
|
+
const noteSummaries = projection.map(
|
|
167
|
+
notes,
|
|
168
|
+
(id, note) => ({ id, preview: note.body.get().slice(0, 80) }),
|
|
169
|
+
{ isEqual: (a, b) => a.id === b.id && a.preview === b.preview }
|
|
170
|
+
);
|
|
171
|
+
const noteCount = projection.value({
|
|
172
|
+
sources: { notes },
|
|
173
|
+
build: ({ notes }) => ({
|
|
174
|
+
value: notes.read.ids().length,
|
|
175
|
+
update: ({ notes }) => ({
|
|
176
|
+
kind: 'changed',
|
|
177
|
+
value: notes.read.ids().length,
|
|
178
|
+
}),
|
|
179
|
+
}),
|
|
172
180
|
});
|
|
173
|
-
|
|
174
|
-
const stop = noteSummaries.item('a').subscribe(() => rerenderRow('a'));
|
|
175
|
-
const all = noteSummaries.all.current();
|
|
176
181
|
```
|
|
177
182
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
## 用 MaterializedView 构建聚合或索引
|
|
181
|
-
|
|
182
|
-
当你拥有可以根据 commit 增量更新的 aggregate 时,使用 materialized view。`update` 期间通过传入的 `impact` 对象检查影响范围;这会记录真实依赖,使之后无关的 commit 可以跳过此次 update。
|
|
183
|
+
map 用于保留 key 和顺序的一对一映射。自定义索引使用 projection.collection,声明 sources,并通过 scoped writer.set/remove/order/replace 更新。previous 与 next 分别读取上次输出和本轮暂存结果。
|
|
183
184
|
|
|
184
|
-
|
|
185
|
-
const notes = documentSchema.collection(path => path.notes);
|
|
186
|
-
|
|
187
|
-
const noteCount = createMaterializedView(runtime, {
|
|
188
|
-
build: ({ read }) => ({
|
|
189
|
-
value: read.notes.ids().length,
|
|
190
|
-
update: ({ impact, read }) => {
|
|
191
|
-
const change = impact.collection(notes);
|
|
192
|
-
if (
|
|
193
|
-
change.kind === 'incremental' &&
|
|
194
|
-
change.added.size === 0 &&
|
|
195
|
-
change.removed.size === 0 &&
|
|
196
|
-
change.updated.size === 0 &&
|
|
197
|
-
!change.orderChanged
|
|
198
|
-
) {
|
|
199
|
-
return { kind: 'unchanged' };
|
|
200
|
-
}
|
|
201
|
-
return {
|
|
202
|
-
kind: 'changed',
|
|
203
|
-
value: read.notes.ids().length,
|
|
204
|
-
change,
|
|
205
|
-
};
|
|
206
|
-
},
|
|
207
|
-
}),
|
|
208
|
-
});
|
|
209
|
-
```
|
|
185
|
+
根据每个 document source 的 commits 中的 impact,或上游 collection change,决定候选 key。依赖显式固定,不通过 reader 自动学习。Doxum 根据 equality 决定最终 change。
|
|
210
186
|
|
|
211
|
-
|
|
187
|
+
update 失败后丢弃实例并限一次全新 build 恢复;持续失败阻断下游,独立分支继续。rebuild 经过同一调度图,不手动 emit。projection 随 owner dispose。使用 projection.batch 包住首次 document commit 之前到 editor cleanup 结束的完整同步动作;batch 内派生读取保持上次发布状态。
|
|
212
188
|
|
|
213
189
|
## 在 React 中绑定读模型
|
|
214
190
|
|