@agentshouse/mdruntime 0.1.0-c2647c1a232dd → 0.1.0-c2aaa9c0e3ca4

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 (166) hide show
  1. package/AGENTS.md +152 -91
  2. package/README.md +20 -10
  3. package/dist/adapter.d.ts +48 -14
  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/attempt.d.ts +16 -0
  8. package/dist/attempt.d.ts.map +1 -0
  9. package/dist/attempt.js +57 -0
  10. package/dist/binary.d.ts +23 -0
  11. package/dist/binary.d.ts.map +1 -0
  12. package/dist/binary.js +8 -0
  13. package/dist/bootstrap.d.ts +17 -5
  14. package/dist/bootstrap.d.ts.map +1 -1
  15. package/dist/bootstrap.js +88 -38
  16. package/dist/change-set.d.ts.map +1 -1
  17. package/dist/change-set.js +19 -12
  18. package/dist/change.d.ts +15 -0
  19. package/dist/change.d.ts.map +1 -1
  20. package/dist/change.js +26 -1
  21. package/dist/chat/acts.d.ts +34 -0
  22. package/dist/chat/acts.d.ts.map +1 -0
  23. package/dist/chat/acts.js +36 -0
  24. package/dist/chat/context.d.ts +3 -0
  25. package/dist/chat/context.d.ts.map +1 -0
  26. package/dist/chat/context.js +10 -0
  27. package/dist/chat/input.d.ts +9 -0
  28. package/dist/chat/input.d.ts.map +1 -0
  29. package/dist/chat/input.js +99 -0
  30. package/dist/chat/kit.d.ts +60 -0
  31. package/dist/chat/kit.d.ts.map +1 -0
  32. package/dist/chat/kit.js +123 -0
  33. package/dist/chat/lasting.d.ts +6 -0
  34. package/dist/chat/lasting.d.ts.map +1 -0
  35. package/dist/chat/lasting.js +19 -0
  36. package/dist/chat/list.d.ts +20 -0
  37. package/dist/chat/list.d.ts.map +1 -0
  38. package/dist/chat/list.js +59 -0
  39. package/dist/chat/markdown.d.ts +19 -0
  40. package/dist/chat/markdown.d.ts.map +1 -0
  41. package/dist/chat/markdown.js +187 -0
  42. package/dist/chat/message.d.ts +6 -0
  43. package/dist/chat/message.d.ts.map +1 -0
  44. package/dist/chat/message.js +192 -0
  45. package/dist/chat/messages.d.ts +21 -0
  46. package/dist/chat/messages.d.ts.map +1 -0
  47. package/dist/chat/messages.js +151 -0
  48. package/dist/chat/name.d.ts +6 -0
  49. package/dist/chat/name.d.ts.map +1 -0
  50. package/dist/chat/name.js +37 -0
  51. package/dist/chat/opened.d.ts +11 -0
  52. package/dist/chat/opened.d.ts.map +1 -0
  53. package/dist/chat/opened.js +17 -0
  54. package/dist/chat/overlay.d.ts +31 -0
  55. package/dist/chat/overlay.d.ts.map +1 -0
  56. package/dist/chat/overlay.js +97 -0
  57. package/dist/chat/parts.d.ts +57 -0
  58. package/dist/chat/parts.d.ts.map +1 -0
  59. package/dist/chat/parts.js +119 -0
  60. package/dist/chat/process.d.ts +20 -0
  61. package/dist/chat/process.d.ts.map +1 -0
  62. package/dist/chat/process.js +32 -0
  63. package/dist/chat/provider.d.ts +63 -0
  64. package/dist/chat/provider.d.ts.map +1 -0
  65. package/dist/chat/provider.js +645 -0
  66. package/dist/chat/question.d.ts +9 -0
  67. package/dist/chat/question.d.ts.map +1 -0
  68. package/dist/chat/question.js +80 -0
  69. package/dist/chat/questions.d.ts +31 -0
  70. package/dist/chat/questions.d.ts.map +1 -0
  71. package/dist/chat/questions.js +111 -0
  72. package/dist/chat/read.d.ts +5 -0
  73. package/dist/chat/read.d.ts.map +1 -0
  74. package/dist/chat/read.js +13 -0
  75. package/dist/chat/ready.d.ts +33 -0
  76. package/dist/chat/ready.d.ts.map +1 -0
  77. package/dist/chat/ready.js +91 -0
  78. package/dist/chat/style.d.ts +4 -0
  79. package/dist/chat/style.d.ts.map +1 -0
  80. package/dist/chat/style.js +302 -0
  81. package/dist/chat.d.ts +14 -0
  82. package/dist/chat.d.ts.map +1 -0
  83. package/dist/chat.js +8 -0
  84. package/dist/client.d.ts +14 -5
  85. package/dist/client.d.ts.map +1 -1
  86. package/dist/client.js +65 -15
  87. package/dist/collection.d.ts +6 -11
  88. package/dist/collection.d.ts.map +1 -1
  89. package/dist/collection.js +56 -19
  90. package/dist/connection.d.ts +21 -9
  91. package/dist/connection.d.ts.map +1 -1
  92. package/dist/connection.js +470 -142
  93. package/dist/conversation.d.ts +11 -3
  94. package/dist/conversation.d.ts.map +1 -1
  95. package/dist/conversation.js +47 -7
  96. package/dist/expectation.d.ts +21 -0
  97. package/dist/expectation.d.ts.map +1 -0
  98. package/dist/expectation.js +70 -0
  99. package/dist/failure.d.ts +13 -0
  100. package/dist/failure.d.ts.map +1 -0
  101. package/dist/failure.js +19 -0
  102. package/dist/host.d.ts +1 -0
  103. package/dist/host.d.ts.map +1 -1
  104. package/dist/host.js +9 -8
  105. package/dist/identity.d.ts +3 -0
  106. package/dist/identity.d.ts.map +1 -1
  107. package/dist/identity.js +13 -1
  108. package/dist/index.d.ts +9 -4
  109. package/dist/index.d.ts.map +1 -1
  110. package/dist/index.js +3 -0
  111. package/dist/memory.d.ts +4 -3
  112. package/dist/memory.d.ts.map +1 -1
  113. package/dist/memory.js +60 -20
  114. package/dist/model.d.ts +10 -2
  115. package/dist/model.d.ts.map +1 -1
  116. package/dist/model.js +2 -0
  117. package/dist/open.d.ts +2 -0
  118. package/dist/open.d.ts.map +1 -0
  119. package/dist/open.js +1 -0
  120. package/dist/outcome.d.ts +28 -3
  121. package/dist/outcome.d.ts.map +1 -1
  122. package/dist/outcome.js +41 -1
  123. package/dist/overlay.d.ts +14 -3
  124. package/dist/overlay.d.ts.map +1 -1
  125. package/dist/overlay.js +75 -37
  126. package/dist/query.d.ts +4 -0
  127. package/dist/query.d.ts.map +1 -1
  128. package/dist/query.js +5 -31
  129. package/dist/reach.d.ts +2 -0
  130. package/dist/reach.d.ts.map +1 -1
  131. package/dist/reach.js +43 -19
  132. package/dist/react.d.ts +26 -16
  133. package/dist/react.d.ts.map +1 -1
  134. package/dist/react.js +10 -8
  135. package/dist/read-side.d.ts.map +1 -1
  136. package/dist/read-side.js +9 -2
  137. package/dist/refresh.d.ts +10 -0
  138. package/dist/refresh.d.ts.map +1 -1
  139. package/dist/refresh.js +42 -38
  140. package/dist/replica.d.ts +8 -5
  141. package/dist/replica.d.ts.map +1 -1
  142. package/dist/replica.js +76 -56
  143. package/dist/room-draft.d.ts +26 -9
  144. package/dist/room-draft.d.ts.map +1 -1
  145. package/dist/room-draft.js +132 -23
  146. package/dist/rows.d.ts +5 -1
  147. package/dist/rows.d.ts.map +1 -1
  148. package/dist/rows.js +10 -2
  149. package/dist/save.d.ts +27 -1
  150. package/dist/save.d.ts.map +1 -1
  151. package/dist/save.js +340 -35
  152. package/dist/search.d.ts +3 -2
  153. package/dist/search.d.ts.map +1 -1
  154. package/dist/status.d.ts +2 -2
  155. package/dist/status.d.ts.map +1 -1
  156. package/dist/status.js +0 -8
  157. package/dist/store.d.ts +33 -9
  158. package/dist/store.d.ts.map +1 -1
  159. package/dist/store.js +273 -164
  160. package/dist/wire.d.ts +7 -3
  161. package/dist/wire.d.ts.map +1 -1
  162. package/dist/wire.js +15 -2
  163. package/package.json +22 -14
  164. package/dist/attachment.d.ts +0 -3
  165. package/dist/attachment.d.ts.map +0 -1
  166. package/dist/attachment.js +0 -8
package/dist/rows.js CHANGED
@@ -7,6 +7,9 @@ function keyOf(...parts) {
7
7
  export function documentKey(room, path) {
8
8
  return keyOf('d', room, path);
9
9
  }
10
+ export function roomOfKey(key) {
11
+ return key.split('\u0000')[1];
12
+ }
10
13
  function plainObject(value) {
11
14
  return typeof value === 'object' && value !== null && !Array.isArray(value);
12
15
  }
@@ -65,10 +68,13 @@ function directoriesOf(path) {
65
68
  const parts = path.split('/').slice(0, -1);
66
69
  return ['', ...parts.map((_, index) => parts.slice(0, index + 1).join('/'))];
67
70
  }
68
- export function rowsOf(room, path, document, composition, change) {
71
+ export function markdownPath(path) {
72
+ return path.endsWith('.md');
73
+ }
74
+ export function rowsOf(room, path, document, composition, change, binary) {
69
75
  const key = documentKey(room, path);
70
76
  const body = keyOf('b', room, path);
71
- if (document === null || !path.endsWith('.md')) {
77
+ if (document === null || !markdownPath(path)) {
72
78
  return [
73
79
  {
74
80
  key,
@@ -82,6 +88,7 @@ export function rowsOf(room, path, document, composition, change) {
82
88
  directories: directoriesOf(path),
83
89
  body,
84
90
  change,
91
+ binary,
85
92
  },
86
93
  ];
87
94
  }
@@ -112,6 +119,7 @@ export function rowsOf(room, path, document, composition, change) {
112
119
  directories: directoriesOf(path),
113
120
  body,
114
121
  change,
122
+ binary,
115
123
  },
116
124
  {
117
125
  key: body,
package/dist/save.d.ts CHANGED
@@ -1,16 +1,19 @@
1
1
  import type { ProviderLimit } from './adapter.js';
2
2
  import { type Change, type ChangeBatch, type Precondition, type WrittenBy } from './change.js';
3
3
  import { type Composition } from './collection.js';
4
+ import { type Layer } from './overlay.js';
4
5
  import type { VisibleLayer } from './replica.js';
5
6
  import type { ChangedPart } from './room-draft.js';
6
7
  export type SaveRefusal = {
7
- rule: string;
8
+ code: string;
9
+ retryable: boolean;
8
10
  file?: string;
9
11
  location?: string;
10
12
  referrers?: readonly string[];
11
13
  parts?: readonly ChangedPart[];
12
14
  };
13
15
  export type SaveInput = {
16
+ local: boolean;
14
17
  composition: Composition;
15
18
  baseRoomVersion: string;
16
19
  operations: import('./adapter.js').OperationScopes;
@@ -20,6 +23,7 @@ export type SaveInput = {
20
23
  handle: string;
21
24
  changes: readonly Change[];
22
25
  bases: readonly Precondition[];
26
+ queued?: ReadonlySet<string>;
23
27
  writtenBy?: WrittenBy;
24
28
  };
25
29
  export type BatchResult = {
@@ -30,5 +34,27 @@ export type BatchResult = {
30
34
  refusal: SaveRefusal;
31
35
  };
32
36
  export declare function providerRewritten(batch: ChangeBatch, visible: VisibleLayer): ChangeBatch;
37
+ export type Requeued = {
38
+ kind: 'requeued';
39
+ queue: readonly ChangeBatch[];
40
+ changes: readonly Change[];
41
+ queued: ReadonlySet<string>;
42
+ withdrawn: ReadonlyMap<string, ChangeBatch | null>;
43
+ owners: ReadonlySet<string>;
44
+ } | {
45
+ kind: 'refused';
46
+ refusal: SaveRefusal;
47
+ };
48
+ export type Joining = {
49
+ kind: 'joined';
50
+ batch: ChangeBatch;
51
+ queue: readonly ChangeBatch[];
52
+ } | {
53
+ kind: 'refused';
54
+ refusal: SaveRefusal;
55
+ };
56
+ export declare function requeued(queue: readonly ChangeBatch[], changes: readonly Change[], visible: VisibleLayer, changeable: (batch: ChangeBatch) => boolean, state: (queue: readonly ChangeBatch[]) => VisibleLayer): Requeued;
57
+ export declare function joined(queue: readonly ChangeBatch[], owner: ChangeBatch, extra: ChangeBatch): Joining;
58
+ export declare function limitedIn(queue: readonly ChangeBatch[], batch: ChangeBatch, limits: readonly ProviderLimit[], state: (queue: readonly ChangeBatch[]) => Layer): SaveRefusal | null;
33
59
  export declare function buildBatch(input: SaveInput): BatchResult;
34
60
  //# sourceMappingURL=save.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"save.d.ts","sourceRoot":"","sources":["../src/save.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAEL,KAAK,MAAM,EACX,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,SAAS,EACf,MAAM,aAAa,CAAC;AACrB,OAAO,EAKL,KAAK,WAAW,EACjB,MAAM,iBAAiB,CAAC;AAIzB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAEnD,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC9B,KAAK,CAAC,EAAE,SAAS,WAAW,EAAE,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG;IACtB,WAAW,EAAE,WAAW,CAAC;IACzB,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,OAAO,cAAc,EAAE,eAAe,CAAC;IACnD,kBAAkB,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,cAAc,EAAE,SAAS,aAAa,EAAE,CAAC;IACzC,OAAO,EAAE,YAAY,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3B,KAAK,EAAE,SAAS,YAAY,EAAE,CAAC;IAC/B,SAAS,CAAC,EAAE,SAAS,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,WAAW,GACnB;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,WAAW,CAAA;CAAE,GACrC;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,OAAO,EAAE,WAAW,CAAA;CAAE,CAAC;AA+K9C,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,YAAY,GAAG,WAAW,CAOxF;AAuQD,wBAAgB,UAAU,CAAC,KAAK,EAAE,SAAS,GAAG,WAAW,CAyFxD"}
1
+ {"version":3,"file":"save.d.ts","sourceRoot":"","sources":["../src/save.ts"],"names":[],"mappings":"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"}
package/dist/save.js CHANGED
@@ -1,15 +1,16 @@
1
1
  import { definitionOf } from './model.js';
2
2
  import { apply, ATTACHMENT, links, parse, pointerSegments, references, relative, relativize, retarget, serialize, target, } from '@agentshouse/mdmodel';
3
- import { immutable, } from './change.js';
4
- import { governing, parseCollection, validateCollection, } from './collection.js';
3
+ import { immutable, queuedFile, departure, } from './change.js';
4
+ import { collectionOf, documentsOf, governing, validateCollection, } from './collection.js';
5
5
  import { changeBatchIdentity } from './identity.js';
6
6
  import { overlay } from './overlay.js';
7
7
  import { covered, normalize } from './paths.js';
8
- function refuse(rule, file, location) {
8
+ function refuse(code, file, location) {
9
9
  return {
10
10
  kind: 'refused',
11
11
  refusal: {
12
- rule,
12
+ code,
13
+ retryable: false,
13
14
  ...(file === undefined ? {} : { file }),
14
15
  ...(location === undefined ? {} : { location }),
15
16
  },
@@ -90,7 +91,8 @@ function relinks(path, moved, before, moves) {
90
91
  return {
91
92
  kind: 'refused',
92
93
  refusal: {
93
- rule: applied.refusal.rule,
94
+ code: applied.refusal.rule,
95
+ retryable: false,
94
96
  file: path,
95
97
  ...(applied.refusal.location === undefined ? {} : { location: applied.refusal.location }),
96
98
  },
@@ -120,7 +122,10 @@ function rewrites(changes, visible) {
120
122
  for (const change of changes) {
121
123
  if (change.kind === 'move')
122
124
  moves.set(change.path, change.to);
123
- if (change.kind === 'remove' || change.kind === 'replace' || change.kind === 'create') {
125
+ if (change.kind === 'remove' ||
126
+ change.kind === 'replace' ||
127
+ change.kind === 'create' ||
128
+ change.kind === 'binary') {
124
129
  settled.add(change.path);
125
130
  }
126
131
  if (change.kind === 'field')
@@ -130,7 +135,7 @@ function rewrites(changes, visible) {
130
135
  return { kind: 'changes', changes };
131
136
  const merged = [...changes];
132
137
  const added = [];
133
- for (const [path, document] of Object.entries(visible.collection.documents)) {
138
+ for (const [path, document] of documentsOf(visible.collection)) {
134
139
  if (settled.has(path))
135
140
  continue;
136
141
  const moved = moves.get(path) ?? path;
@@ -181,7 +186,7 @@ function incoming(collection, removed) {
181
186
  referrers.add(file);
182
187
  found.set(target, referrers);
183
188
  };
184
- for (const [path, document] of Object.entries(collection.documents)) {
189
+ for (const [path, document] of documentsOf(collection)) {
185
190
  const definition = definitionOf(governing(collection.composition, path), document.type);
186
191
  for (const { value, to } of definition === undefined ? [] : references(document, definition)) {
187
192
  if (to.includes(ATTACHMENT))
@@ -190,8 +195,6 @@ function incoming(collection, removed) {
190
195
  if (named !== null)
191
196
  note(named, path);
192
197
  }
193
- for (const link of links(document, path))
194
- note(link.target, path);
195
198
  }
196
199
  const [first] = [...found.keys()].sort();
197
200
  if (first === undefined)
@@ -202,7 +205,12 @@ function incoming(collection, removed) {
202
205
  every.add(file);
203
206
  return {
204
207
  kind: 'refused',
205
- refusal: { rule: 'referenced-missing-file', file: first, referrers: [...every].sort() },
208
+ refusal: {
209
+ code: 'referenced-missing-file',
210
+ retryable: false,
211
+ file: first,
212
+ referrers: [...every].sort(),
213
+ },
206
214
  };
207
215
  }
208
216
  function affected(change) {
@@ -211,9 +219,9 @@ function affected(change) {
211
219
  function guard(changes, input) {
212
220
  const existing = new Set([
213
221
  ...Object.keys(input.visible.documents),
214
- ...input.visible.binaryTargets,
222
+ ...Object.keys(input.visible.binaries),
215
223
  ]);
216
- for (const change of changes) {
224
+ for (const [index, change] of changes.entries()) {
217
225
  for (const path of affected(change)) {
218
226
  const normalized = normalize(path);
219
227
  if (normalized === null || normalized === '')
@@ -222,7 +230,7 @@ function guard(changes, input) {
222
230
  return refuse('provider-owned-path', normalized);
223
231
  }
224
232
  const operation = change.kind === 'remove' ? 'delete' : 'write';
225
- if (!covered(input.operations[operation], normalized))
233
+ if (!input.local && !covered(input.operations[operation], normalized))
226
234
  return refuse(`${operation}-grant`, normalized);
227
235
  }
228
236
  if (change.kind === 'create') {
@@ -231,12 +239,26 @@ function guard(changes, input) {
231
239
  existing.add(change.path);
232
240
  continue;
233
241
  }
242
+ if (change.kind === 'binary') {
243
+ if (input.visible.documents[change.path] !== undefined ||
244
+ (existing.has(change.path) &&
245
+ input.bases.some((base) => base.kind === 'absent' && base.path === change.path))) {
246
+ return refuse('path-collision', change.path);
247
+ }
248
+ if (!input.local &&
249
+ existing.has(change.path) &&
250
+ input.visible.revisions[departure(changes, index)] === undefined) {
251
+ return refuse('unknown-revision', change.path);
252
+ }
253
+ existing.add(change.path);
254
+ continue;
255
+ }
234
256
  if (change.kind === 'move') {
235
257
  if (!existing.has(change.path))
236
258
  return refuse('path-absent', change.path);
237
259
  if (existing.has(change.to))
238
260
  return refuse('path-collision', change.to);
239
- if (unrevisionedDocument(input.visible, change.path)) {
261
+ if (unrevised(input, change.path)) {
240
262
  return refuse('unknown-revision', change.path);
241
263
  }
242
264
  existing.delete(change.path);
@@ -246,30 +268,37 @@ function guard(changes, input) {
246
268
  if (!existing.has(change.path))
247
269
  return refuse('path-absent', change.path);
248
270
  if (change.kind === 'remove') {
249
- if (unrevisionedDocument(input.visible, change.path)) {
271
+ if (unrevised(input, change.path)) {
250
272
  return refuse('unknown-revision', change.path);
251
273
  }
252
274
  existing.delete(change.path);
253
275
  continue;
254
276
  }
255
277
  if (change.kind === 'replace') {
256
- if (input.visible.revisions[change.path] === undefined) {
278
+ if (!input.local && input.visible.revisions[change.path] === undefined) {
257
279
  return refuse('unknown-revision', change.path);
258
280
  }
259
281
  continue;
260
282
  }
261
- if (input.visible.collection.documents[change.path] === undefined) {
283
+ if (input.visible.collection.document(change.path) === undefined) {
262
284
  return refuse('path-absent', change.path);
263
285
  }
264
286
  }
265
287
  return null;
266
288
  }
267
- function unrevisionedDocument(visible, path) {
268
- return visible.revisions[path] === undefined && !visible.binaryTargets.includes(path);
289
+ function unrevised(input, path) {
290
+ return (!input.local && input.queued?.has(path) !== true && input.visible.revisions[path] === undefined);
291
+ }
292
+ function identity(precondition) {
293
+ const part = precondition.kind === 'field' || precondition.kind === 'section' ? precondition.name : '';
294
+ return `${precondition.kind} ${precondition.path} ${part}`;
269
295
  }
270
296
  function existence(visible, path) {
271
297
  const revision = visible.revisions[path];
272
- return revision === undefined ? { kind: 'present', path } : { kind: 'revision', path, revision };
298
+ if (revision !== undefined)
299
+ return { kind: 'revision', path, revision };
300
+ const content = visible.documents[path];
301
+ return content === undefined ? { kind: 'present', path } : { kind: 'content', path, content };
273
302
  }
274
303
  function sameTarget(precondition, change) {
275
304
  if (precondition.kind !== change.kind || precondition.path !== change.path)
@@ -281,11 +310,10 @@ function preconditionsOf(changes, visible, bases) {
281
310
  const preconditions = [];
282
311
  const seen = new Set();
283
312
  const add = (precondition) => {
284
- const part = precondition.kind === 'field' || precondition.kind === 'section' ? precondition.name : '';
285
- const identity = `${precondition.kind} ${precondition.path} ${part}`;
286
- if (seen.has(identity))
313
+ const held = identity(precondition);
314
+ if (seen.has(held))
287
315
  return;
288
- seen.add(identity);
316
+ seen.add(held);
289
317
  preconditions.push(precondition);
290
318
  };
291
319
  for (const change of changes) {
@@ -295,7 +323,7 @@ function preconditionsOf(changes, visible, bases) {
295
323
  add(based);
296
324
  continue;
297
325
  }
298
- const document = visible.collection.documents[change.path];
326
+ const document = visible.collection.document(change.path);
299
327
  if (change.kind === 'field') {
300
328
  add({
301
329
  kind: 'field',
@@ -320,6 +348,13 @@ function preconditionsOf(changes, visible, bases) {
320
348
  add({ kind: 'absent', path: change.path });
321
349
  continue;
322
350
  }
351
+ if (change.kind === 'binary') {
352
+ const revision = visible.revisions[change.path];
353
+ add(visible.binaries[change.path] === undefined || revision === undefined
354
+ ? { kind: 'absent', path: change.path }
355
+ : { kind: 'revision', path: change.path, revision });
356
+ continue;
357
+ }
323
358
  add(existence(visible, change.path));
324
359
  if (change.kind === 'move')
325
360
  add({ kind: 'absent', path: change.to });
@@ -332,7 +367,7 @@ function depth(path) {
332
367
  function exceeded(limits, changes, resulting) {
333
368
  const written = changes.map((change) => (change.kind === 'move' ? change.to : change.path));
334
369
  const created = changes
335
- .filter((change) => change.kind === 'create' || change.kind === 'move')
370
+ .filter((change) => change.kind === 'create' || change.kind === 'move' || change.kind === 'binary')
336
371
  .map((change) => (change.kind === 'move' ? change.to : change.path));
337
372
  for (const limit of limits) {
338
373
  const value = limit.value;
@@ -391,7 +426,7 @@ function relativeChanges(input) {
391
426
  return input.changes.map((change) => {
392
427
  const definition = typed(change.path);
393
428
  if (change.kind === 'field') {
394
- const type = visible.collection.documents[change.path]?.type;
429
+ const type = visible.collection.document(change.path)?.type;
395
430
  const value = relativeField(change.path, change.name, change.value, handle, definition(type));
396
431
  return value === change.value ? change : { ...change, value };
397
432
  }
@@ -414,13 +449,282 @@ function relativeChanges(input) {
414
449
  return { ...change, fields, sections };
415
450
  });
416
451
  }
452
+ function unmodeledType(collection, composition) {
453
+ for (const [path, document] of documentsOf(collection)) {
454
+ if (document.type !== undefined &&
455
+ document.type !== 'any' &&
456
+ governing(composition, path).status === 'unavailable')
457
+ return true;
458
+ }
459
+ return false;
460
+ }
461
+ function unqueueable(file) {
462
+ return { kind: 'refused', refusal: { code: 'unknown-revision', retryable: false, file } };
463
+ }
464
+ function isPart(change) {
465
+ return change.kind === 'field' || change.kind === 'section' || change.kind === 'preamble';
466
+ }
467
+ function touched(change) {
468
+ return change.kind === 'move' ? [change.path, change.to] : [change.path];
469
+ }
470
+ function partApplied(content, path, change) {
471
+ const parsed = parse(content, path);
472
+ if (!parsed.ok)
473
+ return null;
474
+ const document = parsed.document;
475
+ const applied = apply(document, change.kind === 'field'
476
+ ? {
477
+ kind: 'field',
478
+ name: change.name,
479
+ value: change.value,
480
+ prior: document.fields[change.name],
481
+ }
482
+ : change.kind === 'section'
483
+ ? {
484
+ kind: 'section',
485
+ name: change.name,
486
+ value: change.value,
487
+ prior: document.sections[change.name],
488
+ }
489
+ : { kind: 'preamble', value: change.value, prior: document.preamble });
490
+ return applied.outcome === 'applied' ? serialize(applied.document) : null;
491
+ }
492
+ function folded(changes, change, appending) {
493
+ const held = changes.find((item) => item.path === change.path && (item.kind === 'create' || item.kind === 'replace'));
494
+ const whole = held === undefined ? -1 : changes.indexOf(held);
495
+ const occupied = changes.some((item) => !isPart(item) && item !== held && touched(item).includes(change.path));
496
+ if (isPart(change)) {
497
+ const same = changes.findIndex((item) => item.kind === change.kind &&
498
+ item.path === change.path &&
499
+ (change.kind === 'preamble' || ('name' in item && item.name === change.name)));
500
+ if (same !== -1) {
501
+ changes[same] = change;
502
+ return 'folded';
503
+ }
504
+ if (held === undefined) {
505
+ if (!appending)
506
+ return 'skipped';
507
+ if (occupied)
508
+ return 'refused';
509
+ changes.push(change);
510
+ return 'appended';
511
+ }
512
+ if ('content' in held) {
513
+ const content = partApplied(held.content, held.path, change);
514
+ if (content === null)
515
+ return 'refused';
516
+ changes[whole] = { ...held, content };
517
+ return 'folded';
518
+ }
519
+ if (change.kind === 'preamble')
520
+ return 'refused';
521
+ changes[whole] =
522
+ change.kind === 'field'
523
+ ? { ...held, fields: { ...held.fields, [change.name]: change.value } }
524
+ : { ...held, sections: { ...held.sections, [change.name]: change.value } };
525
+ return 'folded';
526
+ }
527
+ if (change.kind === 'replace') {
528
+ if (held !== undefined) {
529
+ if (!('content' in held))
530
+ return 'refused';
531
+ changes[whole] = { ...held, content: change.content };
532
+ return 'folded';
533
+ }
534
+ const parts = changes.filter((item) => isPart(item) && item.path === change.path);
535
+ if (parts.length === 0 && !appending)
536
+ return 'skipped';
537
+ if (occupied)
538
+ return 'refused';
539
+ changes.splice(0, changes.length, ...changes.filter((item) => !parts.includes(item)), change);
540
+ return 'appended';
541
+ }
542
+ if (changes.some((item) => touched(change).some((path) => touched(item).includes(path))))
543
+ return 'refused';
544
+ changes.push(change);
545
+ return 'appended';
546
+ }
547
+ function relinked(batch, move, before, after, appending) {
548
+ const derived = rewrites([move], after);
549
+ if (derived.kind === 'refused')
550
+ return derived;
551
+ const changes = [...batch.changes];
552
+ const appended = [];
553
+ let changed = false;
554
+ for (const change of derived.changes) {
555
+ if (change === move)
556
+ continue;
557
+ const outcome = folded(changes, change, appending);
558
+ if (outcome === 'refused')
559
+ return unqueueable(change.path);
560
+ if (outcome === 'appended')
561
+ appended.push(change);
562
+ if (outcome !== 'skipped')
563
+ changed = true;
564
+ }
565
+ if (!changed)
566
+ return { kind: 'relinked', batch };
567
+ const held = new Set(batch.preconditions.map(identity));
568
+ return {
569
+ kind: 'relinked',
570
+ batch: immutable({
571
+ ...batch,
572
+ id: changeBatchIdentity(),
573
+ changes,
574
+ preconditions: [
575
+ ...batch.preconditions,
576
+ ...preconditionsOf(appended, before, []).filter((precondition) => !held.has(identity(precondition))),
577
+ ],
578
+ }),
579
+ };
580
+ }
581
+ function leading(changes, at) {
582
+ const from = departure(changes, at);
583
+ return new Set(changes.filter((change, index) => index < at && change.kind === 'move' && departure(changes, index) === from));
584
+ }
585
+ function created(batch, at) {
586
+ const file = batch.changes[at];
587
+ return (leading(batch.changes, at).size === 0 &&
588
+ batch.preconditions.some((precondition) => precondition.kind === 'absent' && precondition.path === file.path));
589
+ }
590
+ function withdrawnFile(batch, change, at) {
591
+ const file = batch.changes[at];
592
+ const from = departure(batch.changes, at);
593
+ const moves = leading(batch.changes, at);
594
+ const fresh = created(batch, at);
595
+ const vacated = new Set([...moves].map((move) => move.to));
596
+ if (fresh)
597
+ vacated.add(file.path);
598
+ const replaced = fresh
599
+ ? change.kind === 'remove'
600
+ ? []
601
+ : [{ ...file, path: change.to }]
602
+ : change.kind === 'remove'
603
+ ? [{ kind: 'remove', path: from }]
604
+ : change.to === from
605
+ ? [{ ...file, path: from }]
606
+ : [
607
+ { kind: 'move', path: from, to: change.to },
608
+ { ...file, path: change.to },
609
+ ];
610
+ const added = change.kind === 'move' && (fresh || change.to !== from)
611
+ ? [{ kind: 'absent', path: change.to }]
612
+ : [];
613
+ const changes = batch.changes.flatMap((item) => item === file ? replaced : item.kind === 'move' && moves.has(item) ? [] : [item]);
614
+ if (changes.length === 0)
615
+ return null;
616
+ const preconditions = [
617
+ ...added,
618
+ ...batch.preconditions.filter((precondition) => precondition.kind !== 'absent' || !vacated.has(precondition.path)),
619
+ ];
620
+ return immutable({ ...batch, id: changeBatchIdentity(), changes, preconditions });
621
+ }
622
+ export function requeued(queue, changes, visible, changeable, state) {
623
+ const next = [...queue];
624
+ const origins = new Map();
625
+ const withdrawn = new Map();
626
+ const owners = new Set();
627
+ const queued = new Set();
628
+ const kept = [];
629
+ const originOf = (batch) => origins.get(batch) ?? batch.id;
630
+ const open = (batch) => origins.has(batch) || changeable(batch);
631
+ for (const change of changes) {
632
+ const held = visible.binaries[change.path];
633
+ if ((change.kind !== 'move' && change.kind !== 'remove') || held?.state !== 'pending') {
634
+ kept.push(change);
635
+ continue;
636
+ }
637
+ let index = next.length - 1;
638
+ while (index >= 0 && queuedFile(next[index], change.path, held.localId) === -1)
639
+ index -= 1;
640
+ const owner = next[index];
641
+ if (owner === undefined || !open(owner))
642
+ return unqueueable(change.path);
643
+ const following = next.slice(index);
644
+ const rebuilt = [];
645
+ let before = change.kind === 'move' ? state(next.slice(0, index)) : visible;
646
+ for (const [offset, batch] of following.entries()) {
647
+ let current = batch;
648
+ if (change.kind === 'move') {
649
+ const after = state(next.slice(0, index + offset + 1));
650
+ const appending = offset === 0 && created(batch, queuedFile(batch, change.path, held.localId));
651
+ const relinking = relinked(batch, change, before, after, appending);
652
+ if (relinking.kind === 'refused')
653
+ return relinking;
654
+ current = relinking.batch;
655
+ before = after;
656
+ }
657
+ if (current !== batch && !open(batch))
658
+ return unqueueable(change.path);
659
+ rebuilt.push(offset === 0
660
+ ? withdrawnFile(current, change, queuedFile(current, change.path, held.localId))
661
+ : current);
662
+ }
663
+ for (const [offset, batch] of following.entries()) {
664
+ const replaced = rebuilt[offset];
665
+ if (replaced === batch)
666
+ continue;
667
+ const origin = originOf(batch);
668
+ withdrawn.set(origin, replaced);
669
+ if (replaced !== null)
670
+ origins.set(replaced, origin);
671
+ }
672
+ owners.add(originOf(owner));
673
+ next.splice(index, following.length, ...rebuilt.filter((batch) => batch !== null));
674
+ queued.add(change.path);
675
+ }
676
+ return {
677
+ kind: 'requeued',
678
+ queue: withdrawn.size === 0 ? queue : next,
679
+ changes: kept,
680
+ queued,
681
+ withdrawn,
682
+ owners,
683
+ };
684
+ }
685
+ function covering(precondition) {
686
+ return precondition.kind === 'field' ||
687
+ precondition.kind === 'section' ||
688
+ precondition.kind === 'preamble'
689
+ ? identity(precondition)
690
+ : `path ${precondition.path}`;
691
+ }
692
+ export function joined(queue, owner, extra) {
693
+ const later = new Set(queue.slice(queue.indexOf(owner) + 1).flatMap((batch) => batch.changes.flatMap(touched)));
694
+ const clash = extra.changes.flatMap(touched).find((path) => later.has(path));
695
+ if (clash !== undefined)
696
+ return unqueueable(clash);
697
+ const changes = [...owner.changes];
698
+ for (const change of extra.changes) {
699
+ if (folded(changes, change, true) === 'refused')
700
+ return unqueueable(change.path);
701
+ }
702
+ const held = new Set(owner.preconditions.map(covering));
703
+ const written = new Set(owner.changes.flatMap((change) => change.kind === 'create' || change.kind === 'replace' ? [change.path] : []));
704
+ const preconditions = [
705
+ ...owner.preconditions,
706
+ ...extra.preconditions.filter((precondition) => {
707
+ const key = covering(precondition);
708
+ return (!held.has(key) && (key === `path ${precondition.path}` || !written.has(precondition.path)));
709
+ }),
710
+ ];
711
+ const batch = immutable({
712
+ ...owner,
713
+ ...(extra.validation === undefined ? {} : { validation: extra.validation }),
714
+ id: changeBatchIdentity(),
715
+ changes,
716
+ preconditions,
717
+ });
718
+ return { kind: 'joined', batch, queue: queue.map((item) => (item === owner ? batch : item)) };
719
+ }
720
+ export function limitedIn(queue, batch, limits, state) {
721
+ const limited = exceeded(limits, batch.changes, state(queue.slice(0, queue.indexOf(batch) + 1)));
722
+ return limited?.kind === 'refused' ? limited.refusal : null;
723
+ }
417
724
  export function buildBatch(input) {
418
725
  const changes = relativeChanges(input);
419
726
  const structural = changes.find((change) => change.kind === 'move' || change.kind === 'remove');
420
- if (structural !== undefined &&
421
- Object.entries(input.visible.collection.documents).some(([path, document]) => document.type !== undefined &&
422
- document.type !== 'any' &&
423
- governing(input.composition, path).status === 'unavailable'))
727
+ if (structural !== undefined && unmodeledType(input.visible.collection, input.composition))
424
728
  return refuse('model-unavailable', structural.path);
425
729
  if (changes.length === 0)
426
730
  return refuse('no-change');
@@ -437,7 +741,7 @@ export function buildBatch(input) {
437
741
  ]);
438
742
  const removed = new Set(changes.filter((change) => change.kind === 'remove').map((change) => change.path));
439
743
  if (removed.size > 0) {
440
- const referenced = incoming(parseCollection(resulting.documents, input.composition), removed);
744
+ const referenced = incoming(collectionOf(resulting.documents, input.composition), removed);
441
745
  if (referenced !== null)
442
746
  return referenced;
443
747
  }
@@ -455,12 +759,13 @@ export function buildBatch(input) {
455
759
  const validationText = needsModel
456
760
  ? resulting.documents
457
761
  : Object.fromEntries(Object.entries(resulting.documents).filter(([path]) => predicted.changes.some((change) => affected(change).includes(path))));
458
- const verdict = validateCollection(validationText, resulting.binaryTargets, input.composition);
762
+ const verdict = validateCollection(validationText, Object.keys(resulting.binaries), input.composition);
459
763
  if (verdict.verdict === 'refuse') {
460
764
  return {
461
765
  kind: 'refused',
462
766
  refusal: {
463
- rule: verdict.rule,
767
+ code: verdict.rule,
768
+ retryable: false,
464
769
  file: verdict.file,
465
770
  ...(verdict.location === undefined ? {} : { location: verdict.location }),
466
771
  ...(verdict.referrers === undefined ? {} : { referrers: verdict.referrers }),
package/dist/search.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import type { ChangeSet } from './change-set.js';
2
2
  import { type Filter } from './query.js';
3
- export type DocumentPart = 'fields' | 'sections' | 'preamble' | 'path';
3
+ import type { Open } from './open.js';
4
+ export type DocumentPart = Open<'fields' | 'sections' | 'preamble' | 'path'>;
4
5
  export type SearchRequest = {
5
6
  text: string;
6
7
  documents?: Filter;
@@ -18,7 +19,7 @@ export type MessageHit = {
18
19
  conversation: string;
19
20
  identity: string;
20
21
  score: number;
21
- matches: Readonly<Record<string, readonly 'text'[]>>;
22
+ matches: Readonly<Record<string, readonly Open<'text'>[]>>;
22
23
  };
23
24
  export type SearchResult = {
24
25
  documents: readonly DocumentHit[];