@agentshouse/mdruntime 0.1.0-c30ed2b8e04d5 → 0.1.0-c389df724fd2f

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/AGENTS.md +55 -19
  2. package/README.md +7 -6
  3. package/dist/adapter.d.ts +30 -2
  4. package/dist/adapter.d.ts.map +1 -1
  5. package/dist/attached-model.d.ts.map +1 -1
  6. package/dist/attached-model.js +15 -2
  7. package/dist/bootstrap.d.ts.map +1 -1
  8. package/dist/bootstrap.js +1 -0
  9. package/dist/client.d.ts +6 -3
  10. package/dist/client.d.ts.map +1 -1
  11. package/dist/client.js +35 -0
  12. package/dist/connection.d.ts +8 -5
  13. package/dist/connection.d.ts.map +1 -1
  14. package/dist/connection.js +70 -27
  15. package/dist/conversation.d.ts +17 -6
  16. package/dist/conversation.d.ts.map +1 -1
  17. package/dist/conversation.js +167 -78
  18. package/dist/host.d.ts +1 -0
  19. package/dist/host.d.ts.map +1 -1
  20. package/dist/host.js +1 -0
  21. package/dist/index.d.ts +3 -3
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/memory.d.ts +3 -1
  24. package/dist/memory.d.ts.map +1 -1
  25. package/dist/memory.js +20 -1
  26. package/dist/model.d.ts +10 -6
  27. package/dist/model.d.ts.map +1 -1
  28. package/dist/model.js +6 -3
  29. package/dist/paths.d.ts +0 -1
  30. package/dist/paths.d.ts.map +1 -1
  31. package/dist/paths.js +0 -16
  32. package/dist/query.d.ts +1 -1
  33. package/dist/query.d.ts.map +1 -1
  34. package/dist/query.js +71 -33
  35. package/dist/reach.d.ts.map +1 -1
  36. package/dist/reach.js +9 -16
  37. package/dist/react.d.ts +39 -0
  38. package/dist/react.d.ts.map +1 -0
  39. package/dist/react.js +63 -0
  40. package/dist/read-side.d.ts.map +1 -1
  41. package/dist/read-side.js +102 -46
  42. package/dist/replica.d.ts +3 -3
  43. package/dist/replica.d.ts.map +1 -1
  44. package/dist/replica.js +4 -3
  45. package/dist/rows.d.ts +2 -1
  46. package/dist/rows.d.ts.map +1 -1
  47. package/dist/rows.js +75 -38
  48. package/dist/save.d.ts +1 -0
  49. package/dist/save.d.ts.map +1 -1
  50. package/dist/save.js +89 -45
  51. package/dist/store.d.ts +5 -1
  52. package/dist/store.d.ts.map +1 -1
  53. package/dist/store.js +13 -2
  54. package/dist/wire.d.ts +2 -0
  55. package/dist/wire.d.ts.map +1 -1
  56. package/dist/wire.js +2 -0
  57. package/package.json +14 -2
package/dist/query.js CHANGED
@@ -1,3 +1,5 @@
1
+ import { pointerFacts, } from '@agentshouse/mdmodel';
2
+ import { referenceField } from './model.js';
1
3
  import { normalize } from './paths.js';
2
4
  export class QueryError extends Error {
3
5
  constructor(code, at, problem) {
@@ -11,6 +13,7 @@ const RELATED = ['types', 'where', 'pathPrefix', 'field'];
11
13
  const PARTS = ['fields', 'sections', 'preamble', 'markdown', 'backlinks', 'change'];
12
14
  const ORDER = ['by', 'direction', 'nulls', 'stringSort', 'locale', 'localeOptions'];
13
15
  const CALENDAR_DATE = /^(\d{4})-(\d{2})-(\d{2})$/;
16
+ const STRAY_ESCAPE = /~(?![01])/u;
14
17
  export function invalid(at, problem) {
15
18
  throw new QueryError('invalid-query', at, problem);
16
19
  }
@@ -68,23 +71,29 @@ function calendarDate(value) {
68
71
  const date = new Date(Date.UTC(year, month - 1, day));
69
72
  return (date.getUTCFullYear() === year && date.getUTCMonth() === month - 1 && date.getUTCDate() === day);
70
73
  }
71
- function fits(spec, value) {
72
- const item = spec.kind === 'list' ? spec.of : spec;
73
- if (item.kind === 'number')
74
+ function itemOf(facts) {
75
+ return facts.type === 'array' ? facts.items : facts;
76
+ }
77
+ function fits(facts, value) {
78
+ const item = itemOf(facts);
79
+ if (item?.type === undefined)
80
+ return true;
81
+ if (item.values !== undefined)
82
+ return item.values.includes(value);
83
+ if (item.type === 'number')
74
84
  return typeof value === 'number';
75
- if (typeof value !== 'string')
85
+ if (item.type === 'integer')
86
+ return Number.isSafeInteger(value);
87
+ if (item.type === 'boolean')
88
+ return typeof value === 'boolean';
89
+ if (item.type !== 'string' || typeof value !== 'string')
76
90
  return false;
77
- if (item.kind === 'enum')
78
- return item.values.includes(value);
79
- if (item.kind === 'date')
80
- return calendarDate(value);
81
- if (item.kind === 'reference')
91
+ if (item.reference !== undefined)
82
92
  return value !== '' && normalize(value) === value;
93
+ if (item.format === 'date')
94
+ return calendarDate(value);
83
95
  return true;
84
96
  }
85
- function reference(spec) {
86
- return spec.kind === 'reference' || (spec.kind === 'list' && spec.of.kind === 'reference');
87
- }
88
97
  function typing(facts, rooms, types, at) {
89
98
  const read = rooms === null ? facts.rooms : facts.rooms.filter((room) => rooms.includes(room.locator));
90
99
  const acquiring = !facts.listed &&
@@ -102,22 +111,46 @@ function typing(facts, rooms, types, at) {
102
111
  types: types === null ? declared : declared.filter((type) => types.includes(type.name)),
103
112
  };
104
113
  }
105
- function specs(typed, field, at) {
106
- const declared = typed.types.flatMap((type) => Object.hasOwn(type.fields, field) ? [type.fields[field]] : []);
114
+ function pointer(input, at) {
115
+ if (typeof input !== 'string' || !input.startsWith('/') || STRAY_ESCAPE.test(input)) {
116
+ invalid(at, 'expects a JSON Pointer');
117
+ }
118
+ return input;
119
+ }
120
+ function specs(typed, input, at) {
121
+ const field = pointer(input, at);
122
+ const declared = typed.types.flatMap((type) => {
123
+ const spec = pointerFacts(type, field);
124
+ return spec === undefined ? [] : [spec];
125
+ });
107
126
  if (declared.length === 0 && !typed.untyped) {
108
127
  throw new QueryError('unknown-field', at, `no MD Type it reads declares ${field}`);
109
128
  }
110
- return declared;
129
+ if (!declared.some((spec) => spec.conditional))
130
+ return declared;
131
+ return declared.map((spec) => ({
132
+ facts: { prose: false },
133
+ through: several(spec),
134
+ conditional: true,
135
+ }));
136
+ }
137
+ function several(spec) {
138
+ return spec.through || spec.facts.type === 'array';
139
+ }
140
+ function single(spec) {
141
+ return spec.facts.type !== undefined && spec.facts.type !== 'array';
142
+ }
143
+ function every(declared, holds) {
144
+ return declared.length > 0 && declared.every(holds);
111
145
  }
112
146
  function kind(declared, checked, at) {
113
- if (checked === null || declared.length === 0 || declared.some((spec) => fits(spec, checked))) {
147
+ if (checked === null ||
148
+ declared.length === 0 ||
149
+ declared.some((spec) => fits(spec.facts, checked))) {
114
150
  return;
115
151
  }
116
152
  throw new QueryError('wrong-kind', at, `${JSON.stringify(checked)} is not of its field's kind`);
117
153
  }
118
- function listed(declared, list) {
119
- return declared.length > 0 && declared.every((spec) => (spec.kind === 'list') === list);
120
- }
121
154
  function test(declared, input, at) {
122
155
  if (!plain(input)) {
123
156
  const equal = value(input, at);
@@ -131,8 +164,11 @@ function test(declared, input, at) {
131
164
  const stray = operators.find((operator) => !RANGE.includes(operator));
132
165
  if (stray !== undefined)
133
166
  invalid(place(at, stray), 'cannot join a range');
134
- if (listed(declared, true))
167
+ if (every(declared, (spec) => spec.facts.type === 'array'))
135
168
  invalid(at, 'a range on a list');
169
+ if (every(declared, (spec) => spec.facts.type === 'boolean' || spec.facts.type === 'object')) {
170
+ throw new QueryError('wrong-kind', at, 'its field does not compare in order');
171
+ }
136
172
  return {
137
173
  op: 'range',
138
174
  bounds: RANGE.filter((operator) => operators.includes(operator)).map((operator) => {
@@ -165,7 +201,7 @@ function test(declared, input, at) {
165
201
  return { op: 'nin', values: compared };
166
202
  if (compared.includes(null))
167
203
  invalid(where, 'a list holds no null');
168
- if (listed(declared, false))
204
+ if (every(declared, single))
169
205
  invalid(where, 'the field is not a list');
170
206
  return { op: 'all', values: compared };
171
207
  }
@@ -173,7 +209,7 @@ function test(declared, input, at) {
173
209
  if (!Number.isSafeInteger(operand) || operand < 0) {
174
210
  invalid(where, 'expects a nonnegative integer');
175
211
  }
176
- if (listed(declared, false))
212
+ if (every(declared, single))
177
213
  invalid(where, 'the field is not a list');
178
214
  return { op: 'size', size: operand };
179
215
  }
@@ -186,7 +222,7 @@ function condition(typed, input, at) {
186
222
  invalid(at, 'expects an object of field predicates');
187
223
  const conditions = [];
188
224
  for (const name of Object.keys(input).sort()) {
189
- const where = place(at, name);
225
+ const where = name.startsWith('/') ? `${at}${name}` : place(at, name);
190
226
  const entry = input[name];
191
227
  if (name === '$and' || name === '$or') {
192
228
  const nested = list(entry, where).map((item, index) => condition(typed, item, `${where}[${index}]`));
@@ -215,19 +251,19 @@ function scope(facts, rooms, input, at) {
215
251
  pathPrefix: input.pathPrefix === undefined ? null : directory(input.pathPrefix, place(at, 'pathPrefix')),
216
252
  };
217
253
  }
218
- function referenceField(typed, field, at) {
219
- if (typeof field !== 'string')
220
- invalid(at, 'expects a field name');
254
+ function referenceName(typed, field, at) {
221
255
  const declared = specs(typed, field, at);
222
- if (declared.length > 0 && !declared.some(reference))
256
+ if (declared.length > 0 &&
257
+ !declared.some((spec) => spec.conditional || referenceField(spec.facts))) {
223
258
  invalid(at, 'not a reference field');
259
+ }
224
260
  return field;
225
261
  }
226
262
  function hopOf(facts, rooms, root, input) {
227
263
  if (!plain(input))
228
264
  invalid('hop', 'expects an object');
229
265
  known(input, RELATED, 'hop');
230
- const field = referenceField(root, input.field, 'hop.field');
266
+ const field = referenceName(root, input.field, 'hop.field');
231
267
  const { types, where, pathPrefix } = scope(facts, rooms, input, 'hop');
232
268
  return { types, where, pathPrefix, field };
233
269
  }
@@ -236,14 +272,16 @@ function backlinksOf(facts, rooms, input) {
236
272
  invalid('backlinks', 'expects an object');
237
273
  known(input, RELATED, 'backlinks');
238
274
  const { typed, types, where, pathPrefix } = scope(facts, rooms, input, 'backlinks');
239
- const field = input.field === undefined ? null : referenceField(typed, input.field, 'backlinks.field');
275
+ const field = input.field === undefined ? null : referenceName(typed, input.field, 'backlinks.field');
240
276
  return { types, where, pathPrefix, field };
241
277
  }
242
278
  function keyPlan(typed, input, at, path) {
243
279
  if (input === 'room' || input === 'type' || (path && input === 'path'))
244
280
  return { of: input };
245
- if (plain(input) && typeof input.field === 'string' && Object.keys(input).length === 1) {
246
- specs(typed, input.field, place(at, 'field'));
281
+ if (plain(input) && Object.hasOwn(input, 'field') && Object.keys(input).length === 1) {
282
+ if (specs(typed, input.field, place(at, 'field')).some(several)) {
283
+ invalid(place(at, 'field'), 'a path that can hold several values');
284
+ }
247
285
  return { of: 'field', field: input.field };
248
286
  }
249
287
  return invalid(at, path ? 'expects room, path, type or a field' : 'expects room, type or a field');
@@ -301,10 +339,10 @@ function parts(typed, input, at, fields) {
301
339
  return 'all';
302
340
  const named = list(input, at);
303
341
  named.forEach((name, index) => {
304
- if (typeof name !== 'string')
305
- invalid(`${at}[${index}]`, 'expects a name');
306
342
  if (fields)
307
343
  specs(typed, name, `${at}[${index}]`);
344
+ else if (typeof name !== 'string')
345
+ invalid(`${at}[${index}]`, 'expects a name');
308
346
  });
309
347
  return named;
310
348
  }
@@ -1 +1 @@
1
- {"version":3,"file":"reach.d.ts","sourceRoot":"","sources":["../src/reach.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAW,SAAS,EAAa,YAAY,EAAc,MAAM,iBAAiB,CAAC;AAM/F,OAAO,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAI/D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAGhD,MAAM,MAAM,eAAe,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAE/E,MAAM,MAAM,SAAS,GAAG;IACtB,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,QAAQ,CAAC,SAAS,EAAE,SAAS,eAAe,EAAE,CAAC;IAC/C,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;CACjC,CAAC;AAEF,MAAM,MAAM,OAAO,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;AAEjD,MAAM,MAAM,KAAK,GAAG,CAClB,KAAK,EAAE,YAAY,EACnB,KAAK,EAAE,SAAS,OAAO,EAAE,EACzB,OAAO,EAAE,OAAO,KACb,MAAM,IAAI,CAAC;AAEhB,MAAM,MAAM,WAAW,GAAG;IAAE,EAAE,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,KAAK,CAAA;CAAE,CAAC;AAE1D,MAAM,MAAM,YAAY,GAAG,WAAW,GAAG;IAAE,MAAM,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI,GAAG,IAAI,CAAA;CAAE,CAAC;AAEnF,MAAM,MAAM,SAAS,GAAG,WAAW,GAAG;IACpC,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,GAAG,IAAI,GAAG,IAAI,CAAC;IACpD,KAAK,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IACxC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IACvE,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,GAAG,IAAI,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;CACxE,CAAC;AA2BF,wBAAgB,KAAK,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAMpE;AAMD,wBAAgB,OAAO,CACrB,KAAK,EAAE,SAAS,GAAG,IAAI,EACvB,IAAI,EAAE;IAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,cAAc,EAAE;QAAE,KAAK,EAAE,SAAS,MAAM,EAAE,CAAA;KAAE,GAAG,IAAI,CAAA;CAAE,GAC/F;IAAE,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CAU/D;AAcD,wBAAgB,SAAS,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI,GAAG,SAAS,GAAG,IAAI,CASnE;AA2FD,wBAAgB,QAAQ,CAAC,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,GAAG,IAAI,GAAG,YAAY,CAyJlF"}
1
+ {"version":3,"file":"reach.d.ts","sourceRoot":"","sources":["../src/reach.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAW,SAAS,EAAe,YAAY,EAAc,MAAM,iBAAiB,CAAC;AAMjG,OAAO,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAI/D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAGhD,MAAM,MAAM,eAAe,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAE/E,MAAM,MAAM,SAAS,GAAG;IACtB,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,QAAQ,CAAC,SAAS,EAAE,SAAS,eAAe,EAAE,CAAC;IAC/C,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;CACjC,CAAC;AAEF,MAAM,MAAM,OAAO,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;AAEjD,MAAM,MAAM,KAAK,GAAG,CAClB,KAAK,EAAE,YAAY,EACnB,KAAK,EAAE,SAAS,OAAO,EAAE,EACzB,OAAO,EAAE,OAAO,KACb,MAAM,IAAI,CAAC;AAEhB,MAAM,MAAM,WAAW,GAAG;IAAE,EAAE,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,KAAK,CAAA;CAAE,CAAC;AAE1D,MAAM,MAAM,YAAY,GAAG,WAAW,GAAG;IAAE,MAAM,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI,GAAG,IAAI,CAAA;CAAE,CAAC;AAEnF,MAAM,MAAM,SAAS,GAAG,WAAW,GAAG;IACpC,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,GAAG,IAAI,GAAG,IAAI,CAAC;IACpD,KAAK,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IACxC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IACvE,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,GAAG,IAAI,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;CACxE,CAAC;AA2BF,wBAAgB,KAAK,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAMpE;AAMD,wBAAgB,OAAO,CACrB,KAAK,EAAE,SAAS,GAAG,IAAI,EACvB,IAAI,EAAE;IAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,cAAc,EAAE;QAAE,KAAK,EAAE,SAAS,MAAM,EAAE,CAAA;KAAE,GAAG,IAAI,CAAA;CAAE,GAC/F;IAAE,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CAU/D;AAcD,wBAAgB,SAAS,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI,GAAG,SAAS,GAAG,IAAI,CASnE;AA8ED,wBAAgB,QAAQ,CAAC,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,GAAG,IAAI,GAAG,YAAY,CAgKlF"}
package/dist/reach.js CHANGED
@@ -62,22 +62,7 @@ export function planReach(reach) {
62
62
  };
63
63
  }
64
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
- });
65
+ return rooms.filter((room) => wholly(reach, room.locator));
81
66
  }
82
67
  function typesWithin(reach, room, types) {
83
68
  if (room === undefined)
@@ -201,6 +186,14 @@ export function narrowed(runtime, given) {
201
186
  return () => { };
202
187
  });
203
188
  }
189
+ if (topic === 'conversations') {
190
+ return following(() => {
191
+ if (conversing(reach))
192
+ return watch(topic, input, deliver);
193
+ deliver([]);
194
+ return () => { };
195
+ });
196
+ }
204
197
  return watch(topic, input, deliver);
205
198
  };
206
199
  const conversation = () => (conversing(reach) ? md.conversation : NO_CONVERSATION);
@@ -0,0 +1,39 @@
1
+ import type { MdRuntimeClient } from './client.js';
2
+ import type { AppRoom, ConnectionRefusal, SaveResult } from './connection.js';
3
+ import type { Member, 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 type Observed<T> = {
11
+ readonly state: 'loading';
12
+ } | {
13
+ readonly state: 'ready';
14
+ readonly value: T;
15
+ };
16
+ export declare function useObserve(client: Client, topic: 'deployment-facts'): Observed<unknown>;
17
+ export declare function useObserve(client: Client, topic: 'rooms'): Observed<readonly AppRoom[]>;
18
+ export declare function useObserve(client: Client, topic: 'connection-refusal'): Observed<ConnectionRefusal | null>;
19
+ export declare function useObserve(client: Client, topic: 'storage-loss'): Observed<string | null>;
20
+ export declare function useObserve(client: Client, topic: 'sync-status'): Observed<SyncStatus>;
21
+ export declare function useObserve(client: Client, topic: 'service-mode'): Observed<ServiceMode>;
22
+ export declare function useObserve(client: Client, topic: 'member'): Observed<Member | null>;
23
+ export declare function useObserve(client: Client, topic: 'query', query: DocumentQuery): Observed<DocumentResult>;
24
+ export declare function useObserve(client: Client, topic: 'query', query: GroupQuery): Observed<GroupResult>;
25
+ export declare function useObserve(client: Client, topic: 'queue', locator: string): Observed<readonly QueueEntry[]>;
26
+ export declare function useObserve(client: Client, topic: 'room-drafts'): Observed<readonly RoomDraft[]>;
27
+ export declare function useObserve(client: Client, topic: 'presentation'): Observed<unknown>;
28
+ export declare function useObserve(client: Client, topic: 'conversation', conversation: string, options: ConversationOptions): Observed<ConversationView>;
29
+ export declare function useObserve(client: Client, topic: 'conversations'): Observed<readonly ConversationEntry[]>;
30
+ export type Saving = {
31
+ readonly busy: boolean;
32
+ readonly outcome: SaveResult | null;
33
+ readonly failure: Error | null;
34
+ save(room: string, roomDrafts: ReadonlyMap<string, RoomDraftParts>): Promise<SaveResult>;
35
+ };
36
+ export declare function saveRoomDrafts(client: MdRuntimeClient, room: string, roomDrafts: ReadonlyMap<string, RoomDraftParts>): Promise<SaveResult>;
37
+ export declare function useSave(client: MdRuntimeClient): Saving;
38
+ export {};
39
+ //# 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,63 @@
1
+ import { useCallback, useLayoutEffect, useRef, useState } from 'react';
2
+ const LOADING = { state: 'loading' };
3
+ export function useObserve(client, topic, ...input) {
4
+ const key = JSON.stringify([topic, ...input]);
5
+ const [held, setHeld] = useState(null);
6
+ const opened = useRef(input);
7
+ opened.current = input;
8
+ useLayoutEffect(() => {
9
+ if (client === null)
10
+ return;
11
+ let live = true;
12
+ let observation = null;
13
+ const observe = client.observe;
14
+ void observe(topic, ...opened.current).then((open) => {
15
+ if (!live) {
16
+ open.close();
17
+ return;
18
+ }
19
+ observation = open;
20
+ const take = (value) => {
21
+ if (live)
22
+ setHeld({ client, key, observed: { state: 'ready', value } });
23
+ };
24
+ take(open.current());
25
+ open.subscribe(take);
26
+ });
27
+ return () => {
28
+ live = false;
29
+ observation?.close();
30
+ };
31
+ }, [client, key]);
32
+ return held !== null && held.client === client && held.key === key ? held.observed : LOADING;
33
+ }
34
+ const IDLE = { busy: false, outcome: null, failure: null };
35
+ export async function saveRoomDrafts(client, room, roomDrafts) {
36
+ const kept = [];
37
+ for (const [path, parts] of roomDrafts) {
38
+ const prior = await client.roomDraft.read(room, path);
39
+ const write = await client.roomDraft.write(room, path, prior?.revision ?? null, parts);
40
+ if (write.outcome !== 'written' || write.roomDraft === null) {
41
+ throw new Error(`the Room Draft at ${path} was not written`);
42
+ }
43
+ kept.push({ path, revision: write.roomDraft.revision });
44
+ }
45
+ return client.save(room, kept);
46
+ }
47
+ export function useSave(client) {
48
+ const [state, setState] = useState(IDLE);
49
+ const save = useCallback(async (room, roomDrafts) => {
50
+ setState({ busy: true, outcome: null, failure: null });
51
+ try {
52
+ const outcome = await saveRoomDrafts(client, room, roomDrafts);
53
+ setState({ busy: false, outcome, failure: null });
54
+ return outcome;
55
+ }
56
+ catch (error) {
57
+ const failure = error instanceof Error ? error : new Error(String(error));
58
+ setState({ busy: false, outcome: null, failure });
59
+ throw failure;
60
+ }
61
+ }, [client]);
62
+ return { ...state, save };
63
+ }
@@ -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,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"}
1
+ {"version":3,"file":"read-side.d.ts","sourceRoot":"","sources":["../src/read-side.ts"],"names":[],"mappings":"AAqBA,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;AAuDpD,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;AA+UF,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
@@ -1,54 +1,58 @@
1
- import { and, BasicIndex, configureIndexDevMode, count, createCollection, createLiveQueryCollection, deepEquals, eq, gt, gte, inArray, isNull, isUndefined, lt, lte, not, or, toArray, } from '@tanstack/db';
1
+ import { and, BasicIndex, configureIndexDevMode, count, createCollection, createLiveQueryCollection, deepEquals, eq, inArray, or, toArray, } from '@tanstack/db';
2
+ import { ARRAY_INDEX, pointerSegments } from '@agentshouse/mdmodel';
2
3
  import { rowsOf } from './observation.js';
3
4
  import { documentKey } from './rows.js';
4
5
  function every(expressions) {
5
6
  const [first, second, ...rest] = expressions;
6
7
  return second === undefined ? first : and(first, second, ...rest);
7
8
  }
8
- function some(expressions) {
9
- const [first, second, ...rest] = expressions;
10
- return second === undefined ? first : or(first, second, ...rest);
9
+ function same(held, value) {
10
+ return Array.isArray(held) ? held.includes(value) : held === value;
11
11
  }
12
- function equal(field, value) {
13
- return or(eq(field, value), inArray(value, field));
12
+ function ordered(held, [operator, bound]) {
13
+ if (typeof held !== typeof bound || held === null || typeof held === 'object')
14
+ return false;
15
+ const left = held;
16
+ const right = bound;
17
+ if (operator === 'gt')
18
+ return left > right;
19
+ if (operator === 'gte')
20
+ return left >= right;
21
+ if (operator === 'lt')
22
+ return left < right;
23
+ return left <= right;
14
24
  }
15
- function tested(test, field, size, written) {
25
+ function satisfied(test, held) {
16
26
  if (test.op === 'eq')
17
- return test.value === null ? isNull(field) : equal(field, test.value);
18
- if (test.op === 'ne') {
19
- return test.value === null
20
- ? not(isNull(field))
21
- : or(isUndefined(field), isNull(field), not(equal(field, test.value)));
22
- }
23
- if (test.op === 'in' || test.op === 'nin') {
24
- const matches = test.values.length === 0
25
- ? inArray(field, [])
26
- : some(test.values.map((value) => (value === null ? isNull(field) : equal(field, value))));
27
- if (test.op === 'in')
28
- return matches;
29
- return some([
30
- isUndefined(field),
31
- ...(test.values.includes(null) ? [] : [isNull(field)]),
32
- not(matches),
33
- ]);
34
- }
35
- if (test.op === 'range') {
36
- const compare = { gt, gte, lt, lte };
37
- return every(test.bounds.map(([operator, bound]) => compare[operator](field, bound)));
38
- }
39
- if (test.op === 'all') {
40
- return every([not(isUndefined(size)), ...test.values.map((value) => inArray(value, field))]);
41
- }
42
- if (test.op === 'size')
43
- return eq(size, test.size);
44
- return test.exists ? not(isUndefined(written)) : isUndefined(written);
27
+ return same(held, test.value);
28
+ if (test.op === 'ne')
29
+ return !same(held, test.value);
30
+ if (test.op === 'in')
31
+ return test.values.some((value) => same(held, value));
32
+ if (test.op === 'nin')
33
+ return !test.values.some((value) => same(held, value));
34
+ if (test.op === 'range')
35
+ return test.bounds.every((bound) => ordered(held, bound));
36
+ if (test.op === 'all')
37
+ return Array.isArray(held) && test.values.every((value) => same(held, value));
38
+ return Array.isArray(held) && held.length === test.size;
39
+ }
40
+ function tested(test, reached) {
41
+ if (test.op === 'exists')
42
+ return test.exists === (reached !== undefined);
43
+ if (reached === undefined)
44
+ return test.op === 'ne' || test.op === 'nin';
45
+ return reached.some((held) => satisfied(test, held));
45
46
  }
46
47
  function conditioned(condition, row) {
47
48
  if ('every' in condition)
48
- return every(condition.every.map((item) => conditioned(item, row)));
49
+ return condition.every.every((item) => conditioned(item, row));
49
50
  if ('some' in condition)
50
- return some(condition.some.map((item) => conditioned(item, row)));
51
- return tested(condition.test, row.values[condition.field], row.sizes[condition.field], row.fields[condition.field]);
51
+ return condition.some.some((item) => conditioned(item, row));
52
+ return tested(condition.test, row.reached[condition.field]);
53
+ }
54
+ function narrowed(query, where, alias) {
55
+ return where === null ? query : query.fn.where((row) => conditioned(where, row[alias]));
52
56
  }
53
57
  function reached(reach, room, document) {
54
58
  return reach === null
@@ -62,7 +66,6 @@ function scoped(scope, row, rooms, reach) {
62
66
  ...reached(reach, row.room, row.key),
63
67
  ...(scope.types === null ? [] : [inArray(row.type, [...scope.types])]),
64
68
  ...(scope.pathPrefix === null ? [] : [inArray(scope.pathPrefix, row.directories)]),
65
- ...(scope.where === null ? [] : [conditioned(scope.where, row)]),
66
69
  ]);
67
70
  }
68
71
  function keyed(key, row) {
@@ -77,16 +80,17 @@ function live(query) {
77
80
  function related(q, rows, scope, field, kept, reach) {
78
81
  const matched = kept === 'source' ? 'target' : 'source';
79
82
  const from = q.from({ r: rows });
80
- return from
81
- .where(({ r }) => every([eq(r.kind, 'reference'), ...(field === null ? [] : [eq(r.field, field)])]))
83
+ const joined = from
84
+ .where(({ r }) => every([eq(r.kind, 'reference'), ...(field === null ? [] : [inArray(field, r.forms)])]))
82
85
  .innerJoin({ m: rows }, ({ r, m }) => eq(r[matched], m.key))
83
- .where(({ m }) => scoped(scope, m, null, reach))
86
+ .where(({ m }) => scoped(scope, m, null, reach));
87
+ return narrowed(joined, scope.where, 'm')
84
88
  .groupBy(({ r }) => r[kept])
85
89
  .select(({ r }) => ({ key: r[kept] }));
86
90
  }
87
91
  function filtered(q, rows, plan) {
88
92
  const from = q.from({ d: rows });
89
- let query = from.where(({ d }) => scoped(plan.root, d, plan.rooms, plan.reach));
93
+ let query = narrowed(from.where(({ d }) => scoped(plan.root, d, plan.rooms, plan.reach)), plan.root.where, 'd');
90
94
  if (plan.hop !== null) {
91
95
  const hop = related(q, rows, plan.hop, plan.hop.field, 'source', plan.reach);
92
96
  query = query.innerJoin({ h: hop }, ({ d, h }) => eq(d.key, h.key));
@@ -117,8 +121,7 @@ function documents(q, rows, plan, shape) {
117
121
  room: d.room,
118
122
  path: d.path,
119
123
  type: d.type,
120
- ...(shape.fields === 'all' ? { fields: d.fields } : {}),
121
- ...(Array.isArray(shape.fields) ? named('field', shape.fields, d.fields) : {}),
124
+ ...(shape.fields === null ? {} : { fields: d.fields }),
122
125
  ...(shape.sections === 'all' ? { sections: b.sections } : {}),
123
126
  ...(Array.isArray(shape.sections) ? named('section', shape.sections, b.sections) : {}),
124
127
  ...(shape.preamble ? { preamble: b.preamble } : {}),
@@ -176,6 +179,55 @@ function part(row, prefix, names, whole) {
176
179
  return whole;
177
180
  return present(names.map((name, index) => [name, row[`${prefix}${index}`]]));
178
181
  }
182
+ function masked(pointers) {
183
+ const root = { whole: false, members: new Map() };
184
+ for (const pointer of pointers) {
185
+ let mask = root;
186
+ for (const token of pointerSegments(pointer)) {
187
+ const next = mask.members.get(token) ?? { whole: false, members: new Map() };
188
+ mask.members.set(token, next);
189
+ mask = next;
190
+ }
191
+ mask.whole = true;
192
+ }
193
+ return root;
194
+ }
195
+ function merged(left, right) {
196
+ if (left === undefined)
197
+ return right;
198
+ const members = new Map(left.members);
199
+ for (const [token, mask] of right.members)
200
+ members.set(token, merged(members.get(token), mask));
201
+ return { whole: left.whole || right.whole, members };
202
+ }
203
+ function cut(value, mask) {
204
+ if (mask.whole)
205
+ return value;
206
+ if (Array.isArray(value)) {
207
+ const each = {
208
+ whole: false,
209
+ members: new Map([...mask.members].filter(([token]) => !ARRAY_INDEX.test(token))),
210
+ };
211
+ const kept = [];
212
+ value.forEach((item, index) => {
213
+ const part = cut(item, merged(mask.members.get(String(index)), each));
214
+ if (part !== undefined)
215
+ kept[index] = part;
216
+ });
217
+ return kept.length === 0 ? undefined : kept;
218
+ }
219
+ if (typeof value !== 'object' || value === null)
220
+ return undefined;
221
+ const kept = Object.entries(value).flatMap(([key, held]) => {
222
+ const member = mask.members.get(key);
223
+ const part = member === undefined ? undefined : cut(held, member);
224
+ return part === undefined ? [] : [[key, part]];
225
+ });
226
+ return kept.length === 0 ? undefined : Object.fromEntries(kept);
227
+ }
228
+ function selected(fields, pointers) {
229
+ return (cut(fields, masked(pointers)) ?? {});
230
+ }
179
231
  function documentOf(row, shape) {
180
232
  if (row.type === null)
181
233
  return { room: row.room, path: row.path };
@@ -185,7 +237,11 @@ function documentOf(row, shape) {
185
237
  type: row.type,
186
238
  ...(shape.fields === null
187
239
  ? {}
188
- : { fields: part(row, 'field', shape.fields, row.fields) }),
240
+ : {
241
+ fields: shape.fields === 'all'
242
+ ? row.fields
243
+ : selected(row.fields, shape.fields),
244
+ }),
189
245
  ...(shape.sections === null
190
246
  ? {}
191
247
  : {
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 { RoomAttachment } from './adapter.js';
4
+ import type { HeadChange, 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[], attachments: readonly RoomAttachment[] | undefined, roomVersion: string, writtenAt: string): Staged;
38
+ export declare function applyReturned(confirmed: ConfirmedLayer, documents: Readonly<Record<string, string>>, revisions: Readonly<Record<string, string>>, changes: Readonly<Record<string, HeadChange>>, removals: readonly string[], attachments: readonly RoomAttachment[] | undefined, roomVersion: string, writtenAt: string): Staged;
39
39
  export declare function replicaOf(confirmed: ConfirmedLayer, queue: readonly ChangeBatch[], previous?: Replica): Replica;
40
- export declare function landed(batch: ChangeBatch, confirmed: ConfirmedLayer): boolean;
40
+ export declare function landed(batch: ChangeBatch, confirmed: ConfirmedLayer, current?: Readonly<Record<string, string>>): boolean;
41
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;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"}
1
+ {"version":3,"file":"replica.d.ts","sourceRoot":"","sources":["../src/replica.ts"],"names":[],"mappings":"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"}
package/dist/replica.js CHANGED
@@ -46,7 +46,7 @@ export function readReclassified(record, previous) {
46
46
  ? { kind: 'deferred' }
47
47
  : { kind: 'refused', refusal: verdict };
48
48
  }
49
- export function applyReturned(confirmed, documents, revisions, removals, attachments, roomVersion, writtenAt) {
49
+ export function applyReturned(confirmed, documents, revisions, changes, removals, attachments, roomVersion, writtenAt) {
50
50
  const nextDocuments = { ...confirmed.record.documents, ...documents };
51
51
  const nextRevisions = { ...confirmed.record.revisions, ...revisions };
52
52
  const nextChanges = { ...confirmed.record.changes };
@@ -59,6 +59,7 @@ export function applyReturned(confirmed, documents, revisions, removals, attachm
59
59
  if (confirmed.record.revisions[path] !== revision)
60
60
  delete nextChanges[path];
61
61
  }
62
+ Object.assign(nextChanges, changes);
62
63
  for (const path of removals) {
63
64
  delete nextDocuments[path];
64
65
  delete nextRevisions[path];
@@ -229,8 +230,8 @@ function after(changes, index, path) {
229
230
  }
230
231
  return at;
231
232
  }
232
- export function landed(batch, confirmed) {
233
- const layer = confirmed.record;
233
+ export function landed(batch, confirmed, current = {}) {
234
+ const layer = { ...confirmed.record, documents: { ...confirmed.record.documents, ...current } };
234
235
  return batch.changes.every((change, index) => {
235
236
  if (change.kind === 'replace' || (change.kind === 'create' && 'content' in change)) {
236
237
  const at = after(batch.changes, index, change.path);