doxum 0.1.13 → 0.1.15

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.
Files changed (57) hide show
  1. package/README.md +10 -2
  2. package/dist/advanced.cjs +3 -2
  3. package/dist/advanced.cjs.map +1 -1
  4. package/dist/advanced.d.cts +1 -1
  5. package/dist/advanced.d.ts +1 -1
  6. package/dist/advanced.js +2 -1
  7. package/dist/advanced.js.map +1 -1
  8. package/dist/{contract-BT53Gg94.d.ts → contract-CATCCVu5.d.ts} +1 -1
  9. package/dist/{definition-BlMeHwdz.d.ts → definition-B6J0SSNa.d.ts} +38 -37
  10. package/dist/{definition-kxnWUvX9.d.cts → definition-DN6yW2vi.d.cts} +37 -36
  11. package/dist/definition-Z6TagMup.cjs +107 -0
  12. package/dist/definition-Z6TagMup.cjs.map +1 -0
  13. package/dist/definition-c6XQXzvK.js +72 -0
  14. package/dist/definition-c6XQXzvK.js.map +1 -0
  15. package/dist/index.cjs +2054 -55
  16. package/dist/index.cjs.map +1 -1
  17. package/dist/index.d.cts +16 -3
  18. package/dist/index.d.ts +17 -4
  19. package/dist/index.js +2001 -2
  20. package/dist/index.js.map +1 -1
  21. package/dist/integration-B7VDgAOJ.cjs +30 -0
  22. package/dist/integration-B7VDgAOJ.cjs.map +1 -0
  23. package/dist/{integration-Bco8ZB16.js → integration-CxVhq-KD.js} +3 -7
  24. package/dist/integration-CxVhq-KD.js.map +1 -0
  25. package/dist/integration.cjs +4 -9
  26. package/dist/integration.d.cts +2 -7
  27. package/dist/integration.d.ts +2 -7
  28. package/dist/integration.js +3 -3
  29. package/dist/local-sync.d.ts +1 -1
  30. package/dist/notification-CDSUfiip.cjs +398 -0
  31. package/dist/notification-CDSUfiip.cjs.map +1 -0
  32. package/dist/notification-IQIJyH3Q.js +285 -0
  33. package/dist/notification-IQIJyH3Q.js.map +1 -0
  34. package/dist/react.cjs +6 -52
  35. package/dist/react.cjs.map +1 -1
  36. package/dist/react.js +4 -50
  37. package/dist/react.js.map +1 -1
  38. package/dist/{definition-177L4Amk.cjs → scope-Ezs-Y3TY.cjs} +1 -118
  39. package/dist/scope-Ezs-Y3TY.cjs.map +1 -0
  40. package/dist/{definition-DXrKMVs7.js → scope-d-L87NeC.js} +2 -65
  41. package/dist/scope-d-L87NeC.js.map +1 -0
  42. package/package.json +1 -1
  43. package/skills/doxum-runtime/references/guide.en.md +2 -1
  44. package/skills/doxum-runtime/references/guide.zh-CN.md +2 -1
  45. package/skills/doxum-runtime/references/projections.en.md +15 -6
  46. package/skills/doxum-runtime/references/projections.zh-CN.md +13 -5
  47. package/dist/definition-177L4Amk.cjs.map +0 -1
  48. package/dist/definition-DXrKMVs7.js.map +0 -1
  49. package/dist/integration-Bco8ZB16.js.map +0 -1
  50. package/dist/integration-CEUn96tx.cjs +0 -52
  51. package/dist/integration-CEUn96tx.cjs.map +0 -1
  52. package/dist/store-CDeqZEE3.d.cts +0 -28
  53. package/dist/store-CtgvCVFg.js +0 -2222
  54. package/dist/store-CtgvCVFg.js.map +0 -1
  55. package/dist/store-DcDpDgjk.cjs +0 -2401
  56. package/dist/store-DcDpDgjk.cjs.map +0 -1
  57. package/dist/store-P5dJ7TVe.d.ts +0 -28
@@ -0,0 +1,285 @@
1
+ import { i as AddressIndex } from "./issue-DVaGQGeP.js";
2
+ import { n as createAccess } from "./scope-d-L87NeC.js";
3
+ //#region core/src/impact-target.ts
4
+ const address = (target) => "at" in target ? target.at : target.address;
5
+ const id = (target) => target.kind === "collection" && "id" in target ? target.id : void 0;
6
+ const belongs = (target, schema) => !("schema" in target) || target.schema === schema;
7
+ const same = (left, right) => {
8
+ if (left.kind !== right.kind) return false;
9
+ if ("schema" in left && "schema" in right && left.schema !== right.schema) return false;
10
+ const leftAddress = address(left);
11
+ const rightAddress = address(right);
12
+ if (leftAddress.length !== rightAddress.length) return false;
13
+ for (let index = 0; index < leftAddress.length; index += 1) if (leftAddress[index] !== rightAddress[index]) return false;
14
+ return left.kind !== "collection" || id(left) === id(right);
15
+ };
16
+ const indexedAddress = (target) => {
17
+ const key = id(target);
18
+ return key === void 0 ? address(target) : [...address(target), key];
19
+ };
20
+ const affected = (target, values, orders) => {
21
+ const at = indexedAddress(target);
22
+ return id(target) !== void 0 ? values.hasAncestor(at) : values.overlaps(at) || orders.hasDescendant(at);
23
+ };
24
+ /** Targets are classified once; commit matching never builds a reverse impact index. */
25
+ var SubscriptionIndex = class {
26
+ ordinary = new AddressIndex();
27
+ membership = new AddressIndex();
28
+ constructor(schema) {
29
+ this.schema = schema;
30
+ }
31
+ add(target, value) {
32
+ if (belongs(target, this.schema)) (id(target) === void 0 ? this.ordinary : this.membership).add(indexedAddress(target), value);
33
+ }
34
+ delete(target, value) {
35
+ if (belongs(target, this.schema)) (id(target) === void 0 ? this.ordinary : this.membership).delete(indexedAddress(target), value);
36
+ }
37
+ collect(changes, visit) {
38
+ const values = this.ordinary.query(visit);
39
+ const members = this.membership.query(visit, "descendants");
40
+ const orders = this.ordinary.query(visit, "ancestors");
41
+ for (const change of changes.changes) if (change.kind === "reset") {
42
+ values([]);
43
+ members([]);
44
+ } else if (change.kind === "members") {
45
+ values(change.at, change.members);
46
+ members(change.at, change.members);
47
+ if (change.order) orders(change.at);
48
+ } else {
49
+ values(change.at);
50
+ members(change.at);
51
+ }
52
+ }
53
+ clear() {
54
+ this.ordinary.clear();
55
+ this.membership.clear();
56
+ }
57
+ };
58
+ //#endregion
59
+ //#region core/src/runtime/contract.ts
60
+ var DocumentReentrancyError = class extends Error {
61
+ constructor() {
62
+ super("Document writes cannot be re-entered during an update or notification.");
63
+ this.name = "DocumentReentrancyError";
64
+ }
65
+ };
66
+ var DocumentDisposedError = class extends Error {
67
+ constructor() {
68
+ super("Doxum runtime has been disposed.");
69
+ this.name = "DocumentDisposedError";
70
+ }
71
+ };
72
+ var TransactionRejected = class extends Error {
73
+ issues;
74
+ constructor(input) {
75
+ super("Document update rejected.");
76
+ this.name = "TransactionRejected";
77
+ const issues = Array.isArray(input) ? input : [input];
78
+ this.issues = Object.freeze(issues.map((issue) => Object.freeze({
79
+ code: issue.code,
80
+ message: issue.message,
81
+ source: "application",
82
+ ...issue.address === void 0 ? {} : { address: Object.freeze([...issue.address]) }
83
+ })));
84
+ }
85
+ };
86
+ //#endregion
87
+ //#region core/src/runtime/access.ts
88
+ const states = /* @__PURE__ */ new WeakMap();
89
+ const bindRuntimeAccess = (runtime, state) => {
90
+ states.set(runtime, state);
91
+ };
92
+ const accessOf = (runtime) => {
93
+ const state = states.get(runtime);
94
+ if (!state) throw new Error("Unknown Doxum runtime.");
95
+ return state;
96
+ };
97
+ const readWith = (runtime, run, dependencies) => {
98
+ const state = accessOf(runtime);
99
+ if (state.disposed) throw new DocumentDisposedError();
100
+ state.projectionLocks = (state.projectionLocks ?? 0) + 1;
101
+ try {
102
+ return run(createAccess({
103
+ state,
104
+ dependencies
105
+ }));
106
+ } finally {
107
+ state.projectionLocks--;
108
+ }
109
+ };
110
+ //#endregion
111
+ //#region core/src/runtime/notification.ts
112
+ const notifications = /* @__PURE__ */ new WeakMap();
113
+ const readableOwners = /* @__PURE__ */ new WeakMap();
114
+ const bindDocumentReadable = (readable, runtime) => {
115
+ readableOwners.set(readable, runtime);
116
+ };
117
+ const documentReadableOwner = (readable) => readableOwners.get(readable);
118
+ const createNotification = (runtime) => {
119
+ const notification = {
120
+ root: /* @__PURE__ */ new Set(),
121
+ filtered: /* @__PURE__ */ new Set(),
122
+ index: new SubscriptionIndex(runtime.schema),
123
+ cleanups: /* @__PURE__ */ new Set(),
124
+ processors: [],
125
+ candidates: /* @__PURE__ */ new Set(),
126
+ rootSnapshot: [],
127
+ notifying: false
128
+ };
129
+ notifications.set(runtime, notification);
130
+ return notification;
131
+ };
132
+ const notificationOf = (runtime) => {
133
+ const value = notifications.get(runtime);
134
+ if (!value) throw new Error("Unknown Doxum runtime.");
135
+ return value;
136
+ };
137
+ const shareNotification = (source, target) => {
138
+ notifications.set(target, notificationOf(source));
139
+ };
140
+ const attachProjection = (runtime, processor) => {
141
+ const notification = notificationOf(runtime);
142
+ const entry = {
143
+ processor,
144
+ active: true
145
+ };
146
+ notification.processors.push(entry);
147
+ return () => {
148
+ if (!entry.active) return;
149
+ entry.active = false;
150
+ if (!notification.notifying) {
151
+ const index = notification.processors.indexOf(entry);
152
+ if (index >= 0) notification.processors.splice(index, 1);
153
+ }
154
+ };
155
+ };
156
+ const subscribeRoot = (notification, listener) => {
157
+ const entry = {
158
+ listener,
159
+ active: true
160
+ };
161
+ notification.root.add(entry);
162
+ return () => {
163
+ entry.active = false;
164
+ if (!notification.notifying) notification.root.delete(entry);
165
+ else notification.cleanups.add(() => {
166
+ notification.root.delete(entry);
167
+ });
168
+ };
169
+ };
170
+ const subscribeTargets = (notification, targets, listener) => {
171
+ const entry = {
172
+ targets: Object.freeze(targets.slice()),
173
+ listener,
174
+ active: true
175
+ };
176
+ notification.filtered.add(entry);
177
+ entry.targets.forEach((value) => notification.index.add(value, entry));
178
+ const remove = () => {
179
+ notification.filtered.delete(entry);
180
+ entry.targets.forEach((value) => notification.index.delete(value, entry));
181
+ };
182
+ return () => {
183
+ if (!entry.active) return;
184
+ entry.active = false;
185
+ if (notification.notifying) notification.cleanups.add(remove);
186
+ else remove();
187
+ };
188
+ };
189
+ const notify = (notification, commit, afterSettle) => {
190
+ notification.notifying = true;
191
+ const errors = [];
192
+ const call = (listener) => {
193
+ try {
194
+ listener(commit);
195
+ } catch (error) {
196
+ errors.push(Object.freeze({
197
+ phase: "listener",
198
+ error
199
+ }));
200
+ }
201
+ };
202
+ try {
203
+ const processorCount = notification.processors.length;
204
+ for (let index = 0; index < processorCount; index += 1) {
205
+ const entry = notification.processors[index];
206
+ if (!entry?.active) continue;
207
+ try {
208
+ entry.processor.capture(commit);
209
+ } catch (error) {
210
+ errors.push(Object.freeze({
211
+ phase: "processor",
212
+ error
213
+ }));
214
+ }
215
+ }
216
+ for (let index = 0; index < processorCount; index += 1) {
217
+ const entry = notification.processors[index];
218
+ if (!entry?.active) continue;
219
+ try {
220
+ entry.processor.settle();
221
+ } catch (error) {
222
+ errors.push(Object.freeze({
223
+ phase: "processor",
224
+ error
225
+ }));
226
+ }
227
+ }
228
+ for (let index = 0; index < processorCount; index += 1) {
229
+ const entry = notification.processors[index];
230
+ if (!entry?.active) continue;
231
+ try {
232
+ errors.push(...entry.processor.flush());
233
+ } catch (error) {
234
+ errors.push(Object.freeze({
235
+ phase: "flush",
236
+ error
237
+ }));
238
+ }
239
+ }
240
+ if (afterSettle) errors.push(...afterSettle());
241
+ const candidates = notification.candidates;
242
+ candidates.clear();
243
+ if (notification.filtered.size) notification.index.collect(commit.changes, (entry) => candidates.add(entry));
244
+ candidates.forEach((entry) => {
245
+ if (entry.active) call(entry.listener);
246
+ });
247
+ const rootSnapshot = notification.rootSnapshot;
248
+ rootSnapshot.length = 0;
249
+ notification.root.forEach((entry) => rootSnapshot.push(entry));
250
+ for (const entry of rootSnapshot) if (entry.active) call(entry.listener);
251
+ } finally {
252
+ notification.notifying = false;
253
+ let writeIndex = 0;
254
+ for (const entry of notification.processors) if (entry.active) notification.processors[writeIndex++] = entry;
255
+ notification.processors.length = writeIndex;
256
+ notification.cleanups.forEach((cleanup) => cleanup());
257
+ notification.cleanups.clear();
258
+ }
259
+ return Object.freeze(errors);
260
+ };
261
+ const subscribeDependencies = (runtime, targets, listener) => subscribeTargets(notificationOf(runtime), targets, listener);
262
+ const disposeNotification = (notification) => {
263
+ const attachments = notification.processors.slice();
264
+ notification.root.clear();
265
+ notification.filtered.clear();
266
+ notification.index.clear();
267
+ notification.cleanups.clear();
268
+ notification.processors.length = 0;
269
+ notification.candidates.clear();
270
+ notification.rootSnapshot.length = 0;
271
+ const errors = [];
272
+ attachments.forEach((entry) => {
273
+ if (!entry.active) return;
274
+ try {
275
+ entry.processor.dispose();
276
+ } catch (error) {
277
+ errors.push(error);
278
+ }
279
+ });
280
+ if (errors.length) throw new AggregateError(errors, "Projection disposal notification failed.");
281
+ };
282
+ //#endregion
283
+ export { affected as _, documentReadableOwner as a, subscribeDependencies as c, accessOf as d, bindRuntimeAccess as f, TransactionRejected as g, DocumentReentrancyError as h, disposeNotification as i, subscribeRoot as l, DocumentDisposedError as m, bindDocumentReadable as n, notify as o, readWith as p, createNotification as r, shareNotification as s, attachProjection as t, subscribeTargets as u, belongs as v, same as y };
284
+
285
+ //# sourceMappingURL=notification-IQIJyH3Q.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notification-IQIJyH3Q.js","names":["target.SubscriptionIndex"],"sources":["../core/src/impact-target.ts","../core/src/runtime/contract.ts","../core/src/runtime/access.ts","../core/src/runtime/notification.ts"],"sourcesContent":["import type { DocumentAddress, ObjectNode, ImpactTarget } from './schema';\nimport type { ChangeSet } from './changes';\nimport { AddressIndex } from './address';\n\nexport const address = (target: ImpactTarget<unknown>): DocumentAddress =>\n 'at' in target ? target.at : target.address;\n\nexport const id = (target: ImpactTarget<unknown>): string | undefined =>\n target.kind === 'collection' && 'id' in target ? target.id : undefined;\n\nexport const belongs = (target: ImpactTarget<unknown>, schema: ObjectNode): boolean =>\n !('schema' in target) || target.schema === schema;\n\nexport const same = (left: ImpactTarget<unknown>, right: ImpactTarget<unknown>): boolean => {\n if (left.kind !== right.kind) return false;\n if ('schema' in left && 'schema' in right && left.schema !== right.schema) return false;\n const leftAddress = address(left);\n const rightAddress = address(right);\n if (leftAddress.length !== rightAddress.length) return false;\n for (let index = 0; index < leftAddress.length; index += 1)\n if (leftAddress[index] !== rightAddress[index]) return false;\n return left.kind !== 'collection' || id(left) === id(right);\n};\n\nexport const indexedAddress = (target: ImpactTarget<unknown>): DocumentAddress => {\n const key = id(target);\n return key === undefined ? address(target) : [...address(target), key];\n};\n\nexport const affected = (\n target: ImpactTarget,\n values: AddressIndex<true>,\n orders: AddressIndex<true>\n): boolean => {\n const at = indexedAddress(target);\n return id(target) !== undefined\n ? values.hasAncestor(at)\n : values.overlaps(at) || orders.hasDescendant(at);\n};\n\n/** Targets are classified once; commit matching never builds a reverse impact index. */\nexport class SubscriptionIndex<T> {\n private readonly ordinary = new AddressIndex<T>();\n private readonly membership = new AddressIndex<T>();\n constructor(private readonly schema: ObjectNode) {}\n add(target: ImpactTarget, value: T): void {\n if (belongs(target, this.schema))\n (id(target) === undefined ? this.ordinary : this.membership).add(\n indexedAddress(target),\n value\n );\n }\n delete(target: ImpactTarget, value: T): void {\n if (belongs(target, this.schema))\n (id(target) === undefined ? this.ordinary : this.membership).delete(\n indexedAddress(target),\n value\n );\n }\n collect(changes: ChangeSet, visit: (value: T) => void): void {\n const values = this.ordinary.query(visit);\n const members = this.membership.query(visit, 'descendants');\n const orders = this.ordinary.query(visit, 'ancestors');\n for (const change of changes.changes) {\n if (change.kind === 'reset') {\n values([]);\n members([]);\n } else if (change.kind === 'members') {\n values(change.at, change.members);\n members(change.at, change.members);\n if (change.order) orders(change.at);\n } else {\n values(change.at);\n members(change.at);\n }\n }\n }\n clear(): void {\n this.ordinary.clear();\n this.membership.clear();\n }\n}\n","import type { DocumentAddress, ObjectNode, PathPick, Infer } from '../schema';\nimport type { AddressRef } from '../address';\nimport type { ChangeSet } from '../changes';\nimport type { DocumentImpact } from '../impact';\nimport type { Draft } from '../access/scope';\nimport type { MutationIssue } from '../mutation/issue';\nimport type { Readable } from '../projection/readable';\nexport type Unsubscribe = () => void;\nexport type Synchronous<T> = T extends PromiseLike<unknown> ? never : T;\nexport type CommitSource = 'local' | 'system' | 'history' | 'remote';\nexport class DocumentReentrancyError extends Error {\n constructor() {\n super('Document writes cannot be re-entered during an update or notification.');\n this.name = 'DocumentReentrancyError';\n }\n}\nexport class DocumentDisposedError extends Error {\n constructor() {\n super('Doxum runtime has been disposed.');\n this.name = 'DocumentDisposedError';\n }\n}\nexport type DiagnosticInput = {\n readonly code: string;\n readonly message: string;\n readonly address?: DocumentAddress;\n};\nexport type DocumentDiagnostic = DiagnosticInput & { readonly source: 'application' };\nexport type DocumentProblem = DocumentDiagnostic | MutationIssue;\nexport class TransactionRejected extends Error {\n readonly issues: readonly DocumentDiagnostic[];\n constructor(input: DiagnosticInput | readonly DiagnosticInput[]) {\n super('Document update rejected.');\n this.name = 'TransactionRejected';\n const issues: readonly DiagnosticInput[] = Array.isArray(input)\n ? input\n : [input as DiagnosticInput];\n this.issues = Object.freeze(\n issues.map(issue =>\n Object.freeze({\n code: issue.code,\n message: issue.message,\n source: 'application' as const,\n ...(issue.address === undefined ? {} : { address: Object.freeze([...issue.address]) }),\n })\n )\n );\n }\n}\nexport type DocumentCommit<S extends ObjectNode> = {\n readonly revision: number;\n readonly source: CommitSource;\n readonly changes: ChangeSet;\n readonly impact: DocumentImpact<S>;\n};\nexport type ObserverError = {\n readonly phase: 'processor' | 'flush' | 'listener';\n readonly error: unknown;\n};\nexport type TransactionResult<V, C> =\n | {\n readonly status: 'committed';\n readonly value: V;\n readonly commit: C;\n readonly observerErrors: readonly ObserverError[];\n }\n | { readonly status: 'unchanged'; readonly value: V; readonly revision: number }\n | {\n readonly status: 'rejected';\n readonly issues: readonly DocumentProblem[];\n readonly revision: number;\n };\nexport type OperationResult<C> =\n | {\n readonly status: 'committed';\n readonly commit: C;\n readonly observerErrors: readonly ObserverError[];\n }\n | { readonly status: 'unchanged'; readonly revision: number }\n | {\n readonly status: 'rejected';\n readonly issues: readonly MutationIssue[];\n readonly revision: number;\n };\nexport type HistoryState = { readonly undoDepth: number; readonly redoDepth: number };\nexport type LocalHistory<C> = Readable<HistoryState> & {\n undo(): OperationResult<C>;\n redo(): OperationResult<C>;\n clear(): void;\n group(): { end(): void; cancel(): OperationResult<C> };\n};\nexport type CommitListener<S extends ObjectNode> = (commit: DocumentCommit<S>) => void;\nexport type DocumentReadable<S extends ObjectNode> = {\n readonly address: {\n resolve(address: DocumentAddress): AddressRef | undefined;\n read(address: DocumentAddress): unknown;\n contains(parent: DocumentAddress, child: DocumentAddress): boolean;\n overlaps(left: DocumentAddress, right: DocumentAddress): boolean;\n debugKey(address: DocumentAddress): string;\n };\n revision(): number;\n subscribe(listener: CommitListener<S>): Unsubscribe;\n subscribe(\n pick: PathPick<S> | readonly [PathPick<S>, ...PathPick<S>[]],\n listener: CommitListener<S>\n ): Unsubscribe;\n};\nexport type DocumentRuntime<S extends ObjectNode> = DocumentReadable<S> & {\n readonly schema: S;\n update<V>(\n run: (draft: Draft<S>) => Synchronous<V>,\n options?: {\n readonly source?: Extract<CommitSource, 'local' | 'system'>;\n readonly history?: boolean;\n }\n ): TransactionResult<V, DocumentCommit<S>>;\n apply(\n changes: unknown,\n options: {\n readonly expectedRevision: number;\n readonly source?: Exclude<CommitSource, 'history'>;\n readonly history?: boolean;\n }\n ): OperationResult<DocumentCommit<S>>;\n replace(\n value: Infer<S>,\n options?: { readonly source?: Extract<CommitSource, 'system' | 'remote'> }\n ): OperationResult<DocumentCommit<S>>;\n snapshot(): Infer<S>;\n readonly history: LocalHistory<DocumentCommit<S>>;\n dispose(): void;\n};\n","import type { ObjectNode, Infer } from '../schema';\nimport { createAccess, type Read } from '../access/scope';\nimport type { DependencyTracker } from '../access/dependency';\nimport { DocumentDisposedError } from './contract';\nimport type { DocumentReadable } from './contract';\n\nexport type RuntimeAccessState<TSchema extends ObjectNode> = {\n readonly schema: TSchema;\n document: Infer<TSchema>;\n disposed: boolean;\n projectionLocks?: number;\n};\n\nconst states = new WeakMap<object, RuntimeAccessState<ObjectNode>>();\n\nexport const bindRuntimeAccess = <TSchema extends ObjectNode>(\n runtime: DocumentReadable<TSchema>,\n state: RuntimeAccessState<TSchema>\n): void => {\n states.set(runtime as object, state as RuntimeAccessState<ObjectNode>);\n};\n\nexport const accessOf = <TSchema extends ObjectNode>(\n runtime: DocumentReadable<TSchema>\n): RuntimeAccessState<TSchema> => {\n const state = states.get(runtime as object);\n if (!state) throw new Error('Unknown Doxum runtime.');\n return state as RuntimeAccessState<TSchema>;\n};\n\nexport const readWith = <TSchema extends ObjectNode, TResult>(\n runtime: DocumentReadable<TSchema>,\n run: (read: Read<TSchema>) => TResult,\n dependencies?: DependencyTracker\n): TResult => {\n const state = accessOf(runtime);\n if (state.disposed) throw new DocumentDisposedError();\n state.projectionLocks = (state.projectionLocks ?? 0) + 1;\n try {\n const reader = createAccess({\n state,\n dependencies,\n }) as Read<TSchema>;\n return run(reader);\n } finally {\n // readWith is an internal borrowed-reader boundary. Retaining its reader or\n // collection methods after this callback is undefined behavior.\n state.projectionLocks!--;\n }\n};\n","import type { ObjectNode, ImpactTarget } from '../schema';\nimport type {\n CommitListener,\n DocumentCommit,\n DocumentReadable,\n DocumentRuntime,\n Unsubscribe,\n ObserverError,\n} from './contract';\nimport * as target from '../impact-target';\n\nexport type ProjectionAttachment<TSchema extends ObjectNode> = {\n capture(commit: DocumentCommit<TSchema>): void;\n settle(): void;\n flush(): readonly ObserverError[];\n dispose(): void;\n};\ntype ProcessorEntry<TSchema extends ObjectNode> = {\n readonly processor: ProjectionAttachment<TSchema>;\n active: boolean;\n};\ntype RootEntry<TSchema extends ObjectNode> = {\n readonly listener: CommitListener<TSchema>;\n active: boolean;\n};\ntype FilteredEntry<TSchema extends ObjectNode> = {\n readonly targets: readonly ImpactTarget<unknown>[];\n readonly listener: CommitListener<TSchema>;\n active: boolean;\n};\nexport type RuntimeNotification<TSchema extends ObjectNode> = {\n readonly root: Set<RootEntry<TSchema>>;\n readonly filtered: Set<FilteredEntry<TSchema>>;\n readonly index: target.SubscriptionIndex<FilteredEntry<TSchema>>;\n readonly cleanups: Set<() => void>;\n readonly processors: ProcessorEntry<TSchema>[];\n readonly candidates: Set<FilteredEntry<TSchema>>;\n readonly rootSnapshot: RootEntry<TSchema>[];\n notifying: boolean;\n};\n\nconst notifications = new WeakMap<object, RuntimeNotification<ObjectNode>>();\nconst readableOwners = new WeakMap<object, DocumentReadable<ObjectNode>>();\n\nexport const bindDocumentReadable = (\n readable: object,\n runtime: DocumentReadable<ObjectNode>\n): void => {\n readableOwners.set(readable, runtime);\n};\nexport const documentReadableOwner = (readable: object): DocumentReadable<ObjectNode> | undefined =>\n readableOwners.get(readable);\n\nexport const createNotification = <TSchema extends ObjectNode>(\n runtime: DocumentRuntime<TSchema>\n): RuntimeNotification<TSchema> => {\n const notification: RuntimeNotification<TSchema> = {\n root: new Set(),\n filtered: new Set(),\n index: new target.SubscriptionIndex(runtime.schema),\n cleanups: new Set(),\n processors: [],\n candidates: new Set(),\n rootSnapshot: [],\n notifying: false,\n };\n notifications.set(runtime as object, notification as RuntimeNotification<ObjectNode>);\n return notification;\n};\n\nconst notificationOf = <TSchema extends ObjectNode>(\n runtime: DocumentReadable<TSchema>\n): RuntimeNotification<TSchema> => {\n const value = notifications.get(runtime as object);\n if (!value) throw new Error('Unknown Doxum runtime.');\n return value as RuntimeNotification<TSchema>;\n};\n\nexport const shareNotification = <TSchema extends ObjectNode>(\n source: DocumentRuntime<TSchema>,\n target: DocumentReadable<TSchema>\n): void => {\n notifications.set(target as object, notificationOf(source) as RuntimeNotification<ObjectNode>);\n};\n\nexport const attachProjection = <TSchema extends ObjectNode>(\n runtime: DocumentReadable<TSchema>,\n processor: ProjectionAttachment<TSchema>\n): Unsubscribe => {\n const notification = notificationOf(runtime);\n const entry: ProcessorEntry<TSchema> = { processor, active: true };\n notification.processors.push(entry);\n return () => {\n if (!entry.active) return;\n entry.active = false;\n if (!notification.notifying) {\n const index = notification.processors.indexOf(entry);\n if (index >= 0) notification.processors.splice(index, 1);\n }\n };\n};\n\nexport const subscribeRoot = <TSchema extends ObjectNode>(\n notification: RuntimeNotification<TSchema>,\n listener: CommitListener<TSchema>\n): Unsubscribe => {\n const entry: RootEntry<TSchema> = { listener, active: true };\n notification.root.add(entry);\n return () => {\n entry.active = false;\n if (!notification.notifying) notification.root.delete(entry);\n else\n notification.cleanups.add(() => {\n notification.root.delete(entry);\n });\n };\n};\n\nexport const subscribeTargets = <TSchema extends ObjectNode>(\n notification: RuntimeNotification<TSchema>,\n targets: readonly ImpactTarget<unknown>[],\n listener: CommitListener<TSchema>\n): Unsubscribe => {\n const entry: FilteredEntry<TSchema> = {\n targets: Object.freeze(targets.slice()),\n listener,\n active: true,\n };\n notification.filtered.add(entry);\n entry.targets.forEach(value => notification.index.add(value, entry));\n const remove = () => {\n notification.filtered.delete(entry);\n entry.targets.forEach(value => notification.index.delete(value, entry));\n };\n return () => {\n if (!entry.active) return;\n entry.active = false;\n if (notification.notifying) notification.cleanups.add(remove);\n else remove();\n };\n};\n\nexport const notify = <TSchema extends ObjectNode>(\n notification: RuntimeNotification<TSchema>,\n commit: DocumentCommit<TSchema>,\n afterSettle?: () => readonly ObserverError[]\n): readonly ObserverError[] => {\n notification.notifying = true;\n const errors: ObserverError[] = [];\n const call = (listener: CommitListener<TSchema>): void => {\n try {\n listener(commit);\n } catch (error) {\n errors.push(Object.freeze({ phase: 'listener', error }));\n }\n };\n try {\n const processorCount = notification.processors.length;\n for (let index = 0; index < processorCount; index += 1) {\n const entry = notification.processors[index];\n if (!entry?.active) continue;\n try {\n entry.processor.capture(commit);\n } catch (error) {\n errors.push(Object.freeze({ phase: 'processor', error }));\n }\n }\n for (let index = 0; index < processorCount; index += 1) {\n const entry = notification.processors[index];\n if (!entry?.active) continue;\n try {\n entry.processor.settle();\n } catch (error) {\n errors.push(Object.freeze({ phase: 'processor', error }));\n }\n }\n for (let index = 0; index < processorCount; index += 1) {\n const entry = notification.processors[index];\n if (!entry?.active) continue;\n try {\n errors.push(...entry.processor.flush());\n } catch (error) {\n errors.push(Object.freeze({ phase: 'flush', error }));\n }\n }\n\n if (afterSettle) errors.push(...afterSettle());\n const candidates = notification.candidates;\n candidates.clear();\n if (notification.filtered.size)\n notification.index.collect(commit.changes, entry => candidates.add(entry));\n candidates.forEach(entry => {\n if (entry.active) call(entry.listener);\n });\n const rootSnapshot = notification.rootSnapshot;\n rootSnapshot.length = 0;\n notification.root.forEach(entry => rootSnapshot.push(entry));\n for (const entry of rootSnapshot) if (entry.active) call(entry.listener);\n } finally {\n notification.notifying = false;\n let writeIndex = 0;\n for (const entry of notification.processors)\n if (entry.active) notification.processors[writeIndex++] = entry;\n notification.processors.length = writeIndex;\n notification.cleanups.forEach(cleanup => cleanup());\n notification.cleanups.clear();\n }\n return Object.freeze(errors);\n};\n\nexport const subscribeDependencies = <S extends ObjectNode>(\n runtime: DocumentReadable<S>,\n targets: readonly ImpactTarget[],\n listener: CommitListener<S>\n): Unsubscribe => subscribeTargets(notificationOf(runtime), targets, listener);\n\nexport const disposeNotification = <TSchema extends ObjectNode>(\n notification: RuntimeNotification<TSchema>\n): void => {\n const attachments = notification.processors.slice();\n notification.root.clear();\n notification.filtered.clear();\n notification.index.clear();\n notification.cleanups.clear();\n notification.processors.length = 0;\n notification.candidates.clear();\n notification.rootSnapshot.length = 0;\n const errors: unknown[] = [];\n attachments.forEach(entry => {\n if (!entry.active) return;\n try {\n entry.processor.dispose();\n } catch (error) {\n errors.push(error);\n }\n });\n if (errors.length) throw new AggregateError(errors, 'Projection disposal notification failed.');\n};\n"],"mappings":"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"}
package/dist/react.cjs CHANGED
@@ -1,68 +1,22 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_store = require("./store-DcDpDgjk.cjs");
3
- const require_integration = require("./integration-CEUn96tx.cjs");
2
+ const require_notification = require("./notification-CDSUfiip.cjs");
3
+ const require_integration = require("./integration-B7VDgAOJ.cjs");
4
4
  let react = require("react");
5
5
  //#region react/src/hooks.ts
6
6
  const ProjectionContext = (0, react.createContext)(void 0);
7
7
  const ProjectionProvider = ProjectionContext.Provider;
8
- const sameProjectionSelection = (left, right) => {
9
- if (left.all !== right.all || left.structure !== right.structure || left.keys.size !== right.keys.size) return false;
10
- for (const key of left.keys) if (!right.keys.has(key)) return false;
11
- return true;
12
- };
13
8
  function useProjection(projection, selectorOrRuntime, equalityOrRuntime, suppliedRuntime) {
14
9
  const context = (0, react.useContext)(ProjectionContext);
15
10
  const selector = typeof selectorOrRuntime === "function" ? selectorOrRuntime : void 0;
16
11
  const equality = typeof equalityOrRuntime === "function" ? equalityOrRuntime : Object.is;
17
12
  const owner = (selector ? suppliedRuntime : void 0) ?? (selector ? equalityOrRuntime && typeof equalityOrRuntime !== "function" ? equalityOrRuntime : void 0 : void 0) ?? (selector ? context : typeof selectorOrRuntime === "object" ? selectorOrRuntime : context);
18
13
  if (!owner) throw new Error("ProjectionRuntime is required.");
19
- if (!selector) {
20
- const read = (0, react.useCallback)(() => owner.get(projection), [owner, projection]);
21
- return (0, react.useSyncExternalStore)((0, react.useCallback)((listener) => owner.subscribe(projection, listener), [owner, projection]), read, read);
22
- }
23
- const cache = (0, react.useRef)(void 0);
24
- const read = (0, react.useCallback)(() => {
25
- const selected = require_store.trackProjection(owner, projection, selector);
26
- const previous = cache.current;
27
- const value = previous && equality(previous.value, selected.value) ? previous.value : selected.value;
28
- cache.current = {
29
- value,
30
- selection: selected.selection
31
- };
32
- return value;
33
- }, [
14
+ return useReadable((0, react.useMemo)(() => selector ? owner.readable(projection, selector, equality) : owner.readable(projection), [
34
15
  equality,
35
16
  owner,
36
17
  projection,
37
18
  selector
38
- ]);
39
- return (0, react.useSyncExternalStore)((0, react.useCallback)((listener) => {
40
- read();
41
- let selection = cache.current?.selection ?? {
42
- keys: /* @__PURE__ */ new Set(),
43
- all: true,
44
- structure: false
45
- };
46
- let unsubscribe = require_store.subscribeProjection(owner, projection, selection, onInvalidate);
47
- const reinstall = (next) => {
48
- if (sameProjectionSelection(selection, next)) return;
49
- unsubscribe();
50
- selection = next;
51
- unsubscribe = require_store.subscribeProjection(owner, projection, selection, onInvalidate);
52
- };
53
- function onInvalidate() {
54
- const previous = cache.current?.value;
55
- const next = read();
56
- const nextSelection = cache.current?.selection;
57
- if (nextSelection) reinstall(nextSelection);
58
- if (!Object.is(previous, next)) listener();
59
- }
60
- return () => unsubscribe();
61
- }, [
62
- owner,
63
- projection,
64
- read
65
- ]), read, read);
19
+ ]));
66
20
  }
67
21
  function useInput(input, runtime) {
68
22
  const context = (0, react.useContext)(ProjectionContext);
@@ -75,7 +29,7 @@ function useInput(input, runtime) {
75
29
  const objectIs = (previous, next) => Object.is(previous, next);
76
30
  const sameTargets = (left, right) => {
77
31
  if (left.length !== right.length) return false;
78
- for (let index = 0; index < left.length; index += 1) if (!require_store.same(left[index], right[index])) return false;
32
+ for (let index = 0; index < left.length; index += 1) if (!require_notification.same(left[index], right[index])) return false;
79
33
  return true;
80
34
  };
81
35
  function useDocumentSelector(runtime, selector, options) {
@@ -112,7 +66,7 @@ function useDocumentSelector(runtime, selector, options) {
112
66
  let unsubscribe = () => void 0;
113
67
  const install = () => {
114
68
  if (targets.length === 0) return () => void 0;
115
- return require_store.subscribeDependencies(runtime, targets, onCommit);
69
+ return require_notification.subscribeDependencies(runtime, targets, onCommit);
116
70
  };
117
71
  const onCommit = () => {
118
72
  const previous = cache.current;
@@ -1 +1 @@
1
- {"version":3,"file":"react.cjs","names":["trackProjection","subscribeProjection","sameTarget","track","subscribeDependencies"],"sources":["../react/src/hooks.ts"],"sourcesContent":["import type {\n DocumentReadable,\n ObjectNode,\n DocumentSelector,\n HistoryState,\n LocalHistory,\n OperationResult,\n Readable,\n ProjectionRuntime,\n Projection,\n Input,\n} from 'doxum';\nimport {\n track,\n subscribeDependencies,\n sameTarget,\n type ImpactTarget,\n trackProjection,\n subscribeProjection,\n type ProjectionSelection,\n} from 'doxum/integration';\nimport {\n createContext,\n useCallback,\n useContext,\n useMemo,\n useRef,\n useSyncExternalStore,\n} from 'react';\n\nexport const ProjectionContext = createContext<ProjectionRuntime | undefined>(undefined);\nexport const ProjectionProvider = ProjectionContext.Provider;\n\nconst sameProjectionSelection = (\n left: ProjectionSelection,\n right: ProjectionSelection\n): boolean => {\n if (\n left.all !== right.all ||\n left.structure !== right.structure ||\n left.keys.size !== right.keys.size\n )\n return false;\n for (const key of left.keys) if (!right.keys.has(key)) return false;\n return true;\n};\n\nexport function useProjection<T>(projection: Projection<T>, runtime?: ProjectionRuntime): T;\nexport function useProjection<T, R>(\n projection: Projection<T>,\n selector: (value: T) => R,\n runtime?: ProjectionRuntime\n): R;\nexport function useProjection<T, R>(\n projection: Projection<T>,\n selector: (value: T) => R,\n equality?: (previous: R, next: R) => boolean,\n runtime?: ProjectionRuntime\n): R;\nexport function useProjection<T, R>(\n projection: Projection<T>,\n selectorOrRuntime?: ((value: T) => R) | ProjectionRuntime,\n equalityOrRuntime?: ((previous: R, next: R) => boolean) | ProjectionRuntime,\n suppliedRuntime?: ProjectionRuntime\n): T | R {\n const context = useContext(ProjectionContext);\n const selector = typeof selectorOrRuntime === 'function' ? selectorOrRuntime : undefined;\n const equality = typeof equalityOrRuntime === 'function' ? equalityOrRuntime : Object.is;\n const owner =\n (selector ? suppliedRuntime : undefined) ??\n (selector\n ? equalityOrRuntime && typeof equalityOrRuntime !== 'function'\n ? equalityOrRuntime\n : undefined\n : undefined) ??\n (selector ? context : typeof selectorOrRuntime === 'object' ? selectorOrRuntime : context);\n if (!owner) throw new Error('ProjectionRuntime is required.');\n if (!selector) {\n const read = useCallback(() => owner.get(projection), [owner, projection]);\n const subscribe = useCallback(\n (listener: () => void) => owner.subscribe(projection, listener),\n [owner, projection]\n );\n return useSyncExternalStore(subscribe, read, read) as T;\n }\n const cache = useRef<{ value: R; selection: ProjectionSelection } | undefined>(undefined);\n const read = useCallback(() => {\n const selected = trackProjection(owner, projection, selector);\n const previous = cache.current;\n const value =\n previous && equality(previous.value, selected.value) ? previous.value : selected.value;\n cache.current = { value, selection: selected.selection };\n return value;\n }, [equality, owner, projection, selector]);\n const subscribe = useCallback(\n (listener: () => void) => {\n read();\n let selection = cache.current?.selection ?? {\n keys: new Set<string>(),\n all: true,\n structure: false,\n };\n let unsubscribe = subscribeProjection(owner, projection, selection, onInvalidate);\n const reinstall = (next: ProjectionSelection) => {\n if (sameProjectionSelection(selection, next)) return;\n unsubscribe();\n selection = next;\n unsubscribe = subscribeProjection(owner, projection, selection, onInvalidate);\n };\n function onInvalidate() {\n const previous = cache.current?.value;\n const next = read();\n const nextSelection = cache.current?.selection;\n if (nextSelection) reinstall(nextSelection);\n if (!Object.is(previous, next)) listener();\n }\n return () => unsubscribe();\n },\n [owner, projection, read]\n );\n return useSyncExternalStore(subscribe, read, read);\n}\n\nexport function useInput<T>(\n input: Input<T>,\n runtime?: ProjectionRuntime\n): readonly [T, (value: T) => void] {\n const context = useContext(ProjectionContext);\n const owner = runtime ?? context;\n if (!owner) throw new Error('ProjectionRuntime is required.');\n const value = useProjection(input, owner);\n const set = useCallback((next: T) => owner.set(input, next), [input, owner]);\n return useMemo(() => [value, set] as const, [set, value]);\n}\n\nexport type DocumentSelectorOptions<TResult> = {\n readonly isEqual?: (previous: TResult, next: TResult) => boolean;\n readonly server?: () => TResult;\n};\n\ntype SelectorCache<TSchema extends ObjectNode, TResult> = {\n readonly runtime: DocumentReadable<TSchema>;\n readonly value: TResult;\n readonly targets: readonly ImpactTarget<unknown>[];\n};\n\nconst objectIs = <T>(previous: T, next: T): boolean => Object.is(previous, next);\nconst sameTargets = (\n left: readonly ImpactTarget<unknown>[],\n right: readonly ImpactTarget<unknown>[]\n): boolean => {\n if (left.length !== right.length) return false;\n for (let index = 0; index < left.length; index += 1)\n if (!sameTarget(left[index], right[index])) return false;\n return true;\n};\n\nexport function useDocumentSelector<TSchema extends ObjectNode, TResult>(\n runtime: DocumentReadable<TSchema>,\n selector: DocumentSelector<TSchema, TResult>,\n options?: DocumentSelectorOptions<TResult>\n): TResult {\n const equality = options?.isEqual ?? objectIs;\n const cache = useRef<SelectorCache<TSchema, TResult> | undefined>(undefined);\n const serverCache = useRef<\n | {\n readonly runtime: DocumentReadable<TSchema>;\n readonly selector: DocumentSelector<TSchema, TResult>;\n readonly source: (() => TResult) | undefined;\n readonly value: TResult;\n }\n | undefined\n >(undefined);\n\n const read = useMemo(() => {\n // Each selector closure owns its revision cache, including inline selectors\n // that allocate arrays or objects. Equality only compares distinct snapshots.\n let snapshot: { readonly revision: number; readonly value: TResult } | undefined;\n return (): TResult => {\n const revision = runtime.revision();\n if (snapshot?.revision === revision) return snapshot.value;\n const selection = track(runtime, selector);\n const previous = cache.current;\n const value =\n previous && previous.runtime === runtime && equality(previous.value, selection.value)\n ? previous.value\n : selection.value;\n cache.current = { runtime, value, targets: selection.targets };\n snapshot = { revision, value };\n return value;\n };\n }, [equality, runtime, selector]);\n\n const subscribe = useCallback(\n (listener: () => void) => {\n read();\n let targets = cache.current?.targets ?? [];\n let unsubscribe: () => void = () => undefined;\n const install = () => {\n if (targets.length === 0) return () => undefined;\n return subscribeDependencies(runtime, targets, onCommit);\n };\n const onCommit = () => {\n const previous = cache.current;\n const next = read();\n const nextTargets = cache.current?.targets ?? [];\n if (!sameTargets(targets, nextTargets)) {\n unsubscribe();\n targets = nextTargets;\n unsubscribe = install();\n }\n if (previous && !Object.is(previous.value, next)) listener();\n };\n unsubscribe = install();\n return () => unsubscribe();\n },\n [read, runtime]\n );\n\n const server = options?.server;\n const readServer = useCallback((): TResult => {\n const previous = serverCache.current;\n if (\n previous &&\n previous.source === server &&\n previous.runtime === runtime &&\n previous.selector === selector\n )\n return previous.value;\n const value = server ? server() : read();\n serverCache.current = { runtime, selector, source: server, value };\n return value;\n }, [read, server, runtime, selector]);\n\n return useSyncExternalStore(subscribe, read, readServer);\n}\n\nexport function useReadable<T>(readable: Readable<T>): T {\n const subscribe = useCallback((listener: () => void) => readable.subscribe(listener), [readable]);\n const read = useCallback(() => readable.current(), [readable]);\n return useSyncExternalStore(subscribe, read, read);\n}\n\nexport function useHistory<TCommit>(history: LocalHistory<TCommit>): HistoryState & {\n undo(): OperationResult<TCommit>;\n redo(): OperationResult<TCommit>;\n} {\n const subscribe = useCallback((listener: () => void) => history.subscribe(listener), [history]);\n const read = useCallback(() => history.current(), [history]);\n const state = useSyncExternalStore(subscribe, read, read);\n const undo = useCallback(() => history.undo(), [history]);\n const redo = useCallback(() => history.redo(), [history]);\n\n return useMemo(() => ({ ...state, undo, redo }), [redo, state, undo]);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"react.cjs","names":["sameTarget","track","subscribeDependencies"],"sources":["../react/src/hooks.ts"],"sourcesContent":["import type {\n DocumentReadable,\n ObjectNode,\n DocumentSelector,\n HistoryState,\n LocalHistory,\n OperationResult,\n Readable,\n ProjectionRuntime,\n Projection,\n Input,\n} from 'doxum';\nimport { track, subscribeDependencies, sameTarget, type ImpactTarget } from 'doxum/integration';\nimport {\n createContext,\n useCallback,\n useContext,\n useMemo,\n useRef,\n useSyncExternalStore,\n} from 'react';\n\nexport const ProjectionContext = createContext<ProjectionRuntime | undefined>(undefined);\nexport const ProjectionProvider = ProjectionContext.Provider;\n\nexport function useProjection<T>(projection: Projection<T>, runtime?: ProjectionRuntime): T;\nexport function useProjection<T, R>(\n projection: Projection<T>,\n selector: (value: T) => R,\n runtime?: ProjectionRuntime\n): R;\nexport function useProjection<T, R>(\n projection: Projection<T>,\n selector: (value: T) => R,\n equality?: (previous: R, next: R) => boolean,\n runtime?: ProjectionRuntime\n): R;\nexport function useProjection<T, R>(\n projection: Projection<T>,\n selectorOrRuntime?: ((value: T) => R) | ProjectionRuntime,\n equalityOrRuntime?: ((previous: R, next: R) => boolean) | ProjectionRuntime,\n suppliedRuntime?: ProjectionRuntime\n): T | R {\n const context = useContext(ProjectionContext);\n const selector = typeof selectorOrRuntime === 'function' ? selectorOrRuntime : undefined;\n const equality = typeof equalityOrRuntime === 'function' ? equalityOrRuntime : Object.is;\n const owner =\n (selector ? suppliedRuntime : undefined) ??\n (selector\n ? equalityOrRuntime && typeof equalityOrRuntime !== 'function'\n ? equalityOrRuntime\n : undefined\n : undefined) ??\n (selector ? context : typeof selectorOrRuntime === 'object' ? selectorOrRuntime : context);\n if (!owner) throw new Error('ProjectionRuntime is required.');\n const readable = useMemo(\n () => (selector ? owner.readable(projection, selector, equality) : owner.readable(projection)),\n [equality, owner, projection, selector]\n );\n return useReadable(readable as Readable<T | R>);\n}\n\nexport function useInput<T>(\n input: Input<T>,\n runtime?: ProjectionRuntime\n): readonly [T, (value: T) => void] {\n const context = useContext(ProjectionContext);\n const owner = runtime ?? context;\n if (!owner) throw new Error('ProjectionRuntime is required.');\n const value = useProjection(input, owner);\n const set = useCallback((next: T) => owner.set(input, next), [input, owner]);\n return useMemo(() => [value, set] as const, [set, value]);\n}\n\nexport type DocumentSelectorOptions<TResult> = {\n readonly isEqual?: (previous: TResult, next: TResult) => boolean;\n readonly server?: () => TResult;\n};\n\ntype SelectorCache<TSchema extends ObjectNode, TResult> = {\n readonly runtime: DocumentReadable<TSchema>;\n readonly value: TResult;\n readonly targets: readonly ImpactTarget<unknown>[];\n};\n\nconst objectIs = <T>(previous: T, next: T): boolean => Object.is(previous, next);\nconst sameTargets = (\n left: readonly ImpactTarget<unknown>[],\n right: readonly ImpactTarget<unknown>[]\n): boolean => {\n if (left.length !== right.length) return false;\n for (let index = 0; index < left.length; index += 1)\n if (!sameTarget(left[index], right[index])) return false;\n return true;\n};\n\nexport function useDocumentSelector<TSchema extends ObjectNode, TResult>(\n runtime: DocumentReadable<TSchema>,\n selector: DocumentSelector<TSchema, TResult>,\n options?: DocumentSelectorOptions<TResult>\n): TResult {\n const equality = options?.isEqual ?? objectIs;\n const cache = useRef<SelectorCache<TSchema, TResult> | undefined>(undefined);\n const serverCache = useRef<\n | {\n readonly runtime: DocumentReadable<TSchema>;\n readonly selector: DocumentSelector<TSchema, TResult>;\n readonly source: (() => TResult) | undefined;\n readonly value: TResult;\n }\n | undefined\n >(undefined);\n\n const read = useMemo(() => {\n // Each selector closure owns its revision cache, including inline selectors\n // that allocate arrays or objects. Equality only compares distinct snapshots.\n let snapshot: { readonly revision: number; readonly value: TResult } | undefined;\n return (): TResult => {\n const revision = runtime.revision();\n if (snapshot?.revision === revision) return snapshot.value;\n const selection = track(runtime, selector);\n const previous = cache.current;\n const value =\n previous && previous.runtime === runtime && equality(previous.value, selection.value)\n ? previous.value\n : selection.value;\n cache.current = { runtime, value, targets: selection.targets };\n snapshot = { revision, value };\n return value;\n };\n }, [equality, runtime, selector]);\n\n const subscribe = useCallback(\n (listener: () => void) => {\n read();\n let targets = cache.current?.targets ?? [];\n let unsubscribe: () => void = () => undefined;\n const install = () => {\n if (targets.length === 0) return () => undefined;\n return subscribeDependencies(runtime, targets, onCommit);\n };\n const onCommit = () => {\n const previous = cache.current;\n const next = read();\n const nextTargets = cache.current?.targets ?? [];\n if (!sameTargets(targets, nextTargets)) {\n unsubscribe();\n targets = nextTargets;\n unsubscribe = install();\n }\n if (previous && !Object.is(previous.value, next)) listener();\n };\n unsubscribe = install();\n return () => unsubscribe();\n },\n [read, runtime]\n );\n\n const server = options?.server;\n const readServer = useCallback((): TResult => {\n const previous = serverCache.current;\n if (\n previous &&\n previous.source === server &&\n previous.runtime === runtime &&\n previous.selector === selector\n )\n return previous.value;\n const value = server ? server() : read();\n serverCache.current = { runtime, selector, source: server, value };\n return value;\n }, [read, server, runtime, selector]);\n\n return useSyncExternalStore(subscribe, read, readServer);\n}\n\nexport function useReadable<T>(readable: Readable<T>): T {\n const subscribe = useCallback((listener: () => void) => readable.subscribe(listener), [readable]);\n const read = useCallback(() => readable.current(), [readable]);\n return useSyncExternalStore(subscribe, read, read);\n}\n\nexport function useHistory<TCommit>(history: LocalHistory<TCommit>): HistoryState & {\n undo(): OperationResult<TCommit>;\n redo(): OperationResult<TCommit>;\n} {\n const subscribe = useCallback((listener: () => void) => history.subscribe(listener), [history]);\n const read = useCallback(() => history.current(), [history]);\n const state = useSyncExternalStore(subscribe, read, read);\n const undo = useCallback(() => history.undo(), [history]);\n const redo = useCallback(() => history.redo(), [history]);\n\n return useMemo(() => ({ ...state, undo, redo }), [redo, state, undo]);\n}\n"],"mappings":"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"}
package/dist/react.js CHANGED
@@ -1,67 +1,21 @@
1
- import { d as subscribeDependencies, n as subscribeProjection, r as trackProjection, x as same } from "./store-CtgvCVFg.js";
2
- import { i as track } from "./integration-Bco8ZB16.js";
1
+ import { c as subscribeDependencies, y as same } from "./notification-IQIJyH3Q.js";
2
+ import { t as track } from "./integration-CxVhq-KD.js";
3
3
  import { createContext, useCallback, useContext, useMemo, useRef, useSyncExternalStore } from "react";
4
4
  //#region react/src/hooks.ts
5
5
  const ProjectionContext = createContext(void 0);
6
6
  const ProjectionProvider = ProjectionContext.Provider;
7
- const sameProjectionSelection = (left, right) => {
8
- if (left.all !== right.all || left.structure !== right.structure || left.keys.size !== right.keys.size) return false;
9
- for (const key of left.keys) if (!right.keys.has(key)) return false;
10
- return true;
11
- };
12
7
  function useProjection(projection, selectorOrRuntime, equalityOrRuntime, suppliedRuntime) {
13
8
  const context = useContext(ProjectionContext);
14
9
  const selector = typeof selectorOrRuntime === "function" ? selectorOrRuntime : void 0;
15
10
  const equality = typeof equalityOrRuntime === "function" ? equalityOrRuntime : Object.is;
16
11
  const owner = (selector ? suppliedRuntime : void 0) ?? (selector ? equalityOrRuntime && typeof equalityOrRuntime !== "function" ? equalityOrRuntime : void 0 : void 0) ?? (selector ? context : typeof selectorOrRuntime === "object" ? selectorOrRuntime : context);
17
12
  if (!owner) throw new Error("ProjectionRuntime is required.");
18
- if (!selector) {
19
- const read = useCallback(() => owner.get(projection), [owner, projection]);
20
- return useSyncExternalStore(useCallback((listener) => owner.subscribe(projection, listener), [owner, projection]), read, read);
21
- }
22
- const cache = useRef(void 0);
23
- const read = useCallback(() => {
24
- const selected = trackProjection(owner, projection, selector);
25
- const previous = cache.current;
26
- const value = previous && equality(previous.value, selected.value) ? previous.value : selected.value;
27
- cache.current = {
28
- value,
29
- selection: selected.selection
30
- };
31
- return value;
32
- }, [
13
+ return useReadable(useMemo(() => selector ? owner.readable(projection, selector, equality) : owner.readable(projection), [
33
14
  equality,
34
15
  owner,
35
16
  projection,
36
17
  selector
37
- ]);
38
- return useSyncExternalStore(useCallback((listener) => {
39
- read();
40
- let selection = cache.current?.selection ?? {
41
- keys: /* @__PURE__ */ new Set(),
42
- all: true,
43
- structure: false
44
- };
45
- let unsubscribe = subscribeProjection(owner, projection, selection, onInvalidate);
46
- const reinstall = (next) => {
47
- if (sameProjectionSelection(selection, next)) return;
48
- unsubscribe();
49
- selection = next;
50
- unsubscribe = subscribeProjection(owner, projection, selection, onInvalidate);
51
- };
52
- function onInvalidate() {
53
- const previous = cache.current?.value;
54
- const next = read();
55
- const nextSelection = cache.current?.selection;
56
- if (nextSelection) reinstall(nextSelection);
57
- if (!Object.is(previous, next)) listener();
58
- }
59
- return () => unsubscribe();
60
- }, [
61
- owner,
62
- projection,
63
- read
64
- ]), read, read);
18
+ ]));
65
19
  }
66
20
  function useInput(input, runtime) {
67
21
  const context = useContext(ProjectionContext);
package/dist/react.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"react.js","names":["sameTarget"],"sources":["../react/src/hooks.ts"],"sourcesContent":["import type {\n DocumentReadable,\n ObjectNode,\n DocumentSelector,\n HistoryState,\n LocalHistory,\n OperationResult,\n Readable,\n ProjectionRuntime,\n Projection,\n Input,\n} from 'doxum';\nimport {\n track,\n subscribeDependencies,\n sameTarget,\n type ImpactTarget,\n trackProjection,\n subscribeProjection,\n type ProjectionSelection,\n} from 'doxum/integration';\nimport {\n createContext,\n useCallback,\n useContext,\n useMemo,\n useRef,\n useSyncExternalStore,\n} from 'react';\n\nexport const ProjectionContext = createContext<ProjectionRuntime | undefined>(undefined);\nexport const ProjectionProvider = ProjectionContext.Provider;\n\nconst sameProjectionSelection = (\n left: ProjectionSelection,\n right: ProjectionSelection\n): boolean => {\n if (\n left.all !== right.all ||\n left.structure !== right.structure ||\n left.keys.size !== right.keys.size\n )\n return false;\n for (const key of left.keys) if (!right.keys.has(key)) return false;\n return true;\n};\n\nexport function useProjection<T>(projection: Projection<T>, runtime?: ProjectionRuntime): T;\nexport function useProjection<T, R>(\n projection: Projection<T>,\n selector: (value: T) => R,\n runtime?: ProjectionRuntime\n): R;\nexport function useProjection<T, R>(\n projection: Projection<T>,\n selector: (value: T) => R,\n equality?: (previous: R, next: R) => boolean,\n runtime?: ProjectionRuntime\n): R;\nexport function useProjection<T, R>(\n projection: Projection<T>,\n selectorOrRuntime?: ((value: T) => R) | ProjectionRuntime,\n equalityOrRuntime?: ((previous: R, next: R) => boolean) | ProjectionRuntime,\n suppliedRuntime?: ProjectionRuntime\n): T | R {\n const context = useContext(ProjectionContext);\n const selector = typeof selectorOrRuntime === 'function' ? selectorOrRuntime : undefined;\n const equality = typeof equalityOrRuntime === 'function' ? equalityOrRuntime : Object.is;\n const owner =\n (selector ? suppliedRuntime : undefined) ??\n (selector\n ? equalityOrRuntime && typeof equalityOrRuntime !== 'function'\n ? equalityOrRuntime\n : undefined\n : undefined) ??\n (selector ? context : typeof selectorOrRuntime === 'object' ? selectorOrRuntime : context);\n if (!owner) throw new Error('ProjectionRuntime is required.');\n if (!selector) {\n const read = useCallback(() => owner.get(projection), [owner, projection]);\n const subscribe = useCallback(\n (listener: () => void) => owner.subscribe(projection, listener),\n [owner, projection]\n );\n return useSyncExternalStore(subscribe, read, read) as T;\n }\n const cache = useRef<{ value: R; selection: ProjectionSelection } | undefined>(undefined);\n const read = useCallback(() => {\n const selected = trackProjection(owner, projection, selector);\n const previous = cache.current;\n const value =\n previous && equality(previous.value, selected.value) ? previous.value : selected.value;\n cache.current = { value, selection: selected.selection };\n return value;\n }, [equality, owner, projection, selector]);\n const subscribe = useCallback(\n (listener: () => void) => {\n read();\n let selection = cache.current?.selection ?? {\n keys: new Set<string>(),\n all: true,\n structure: false,\n };\n let unsubscribe = subscribeProjection(owner, projection, selection, onInvalidate);\n const reinstall = (next: ProjectionSelection) => {\n if (sameProjectionSelection(selection, next)) return;\n unsubscribe();\n selection = next;\n unsubscribe = subscribeProjection(owner, projection, selection, onInvalidate);\n };\n function onInvalidate() {\n const previous = cache.current?.value;\n const next = read();\n const nextSelection = cache.current?.selection;\n if (nextSelection) reinstall(nextSelection);\n if (!Object.is(previous, next)) listener();\n }\n return () => unsubscribe();\n },\n [owner, projection, read]\n );\n return useSyncExternalStore(subscribe, read, read);\n}\n\nexport function useInput<T>(\n input: Input<T>,\n runtime?: ProjectionRuntime\n): readonly [T, (value: T) => void] {\n const context = useContext(ProjectionContext);\n const owner = runtime ?? context;\n if (!owner) throw new Error('ProjectionRuntime is required.');\n const value = useProjection(input, owner);\n const set = useCallback((next: T) => owner.set(input, next), [input, owner]);\n return useMemo(() => [value, set] as const, [set, value]);\n}\n\nexport type DocumentSelectorOptions<TResult> = {\n readonly isEqual?: (previous: TResult, next: TResult) => boolean;\n readonly server?: () => TResult;\n};\n\ntype SelectorCache<TSchema extends ObjectNode, TResult> = {\n readonly runtime: DocumentReadable<TSchema>;\n readonly value: TResult;\n readonly targets: readonly ImpactTarget<unknown>[];\n};\n\nconst objectIs = <T>(previous: T, next: T): boolean => Object.is(previous, next);\nconst sameTargets = (\n left: readonly ImpactTarget<unknown>[],\n right: readonly ImpactTarget<unknown>[]\n): boolean => {\n if (left.length !== right.length) return false;\n for (let index = 0; index < left.length; index += 1)\n if (!sameTarget(left[index], right[index])) return false;\n return true;\n};\n\nexport function useDocumentSelector<TSchema extends ObjectNode, TResult>(\n runtime: DocumentReadable<TSchema>,\n selector: DocumentSelector<TSchema, TResult>,\n options?: DocumentSelectorOptions<TResult>\n): TResult {\n const equality = options?.isEqual ?? objectIs;\n const cache = useRef<SelectorCache<TSchema, TResult> | undefined>(undefined);\n const serverCache = useRef<\n | {\n readonly runtime: DocumentReadable<TSchema>;\n readonly selector: DocumentSelector<TSchema, TResult>;\n readonly source: (() => TResult) | undefined;\n readonly value: TResult;\n }\n | undefined\n >(undefined);\n\n const read = useMemo(() => {\n // Each selector closure owns its revision cache, including inline selectors\n // that allocate arrays or objects. Equality only compares distinct snapshots.\n let snapshot: { readonly revision: number; readonly value: TResult } | undefined;\n return (): TResult => {\n const revision = runtime.revision();\n if (snapshot?.revision === revision) return snapshot.value;\n const selection = track(runtime, selector);\n const previous = cache.current;\n const value =\n previous && previous.runtime === runtime && equality(previous.value, selection.value)\n ? previous.value\n : selection.value;\n cache.current = { runtime, value, targets: selection.targets };\n snapshot = { revision, value };\n return value;\n };\n }, [equality, runtime, selector]);\n\n const subscribe = useCallback(\n (listener: () => void) => {\n read();\n let targets = cache.current?.targets ?? [];\n let unsubscribe: () => void = () => undefined;\n const install = () => {\n if (targets.length === 0) return () => undefined;\n return subscribeDependencies(runtime, targets, onCommit);\n };\n const onCommit = () => {\n const previous = cache.current;\n const next = read();\n const nextTargets = cache.current?.targets ?? [];\n if (!sameTargets(targets, nextTargets)) {\n unsubscribe();\n targets = nextTargets;\n unsubscribe = install();\n }\n if (previous && !Object.is(previous.value, next)) listener();\n };\n unsubscribe = install();\n return () => unsubscribe();\n },\n [read, runtime]\n );\n\n const server = options?.server;\n const readServer = useCallback((): TResult => {\n const previous = serverCache.current;\n if (\n previous &&\n previous.source === server &&\n previous.runtime === runtime &&\n previous.selector === selector\n )\n return previous.value;\n const value = server ? server() : read();\n serverCache.current = { runtime, selector, source: server, value };\n return value;\n }, [read, server, runtime, selector]);\n\n return useSyncExternalStore(subscribe, read, readServer);\n}\n\nexport function useReadable<T>(readable: Readable<T>): T {\n const subscribe = useCallback((listener: () => void) => readable.subscribe(listener), [readable]);\n const read = useCallback(() => readable.current(), [readable]);\n return useSyncExternalStore(subscribe, read, read);\n}\n\nexport function useHistory<TCommit>(history: LocalHistory<TCommit>): HistoryState & {\n undo(): OperationResult<TCommit>;\n redo(): OperationResult<TCommit>;\n} {\n const subscribe = useCallback((listener: () => void) => history.subscribe(listener), [history]);\n const read = useCallback(() => history.current(), [history]);\n const state = useSyncExternalStore(subscribe, read, read);\n const undo = useCallback(() => history.undo(), [history]);\n const redo = useCallback(() => history.redo(), [history]);\n\n return useMemo(() => ({ ...state, undo, redo }), [redo, state, undo]);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"react.js","names":["sameTarget"],"sources":["../react/src/hooks.ts"],"sourcesContent":["import type {\n DocumentReadable,\n ObjectNode,\n DocumentSelector,\n HistoryState,\n LocalHistory,\n OperationResult,\n Readable,\n ProjectionRuntime,\n Projection,\n Input,\n} from 'doxum';\nimport { track, subscribeDependencies, sameTarget, type ImpactTarget } from 'doxum/integration';\nimport {\n createContext,\n useCallback,\n useContext,\n useMemo,\n useRef,\n useSyncExternalStore,\n} from 'react';\n\nexport const ProjectionContext = createContext<ProjectionRuntime | undefined>(undefined);\nexport const ProjectionProvider = ProjectionContext.Provider;\n\nexport function useProjection<T>(projection: Projection<T>, runtime?: ProjectionRuntime): T;\nexport function useProjection<T, R>(\n projection: Projection<T>,\n selector: (value: T) => R,\n runtime?: ProjectionRuntime\n): R;\nexport function useProjection<T, R>(\n projection: Projection<T>,\n selector: (value: T) => R,\n equality?: (previous: R, next: R) => boolean,\n runtime?: ProjectionRuntime\n): R;\nexport function useProjection<T, R>(\n projection: Projection<T>,\n selectorOrRuntime?: ((value: T) => R) | ProjectionRuntime,\n equalityOrRuntime?: ((previous: R, next: R) => boolean) | ProjectionRuntime,\n suppliedRuntime?: ProjectionRuntime\n): T | R {\n const context = useContext(ProjectionContext);\n const selector = typeof selectorOrRuntime === 'function' ? selectorOrRuntime : undefined;\n const equality = typeof equalityOrRuntime === 'function' ? equalityOrRuntime : Object.is;\n const owner =\n (selector ? suppliedRuntime : undefined) ??\n (selector\n ? equalityOrRuntime && typeof equalityOrRuntime !== 'function'\n ? equalityOrRuntime\n : undefined\n : undefined) ??\n (selector ? context : typeof selectorOrRuntime === 'object' ? selectorOrRuntime : context);\n if (!owner) throw new Error('ProjectionRuntime is required.');\n const readable = useMemo(\n () => (selector ? owner.readable(projection, selector, equality) : owner.readable(projection)),\n [equality, owner, projection, selector]\n );\n return useReadable(readable as Readable<T | R>);\n}\n\nexport function useInput<T>(\n input: Input<T>,\n runtime?: ProjectionRuntime\n): readonly [T, (value: T) => void] {\n const context = useContext(ProjectionContext);\n const owner = runtime ?? context;\n if (!owner) throw new Error('ProjectionRuntime is required.');\n const value = useProjection(input, owner);\n const set = useCallback((next: T) => owner.set(input, next), [input, owner]);\n return useMemo(() => [value, set] as const, [set, value]);\n}\n\nexport type DocumentSelectorOptions<TResult> = {\n readonly isEqual?: (previous: TResult, next: TResult) => boolean;\n readonly server?: () => TResult;\n};\n\ntype SelectorCache<TSchema extends ObjectNode, TResult> = {\n readonly runtime: DocumentReadable<TSchema>;\n readonly value: TResult;\n readonly targets: readonly ImpactTarget<unknown>[];\n};\n\nconst objectIs = <T>(previous: T, next: T): boolean => Object.is(previous, next);\nconst sameTargets = (\n left: readonly ImpactTarget<unknown>[],\n right: readonly ImpactTarget<unknown>[]\n): boolean => {\n if (left.length !== right.length) return false;\n for (let index = 0; index < left.length; index += 1)\n if (!sameTarget(left[index], right[index])) return false;\n return true;\n};\n\nexport function useDocumentSelector<TSchema extends ObjectNode, TResult>(\n runtime: DocumentReadable<TSchema>,\n selector: DocumentSelector<TSchema, TResult>,\n options?: DocumentSelectorOptions<TResult>\n): TResult {\n const equality = options?.isEqual ?? objectIs;\n const cache = useRef<SelectorCache<TSchema, TResult> | undefined>(undefined);\n const serverCache = useRef<\n | {\n readonly runtime: DocumentReadable<TSchema>;\n readonly selector: DocumentSelector<TSchema, TResult>;\n readonly source: (() => TResult) | undefined;\n readonly value: TResult;\n }\n | undefined\n >(undefined);\n\n const read = useMemo(() => {\n // Each selector closure owns its revision cache, including inline selectors\n // that allocate arrays or objects. Equality only compares distinct snapshots.\n let snapshot: { readonly revision: number; readonly value: TResult } | undefined;\n return (): TResult => {\n const revision = runtime.revision();\n if (snapshot?.revision === revision) return snapshot.value;\n const selection = track(runtime, selector);\n const previous = cache.current;\n const value =\n previous && previous.runtime === runtime && equality(previous.value, selection.value)\n ? previous.value\n : selection.value;\n cache.current = { runtime, value, targets: selection.targets };\n snapshot = { revision, value };\n return value;\n };\n }, [equality, runtime, selector]);\n\n const subscribe = useCallback(\n (listener: () => void) => {\n read();\n let targets = cache.current?.targets ?? [];\n let unsubscribe: () => void = () => undefined;\n const install = () => {\n if (targets.length === 0) return () => undefined;\n return subscribeDependencies(runtime, targets, onCommit);\n };\n const onCommit = () => {\n const previous = cache.current;\n const next = read();\n const nextTargets = cache.current?.targets ?? [];\n if (!sameTargets(targets, nextTargets)) {\n unsubscribe();\n targets = nextTargets;\n unsubscribe = install();\n }\n if (previous && !Object.is(previous.value, next)) listener();\n };\n unsubscribe = install();\n return () => unsubscribe();\n },\n [read, runtime]\n );\n\n const server = options?.server;\n const readServer = useCallback((): TResult => {\n const previous = serverCache.current;\n if (\n previous &&\n previous.source === server &&\n previous.runtime === runtime &&\n previous.selector === selector\n )\n return previous.value;\n const value = server ? server() : read();\n serverCache.current = { runtime, selector, source: server, value };\n return value;\n }, [read, server, runtime, selector]);\n\n return useSyncExternalStore(subscribe, read, readServer);\n}\n\nexport function useReadable<T>(readable: Readable<T>): T {\n const subscribe = useCallback((listener: () => void) => readable.subscribe(listener), [readable]);\n const read = useCallback(() => readable.current(), [readable]);\n return useSyncExternalStore(subscribe, read, read);\n}\n\nexport function useHistory<TCommit>(history: LocalHistory<TCommit>): HistoryState & {\n undo(): OperationResult<TCommit>;\n redo(): OperationResult<TCommit>;\n} {\n const subscribe = useCallback((listener: () => void) => history.subscribe(listener), [history]);\n const read = useCallback(() => history.current(), [history]);\n const state = useSyncExternalStore(subscribe, read, read);\n const undo = useCallback(() => history.undo(), [history]);\n const redo = useCallback(() => history.redo(), [history]);\n\n return useMemo(() => ({ ...state, undo, redo }), [redo, state, undo]);\n}\n"],"mappings":"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"}