@agentshouse/mdruntime 0.1.0-cf28918c2bf91 → 0.1.0-cff8ba8fa2542

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 (66) hide show
  1. package/AGENTS.md +32 -8
  2. package/README.md +10 -10
  3. package/dist/adapter.d.ts +50 -25
  4. package/dist/adapter.d.ts.map +1 -1
  5. package/dist/adapter.js +1 -9
  6. package/dist/attached-model.d.ts +11 -0
  7. package/dist/attached-model.d.ts.map +1 -0
  8. package/dist/attached-model.js +34 -0
  9. package/dist/bootstrap.d.ts +5 -3
  10. package/dist/bootstrap.d.ts.map +1 -1
  11. package/dist/bootstrap.js +2 -2
  12. package/dist/client.d.ts +3 -2
  13. package/dist/client.d.ts.map +1 -1
  14. package/dist/client.js +3 -10
  15. package/dist/connection.d.ts +9 -12
  16. package/dist/connection.d.ts.map +1 -1
  17. package/dist/connection.js +495 -511
  18. package/dist/conversation.d.ts +18 -6
  19. package/dist/conversation.d.ts.map +1 -1
  20. package/dist/conversation.js +179 -78
  21. package/dist/host.d.ts +7 -1
  22. package/dist/host.d.ts.map +1 -1
  23. package/dist/host.js +34 -31
  24. package/dist/index.d.ts +3 -3
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/memory.d.ts +11 -5
  27. package/dist/memory.d.ts.map +1 -1
  28. package/dist/memory.js +77 -26
  29. package/dist/model.d.ts +4 -11
  30. package/dist/model.d.ts.map +1 -1
  31. package/dist/model.js +4 -3
  32. package/dist/observation.d.ts +2 -0
  33. package/dist/observation.d.ts.map +1 -1
  34. package/dist/observation.js +24 -14
  35. package/dist/query.d.ts +7 -2
  36. package/dist/query.d.ts.map +1 -1
  37. package/dist/query.js +29 -20
  38. package/dist/reach.d.ts +40 -0
  39. package/dist/reach.d.ts.map +1 -0
  40. package/dist/reach.js +285 -0
  41. package/dist/react.d.ts +31 -0
  42. package/dist/react.d.ts.map +1 -0
  43. package/dist/react.js +62 -0
  44. package/dist/read-side.d.ts +4 -0
  45. package/dist/read-side.d.ts.map +1 -1
  46. package/dist/read-side.js +16 -10
  47. package/dist/refresh.d.ts +2 -1
  48. package/dist/refresh.d.ts.map +1 -1
  49. package/dist/refresh.js +7 -3
  50. package/dist/replica.d.ts +4 -4
  51. package/dist/replica.d.ts.map +1 -1
  52. package/dist/replica.js +12 -13
  53. package/dist/room-draft.d.ts +3 -0
  54. package/dist/room-draft.d.ts.map +1 -1
  55. package/dist/room-draft.js +6 -4
  56. package/dist/rows.d.ts.map +1 -1
  57. package/dist/rows.js +1 -4
  58. package/dist/save.d.ts +0 -2
  59. package/dist/save.d.ts.map +1 -1
  60. package/dist/save.js +3 -8
  61. package/dist/store.d.ts +0 -1
  62. package/dist/store.d.ts.map +1 -1
  63. package/dist/wire.d.ts +51 -16
  64. package/dist/wire.d.ts.map +1 -1
  65. package/dist/wire.js +50 -59
  66. package/package.json +14 -2
package/dist/reach.js ADDED
@@ -0,0 +1,285 @@
1
+ import { unheldView } from './conversation.js';
2
+ import { modelTypes, NO_MODEL } from './model.js';
3
+ import { applied, observing } from './observation.js';
4
+ import { beneath } from './paths.js';
5
+ import { reconciled } from './read-side.js';
6
+ import { moveTarget, readParts } from './room-draft.js';
7
+ import { documentKey } from './rows.js';
8
+ const UNHELD_TYPES = { collection: modelTypes(NO_MODEL), typedSelection: null };
9
+ const UNDECLARED = { kind: 'undeclared' };
10
+ function undeclared() {
11
+ return Promise.resolve(UNDECLARED);
12
+ }
13
+ const NO_CONVERSATION = {
14
+ list: () => Promise.resolve([]),
15
+ read: (conversation) => Promise.resolve(unheldView(conversation)),
16
+ create: undeclared,
17
+ send: undeclared,
18
+ answer: undeclared,
19
+ interrupt: undeclared,
20
+ kill: undeclared,
21
+ cancel: undeclared,
22
+ delete: undeclared,
23
+ };
24
+ const NO_CHAT_DRAFT = {
25
+ write: () => Promise.resolve(),
26
+ discard: () => Promise.resolve(),
27
+ };
28
+ export function holds(reach, room) {
29
+ return (reach === null ||
30
+ reach.rooms.includes(room) ||
31
+ reach.documents.some((document) => document.room === room));
32
+ }
33
+ function wholly(reach, room) {
34
+ return reach === null || reach.rooms.includes(room);
35
+ }
36
+ export function governs(reach, room) {
37
+ if (wholly(reach, room.locator)) {
38
+ return { collection: true, selection: room.typedSelection !== null };
39
+ }
40
+ const selected = (path) => room.typedSelection?.roots.some((root) => beneath(root, path)) ?? false;
41
+ const paths = reach.documents
42
+ .filter((document) => document.room === room.locator)
43
+ .map(({ path }) => path);
44
+ return { collection: paths.some((path) => !selected(path)), selection: paths.some(selected) };
45
+ }
46
+ function covers(reach, room, path) {
47
+ return (reach === null ||
48
+ reach.rooms.includes(room) ||
49
+ reach.documents.some((document) => document.room === room && document.path === path));
50
+ }
51
+ function conversing(reach) {
52
+ return reach === null || reach.conversations;
53
+ }
54
+ export function planReach(reach) {
55
+ return reach === null
56
+ ? null
57
+ : {
58
+ rooms: [...new Set(reach.rooms)].sort(),
59
+ documents: [
60
+ ...new Set(reach.documents.map(({ room, path }) => documentKey(room, path))),
61
+ ].sort(),
62
+ };
63
+ }
64
+ function roomsWithin(reach, rooms) {
65
+ if (reach === null)
66
+ return rooms;
67
+ return rooms
68
+ .filter((room) => holds(reach, room.locator))
69
+ .map((room) => {
70
+ if (wholly(reach, room.locator))
71
+ return room;
72
+ const governed = governs(reach, room);
73
+ return {
74
+ ...room,
75
+ model: governed.collection ? room.model : modelTypes(NO_MODEL),
76
+ typedSelection: governed.selection ? room.typedSelection : null,
77
+ roomVersion: null,
78
+ attachments: [],
79
+ };
80
+ });
81
+ }
82
+ function typesWithin(reach, room, types) {
83
+ if (room === undefined)
84
+ return structuredClone(UNHELD_TYPES);
85
+ const governed = governs(reach, room);
86
+ return {
87
+ collection: governed.collection ? types.collection : modelTypes(NO_MODEL),
88
+ typedSelection: governed.selection ? types.typedSelection : null,
89
+ };
90
+ }
91
+ function moves(reach, room, move) {
92
+ return move === undefined || covers(reach, room, moveTarget(move));
93
+ }
94
+ function draftWithin(reach, draft) {
95
+ return covers(reach, draft.room, draft.path) && moves(reach, draft.room, draft.parts.move);
96
+ }
97
+ function draftsWithin(reach, drafts) {
98
+ return drafts.filter((draft) => draftWithin(reach, draft));
99
+ }
100
+ function shownDraft(reach, draft) {
101
+ return draft !== null && draftWithin(reach, draft) ? draft : null;
102
+ }
103
+ function writeWithin(reach, write) {
104
+ return write.outcome === 'written'
105
+ ? { outcome: 'written', roomDraft: shownDraft(reach, write.roomDraft) }
106
+ : { outcome: 'stale', current: shownDraft(reach, write.current) };
107
+ }
108
+ function savedWithin(reach, room, result) {
109
+ if (reach === null || result.outcome === 'saved')
110
+ return result;
111
+ const { file, location, referrers, ...refusal } = result;
112
+ const hidden = file !== undefined && !covers(reach, room, file);
113
+ return {
114
+ ...refusal,
115
+ ...(file === undefined || hidden ? {} : { file }),
116
+ ...(location === undefined || hidden ? {} : { location }),
117
+ ...(referrers === undefined
118
+ ? {}
119
+ : { referrers: referrers.filter((path) => covers(reach, room, path)) }),
120
+ };
121
+ }
122
+ function statusWithin(reach, status) {
123
+ return {
124
+ ...status,
125
+ attentionRequired: status.attentionRequired.filter((entry) => covers(reach, entry.room, entry.path)),
126
+ };
127
+ }
128
+ function queueWithin(reach, room, entries) {
129
+ return entries.filter(({ batch }) => batch.changes.every((change) => covers(reach, room, change.path) &&
130
+ (change.kind !== 'move' || covers(reach, room, change.to))));
131
+ }
132
+ function unreached(room) {
133
+ return new Error(`No Local Replica for ${room}`);
134
+ }
135
+ export function narrowed(runtime, given) {
136
+ const { md, watch } = runtime;
137
+ let reach = given;
138
+ const open = new Set();
139
+ function following(begin) {
140
+ let stop = begin();
141
+ const restart = () => {
142
+ stop();
143
+ stop = begin();
144
+ };
145
+ open.add(restart);
146
+ return () => {
147
+ open.delete(restart);
148
+ stop();
149
+ };
150
+ }
151
+ const within = (topic, input, deliver) => {
152
+ if (topic === 'rooms') {
153
+ return following(() => watch(topic, input, (rooms) => deliver(roomsWithin(reach, rooms))));
154
+ }
155
+ if (topic === 'sync-status') {
156
+ return following(() => watch(topic, input, (status) => deliver(statusWithin(reach, status))));
157
+ }
158
+ if (topic === 'room-drafts') {
159
+ return following(() => watch(topic, input, (drafts) => deliver(draftsWithin(reach, drafts))));
160
+ }
161
+ if (topic === 'queue') {
162
+ const room = input[0];
163
+ return following(() => {
164
+ if (!holds(reach, room)) {
165
+ deliver([]);
166
+ return () => { };
167
+ }
168
+ return watch(topic, input, (entries) => deliver(queueWithin(reach, room, entries)));
169
+ });
170
+ }
171
+ if (topic === 'query') {
172
+ const plan = runtime.plan(input[0], reach);
173
+ let held = null;
174
+ return following(() => {
175
+ let first = true;
176
+ return runtime.watchPlan({ ...plan, reach: planReach(reach) }, (message) => {
177
+ if (!first) {
178
+ held = applied(held, message);
179
+ deliver(message);
180
+ return;
181
+ }
182
+ first = false;
183
+ if (held === null) {
184
+ held = message;
185
+ deliver(message);
186
+ return;
187
+ }
188
+ const next = reconciled(held, message);
189
+ if (next === null)
190
+ return;
191
+ held = next.keyed;
192
+ deliver(next.change);
193
+ });
194
+ });
195
+ }
196
+ if (topic === 'conversation') {
197
+ return following(() => {
198
+ if (conversing(reach))
199
+ return watch(topic, input, deliver);
200
+ deliver(unheldView(input[0]));
201
+ return () => { };
202
+ });
203
+ }
204
+ if (topic === 'conversations') {
205
+ return following(() => {
206
+ if (conversing(reach))
207
+ return watch(topic, input, deliver);
208
+ deliver([]);
209
+ return () => { };
210
+ });
211
+ }
212
+ return watch(topic, input, deliver);
213
+ };
214
+ const conversation = () => (conversing(reach) ? md.conversation : NO_CONVERSATION);
215
+ const chatDraft = () => (conversing(reach) ? md.chatDraft : NO_CHAT_DRAFT);
216
+ const view = {
217
+ ...md,
218
+ rooms: () => roomsWithin(reach, md.rooms()),
219
+ query: ((input) => runtime.query(runtime.plan(input, reach))),
220
+ search: (input) => runtime.search(input, reach),
221
+ types(room) {
222
+ if (wholly(reach, room))
223
+ return md.types(room);
224
+ if (!holds(reach, room))
225
+ return structuredClone(UNHELD_TYPES);
226
+ const listed = md.rooms().find(({ locator }) => locator === room);
227
+ return typesWithin(reach, listed, md.types(room));
228
+ },
229
+ async save(room, roomDrafts, writtenBy) {
230
+ if (!holds(reach, room))
231
+ throw unreached(room);
232
+ const outside = roomDrafts.find(({ path }) => !covers(reach, room, path) ||
233
+ !moves(reach, room, md.roomDraft.read(room, path)?.parts.move));
234
+ if (outside !== undefined)
235
+ return { outcome: 'refused', rule: 'stale', file: outside.path };
236
+ return savedWithin(reach, room, await md.save(room, roomDrafts, writtenBy));
237
+ },
238
+ history: (room, path, query) => covers(reach, room, path)
239
+ ? md.history(room, path, query)
240
+ : Promise.resolve({ kind: 'room-not-readable', locator: room }),
241
+ reached: (room, roomVersion, path) => (path === undefined ? wholly(reach, room) : covers(reach, room, path)) &&
242
+ md.reached(room, roomVersion, path),
243
+ log: () => (reach === null ? md.log() : ''),
244
+ roomDraft: {
245
+ list: () => md.roomDraft.list().then((drafts) => draftsWithin(reach, drafts)),
246
+ read: (room, path) => covers(reach, room, path) ? shownDraft(reach, md.roomDraft.read(room, path)) : null,
247
+ async write(room, path, revision, parts) {
248
+ if (!covers(reach, room, path) || !moves(reach, room, readParts(parts).move)) {
249
+ throw unreached(room);
250
+ }
251
+ return writeWithin(reach, await md.roomDraft.write(room, path, revision, parts));
252
+ },
253
+ async discard(room, path, revision) {
254
+ if (!covers(reach, room, path))
255
+ return { outcome: 'stale', current: null };
256
+ return writeWithin(reach, await md.roomDraft.discard(room, path, revision));
257
+ },
258
+ },
259
+ conversation: {
260
+ list: () => conversation().list(),
261
+ read: (id, options) => conversation().read(id, options),
262
+ create: (input) => conversation().create(input),
263
+ send: (id) => conversation().send(id),
264
+ answer: (id, answer) => conversation().answer(id, answer),
265
+ interrupt: (id) => conversation().interrupt(id),
266
+ kill: (id) => conversation().kill(id),
267
+ cancel: (id, turn) => conversation().cancel(id, turn),
268
+ delete: (id) => conversation().delete(id),
269
+ },
270
+ chatDraft: {
271
+ write: (id, draft) => chatDraft().write(id, draft),
272
+ discard: (id) => chatDraft().discard(id),
273
+ },
274
+ observe: observing(within),
275
+ };
276
+ return {
277
+ md: view,
278
+ watch: within,
279
+ narrow(next) {
280
+ reach = next;
281
+ for (const restart of [...open])
282
+ restart();
283
+ },
284
+ };
285
+ }
@@ -0,0 +1,31 @@
1
+ import type { MdRuntimeClient } from './client.js';
2
+ import type { AppRoom, ConnectionRefusal, SaveResult } from './connection.js';
3
+ import type { ServiceMode } from './adapter.js';
4
+ import type { ConversationEntry, ConversationOptions, ConversationView } from './conversation.js';
5
+ import type { QueueEntry } from './outcome.js';
6
+ import type { DocumentQuery, DocumentResult, GroupQuery, GroupResult } from './query.js';
7
+ import type { RoomDraft, RoomDraftParts } from './room-draft.js';
8
+ import type { SyncStatus } from './status.js';
9
+ type Client = MdRuntimeClient | null;
10
+ export declare function useObserve(client: Client, topic: 'deployment-facts'): unknown;
11
+ export declare function useObserve(client: Client, topic: 'rooms'): readonly AppRoom[] | undefined;
12
+ export declare function useObserve(client: Client, topic: 'connection-refusal'): ConnectionRefusal | null | undefined;
13
+ export declare function useObserve(client: Client, topic: 'storage-loss'): string | null | undefined;
14
+ export declare function useObserve(client: Client, topic: 'sync-status'): SyncStatus | undefined;
15
+ export declare function useObserve(client: Client, topic: 'service-mode'): ServiceMode | undefined;
16
+ export declare function useObserve(client: Client, topic: 'query', query: DocumentQuery): DocumentResult | undefined;
17
+ export declare function useObserve(client: Client, topic: 'query', query: GroupQuery): GroupResult | undefined;
18
+ export declare function useObserve(client: Client, topic: 'queue', locator: string): readonly QueueEntry[] | undefined;
19
+ export declare function useObserve(client: Client, topic: 'room-drafts'): readonly RoomDraft[] | undefined;
20
+ export declare function useObserve(client: Client, topic: 'presentation'): unknown;
21
+ export declare function useObserve(client: Client, topic: 'conversation', conversation: string, options: ConversationOptions): ConversationView | undefined;
22
+ export declare function useObserve(client: Client, topic: 'conversations'): readonly ConversationEntry[] | undefined;
23
+ export type Saving = {
24
+ readonly busy: boolean;
25
+ readonly outcome: SaveResult | null;
26
+ readonly failure: Error | null;
27
+ save(room: string, roomDrafts: ReadonlyMap<string, RoomDraftParts>): Promise<SaveResult>;
28
+ };
29
+ export declare function useSave(client: MdRuntimeClient): Saving;
30
+ export {};
31
+ //# sourceMappingURL=react.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../src/react.ts"],"names":[],"mappings":"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"}
package/dist/react.js ADDED
@@ -0,0 +1,62 @@
1
+ import { useCallback, useEffect, useRef, useState } from 'react';
2
+ export function useObserve(client, topic, ...input) {
3
+ const key = JSON.stringify([topic, ...input]);
4
+ const [held, setHeld] = useState(null);
5
+ const opened = useRef(input);
6
+ opened.current = input;
7
+ useEffect(() => {
8
+ if (client === null)
9
+ return;
10
+ let live = true;
11
+ let observation = null;
12
+ const observe = client.observe;
13
+ void observe(topic, ...opened.current).then((open) => {
14
+ if (!live) {
15
+ open.close();
16
+ return;
17
+ }
18
+ observation = open;
19
+ const take = (value) => {
20
+ if (live)
21
+ setHeld({ client, key, value });
22
+ };
23
+ take(open.current());
24
+ open.subscribe(take);
25
+ });
26
+ return () => {
27
+ live = false;
28
+ observation?.close();
29
+ };
30
+ }, [client, key]);
31
+ return held !== null && held.client === client && held.key === key ? held.value : undefined;
32
+ }
33
+ const IDLE = { busy: false, outcome: null, failure: null };
34
+ async function written(client, room, roomDrafts) {
35
+ const kept = [];
36
+ for (const [path, parts] of roomDrafts) {
37
+ const prior = await client.roomDraft.read(room, path);
38
+ const write = await client.roomDraft.write(room, path, prior?.revision ?? null, parts);
39
+ if (write.outcome !== 'written' || write.roomDraft === null) {
40
+ throw new Error(`the Room Draft at ${path} was not written`);
41
+ }
42
+ kept.push({ path, revision: write.roomDraft.revision });
43
+ }
44
+ return client.save(room, kept);
45
+ }
46
+ export function useSave(client) {
47
+ const [state, setState] = useState(IDLE);
48
+ const save = useCallback(async (room, roomDrafts) => {
49
+ setState({ busy: true, outcome: null, failure: null });
50
+ try {
51
+ const outcome = await written(client, room, roomDrafts);
52
+ setState({ busy: false, outcome, failure: null });
53
+ return outcome;
54
+ }
55
+ catch (error) {
56
+ const failure = error instanceof Error ? error : new Error(String(error));
57
+ setState({ busy: false, outcome: null, failure });
58
+ throw failure;
59
+ }
60
+ }, [client]);
61
+ return { ...state, save };
62
+ }
@@ -10,5 +10,9 @@ export type ReadSide = {
10
10
  document(room: string, path: string): SearchedDocument | null;
11
11
  close(): void;
12
12
  };
13
+ export declare function reconciled(previous: KeyedResult, next: KeyedResult): {
14
+ keyed: KeyedResult;
15
+ change: QueryChange;
16
+ } | null;
13
17
  export declare function createReadSide(): ReadSide;
14
18
  //# sourceMappingURL=read-side.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"read-side.d.ts","sourceRoot":"","sources":["../src/read-side.ts"],"names":[],"mappings":"AA2BA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAU,KAAK,WAAW,EAAE,KAAK,WAAW,EAAe,MAAM,kBAAkB,CAAC;AAC3F,OAAO,KAAK,EAOV,IAAI,EAGJ,WAAW,EAIZ,MAAM,YAAY,CAAC;AAEpB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAqDpD,MAAM,MAAM,QAAQ,GAAG;IACrB,KAAK,CAAC,GAAG,EAAE,SAAS,GAAG,IAAI,CAAC;IAC5B,KAAK,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IACxC,KAAK,CACH,IAAI,EAAE,IAAI,EACV,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,CAAC,OAAO,EAAE,WAAW,GAAG,WAAW,KAAK,IAAI,GACpD,MAAM,IAAI,CAAC;IACd,SAAS,IAAI,SAAS,gBAAgB,EAAE,CAAC;IACzC,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CAAC;IAC9D,KAAK,IAAI,IAAI,CAAC;CACf,CAAC;AAySF,wBAAgB,cAAc,IAAI,QAAQ,CA4HzC"}
1
+ {"version":3,"file":"read-side.d.ts","sourceRoot":"","sources":["../src/read-side.ts"],"names":[],"mappings":"AA2BA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAU,KAAK,WAAW,EAAE,KAAK,WAAW,EAAe,MAAM,kBAAkB,CAAC;AAC3F,OAAO,KAAK,EAOV,IAAI,EAIJ,WAAW,EAIZ,MAAM,YAAY,CAAC;AAEpB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAqDpD,MAAM,MAAM,QAAQ,GAAG;IACrB,KAAK,CAAC,GAAG,EAAE,SAAS,GAAG,IAAI,CAAC;IAC5B,KAAK,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IACxC,KAAK,CACH,IAAI,EAAE,IAAI,EACV,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,CAAC,OAAO,EAAE,WAAW,GAAG,WAAW,KAAK,IAAI,GACpD,MAAM,IAAI,CAAC;IACd,SAAS,IAAI,SAAS,gBAAgB,EAAE,CAAC;IACzC,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CAAC;IAC9D,KAAK,IAAI,IAAI,CAAC;CACf,CAAC;AA8RF,wBAAgB,UAAU,CACxB,QAAQ,EAAE,WAAW,EACrB,IAAI,EAAE,WAAW,GAChB;IAAE,KAAK,EAAE,WAAW,CAAC;IAAC,MAAM,EAAE,WAAW,CAAA;CAAE,GAAG,IAAI,CA4BpD;AAED,wBAAgB,cAAc,IAAI,QAAQ,CA4HzC"}
package/dist/read-side.js CHANGED
@@ -50,10 +50,16 @@ function conditioned(condition, row) {
50
50
  return some(condition.some.map((item) => conditioned(item, row)));
51
51
  return tested(condition.test, row.values[condition.field], row.sizes[condition.field], row.fields[condition.field]);
52
52
  }
53
- function scoped(scope, row, rooms) {
53
+ function reached(reach, room, document) {
54
+ return reach === null
55
+ ? []
56
+ : [or(inArray(room, [...reach.rooms]), inArray(document, [...reach.documents]))];
57
+ }
58
+ function scoped(scope, row, rooms, reach) {
54
59
  return every([
55
60
  eq(row.kind, 'document'),
56
61
  ...(rooms === null ? [] : [inArray(row.room, [...rooms])]),
62
+ ...reached(reach, row.room, row.key),
57
63
  ...(scope.types === null ? [] : [inArray(row.type, [...scope.types])]),
58
64
  ...(scope.pathPrefix === null ? [] : [inArray(scope.pathPrefix, row.directories)]),
59
65
  ...(scope.where === null ? [] : [conditioned(scope.where, row)]),
@@ -68,25 +74,25 @@ function live(query) {
68
74
  startSync: true,
69
75
  });
70
76
  }
71
- function related(q, rows, scope, field, kept) {
77
+ function related(q, rows, scope, field, kept, reach) {
72
78
  const matched = kept === 'source' ? 'target' : 'source';
73
79
  const from = q.from({ r: rows });
74
80
  return from
75
81
  .where(({ r }) => every([eq(r.kind, 'reference'), ...(field === null ? [] : [eq(r.field, field)])]))
76
82
  .innerJoin({ m: rows }, ({ r, m }) => eq(r[matched], m.key))
77
- .where(({ m }) => scoped(scope, m, null))
83
+ .where(({ m }) => scoped(scope, m, null, reach))
78
84
  .groupBy(({ r }) => r[kept])
79
85
  .select(({ r }) => ({ key: r[kept] }));
80
86
  }
81
87
  function filtered(q, rows, plan) {
82
88
  const from = q.from({ d: rows });
83
- let query = from.where(({ d }) => scoped(plan.root, d, plan.rooms));
89
+ let query = from.where(({ d }) => scoped(plan.root, d, plan.rooms, plan.reach));
84
90
  if (plan.hop !== null) {
85
- const hop = related(q, rows, plan.hop, plan.hop.field, 'source');
91
+ const hop = related(q, rows, plan.hop, plan.hop.field, 'source', plan.reach);
86
92
  query = query.innerJoin({ h: hop }, ({ d, h }) => eq(d.key, h.key));
87
93
  }
88
94
  if (plan.backlinks !== null) {
89
- const backlinks = related(q, rows, plan.backlinks, plan.backlinks.field, 'target');
95
+ const backlinks = related(q, rows, plan.backlinks, plan.backlinks.field, 'target', plan.reach);
90
96
  query = query.innerJoin({ k: backlinks }, ({ d, k }) => eq(d.key, k.key));
91
97
  }
92
98
  return query;
@@ -117,14 +123,14 @@ function documents(q, rows, plan, shape) {
117
123
  ...(Array.isArray(shape.sections) ? named('section', shape.sections, b.sections) : {}),
118
124
  ...(shape.preamble ? { preamble: b.preamble } : {}),
119
125
  ...(shape.markdown ? { markdown: b.markdown } : {}),
120
- ...(shape.backlinks ? { backlinks: toArray(backlinked(q, rows, d)) } : {}),
126
+ ...(shape.backlinks ? { backlinks: toArray(backlinked(q, rows, d, plan.reach)) } : {}),
121
127
  ...(shape.change ? { change: d.change } : {}),
122
128
  }));
123
129
  }
124
- function backlinked(q, rows, document) {
130
+ function backlinked(q, rows, document, reach) {
125
131
  const from = q.from({ r: rows });
126
132
  return from
127
- .where(({ r }) => eq(r.target, document.key))
133
+ .where(({ r }) => every([eq(r.target, document.key), ...reached(reach, r.room, r.source)]))
128
134
  .orderBy(({ r }) => r.path, { stringSort: 'lexical' })
129
135
  .orderBy(({ r }) => r.field, { stringSort: 'lexical' })
130
136
  .select(({ r }) => ({ path: r.path, type: r.type, field: r.field }));
@@ -214,7 +220,7 @@ function keyedOf(plan, entries) {
214
220
  keys: bounded.map(([key]) => key),
215
221
  };
216
222
  }
217
- function reconciled(previous, next) {
223
+ export function reconciled(previous, next) {
218
224
  const before = rowsOf(previous.result);
219
225
  const held = new Map(previous.keys.map((key, index) => [key, before[index]]));
220
226
  const changed = [];
package/dist/refresh.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { Adapter, AdapterFailure, Authorization, RetryableRefusal } from './adapter.js';
2
+ import type { Remodel } from './bootstrap.js';
2
3
  import { type ConfirmedLayer } from './replica.js';
3
4
  import { type Refused, type Unconfirmed } from './transfer.js';
4
5
  export type RefreshOutcome = {
@@ -9,5 +10,5 @@ export type RefreshOutcome = {
9
10
  } | {
10
11
  kind: 'beyond-horizon';
11
12
  } | RetryableRefusal | Unconfirmed | Refused | AdapterFailure;
12
- export declare function refreshRoom(adapter: Adapter, authorization: Authorization, confirmed: ConfirmedLayer, continuing: () => boolean, minPosition: string | undefined): Promise<RefreshOutcome>;
13
+ export declare function refreshRoom(adapter: Adapter, authorization: Authorization, confirmed: ConfirmedLayer, remodel: Remodel, continuing: () => boolean): Promise<RefreshOutcome>;
13
14
  //# sourceMappingURL=refresh.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"refresh.d.ts","sourceRoot":"","sources":["../src/refresh.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,OAAO,EACP,cAAc,EACd,aAAa,EAGb,gBAAgB,EAEjB,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAc,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AAC/D,OAAO,EAAe,KAAK,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC;AAE5E,MAAM,MAAM,cAAc,GACtB;IAAE,IAAI,EAAE,eAAe,CAAA;CAAE,GACzB;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,SAAS,EAAE,cAAc,CAAA;CAAE,GAC/C;IAAE,IAAI,EAAE,gBAAgB,CAAA;CAAE,GAC1B,gBAAgB,GAChB,WAAW,GACX,OAAO,GACP,cAAc,CAAC;AAEnB,wBAAsB,WAAW,CAC/B,OAAO,EAAE,OAAO,EAChB,aAAa,EAAE,aAAa,EAC5B,SAAS,EAAE,cAAc,EACzB,UAAU,EAAE,MAAM,OAAO,EACzB,WAAW,EAAE,MAAM,GAAG,SAAS,GAC9B,OAAO,CAAC,cAAc,CAAC,CA0EzB"}
1
+ {"version":3,"file":"refresh.d.ts","sourceRoot":"","sources":["../src/refresh.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,OAAO,EACP,cAAc,EACd,aAAa,EAGb,gBAAgB,EAEjB,MAAM,cAAc,CAAC;AAEtB,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAc,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AAC/D,OAAO,EAAe,KAAK,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC;AAE5E,MAAM,MAAM,cAAc,GACtB;IAAE,IAAI,EAAE,eAAe,CAAA;CAAE,GACzB;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,SAAS,EAAE,cAAc,CAAA;CAAE,GAC/C;IAAE,IAAI,EAAE,gBAAgB,CAAA;CAAE,GAC1B,gBAAgB,GAChB,WAAW,GACX,OAAO,GACP,cAAc,CAAC;AAEnB,wBAAsB,WAAW,CAC/B,OAAO,EAAE,OAAO,EAChB,aAAa,EAAE,aAAa,EAC5B,SAAS,EAAE,cAAc,EACzB,OAAO,EAAE,OAAO,EAChB,UAAU,EAAE,MAAM,OAAO,GACxB,OAAO,CAAC,cAAc,CAAC,CA8EzB"}
package/dist/refresh.js CHANGED
@@ -1,18 +1,21 @@
1
1
  import { declaredAttachments } from './attachment.js';
2
2
  import { readStaged } from './replica.js';
3
3
  import { unconfirmed } from './transfer.js';
4
- export async function refreshRoom(adapter, authorization, confirmed, continuing, minPosition) {
4
+ export async function refreshRoom(adapter, authorization, confirmed, remodel, continuing) {
5
5
  const locator = confirmed.record.locator;
6
6
  const fromRoomVersion = confirmed.record.roomVersion;
7
7
  const first = await adapter.refresh(authorization, {
8
8
  locator,
9
9
  selection: confirmed.record.replica.selection,
10
10
  fromRoomVersion,
11
- ...(minPosition === undefined ? {} : { minPosition }),
12
11
  });
13
12
  if (first.kind !== 'net-advancement')
14
13
  return first;
15
14
  const roomVersion = first.roomVersion;
15
+ const models = remodel(first.models, {
16
+ collection: confirmed.record.model,
17
+ selection: confirmed.record.selectionModel,
18
+ });
16
19
  const documents = { ...confirmed.record.documents };
17
20
  const revisions = { ...confirmed.record.revisions };
18
21
  const changes = { ...confirmed.record.changes };
@@ -54,7 +57,6 @@ export async function refreshRoom(adapter, authorization, confirmed, continuing,
54
57
  locator,
55
58
  selection: confirmed.record.replica.selection,
56
59
  fromRoomVersion,
57
- ...(minPosition === undefined ? {} : { minPosition }),
58
60
  pageToken,
59
61
  });
60
62
  if (page.kind === 'still-current' || page.kind === 'beyond-horizon')
@@ -68,6 +70,8 @@ export async function refreshRoom(adapter, authorization, confirmed, continuing,
68
70
  }
69
71
  const staged = readStaged({
70
72
  ...confirmed.record,
73
+ model: models.collection,
74
+ selectionModel: models.selection,
71
75
  roomVersion,
72
76
  documents,
73
77
  binaryTargets: [...binaryTargets].sort(),
package/dist/replica.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { type ChangeBatch } from './change.js';
2
2
  import { type Collection, type Composition } from './collection.js';
3
3
  import { type Layer } from './overlay.js';
4
- import { type Rewriting } from './save.js';
4
+ import type { RoomAttachment } from './adapter.js';
5
5
  import type { ReplicaRecord } from './store.js';
6
6
  import type { Refused } from './transfer.js';
7
7
  export type ConfirmedLayer = {
@@ -35,9 +35,9 @@ export declare function readStaged(record: ReplicaRecord, previous?: ConfirmedLa
35
35
  export declare function readReclassified(record: ReplicaRecord, previous: ConfirmedLayer): Staged | {
36
36
  kind: 'deferred';
37
37
  };
38
- export declare function applyReturned(confirmed: ConfirmedLayer, documents: Readonly<Record<string, string>>, revisions: Readonly<Record<string, string>>, removals: readonly string[], roomVersion: string): Staged;
39
- export declare function replicaOf(confirmed: ConfirmedLayer, queue: readonly ChangeBatch[], rewriting: Rewriting, previous?: Replica): Replica;
38
+ export declare function applyReturned(confirmed: ConfirmedLayer, documents: Readonly<Record<string, string>>, revisions: Readonly<Record<string, string>>, removals: readonly string[], attachments: readonly RoomAttachment[] | undefined, roomVersion: string, writtenAt: string): Staged;
39
+ export declare function replicaOf(confirmed: ConfirmedLayer, queue: readonly ChangeBatch[], previous?: Replica): Replica;
40
40
  export declare function landed(batch: ChangeBatch, confirmed: ConfirmedLayer): boolean;
41
- export declare function withdrawals(confirmed: ConfirmedLayer, queue: readonly ChangeBatch[], withdrawable: (batch: ChangeBatch) => boolean, rewriting: Rewriting): readonly ChangeBatch[];
41
+ export declare function withdrawals(confirmed: ConfirmedLayer, queue: readonly ChangeBatch[], withdrawable: (batch: ChangeBatch) => boolean): readonly ChangeBatch[];
42
42
  export {};
43
43
  //# sourceMappingURL=replica.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"replica.d.ts","sourceRoot":"","sources":["../src/replica.ts"],"names":[],"mappings":"AACA,OAAO,EAA0B,KAAK,WAAW,EAAqB,MAAM,aAAa,CAAC;AAC1F,OAAO,EAIL,KAAK,UAAU,EACf,KAAK,WAAW,EACjB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAA0C,KAAK,KAAK,EAAE,MAAM,cAAc,CAAC;AAClF,OAAO,EAAqB,KAAK,SAAS,EAAE,MAAM,WAAW,CAAC;AAE9D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAE7C,MAAM,MAAM,cAAc,GAAG;IAC3B,MAAM,EAAE,aAAa,CAAC;IACtB,UAAU,EAAE,UAAU,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG,KAAK,GAAG;IACjC,UAAU,EAAE,UAAU,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,MAAM,GAAG;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,SAAS,EAAE,cAAc,CAAA;CAAE,GAAG,OAAO,CAAC;AAE7E,MAAM,MAAM,OAAO,GAAG;IACpB,SAAS,EAAE,cAAc,CAAC;IAC1B,KAAK,EAAE,SAAS,WAAW,EAAE,CAAC;IAC9B,OAAO,EAAE,YAAY,CAAC;IACtB,MAAM,EAAE,SAAS,UAAU,EAAE,CAAC;CAC/B,CAAC;AAEF,KAAK,UAAU,GAAG;IAChB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,KAAK,CAAC;IACd,KAAK,EAAE,KAAK,CAAC;IACb,SAAS,EAAE,WAAW,CAAC;IACvB,UAAU,CAAC,EAAE,UAAU,CAAC;CACzB,CAAC;AAEF,wBAAgB,aAAa,CAAC,MAAM,EAAE,aAAa,GAAG,WAAW,CAQhE;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,aAAa,EAAE,QAAQ,CAAC,EAAE,cAAc,GAAG,MAAM,CAWtF;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,aAAa,EAAE,KAAK,EAAE,aAAa,GAAG,OAAO,CAKnF;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,aAAa,EAAE,QAAQ,CAAC,EAAE,cAAc,GAAG,MAAM,CAkBnF;AAED,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,aAAa,EACrB,QAAQ,EAAE,cAAc,GACvB,MAAM,GAAG;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,CAM/B;AAED,wBAAgB,aAAa,CAC3B,SAAS,EAAE,cAAc,EACzB,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAC3C,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAC3C,QAAQ,EAAE,SAAS,MAAM,EAAE,EAC3B,WAAW,EAAE,MAAM,GAClB,MAAM,CA+BR;AAsDD,wBAAgB,SAAS,CACvB,SAAS,EAAE,cAAc,EACzB,KAAK,EAAE,SAAS,WAAW,EAAE,EAC7B,SAAS,EAAE,SAAS,EACpB,QAAQ,CAAC,EAAE,OAAO,GACjB,OAAO,CA4CT;AAkFD,wBAAgB,MAAM,CAAC,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,cAAc,GAAG,OAAO,CAa7E;AAED,wBAAgB,WAAW,CACzB,SAAS,EAAE,cAAc,EACzB,KAAK,EAAE,SAAS,WAAW,EAAE,EAC7B,YAAY,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,OAAO,EAC7C,SAAS,EAAE,SAAS,GACnB,SAAS,WAAW,EAAE,CAoBxB"}
1
+ {"version":3,"file":"replica.d.ts","sourceRoot":"","sources":["../src/replica.ts"],"names":[],"mappings":"AACA,OAAO,EAA0B,KAAK,WAAW,EAAqB,MAAM,aAAa,CAAC;AAC1F,OAAO,EAIL,KAAK,UAAU,EACf,KAAK,WAAW,EACjB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAA0C,KAAK,KAAK,EAAE,MAAM,cAAc,CAAC;AAGlF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAEnD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAE7C,MAAM,MAAM,cAAc,GAAG;IAC3B,MAAM,EAAE,aAAa,CAAC;IACtB,UAAU,EAAE,UAAU,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG,KAAK,GAAG;IACjC,UAAU,EAAE,UAAU,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,MAAM,GAAG;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,SAAS,EAAE,cAAc,CAAA;CAAE,GAAG,OAAO,CAAC;AAE7E,MAAM,MAAM,OAAO,GAAG;IACpB,SAAS,EAAE,cAAc,CAAC;IAC1B,KAAK,EAAE,SAAS,WAAW,EAAE,CAAC;IAC9B,OAAO,EAAE,YAAY,CAAC;IACtB,MAAM,EAAE,SAAS,UAAU,EAAE,CAAC;CAC/B,CAAC;AAEF,KAAK,UAAU,GAAG;IAChB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,KAAK,CAAC;IACd,KAAK,EAAE,KAAK,CAAC;IACb,SAAS,EAAE,WAAW,CAAC;IACvB,UAAU,CAAC,EAAE,UAAU,CAAC;CACzB,CAAC;AAEF,wBAAgB,aAAa,CAAC,MAAM,EAAE,aAAa,GAAG,WAAW,CAQhE;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,aAAa,EAAE,QAAQ,CAAC,EAAE,cAAc,GAAG,MAAM,CAWtF;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,aAAa,EAAE,KAAK,EAAE,aAAa,GAAG,OAAO,CAKnF;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,aAAa,EAAE,QAAQ,CAAC,EAAE,cAAc,GAAG,MAAM,CAkBnF;AAED,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,aAAa,EACrB,QAAQ,EAAE,cAAc,GACvB,MAAM,GAAG;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,CAM/B;AAED,wBAAgB,aAAa,CAC3B,SAAS,EAAE,cAAc,EACzB,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAC3C,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAC3C,QAAQ,EAAE,SAAS,MAAM,EAAE,EAC3B,WAAW,EAAE,SAAS,cAAc,EAAE,GAAG,SAAS,EAClD,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,GAChB,MAAM,CAmCR;AAsDD,wBAAgB,SAAS,CACvB,SAAS,EAAE,cAAc,EACzB,KAAK,EAAE,SAAS,WAAW,EAAE,EAC7B,QAAQ,CAAC,EAAE,OAAO,GACjB,OAAO,CAgCT;AAkFD,wBAAgB,MAAM,CAAC,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,cAAc,GAAG,OAAO,CAa7E;AAED,wBAAgB,WAAW,CACzB,SAAS,EAAE,cAAc,EACzB,KAAK,EAAE,SAAS,WAAW,EAAE,EAC7B,YAAY,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,OAAO,GAC5C,SAAS,WAAW,EAAE,CAmBxB"}
package/dist/replica.js CHANGED
@@ -4,6 +4,7 @@ import { parseCollection, readCollection, validateCollection, } from './collecti
4
4
  import { overlay, overlayChanged, sameLayerText } from './overlay.js';
5
5
  import { providerRewritten } from './save.js';
6
6
  import { equalValue } from './room-draft.js';
7
+ import { declaredAttachments } from './attachment.js';
7
8
  export function compositionOf(record) {
8
9
  return {
9
10
  model: record.model,
@@ -25,7 +26,7 @@ export function sameMembership(record, other) {
25
26
  JSON.stringify(other.typedSelection?.roots ?? null));
26
27
  }
27
28
  export function readStaged(record, previous) {
28
- if ((record.model.status === 'current' || record.selectionModel.status === 'current') &&
29
+ if ((record.model.status !== 'unavailable' || record.selectionModel.status !== 'unavailable') &&
29
30
  (previous === undefined ||
30
31
  !sameLayerText(record, previous.record) ||
31
32
  record.model !== previous.record.model ||
@@ -45,13 +46,13 @@ export function readReclassified(record, previous) {
45
46
  ? { kind: 'deferred' }
46
47
  : { kind: 'refused', refusal: verdict };
47
48
  }
48
- export function applyReturned(confirmed, documents, revisions, removals, roomVersion) {
49
+ export function applyReturned(confirmed, documents, revisions, removals, attachments, roomVersion, writtenAt) {
49
50
  const nextDocuments = { ...confirmed.record.documents, ...documents };
50
51
  const nextRevisions = { ...confirmed.record.revisions, ...revisions };
51
52
  const nextChanges = { ...confirmed.record.changes };
52
53
  const nextWritten = {
53
54
  ...confirmed.record.written,
54
- ...Object.fromEntries(Object.keys(documents).map((path) => [path, roomVersion])),
55
+ ...Object.fromEntries(Object.keys(documents).map((path) => [path, writtenAt])),
55
56
  };
56
57
  const binaryTargets = new Set(confirmed.record.binaryTargets);
57
58
  for (const [path, revision] of Object.entries(revisions)) {
@@ -73,6 +74,9 @@ export function applyReturned(confirmed, documents, revisions, removals, roomVer
73
74
  changes: nextChanges,
74
75
  written: nextWritten,
75
76
  binaryTargets: [...binaryTargets].sort(),
77
+ attachments: attachments === undefined
78
+ ? confirmed.record.attachments
79
+ : declaredAttachments([attachments]),
76
80
  }, confirmed);
77
81
  }
78
82
  function predicted(confirmed, batches, previous) {
@@ -116,7 +120,7 @@ function predicted(confirmed, batches, previous) {
116
120
  }
117
121
  return { layer, replay };
118
122
  }
119
- export function replicaOf(confirmed, queue, rewriting, previous) {
123
+ export function replicaOf(confirmed, queue, previous) {
120
124
  const batches = Object.freeze(queue.map(immutable));
121
125
  if (batches.length === 0) {
122
126
  return {
@@ -132,16 +136,11 @@ export function replicaOf(confirmed, queue, rewriting, previous) {
132
136
  replay: [],
133
137
  };
134
138
  }
135
- const predictedState = rewriting === 'provider' ? predicted(confirmed, batches, previous) : null;
136
- const layer = rewriting === 'provider'
137
- ? predictedState.layer
138
- : previous === undefined
139
- ? overlay(confirmed.record, batches)
140
- : overlayChanged(confirmed.record, batches, previous.confirmed.record, previous.queue, previous.visible);
139
+ const { layer, replay } = predicted(confirmed, batches, previous);
141
140
  return {
142
141
  confirmed,
143
142
  queue: batches,
144
- replay: predictedState?.replay ?? [],
143
+ replay,
145
144
  visible: {
146
145
  ...layer,
147
146
  collection: parseCollection(layer.documents, compositionOf(confirmed.record), previous === undefined
@@ -245,7 +244,7 @@ export function landed(batch, confirmed) {
245
244
  });
246
245
  });
247
246
  }
248
- export function withdrawals(confirmed, queue, withdrawable, rewriting) {
247
+ export function withdrawals(confirmed, queue, withdrawable) {
249
248
  if (!queue.some(withdrawable))
250
249
  return [];
251
250
  const composition = compositionOf(confirmed.record);
@@ -256,7 +255,7 @@ export function withdrawals(confirmed, queue, withdrawable, rewriting) {
256
255
  withdrawn.push(batch);
257
256
  continue;
258
257
  }
259
- const effective = rewriting === 'provider' && batch.changes.some((change) => change.kind === 'move')
258
+ const effective = batch.changes.some((change) => change.kind === 'move')
260
259
  ? providerRewritten(batch, {
261
260
  ...layer,
262
261
  collection: parseCollection(layer.documents, composition),