doxum 0.1.29 → 0.1.30

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.
@@ -0,0 +1,623 @@
1
+ # Doxum Public API
2
+
3
+ This is the canonical consumer-facing API reference for the published package. Use it instead of implementation source. The type snippets intentionally omit private brands and internal helper types while preserving the public call shape and lifecycle-relevant fields.
4
+
5
+ ## Package entry points
6
+
7
+ | Package | Owns |
8
+ | ------------------ | ------------------------------------------------------------------------------------------------------------------ |
9
+ | `doxum` | schemas, canonical document runtime, reads/subscriptions, history/impact, projections, external projection sources |
10
+ | `doxum/advanced` | retained/incremental processors and processor-facing `CollectionChange` |
11
+ | `doxum/react` | React adapters for document/projection/readable/history APIs |
12
+ | `doxum/local-sync` | IndexedDB durability and browser leader/follower coordination |
13
+
14
+ Application imports must come from these package entry points only.
15
+
16
+ ## `doxum`
17
+
18
+ ### Public symbol inventory
19
+
20
+ <!-- exports:doxum:start -->
21
+
22
+ | Public export | Role |
23
+ | -------------------------- | ------------------------------------------------------- |
24
+ | `field` | Atomic payload schema |
25
+ | `optional` | Optional schema member wrapper |
26
+ | `object` | Fixed object schema |
27
+ | `variant` | Tagged object-union schema |
28
+ | `table` | Ordered keyed entity schema |
29
+ | `map` | Dynamic keyed record schema |
30
+ | `list` | Ordered keyed atomic-item schema |
31
+ | `tree` | Empty-or-single-root tree schema |
32
+ | `DocumentAddress` | Schema address type |
33
+ | `DocumentAnchor` | Ordered move/create anchor |
34
+ | `DocumentListConfig` | List `keyOf` config |
35
+ | `DocumentTreeNode` | Tree node value type |
36
+ | `DocumentTreeValue` | Whole tree value type |
37
+ | `Schema` | Portable schema handle |
38
+ | `ObjectSchema` | Portable root object schema handle |
39
+ | `Infer` | Infer schema data type |
40
+ | `ReadonlyValue` | Deep readonly payload view |
41
+ | `Validator` | Synchronous validator contract |
42
+ | `SchemaPath` | Typed symbolic path root |
43
+ | `PathValueOf` | Infer symbolic path value |
44
+ | `parse` | Validate/copy unknown input into schema data |
45
+ | `ParseError` | Parse failure class |
46
+ | `ParseIssue` | Parse issue type |
47
+ | `snapshot` | Convert borrowed access to durable readonly value |
48
+ | `replace` | Replace object/variant member with plain inferred value |
49
+ | `Read` | Borrowed readonly schema access type |
50
+ | `Draft` | Borrowed writable schema access type |
51
+ | `Change` | Document change union |
52
+ | `ChangeSet` | Normalized reversible document change set |
53
+ | `MemberChange` | Added/updated/removed member transition |
54
+ | `ValueTransition` | Generic added/updated/removed transition |
55
+ | `CollectionImpact` | Collection impact summary |
56
+ | `DocumentImpact` | Commit impact query API |
57
+ | `createDocument` | Create canonical document runtime |
58
+ | `DocumentReentrancyError` | Reentrant document write/read-lifecycle failure |
59
+ | `DocumentDisposedError` | Access after document disposal |
60
+ | `TransactionRejected` | Expected application-level transaction rejection |
61
+ | `Unsubscribe` | Subscription cleanup function |
62
+ | `CommitSource` | Commit source label |
63
+ | `DocumentCommit` | Accepted commit record |
64
+ | `ReadonlyDocument` | Read/subscription-only document surface |
65
+ | `TransactionResult` | `update` result union |
66
+ | `OperationResult` | apply/history/replace result union |
67
+ | `DocumentRuntime` | Full document runtime surface |
68
+ | `HistoryState` | Undo/redo depth state |
69
+ | `LocalHistory` | Local undo/redo API |
70
+ | `DocumentDiagnostic` | Application rejection issue |
71
+ | `DocumentProblem` | Application or mutation issue union |
72
+ | `ObserverError` | Post-commit observer failure |
73
+ | `MutationIssue` | Engine mutation rejection issue |
74
+ | `MutationIssueCode` | Mutation issue code union |
75
+ | `Readable` | Standard revisioned observable value |
76
+ | `read` | One synchronous document selector read |
77
+ | `select` | Document selector `Readable` |
78
+ | `DocumentSelector` | Document selector callback type |
79
+ | `input` | Scalar projection input factory |
80
+ | `observe` | Projection source declaration |
81
+ | `derive` | Pure value/keyed derivation family |
82
+ | `createProjectionRuntime` | Materialize and own projections |
83
+ | `ProjectionError` | Projection source/processor/listener/blocked failure |
84
+ | `ProjectionDisposedError` | Projection access after disposal |
85
+ | `Projection` | Lazy scalar projection handle |
86
+ | `KeyedProjection` | Lazy keyed projection handle |
87
+ | `Input` | Writable scalar projection input |
88
+ | `CollectionInput` | Writable keyed projection input |
89
+ | `CollectionInputDraft` | Borrowed collection-input editor |
90
+ | `ExternalCollectionEvent` | External keyed source event |
91
+ | `ExternalCollectionRead` | External keyed source snapshot interface |
92
+ | `ExternalCollectionSource` | External keyed projection source |
93
+ | `ExternalValueEvent` | External scalar source event |
94
+ | `ExternalValueSource` | External scalar projection source |
95
+ | `ProjectionRuntime` | Root projection runtime API |
96
+ | `ProjectionScope` | Scoped projection lifecycle API |
97
+ | `ProjectionItems` | Runtime-owned keyed item readable family |
98
+
99
+ <!-- exports:doxum:end -->
100
+
101
+ ### Schema factories
102
+
103
+ ```ts
104
+ field<T>(validator?: Validator<T>): Schema<ReadonlyValue<T>>
105
+ optional(schema): Schema<... | undefined>
106
+ object(shape): ObjectSchema<...>
107
+ variant(tag, variants): Schema<tagged union>
108
+ map(valueSchema, options?: { key: Validator<K> }): Schema<Readonly<Record<K, V>>>
109
+ table(entitySchema, options?: { key: Validator<K> }): Schema<{ ids: readonly K[]; byId: Readonly<Record<K,V>> }>
110
+ list(fieldSchema, { keyOf }): Schema<readonly T[]>
111
+ tree(fieldSchema): Schema<DocumentTreeValue<T>>
112
+ parse(schema, input): Infer<typeof schema>
113
+ ```
114
+
115
+ `optional` supports field, variant, map, list, and tree schemas. `table` accepts object/variant entities. `map` accepts field/object/variant values. `list` accepts an atomic field schema and obtains stable identity from `keyOf`. `tree(optional(field(...)))` means node payload may be absent; `optional(tree(...))` means the entire tree member may be absent.
116
+
117
+ `Validator<T>` is synchronous and either a predicate/assertion function or Standard Schema v1 validator. Successful Standard Schema validation must return the original input by identity; transformed output is not accepted.
118
+
119
+ ### Read/Draft container surface
120
+
121
+ Object/variant members are properties. Atomic `field` values are readonly and replaced whole. Collection nodes expose methods:
122
+
123
+ | Container | Read methods | Draft-only methods |
124
+ | --------- | -------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
125
+ | map | `get(id)`, `has(id)`, `ids()` | `put(id,value)`, `remove(id)`, `replace(record)` |
126
+ | table | `get(id)`, `has(id)`, `ids()` | `create(id,value,anchor?)`, `create(entries,anchor?)`, `remove(id | ids)`, `move(id | ids,anchor?)`, `reorder(ids)`, `replace(id,value)`, `replace(table)` |
127
+ | list | `get(key)`, `has(key)`, `ids()` | `insert(value,anchor?)`, `remove(key)`, `move(key | keys,anchor?)`, `reorder(keys)`, `replace(key,value)`, `replace(list)` |
128
+ | tree | `rootId()`, `get(id)`, `has(id)`, `parent(id)`, `children(id)` | `insert(id,value,position?)`, `move(id,position?)`, `remove(id)`, `replace(id,value)`, `replace(tree)` |
129
+
130
+ Ordered anchors are:
131
+
132
+ ```ts
133
+ type DocumentAnchor<K extends string = string> =
134
+ { at: 'start' | 'end' } | { before: K } | { after: K };
135
+ ```
136
+
137
+ Tree positions are `{ parentId?: string; index?: number }`.
138
+
139
+ For an object/variant member whose Draft representation contains collection methods, replace it with its plain inferred value through:
140
+
141
+ ```ts
142
+ replace(parentDraft, key, plainInferValue): void
143
+ ```
144
+
145
+ ### Durable values and borrowed values
146
+
147
+ ```ts
148
+ snapshot<T>(borrowedOrValue: T): durableReadonlyValue
149
+ ```
150
+
151
+ `Read` and `Draft` values are borrowed for the synchronous callback that received them. Do not retain their proxies or collection methods. `snapshot` copies schema-owned structure and shares immutable atomic payload references.
152
+
153
+ ### Canonical document runtime
154
+
155
+ ```ts
156
+ const document = createDocument({
157
+ schema,
158
+ initial,
159
+ history?: { capacity?: number } | false,
160
+ });
161
+ ```
162
+
163
+ Public shape:
164
+
165
+ ```ts
166
+ type ReadonlyDocument<S> = {
167
+ revision(): number;
168
+ subscribe(listener: (commit: DocumentCommit<S>) => void): Unsubscribe;
169
+ subscribe(
170
+ pick: PathPick<S> | readonly [PathPick<S>, ...PathPick<S>[]],
171
+ listener: (commit: DocumentCommit<S>) => void
172
+ ): Unsubscribe;
173
+ };
174
+
175
+ type DocumentRuntime<S> = ReadonlyDocument<S> & {
176
+ readonly schema: S;
177
+ readonly(): ReadonlyDocument<S>;
178
+ update<V>(
179
+ run: (draft: Draft<S>) => V,
180
+ options?: { source?: 'local' | 'system'; history?: boolean }
181
+ ): TransactionResult<V, DocumentCommit<S>>;
182
+ apply(
183
+ changes: unknown,
184
+ options:
185
+ | { expectedRevision: number; source?: 'local' | 'system'; history?: boolean }
186
+ | { expectedRevision: number; source: 'remote'; history?: never }
187
+ ): OperationResult<DocumentCommit<S>>;
188
+ replace(
189
+ value: Infer<S>,
190
+ options?: { source?: 'system' | 'remote' }
191
+ ): OperationResult<DocumentCommit<S>>;
192
+ snapshot(): Infer<S>;
193
+ readonly history: LocalHistory<DocumentCommit<S>>;
194
+ dispose(): void;
195
+ };
196
+ ```
197
+
198
+ `update` is synchronous and atomic. It accepts only non-Promise callback results. `apply` always requires `expectedRevision`. `replace` is a whole-document reset operation.
199
+
200
+ ### Transaction and operation results
201
+
202
+ ```ts
203
+ type TransactionResult<V, C> =
204
+ | { status: 'committed'; value: V; commit: C; observerErrors: readonly ObserverError[] }
205
+ | { status: 'unchanged'; value: V; revision: number }
206
+ | { status: 'rejected'; issues: readonly DocumentProblem[]; revision: number };
207
+
208
+ type OperationResult<C> =
209
+ | { status: 'committed'; commit: C; observerErrors: readonly ObserverError[] }
210
+ | { status: 'unchanged'; revision: number }
211
+ | { status: 'rejected'; issues: readonly MutationIssue[]; revision: number };
212
+ ```
213
+
214
+ Throw `new TransactionRejected(issueOrIssues)` inside `update` for expected application rejection. The runtime converts it to `status: 'rejected'`. Any other thrown value rolls back and is rethrown unchanged.
215
+
216
+ ### Commits, ChangeSets, impact, and history
217
+
218
+ ```ts
219
+ type DocumentCommit<S> = {
220
+ revision: number;
221
+ source: 'local' | 'system' | 'history' | 'remote';
222
+ changes: ChangeSet;
223
+ impact: DocumentImpact<S>;
224
+ };
225
+
226
+ type ChangeSet = { changes: readonly Change[] };
227
+
228
+ type Change =
229
+ | {
230
+ kind: 'members';
231
+ at: readonly string[];
232
+ members: readonly MemberChange[];
233
+ order?: { before: readonly string[]; after: readonly string[] };
234
+ }
235
+ | {
236
+ kind: 'tree';
237
+ at: readonly string[];
238
+ before: string | null;
239
+ after: string | null;
240
+ nodes: readonly ({ id: string } & ValueTransition<DocumentTreeNode<unknown>>)[];
241
+ }
242
+ | { kind: 'reset'; before: unknown; after: unknown };
243
+ ```
244
+
245
+ Member transitions are exactly one of:
246
+
247
+ ```ts
248
+ {
249
+ kind: 'added';
250
+ key: string;
251
+ after: T;
252
+ }
253
+ {
254
+ kind: 'updated';
255
+ key: string;
256
+ before: T;
257
+ after: T;
258
+ }
259
+ {
260
+ kind: 'removed';
261
+ key: string;
262
+ before: T;
263
+ }
264
+ ```
265
+
266
+ `DocumentImpact` provides:
267
+
268
+ ```ts
269
+ impact.affects(path => path.some.member): boolean
270
+ impact.collection(path => path.some.collection):
271
+ | { kind: 'reset' }
272
+ | { kind: 'incremental'; added: ReadonlySet<K>; removed: ReadonlySet<K>; updated: ReadonlySet<K>; orderChanged: boolean }
273
+ ```
274
+
275
+ History:
276
+
277
+ ```ts
278
+ type LocalHistory<C> = Readable<{ undoDepth: number; redoDepth: number }> & {
279
+ undo(): OperationResult<C>;
280
+ redo(): OperationResult<C>;
281
+ clear(): void;
282
+ group(): { end(): void; cancel(): OperationResult<C> };
283
+ };
284
+ ```
285
+
286
+ ### Document reads and selectors
287
+
288
+ ```ts
289
+ read(document, selector): TResult
290
+ select(document, selector, equality?): Readable<TResult>
291
+ ```
292
+
293
+ A selector receives borrowed `Read<S>`. `read` executes once. `select` returns a standard `Readable` and dynamically tracks the locations actually read by the selector.
294
+
295
+ ```ts
296
+ type Readable<T> = {
297
+ current(): T;
298
+ revision(): number;
299
+ subscribe(listener: () => void): Unsubscribe;
300
+ };
301
+ ```
302
+
303
+ ### Projection declarations
304
+
305
+ ```ts
306
+ input<T>(initial: T, equality?): Input<T>
307
+ input.collection<K,V>(initial?: ReadonlyMap<K,V>, equality?): CollectionInput<K,V>
308
+
309
+ observe(document): Projection<Infer<S>>
310
+ observe(document, path => collectionPath): KeyedProjection<K,V>
311
+ observe(document, path => valuePath): Projection<T>
312
+ observe(readable): Projection<T>
313
+ observe(externalValueSource): Projection<T>
314
+ observe(externalCollectionSource): KeyedProjection<K,V>
315
+
316
+ derive({ dependencyName: projection, ... }, values => result, equality?): Projection<T>
317
+ ```
318
+
319
+ The keyed family:
320
+
321
+ ```ts
322
+ derive.keyed(source, (value, key) => result, equality?): KeyedProjection<K,T>
323
+ derive.keyed(source, dependencies, (value, key, dependencies) => result, equality?): KeyedProjection<K,T>
324
+
325
+ derive.keyed.keys(source): Projection<readonly K[]>
326
+ derive.keyed.values(source): Projection<readonly V[]>
327
+ derive.keyed.entries(source): Projection<readonly (readonly [K,V])[]>
328
+ derive.keyed.get(source, keyProjection, equality?): Projection<V | undefined>
329
+ derive.keyed.subset(source, orderedKeysOrProjection): KeyedProjection<K,V>
330
+ derive.keyed.filter(source, predicate): KeyedProjection<K,V>
331
+ derive.keyed.filter(source, dependencies, predicate): KeyedProjection<K,V>
332
+ derive.keyed.compact(source, selector, equality?): KeyedProjection<K,T>
333
+ derive.keyed.compact(source, dependencies, selector, equality?): KeyedProjection<K,T>
334
+ derive.keyed.groupBy(source, selector): KeyedProjection<GroupKey, readonly K[]>
335
+ derive.keyed.groupBy(source, dependencies, selector): KeyedProjection<GroupKey, readonly K[]>
336
+ derive.keyed.singleton(sourceProjection, keyOf, equality?): KeyedProjection<K,V>
337
+ ```
338
+
339
+ Dynamic keyed dependency entries are either ordinary global `Projection`s or:
340
+
341
+ ```ts
342
+ { source: keyedProjection, key: (driverValue, driverKey) => sourceKey | undefined }
343
+ { source: keyedProjection, keys: (driverValue, driverKey) => readonly sourceKey[] }
344
+ ```
345
+
346
+ The singular resolved value is `V | undefined`; plural resolves to `ReadonlyMap<K,V>` in the requested key order, including only currently present entries.
347
+
348
+ ### Projection runtime and scope
349
+
350
+ ```ts
351
+ const runtime = createProjectionRuntime({
352
+ onError?: (error: ProjectionError) => void,
353
+ });
354
+ ```
355
+
356
+ ```ts
357
+ type ProjectionRuntime = {
358
+ read<T>(projection: Projection<T>): T;
359
+ select<T>(projection: Projection<T>): Readable<T>;
360
+ select<T,R>(projection: Projection<T>, selector: (value: T) => R, equality?): Readable<R>;
361
+ items<K,V>(projection: KeyedProjection<K,V>): ProjectionItems<K,V>;
362
+ update<T>(input: Input<T>, value: T): void;
363
+ update<K,V>(input: CollectionInput<K,V>, run: (draft: CollectionInputDraft<K,V>) => void): void;
364
+ batch<T>(run: () => T, options?: { cause?: unknown }): T;
365
+ scope(): ProjectionScope;
366
+ dispose(): void;
367
+ };
368
+
369
+ type ProjectionScope = ProjectionRuntime-like surface & {
370
+ own<P extends Projection<unknown>>(projection: P): P;
371
+ own<T extends nested projection tree>(tree: T): T;
372
+ };
373
+
374
+ type ProjectionItems<K,V> = {
375
+ readonly keys: Readable<readonly K[]>;
376
+ get(key: K): Readable<V | undefined>;
377
+ };
378
+
379
+ type CollectionInputDraft<K,V> = {
380
+ get(key: K): V | undefined;
381
+ has(key: K): boolean;
382
+ set(key: K, value: V): void;
383
+ remove(key: K): void;
384
+ };
385
+ ```
386
+
387
+ ### External projection sources
388
+
389
+ ```ts
390
+ type ExternalValueSource<T> = {
391
+ kind: 'value';
392
+ current(): T;
393
+ revision(): number;
394
+ subscribe(listener: (event: ExternalValueEvent<T>) => void): Unsubscribe;
395
+ };
396
+
397
+ type ExternalValueEvent<T> = {
398
+ value: T;
399
+ revision: number;
400
+ reset?: boolean;
401
+ cause?: unknown;
402
+ };
403
+
404
+ type ExternalCollectionRead<K, V> = {
405
+ get(key: K): V | undefined;
406
+ has(key: K): boolean;
407
+ ids(): readonly K[];
408
+ };
409
+
410
+ type ExternalCollectionSource<K, V> = {
411
+ kind: 'collection';
412
+ current(): ExternalCollectionRead<K, V>;
413
+ revision(): number;
414
+ subscribe(listener: (event: ExternalCollectionEvent<K, V>) => void): Unsubscribe;
415
+ };
416
+
417
+ type ExternalCollectionEvent<K, V> = {
418
+ previous: ExternalCollectionRead<K, V>;
419
+ revision: number;
420
+ impact?: CollectionImpact<K>;
421
+ cause?: unknown;
422
+ };
423
+ ```
424
+
425
+ The external collection event carries the stable previous read and may provide an impact hint. Doxum resolves the exact processor-facing collection change before processors consume it.
426
+
427
+ ### Public error classes
428
+
429
+ | Error | Meaning |
430
+ | ------------------------- | ---------------------------------------------------------------------------------------- |
431
+ | `ParseError` | `parse` rejected unknown input; inspect `.issues`. |
432
+ | `TransactionRejected` | Expected application rejection thrown inside `update`; runtime returns rejected result. |
433
+ | `DocumentReentrancyError` | Operation violates document reentrancy rules. |
434
+ | `DocumentDisposedError` | Document accessed after disposal. |
435
+ | `ProjectionError` | Projection `phase` is `processor`, `listener`, `source`, or `blocked`; inspect `.cause`. |
436
+ | `ProjectionDisposedError` | Projection Runtime/scope access after disposal. |
437
+
438
+ `MutationIssueCode` values are:
439
+ `invalid-address`, `invalid-value`, `invalid-key`, `invalid-changes`, `baseline-mismatch`, `required-field`, `invalid-anchor`, `duplicate-entity`, `missing-entity`, `invalid-collection`, `invalid-list-keys`, `invalid-list-key`, `duplicate-list-item`, `missing-list-item`, `invalid-tree`, `duplicate-tree-node`, `missing-tree-parent`, `invalid-tree-index`, `missing-tree-node`, `tree-cycle`.
440
+
441
+ ## `doxum/advanced`
442
+
443
+ Use this package only for retained or directly incremental processing that ordinary public derive primitives cannot express.
444
+
445
+ ### Public symbol inventory
446
+
447
+ <!-- exports:doxum/advanced:start -->
448
+
449
+ | Public export | Role |
450
+ | --------------------------------- | --------------------------------------------------------- |
451
+ | `IncrementalValueContext` | Value processor context type |
452
+ | `IncrementalValueDefinition` | Value processor definition type |
453
+ | `IncrementalCollectionContext` | Collection processor context type |
454
+ | `IncrementalCollectionDefinition` | Collection processor definition type |
455
+ | `IncrementalKeyedContext` | Per-driver-key processor context type |
456
+ | `IncrementalKeyedDefinition` | Per-driver-key processor definition type |
457
+ | `IncrementalGroupOutput` | Group output declaration token type |
458
+ | `IncrementalGroupResult` | Group declaration-to-projection-tree result type |
459
+ | `IncrementalGroupContext` | Multi-output processor context type |
460
+ | `incremental` | Value/collection/keyed/group incremental processor family |
461
+ | `collectionChange` | Helpers for incremental collection changes |
462
+ | `CollectionChange` | Processor-facing keyed change transport type |
463
+
464
+ <!-- exports:doxum/advanced:end -->
465
+
466
+ ### `CollectionChange`
467
+
468
+ ```ts
469
+ type CollectionChange<K extends string, V> =
470
+ | { kind: 'reset' }
471
+ | {
472
+ kind: 'incremental';
473
+ added: readonly { key: K; after: V }[];
474
+ updated: readonly { key: K; before: V; after: V }[];
475
+ removed: readonly { key: K; before: V }[];
476
+ order?: { before: readonly K[]; after: readonly K[] };
477
+ };
478
+
479
+ collectionChange.keys(incrementalChange): Iterable<K>
480
+ ```
481
+
482
+ `collectionChange.keys` yields added, then updated, then removed transition keys. It accepts only an incremental change and does not interpret reset or order changes.
483
+
484
+ ### Incremental value
485
+
486
+ ```ts
487
+ incremental(
488
+ dependencies,
489
+ {
490
+ state?: () => State,
491
+ process: ({ values, changes, previous, reset, cause, state? }) => T,
492
+ }
493
+ ): Projection<T>
494
+ ```
495
+
496
+ ### Incremental collection
497
+
498
+ ```ts
499
+ incremental.collection(
500
+ dependencies,
501
+ {
502
+ state?: () => State,
503
+ process: ({ values, changes, previous, next, output, reset, cause, state? }) => void,
504
+ }
505
+ ): KeyedProjection<K,V>
506
+ ```
507
+
508
+ `output` provides `set(key,value)`, `remove(key)`, and `order(ids)`.
509
+
510
+ ### Incremental keyed
511
+
512
+ ```ts
513
+ incremental.keyed(
514
+ driver,
515
+ dependencies,
516
+ {
517
+ state?: (driverValue, driverKey) => State,
518
+ equality?: (previous, next) => boolean,
519
+ process: ({ key, value, dependencies, reset, cause, state? }) => result,
520
+ }
521
+ ): KeyedProjection<DriverKey,Result>
522
+ ```
523
+
524
+ The driver owns output membership/order. Each output key has an independent membership lifecycle and optional retained state.
525
+
526
+ ### Incremental group
527
+
528
+ ```ts
529
+ incremental.group(
530
+ dependencies,
531
+ {
532
+ output: define => ({
533
+ someCollection: define.collection<K,V>(equality?),
534
+ someValue: define.value<T>(equality?),
535
+ }),
536
+ state?: () => State,
537
+ process: ({ values, changes, previous, next, output, reset, cause, state? }) => void,
538
+ }
539
+ ): same-shaped projection tree
540
+ ```
541
+
542
+ Every declared output must be returned from the static output tree exactly once.
543
+
544
+ ## `doxum/react`
545
+
546
+ ### Public symbol inventory
547
+
548
+ <!-- exports:doxum/react:start -->
549
+
550
+ | Public export | Role |
551
+ | --------------------- | -------------------------------------------------- |
552
+ | `ProjectionProvider` | Provide a `ProjectionRuntime` or `ProjectionScope` |
553
+ | `useDocumentSelector` | React adapter for Core document `select` |
554
+ | `useReadable` | Subscribe to any Doxum `Readable` |
555
+ | `useHistory` | Read history state and expose undo/redo callbacks |
556
+ | `useProjection` | Read projection or projection selector |
557
+ | `useInput` | Read/update scalar or collection projection input |
558
+
559
+ <!-- exports:doxum/react:end -->
560
+
561
+ ```ts
562
+ <ProjectionProvider value={runtimeOrScope}>...</ProjectionProvider>
563
+
564
+ useProjection(projection): T
565
+ useProjection(projection, selector, equality?): R
566
+ useInput(input): readonly [T, (value: T) => void]
567
+ useInput(collectionInput): readonly [ReadonlyMap<K,V>, (edit: (draft: CollectionInputDraft<K,V>) => void) => void]
568
+ useDocumentSelector(document, selector, equality?): R
569
+ useReadable(readable): T
570
+ useHistory(history): HistoryState & { undo(): OperationResult<C>; redo(): OperationResult<C> }
571
+ ```
572
+
573
+ ## `doxum/local-sync`
574
+
575
+ ### Public symbol inventory
576
+
577
+ <!-- exports:doxum/local-sync:start -->
578
+
579
+ | Public export | Role |
580
+ | ------------------------- | --------------------------------------------------- |
581
+ | `attachLocalSync` | Attach browser persistence/leadership to a document |
582
+ | `AttachLocalSyncOptions` | Attach options type |
583
+ | `LocalSync` | Attached sync handle |
584
+ | `LocalSyncState` | Leader/follower/error/disposed state union |
585
+ | `LocalSyncError` | Operational local-sync error class |
586
+ | `LocalSyncErrorCode` | Local-sync error code union |
587
+ | `defaultJsonChangeLimits` | Resolved default admission limits |
588
+ | `JsonChangeLimits` | Configurable local-commit limits |
589
+ | `JsonPrimitive` | JSON primitive type |
590
+ | `JsonValue` | Recursive JSON value type |
591
+
592
+ <!-- exports:doxum/local-sync:end -->
593
+
594
+ ```ts
595
+ const sync = await attachLocalSync({
596
+ runtime: document,
597
+ database: 'app',
598
+ documentId: 'document:1',
599
+ schemaVersion?: 1,
600
+ changeLimits?: {
601
+ maxChanges?: number,
602
+ maxBytes?: number,
603
+ maxDepth?: number,
604
+ maxStringLength?: number,
605
+ },
606
+ onError?: error => {},
607
+ });
608
+ ```
609
+
610
+ ```ts
611
+ type LocalSync = {
612
+ readonly state: Readable<LocalSyncState>;
613
+ flush(): Promise<void>;
614
+ dispose(): Promise<void>;
615
+ };
616
+
617
+ type LocalSyncState =
618
+ | { status: 'leader' | 'follower'; headSeq: number; checkpointSeq: number }
619
+ | { status: 'error'; headSeq: number; checkpointSeq: number; error: LocalSyncError }
620
+ | { status: 'disposed' };
621
+ ```
622
+
623
+ `LocalSyncErrorCode` is one of `unavailable`, `schema-mismatch`, `consistency`, `read-only`, `unsupported-operation`, `disposed`, `invalid-data`.