@agentshouse/mdruntime 0.1.0-c97456d63baba → 0.1.0-c9b1e59e5baeb

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 (133) hide show
  1. package/AGENTS.md +221 -221
  2. package/README.md +8 -8
  3. package/dist/adapter.d.ts +35 -6
  4. package/dist/adapter.d.ts.map +1 -1
  5. package/dist/bootstrap.d.ts +2 -1
  6. package/dist/bootstrap.d.ts.map +1 -1
  7. package/dist/bootstrap.js +5 -1
  8. package/dist/change-set.d.ts +5 -2
  9. package/dist/change-set.d.ts.map +1 -1
  10. package/dist/change-set.js +8 -2
  11. package/dist/chat/earlier.d.ts +10 -0
  12. package/dist/chat/earlier.d.ts.map +1 -0
  13. package/dist/chat/earlier.js +54 -0
  14. package/dist/chat/kit.js +1 -1
  15. package/dist/chat/list.d.ts +2 -1
  16. package/dist/chat/list.d.ts.map +1 -1
  17. package/dist/chat/list.js +40 -4
  18. package/dist/chat/markdown.d.ts +1 -0
  19. package/dist/chat/markdown.d.ts.map +1 -1
  20. package/dist/chat/markdown.js +78 -20
  21. package/dist/chat/message.d.ts.map +1 -1
  22. package/dist/chat/message.js +9 -2
  23. package/dist/chat/messages.d.ts.map +1 -1
  24. package/dist/chat/messages.js +36 -16
  25. package/dist/chat/overlay.d.ts +1 -0
  26. package/dist/chat/overlay.d.ts.map +1 -1
  27. package/dist/chat/overlay.js +10 -0
  28. package/dist/chat/pieces.d.ts +62 -0
  29. package/dist/chat/pieces.d.ts.map +1 -0
  30. package/dist/chat/pieces.js +1110 -0
  31. package/dist/chat/provider.d.ts +1 -0
  32. package/dist/chat/provider.d.ts.map +1 -1
  33. package/dist/chat/provider.js +22 -14
  34. package/dist/chat/question.d.ts +1 -1
  35. package/dist/chat/question.d.ts.map +1 -1
  36. package/dist/chat/question.js +7 -18
  37. package/dist/chat/ready.d.ts +1 -1
  38. package/dist/chat/ready.d.ts.map +1 -1
  39. package/dist/chat/ready.js +1 -1
  40. package/dist/chat/steady.d.ts +3 -0
  41. package/dist/chat/steady.d.ts.map +1 -0
  42. package/dist/chat/steady.js +26 -0
  43. package/dist/chat/style.js +1 -1
  44. package/dist/chat.d.ts +2 -2
  45. package/dist/chat.d.ts.map +1 -1
  46. package/dist/chat.js +2 -2
  47. package/dist/client.d.ts +6 -2
  48. package/dist/client.d.ts.map +1 -1
  49. package/dist/client.js +16 -5
  50. package/dist/collection.d.ts +4 -3
  51. package/dist/collection.d.ts.map +1 -1
  52. package/dist/collection.js +128 -63
  53. package/dist/connection.d.ts +13 -5
  54. package/dist/connection.d.ts.map +1 -1
  55. package/dist/connection.js +506 -228
  56. package/dist/conversation.d.ts +11 -0
  57. package/dist/conversation.d.ts.map +1 -1
  58. package/dist/conversation.js +195 -41
  59. package/dist/entry.d.ts.map +1 -1
  60. package/dist/entry.js +3 -2
  61. package/dist/host.d.ts +9 -7
  62. package/dist/host.d.ts.map +1 -1
  63. package/dist/host.js +58 -9
  64. package/dist/index.d.ts +6 -5
  65. package/dist/index.d.ts.map +1 -1
  66. package/dist/index.js +1 -0
  67. package/dist/memory.d.ts +7 -3
  68. package/dist/memory.d.ts.map +1 -1
  69. package/dist/memory.js +24 -11
  70. package/dist/observation.d.ts +2 -0
  71. package/dist/observation.d.ts.map +1 -1
  72. package/dist/observation.js +9 -3
  73. package/dist/outcome.d.ts +8 -2
  74. package/dist/outcome.d.ts.map +1 -1
  75. package/dist/outcome.js +3 -0
  76. package/dist/overlay.d.ts +5 -4
  77. package/dist/overlay.d.ts.map +1 -1
  78. package/dist/overlay.js +197 -132
  79. package/dist/query.d.ts +27 -1
  80. package/dist/query.d.ts.map +1 -1
  81. package/dist/query.js +36 -7
  82. package/dist/reach.d.ts +1 -2
  83. package/dist/reach.d.ts.map +1 -1
  84. package/dist/reach.js +17 -4
  85. package/dist/react.d.ts +3 -3
  86. package/dist/react.d.ts.map +1 -1
  87. package/dist/react.js +7 -17
  88. package/dist/read-side.d.ts +3 -1
  89. package/dist/read-side.d.ts.map +1 -1
  90. package/dist/read-side.js +256 -69
  91. package/dist/reads.d.ts +11 -0
  92. package/dist/reads.d.ts.map +1 -0
  93. package/dist/reads.js +98 -0
  94. package/dist/refresh.d.ts +4 -4
  95. package/dist/refresh.d.ts.map +1 -1
  96. package/dist/refresh.js +39 -14
  97. package/dist/replica.d.ts +11 -5
  98. package/dist/replica.d.ts.map +1 -1
  99. package/dist/replica.js +79 -41
  100. package/dist/room-draft.d.ts +12 -0
  101. package/dist/room-draft.d.ts.map +1 -1
  102. package/dist/room-draft.js +11 -12
  103. package/dist/rows.d.ts +3 -1
  104. package/dist/rows.d.ts.map +1 -1
  105. package/dist/rows.js +8 -1
  106. package/dist/save.d.ts +4 -2
  107. package/dist/save.d.ts.map +1 -1
  108. package/dist/save.js +195 -111
  109. package/dist/search.d.ts +27 -6
  110. package/dist/search.d.ts.map +1 -1
  111. package/dist/search.js +89 -7
  112. package/dist/seed.d.ts +16 -0
  113. package/dist/seed.d.ts.map +1 -0
  114. package/dist/seed.js +75 -0
  115. package/dist/shared.d.ts +4 -0
  116. package/dist/shared.d.ts.map +1 -1
  117. package/dist/shared.js +63 -33
  118. package/dist/store.d.ts +20 -4
  119. package/dist/store.d.ts.map +1 -1
  120. package/dist/store.js +404 -317
  121. package/dist/turn.d.ts +5 -3
  122. package/dist/turn.d.ts.map +1 -1
  123. package/dist/turn.js +26 -7
  124. package/dist/windows.d.ts +26 -0
  125. package/dist/windows.d.ts.map +1 -0
  126. package/dist/windows.js +93 -0
  127. package/dist/wire.d.ts +16 -0
  128. package/dist/wire.d.ts.map +1 -1
  129. package/dist/wire.js +4 -0
  130. package/package.json +2 -2
  131. package/dist/acquisition.d.ts +0 -14
  132. package/dist/acquisition.d.ts.map +0 -1
  133. package/dist/acquisition.js +0 -24
package/dist/save.js CHANGED
@@ -3,8 +3,10 @@ import { apply, ATTACHMENT, links, parse, pointerSegments, references, relative,
3
3
  import { immutable, queuedFile, departure, } from './change.js';
4
4
  import { available, changedCleanly, collectionOf, documentsOf, governing, referring, validateCollection, } from './collection.js';
5
5
  import { changeBatchIdentity } from './identity.js';
6
- import { overlay } from './overlay.js';
6
+ import { overlaying } from './overlay.js';
7
7
  import { covered, normalize } from './paths.js';
8
+ import { differences } from './shared.js';
9
+ import { finished, ROW_WEIGHT, stepped } from './turn.js';
8
10
  function refuse(code, file, location) {
9
11
  return {
10
12
  kind: 'refused',
@@ -20,7 +22,7 @@ function repointed(document, definition, from, to, moves, settled) {
20
22
  const parsed = parse(document.content, from);
21
23
  if (!parsed.ok)
22
24
  return [];
23
- let next = parsed.document;
25
+ const changes = [];
24
26
  const names = new Set();
25
27
  for (const { pointer, value } of definition === undefined
26
28
  ? []
@@ -32,38 +34,39 @@ function repointed(document, definition, from, to, moves, settled) {
32
34
  const destination = moves.get(named) ?? named;
33
35
  if (target(to, value) === destination)
34
36
  continue;
35
- const applied = apply(next, {
37
+ changes.push({
36
38
  kind: 'reference',
37
39
  pointer,
38
40
  value: retarget(value, relative(to, destination)),
39
41
  prior: value,
40
42
  });
41
- if (applied.outcome !== 'applied')
42
- continue;
43
- next = applied.document;
44
43
  names.add(name);
45
44
  }
45
+ if (changes.length === 0)
46
+ return [];
47
+ const applied = apply(parsed.document, changes);
48
+ if (applied.outcome !== 'applied')
49
+ return [];
50
+ const next = applied.document;
46
51
  return [...names].map((name) => ({ kind: 'field', path: from, name, value: next.fields[name] }));
47
52
  }
48
- function edited(document, path, changes) {
49
- let next = document;
53
+ function edited(document, changes) {
54
+ const parts = new Map();
50
55
  for (const change of changes) {
51
- if (change.path !== path)
52
- continue;
53
- if (change.kind !== 'section' && change.kind !== 'preamble')
54
- continue;
55
- const applied = apply(next, change.kind === 'preamble'
56
- ? { kind: 'preamble', value: change.value, prior: next.preamble }
57
- : {
56
+ if (change.kind === 'preamble')
57
+ parts.set('preamble', { kind: 'preamble', value: change.value, prior: document.preamble });
58
+ if (change.kind === 'section')
59
+ parts.set(`section ${change.name}`, {
58
60
  kind: 'section',
59
61
  name: change.name,
60
62
  value: change.value,
61
- prior: next.sections[change.name],
63
+ prior: document.sections[change.name] ?? '',
62
64
  });
63
- if (applied.outcome === 'applied')
64
- next = applied.document;
65
65
  }
66
- return next;
66
+ if (parts.size === 0)
67
+ return document;
68
+ const applied = apply(document, [...parts.values()]);
69
+ return applied.outcome === 'applied' ? applied.document : document;
67
70
  }
68
71
  function bodies(path, before, after) {
69
72
  const changes = [];
@@ -78,7 +81,7 @@ function bodies(path, before, after) {
78
81
  return changes;
79
82
  }
80
83
  function relinks(path, moved, before, moves) {
81
- let after = before;
84
+ const changes = [];
82
85
  for (const link of links(before, path)) {
83
86
  const destination = moves.get(link.target) ?? link.target;
84
87
  if (destination === link.target && moved === path)
@@ -86,20 +89,23 @@ function relinks(path, moved, before, moves) {
86
89
  const to = relative(moved, destination);
87
90
  if (to === relative(path, link.target))
88
91
  continue;
89
- const applied = apply(after, { kind: 'link', href: link.href, to });
90
- if (applied.outcome === 'refuse') {
91
- return {
92
- kind: 'refused',
93
- refusal: {
94
- code: applied.refusal.rule,
95
- retryable: false,
96
- file: path,
97
- ...(applied.refusal.location === undefined ? {} : { location: applied.refusal.location }),
98
- },
99
- };
100
- }
101
- after = applied.document;
92
+ changes.push({ kind: 'link', href: link.href, to });
102
93
  }
94
+ if (changes.length === 0)
95
+ return { kind: 'changes', changes: [] };
96
+ const applied = apply(before, changes);
97
+ if (applied.outcome === 'refuse') {
98
+ return {
99
+ kind: 'refused',
100
+ refusal: {
101
+ code: applied.refusal.rule,
102
+ retryable: false,
103
+ file: path,
104
+ ...(applied.refusal.location === undefined ? {} : { location: applied.refusal.location }),
105
+ },
106
+ };
107
+ }
108
+ const after = applied.document;
103
109
  if (after === before)
104
110
  return { kind: 'changes', changes: [] };
105
111
  if (before.type === undefined) {
@@ -107,19 +113,14 @@ function relinks(path, moved, before, moves) {
107
113
  }
108
114
  return { kind: 'changes', changes: bodies(path, before, after) };
109
115
  }
110
- function subsumed(merged, path) {
111
- for (let index = merged.length - 1; index >= 0; index -= 1) {
112
- const item = merged[index];
113
- if (item.path !== path)
114
- continue;
115
- if (item.kind === 'section' || item.kind === 'preamble')
116
- merged.splice(index, 1);
117
- }
116
+ function partKey(change) {
117
+ return change.kind === 'section' ? `section ${change.name}` : change.kind;
118
118
  }
119
- function rewrites(changes, visible) {
119
+ function* rewrites(changes, visible) {
120
120
  const moves = new Map();
121
121
  const settled = new Set();
122
- for (const change of changes) {
122
+ const parts = new Map();
123
+ for (const [index, change] of changes.entries()) {
123
124
  if (change.kind === 'move')
124
125
  moves.set(change.path, change.to);
125
126
  if (change.kind === 'remove' ||
@@ -130,6 +131,13 @@ function rewrites(changes, visible) {
130
131
  }
131
132
  if (change.kind === 'field')
132
133
  settled.add(`${change.path} ${change.name}`);
134
+ if (isPart(change)) {
135
+ const held = parts.get(change.path);
136
+ if (held === undefined)
137
+ parts.set(change.path, [index]);
138
+ else
139
+ held.push(index);
140
+ }
133
141
  }
134
142
  if (moves.size === 0)
135
143
  return { kind: 'changes', changes };
@@ -141,43 +149,57 @@ function rewrites(changes, visible) {
141
149
  const moved = moves.get(path) ?? path;
142
150
  const definition = definitionOf(governing(visible.collection.composition, path), document.type);
143
151
  added.push(...repointed(document, definition, path, moved, moves, settled));
144
- const relinked = relinks(path, moved, edited(document, path, merged), moves);
152
+ const own = parts.get(path) ?? [];
153
+ const relinked = relinks(path, moved, edited(document, own.map((index) => merged[index])), moves);
145
154
  if (relinked.kind === 'refused')
146
155
  return relinked;
156
+ const keyed = new Map();
157
+ for (const index of own) {
158
+ const key = partKey(merged[index]);
159
+ if (!keyed.has(key))
160
+ keyed.set(key, index);
161
+ }
147
162
  for (const change of relinked.changes) {
148
163
  if (change.kind === 'replace') {
149
- subsumed(merged, path);
164
+ for (const index of own) {
165
+ const kind = merged[index]?.kind;
166
+ if (kind === 'section' || kind === 'preamble')
167
+ merged[index] = null;
168
+ }
150
169
  added.push(change);
151
170
  continue;
152
171
  }
153
- const index = merged.findIndex((item) => item.kind === change.kind &&
154
- item.path === path &&
155
- (change.kind !== 'section' || (item.kind === 'section' && item.name === change.name)));
156
- if (index === -1)
172
+ const index = keyed.get(partKey(change));
173
+ if (index === undefined)
157
174
  added.push(change);
158
175
  else
159
176
  merged[index] = change;
160
177
  }
178
+ yield ROW_WEIGHT + visible.collection.text[path].length;
161
179
  }
162
- return { kind: 'changes', changes: [...added, ...merged] };
180
+ return {
181
+ kind: 'changes',
182
+ changes: [...added, ...merged.filter((change) => change !== null)],
183
+ };
163
184
  }
164
- function anticipated(changes, preconditions, rewritten, visible) {
165
- const derived = rewritten.filter((change) => !changes.includes(change));
185
+ function* anticipated(changes, preconditions, rewritten, visible) {
186
+ const given = new Set(changes);
187
+ const derived = rewritten.filter((change) => !given.has(change));
166
188
  return {
167
189
  changes: rewritten,
168
- preconditions: [...preconditionsOf(derived, visible, []), ...preconditions],
190
+ preconditions: [...(yield* preconditionsOf(derived, visible, [])), ...preconditions],
169
191
  };
170
192
  }
171
- export function providerRewritten(batch, visible) {
172
- const rewritten = rewrites(batch.changes, visible);
193
+ export function* providerRewritten(batch, visible) {
194
+ const rewritten = yield* rewrites(batch.changes, visible);
173
195
  if (rewritten.kind === 'refused')
174
196
  return batch;
175
197
  return {
176
198
  ...batch,
177
- ...anticipated(batch.changes, batch.preconditions, rewritten.changes, visible),
199
+ ...(yield* anticipated(batch.changes, batch.preconditions, rewritten.changes, visible)),
178
200
  };
179
201
  }
180
- function incoming(collection, removed, candidates) {
202
+ function* incoming(collection, removed, candidates) {
181
203
  const found = new Map();
182
204
  const note = (target, file) => {
183
205
  if (!removed.has(target))
@@ -186,13 +208,14 @@ function incoming(collection, removed, candidates) {
186
208
  referrers.add(file);
187
209
  found.set(target, referrers);
188
210
  };
189
- const read = candidates === null
190
- ? documentsOf(collection)
191
- : [...new Set(candidates)].sort().flatMap((path) => {
211
+ const read = function* (paths) {
212
+ for (const path of [...new Set(paths)].sort()) {
192
213
  const document = collection.document(path);
193
- return document === undefined ? [] : [[path, document]];
194
- });
195
- for (const [path, document] of read) {
214
+ if (document !== undefined)
215
+ yield [path, document];
216
+ }
217
+ };
218
+ for (const [path, document] of candidates === null ? documentsOf(collection) : read(candidates)) {
196
219
  const definition = definitionOf(governing(collection.composition, path), document.type);
197
220
  for (const { value, to } of definition === undefined ? [] : references(document, definition)) {
198
221
  if (to.includes(ATTACHMENT))
@@ -201,6 +224,7 @@ function incoming(collection, removed, candidates) {
201
224
  if (named !== null)
202
225
  note(named, path);
203
226
  }
227
+ yield ROW_WEIGHT + collection.text[path].length;
204
228
  }
205
229
  const [first] = [...found.keys()].sort();
206
230
  if (first === undefined)
@@ -284,7 +308,7 @@ function guard(changes, input) {
284
308
  continue;
285
309
  }
286
310
  if (change.kind === 'replace') {
287
- if (!input.local && input.visible.revisions[change.path] === undefined) {
311
+ if (unrevised(input, change.path)) {
288
312
  return refuse('unknown-revision', change.path);
289
313
  }
290
314
  continue;
@@ -298,6 +322,22 @@ function guard(changes, input) {
298
322
  function unrevised(input, path) {
299
323
  return (!input.local && input.queued?.has(path) !== true && input.visible.revisions[path] === undefined);
300
324
  }
325
+ export function queuedDocuments(queue) {
326
+ const paths = new Set();
327
+ for (const batch of queue) {
328
+ for (const change of batch.changes) {
329
+ if (change.kind === 'binary')
330
+ continue;
331
+ if (change.kind === 'remove')
332
+ paths.delete(change.path);
333
+ else if (change.kind !== 'move')
334
+ paths.add(change.path);
335
+ else if (paths.delete(change.path))
336
+ paths.add(change.to);
337
+ }
338
+ }
339
+ return paths;
340
+ }
301
341
  function identity(precondition) {
302
342
  const part = precondition.kind === 'field' || precondition.kind === 'section' ? precondition.name : '';
303
343
  return `${precondition.kind} ${precondition.path} ${part}`;
@@ -315,7 +355,7 @@ function sameTarget(precondition, change) {
315
355
  return ((precondition.kind !== 'field' && precondition.kind !== 'section') ||
316
356
  ((change.kind === 'field' || change.kind === 'section') && precondition.name === change.name));
317
357
  }
318
- function preconditionsOf(changes, visible, bases) {
358
+ function* preconditionsOf(changes, visible, bases) {
319
359
  const preconditions = [];
320
360
  const seen = new Set();
321
361
  const add = (precondition) => {
@@ -326,6 +366,7 @@ function preconditionsOf(changes, visible, bases) {
326
366
  preconditions.push(precondition);
327
367
  };
328
368
  for (const change of changes) {
369
+ yield ROW_WEIGHT;
329
370
  if (change.kind === 'field' || change.kind === 'section' || change.kind === 'preamble') {
330
371
  const based = bases.find((base) => sameTarget(base, change));
331
372
  if (based !== undefined) {
@@ -373,7 +414,7 @@ function preconditionsOf(changes, visible, bases) {
373
414
  function depth(path) {
374
415
  return path.split('/').length;
375
416
  }
376
- function exceeded(limits, changes, effective, resulting) {
417
+ function* exceeding(limits, changes, effective, resulting) {
377
418
  const written = effective.map((change) => (change.kind === 'move' ? change.to : change.path));
378
419
  const created = changes
379
420
  .filter((change) => change.kind === 'create' || change.kind === 'move' || change.kind === 'binary')
@@ -390,9 +431,13 @@ function exceeded(limits, changes, effective, resulting) {
390
431
  return refuse('provider-limit', undefined, limit.name);
391
432
  }
392
433
  if (limit.name === 'document-size') {
393
- const large = written.find((path) => new TextEncoder().encode(resulting.documents[path] ?? '').byteLength > value);
394
- if (large !== undefined)
395
- return refuse('provider-limit', large, limit.name);
434
+ for (const path of written) {
435
+ const text = resulting.documents[path] ?? '';
436
+ if (new TextEncoder().encode(text).byteLength > value) {
437
+ return refuse('provider-limit', path, limit.name);
438
+ }
439
+ yield ROW_WEIGHT + text.length;
440
+ }
396
441
  }
397
442
  if (limit.name === 'path-length') {
398
443
  const long = created.find((path) => path.length > value);
@@ -422,7 +467,7 @@ function relativeField(path, name, value, handle, definition) {
422
467
  !opened.ok ||
423
468
  !JSON.stringify(value ?? null).includes(`/rooms/${handle}/`))
424
469
  return value;
425
- const held = apply(opened.document, { kind: 'field', name, value, prior: undefined });
470
+ const held = apply(opened.document, [{ kind: 'field', name, value, prior: undefined }]);
426
471
  if (held.outcome !== 'applied')
427
472
  return value;
428
473
  const result = relativize(held.document, path, handle, definition);
@@ -458,7 +503,7 @@ function relativeChanges(input) {
458
503
  return { ...change, fields, sections };
459
504
  });
460
505
  }
461
- function unmodeledType(collection, composition) {
506
+ function* unmodeledType(collection, composition) {
462
507
  if (available(composition))
463
508
  return false;
464
509
  for (const [path, document] of documentsOf(collection)) {
@@ -466,6 +511,7 @@ function unmodeledType(collection, composition) {
466
511
  document.type !== 'any' &&
467
512
  governing(composition, path).status === 'unavailable')
468
513
  return true;
514
+ yield ROW_WEIGHT + collection.text[path].length;
469
515
  }
470
516
  return false;
471
517
  }
@@ -478,29 +524,44 @@ function isPart(change) {
478
524
  function touched(change) {
479
525
  return change.kind === 'move' ? [change.path, change.to] : [change.path];
480
526
  }
481
- function partApplied(content, path, change) {
527
+ function partsApplied(content, path, changes) {
482
528
  const parsed = parse(content, path);
483
529
  if (!parsed.ok)
484
530
  return null;
485
531
  const document = parsed.document;
486
- const applied = apply(document, change.kind === 'field'
487
- ? {
488
- kind: 'field',
489
- name: change.name,
490
- value: change.value,
491
- prior: document.fields[change.name],
492
- }
493
- : change.kind === 'section'
494
- ? {
532
+ const parts = new Map();
533
+ for (const change of changes) {
534
+ if (change.kind === 'field')
535
+ parts.set(`field ${change.name}`, {
536
+ kind: 'field',
537
+ name: change.name,
538
+ value: change.value,
539
+ prior: document.fields[change.name],
540
+ });
541
+ else if (change.kind === 'section')
542
+ parts.set(`section ${change.name}`, {
495
543
  kind: 'section',
496
544
  name: change.name,
497
545
  value: change.value,
498
- prior: document.sections[change.name],
499
- }
500
- : { kind: 'preamble', value: change.value, prior: document.preamble });
546
+ prior: document.sections[change.name] ?? '',
547
+ });
548
+ else
549
+ parts.set('preamble', { kind: 'preamble', value: change.value, prior: document.preamble });
550
+ }
551
+ const applied = apply(document, [...parts.values()]);
501
552
  return applied.outcome === 'applied' ? serialize(applied.document) : null;
502
553
  }
503
- function folded(changes, change, appending) {
554
+ function settledParts(changes, deferred) {
555
+ for (const [held, parts] of deferred) {
556
+ const content = partsApplied(held.content, held.path, parts);
557
+ if (content === null)
558
+ return held.path;
559
+ changes[changes.indexOf(held)] = { ...held, content };
560
+ }
561
+ deferred.clear();
562
+ return null;
563
+ }
564
+ function folded(changes, change, appending, deferred) {
504
565
  const held = changes.find((item) => item.path === change.path && (item.kind === 'create' || item.kind === 'replace'));
505
566
  const whole = held === undefined ? -1 : changes.indexOf(held);
506
567
  const occupied = changes.some((item) => !isPart(item) && item !== held && touched(item).includes(change.path));
@@ -521,10 +582,11 @@ function folded(changes, change, appending) {
521
582
  return 'appended';
522
583
  }
523
584
  if ('content' in held) {
524
- const content = partApplied(held.content, held.path, change);
525
- if (content === null)
526
- return 'refused';
527
- changes[whole] = { ...held, content };
585
+ const parts = deferred.get(held);
586
+ if (parts === undefined)
587
+ deferred.set(held, [change]);
588
+ else
589
+ parts.push(change);
528
590
  return 'folded';
529
591
  }
530
592
  if (change.kind === 'preamble')
@@ -556,16 +618,20 @@ function folded(changes, change, appending) {
556
618
  return 'appended';
557
619
  }
558
620
  function relinked(batch, move, before, after, appending) {
559
- const derived = rewrites([move], after);
621
+ const derived = finished(rewrites([move], after));
560
622
  if (derived.kind === 'refused')
561
623
  return derived;
562
624
  const changes = [...batch.changes];
563
625
  const appended = [];
564
626
  let changed = false;
627
+ const deferred = new Map();
565
628
  for (const change of derived.changes) {
566
629
  if (change === move)
567
630
  continue;
568
- const outcome = folded(changes, change, appending);
631
+ const unsettled = isPart(change) ? null : settledParts(changes, deferred);
632
+ if (unsettled !== null)
633
+ return unqueueable(unsettled);
634
+ const outcome = folded(changes, change, appending, deferred);
569
635
  if (outcome === 'refused')
570
636
  return unqueueable(change.path);
571
637
  if (outcome === 'appended')
@@ -573,6 +639,9 @@ function relinked(batch, move, before, after, appending) {
573
639
  if (outcome !== 'skipped')
574
640
  changed = true;
575
641
  }
642
+ const unsettled = settledParts(changes, deferred);
643
+ if (unsettled !== null)
644
+ return unqueueable(unsettled);
576
645
  if (!changed)
577
646
  return { kind: 'relinked', batch };
578
647
  const held = new Set(batch.preconditions.map(identity));
@@ -584,7 +653,7 @@ function relinked(batch, move, before, after, appending) {
584
653
  changes,
585
654
  preconditions: [
586
655
  ...batch.preconditions,
587
- ...preconditionsOf(appended, before, []).filter((precondition) => !held.has(identity(precondition))),
656
+ ...finished(preconditionsOf(appended, before, [])).filter((precondition) => !held.has(identity(precondition))),
588
657
  ],
589
658
  }),
590
659
  };
@@ -706,10 +775,17 @@ export function joined(queue, owner, extra) {
706
775
  if (clash !== undefined)
707
776
  return unqueueable(clash);
708
777
  const changes = [...owner.changes];
778
+ const deferred = new Map();
709
779
  for (const change of extra.changes) {
710
- if (folded(changes, change, true) === 'refused')
780
+ const unsettled = isPart(change) ? null : settledParts(changes, deferred);
781
+ if (unsettled !== null)
782
+ return unqueueable(unsettled);
783
+ if (folded(changes, change, true, deferred) === 'refused')
711
784
  return unqueueable(change.path);
712
785
  }
786
+ const unsettled = settledParts(changes, deferred);
787
+ if (unsettled !== null)
788
+ return unqueueable(unsettled);
713
789
  const held = new Set(owner.preconditions.map(covering));
714
790
  const written = new Set(owner.changes.flatMap((change) => change.kind === 'create' || change.kind === 'replace' ? [change.path] : []));
715
791
  const preconditions = [
@@ -730,37 +806,35 @@ export function joined(queue, owner, extra) {
730
806
  }
731
807
  export function limitedIn(queue, batch, limits, state) {
732
808
  const at = queue.indexOf(batch);
733
- const limited = exceeded(limits, batch.changes, providerRewritten(batch, state(queue.slice(0, at))).changes, state(queue.slice(0, at + 1)));
809
+ const limited = finished(exceeding(limits, batch.changes, finished(providerRewritten(batch, state(queue.slice(0, at)))).changes, state(queue.slice(0, at + 1))));
734
810
  return limited?.kind === 'refused' ? limited.refusal : null;
735
811
  }
736
- export function buildBatch(input) {
812
+ function* batching(input) {
737
813
  const changes = relativeChanges(input);
738
814
  const structural = changes.find((change) => change.kind === 'move' || change.kind === 'remove');
739
- if (structural !== undefined && unmodeledType(input.visible.collection, input.composition))
815
+ if (structural !== undefined &&
816
+ (yield* unmodeledType(input.visible.collection, input.composition)))
740
817
  return refuse('model-unavailable', structural.path);
741
818
  if (changes.length === 0)
742
819
  return refuse('no-change');
743
- const rewritten = rewrites(changes, input.visible);
820
+ const rewritten = yield* rewrites(changes, input.visible);
744
821
  if (rewritten.kind === 'refused')
745
822
  return rewritten;
746
823
  const refusal = guard(changes, input);
747
824
  if (refusal !== null)
748
825
  return refusal;
749
- const preconditions = preconditionsOf(changes, input.visible, input.bases);
750
- const predicted = anticipated(changes, preconditions, rewritten.changes, input.visible);
751
- const resulting = overlay(input.visible, [
826
+ const preconditions = yield* preconditionsOf(changes, input.visible, input.bases);
827
+ const predicted = yield* anticipated(changes, preconditions, rewritten.changes, input.visible);
828
+ const resulting = yield* overlaying(input.visible, [
752
829
  { id: '', baseRoomVersion: input.baseRoomVersion, ...predicted },
753
830
  ]);
754
- const clean = input.clean !== undefined &&
755
- input.clean.text === input.visible.documents &&
756
- input.clean.binaries === input.visible.binaries &&
757
- available(input.composition)
758
- ? input.clean
759
- : null;
831
+ const clean = input.clean !== undefined && available(input.composition) ? input.clean : null;
760
832
  const removed = new Set(changes.filter((change) => change.kind === 'remove').map((change) => change.path));
761
833
  if (removed.size > 0) {
762
834
  const referrers = clean === null ? null : referring(clean, input.composition, removed);
763
- const referenced = incoming(collectionOf(resulting.documents, input.composition), removed, referrers === null ? null : [...referrers, ...predicted.changes.flatMap(affected)]);
835
+ const referenced = yield* incoming(collectionOf(resulting.documents, input.composition), removed, clean === null || referrers === null
836
+ ? null
837
+ : [...referrers, ...(yield* differences(clean.text, resulting.documents))]);
764
838
  if (referenced !== null)
765
839
  return referenced;
766
840
  }
@@ -771,15 +845,22 @@ export function buildBatch(input) {
771
845
  return parsed.ok && parsed.document.type !== undefined && parsed.document.type !== 'any';
772
846
  };
773
847
  const touched = [...new Set(predicted.changes.flatMap(affected))];
774
- const typedPaths = touched.filter((path) => typed(input.visible.documents[path]) || typed(resulting.documents[path]));
848
+ const typedPaths = [];
849
+ for (const path of touched) {
850
+ const before = input.visible.documents[path];
851
+ const after = resulting.documents[path];
852
+ if (typed(before) || typed(after))
853
+ typedPaths.push(path);
854
+ yield ROW_WEIGHT + (before?.length ?? 0) + (after?.length ?? 0);
855
+ }
775
856
  const needsModel = typedPaths.length > 0;
776
857
  const withoutModel = typedPaths.find((path) => governing(input.composition, path).status === 'unavailable');
777
858
  if (withoutModel !== undefined)
778
859
  return refuse('model-unavailable', withoutModel);
779
860
  const verdict = clean !== null &&
780
- changedCleanly({ text: resulting.documents, binaries: resulting.binaries }, clean, input.composition)
861
+ (yield* changedCleanly({ text: resulting.documents, binaries: resulting.binaries }, clean, input.composition))
781
862
  ? { verdict: 'accept' }
782
- : validateCollection(needsModel
863
+ : yield* validateCollection(needsModel
783
864
  ? resulting.documents
784
865
  : Object.fromEntries(touched.flatMap((path) => resulting.documents[path] === undefined
785
866
  ? []
@@ -796,7 +877,7 @@ export function buildBatch(input) {
796
877
  },
797
878
  };
798
879
  }
799
- const limited = exceeded(input.providerLimits, changes, predicted.changes, resulting);
880
+ const limited = yield* exceeding(input.providerLimits, changes, predicted.changes, resulting);
800
881
  if (limited !== null)
801
882
  return limited;
802
883
  const validated = typedPaths
@@ -822,3 +903,6 @@ export function buildBatch(input) {
822
903
  }),
823
904
  };
824
905
  }
906
+ export function buildBatch(input) {
907
+ return stepped(batching(input));
908
+ }
package/dist/search.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import type { ProviderRefusal } from './adapter.js';
1
2
  import type { ChangeSet } from './change-set.js';
2
3
  import { type CoveredRoom, type Filter, type PendingRoom } from './query.js';
3
4
  import type { Open } from './open.js';
@@ -5,8 +6,11 @@ export type DocumentPart = Open<'fields' | 'sections' | 'preamble' | 'path'>;
5
6
  export type SearchRequest = {
6
7
  text: string;
7
8
  documents?: Filter;
9
+ collection?: true;
8
10
  conversations?: readonly string[];
9
11
  limit?: number;
12
+ after?: string;
13
+ total?: true;
10
14
  };
11
15
  export type DocumentHit = {
12
16
  room: string;
@@ -31,16 +35,28 @@ export type PendingConversation = {
31
35
  conversation: string;
32
36
  awaits: Open<'index'>;
33
37
  };
34
- export type SearchPending = {
35
- pending: readonly (PendingRoom | PendingConversation)[];
36
- };
37
38
  export type SearchHits = {
38
39
  documents: readonly DocumentHit[];
39
40
  messages: readonly MessageHit[];
40
41
  };
41
- export type SearchResult = (SearchHits & {
42
+ export type SearchWindow = SearchHits & {
43
+ next?: string;
44
+ total?: {
45
+ documents: number;
46
+ messages: number;
47
+ };
48
+ };
49
+ export type SearchPending = SearchHits & {
50
+ pending: readonly (PendingRoom | PendingConversation)[];
51
+ coverage: SearchCoverage;
52
+ };
53
+ export type SearchUndeclared = ProviderRefusal & {
54
+ kind: 'undeclared';
55
+ };
56
+ export type SearchResult = (SearchWindow & {
42
57
  coverage: SearchCoverage;
43
- }) | SearchPending;
58
+ }) | SearchPending | SearchUndeclared;
59
+ export declare function undeclaredSearch(): SearchUndeclared;
44
60
  export type SearchedDocument = {
45
61
  room: string;
46
62
  path: string;
@@ -58,11 +74,15 @@ export type SearchSource = {
58
74
  room(room: string): readonly string[];
59
75
  document(room: string, path: string): SearchedDocument | null;
60
76
  };
77
+ type HitCursor = readonly [number, string, string] | readonly [] | null;
61
78
  export type SearchPlan = {
62
79
  text: string;
63
80
  documents: unknown;
81
+ collection: boolean;
64
82
  conversations: readonly string[] | null;
65
83
  limit: number | null;
84
+ after: readonly [HitCursor, HitCursor] | null;
85
+ total: boolean;
66
86
  };
67
87
  export type SearchIndex = {
68
88
  generation(): string;
@@ -77,8 +97,9 @@ export type SearchIndex = {
77
97
  search(plan: SearchPlan, documents: readonly {
78
98
  room: string;
79
99
  path: string;
80
- }[] | null, conversations: readonly string[]): SearchHits;
100
+ }[] | null, conversations: readonly string[], rooms: ReadonlySet<string> | null, selections: ReadonlyMap<string, readonly string[]> | null): SearchWindow;
81
101
  };
82
102
  export declare function searchPlanOf(input: unknown): SearchPlan;
83
103
  export declare function createSearchIndex(source: SearchSource): SearchIndex;
104
+ export {};
84
105
  //# sourceMappingURL=search.d.ts.map