@agentshouse/mdruntime 0.1.0-alpha.9 → 0.1.0-c0105dbd22806

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 (214) hide show
  1. package/AGENTS.md +299 -0
  2. package/README.md +54 -0
  3. package/dist/adapter.d.ts +246 -40
  4. package/dist/adapter.d.ts.map +1 -1
  5. package/dist/adapter.js +1 -10
  6. package/dist/attached-model.d.ts +11 -0
  7. package/dist/attached-model.d.ts.map +1 -0
  8. package/dist/attached-model.js +47 -0
  9. package/dist/attempt.d.ts +16 -0
  10. package/dist/attempt.d.ts.map +1 -0
  11. package/dist/attempt.js +70 -0
  12. package/dist/binary.d.ts +23 -0
  13. package/dist/binary.d.ts.map +1 -0
  14. package/dist/binary.js +8 -0
  15. package/dist/bootstrap.d.ts +17 -7
  16. package/dist/bootstrap.d.ts.map +1 -1
  17. package/dist/bootstrap.js +97 -41
  18. package/dist/change-set.d.ts +33 -0
  19. package/dist/change-set.d.ts.map +1 -0
  20. package/dist/change-set.js +90 -0
  21. package/dist/change.d.ts +24 -0
  22. package/dist/change.d.ts.map +1 -1
  23. package/dist/change.js +31 -5
  24. package/dist/chat/acts.d.ts +34 -0
  25. package/dist/chat/acts.d.ts.map +1 -0
  26. package/dist/chat/acts.js +36 -0
  27. package/dist/chat/context.d.ts +3 -0
  28. package/dist/chat/context.d.ts.map +1 -0
  29. package/dist/chat/context.js +10 -0
  30. package/dist/chat/input.d.ts +9 -0
  31. package/dist/chat/input.d.ts.map +1 -0
  32. package/dist/chat/input.js +99 -0
  33. package/dist/chat/kit.d.ts +60 -0
  34. package/dist/chat/kit.d.ts.map +1 -0
  35. package/dist/chat/kit.js +123 -0
  36. package/dist/chat/lasting.d.ts +6 -0
  37. package/dist/chat/lasting.d.ts.map +1 -0
  38. package/dist/chat/lasting.js +19 -0
  39. package/dist/chat/list.d.ts +26 -0
  40. package/dist/chat/list.d.ts.map +1 -0
  41. package/dist/chat/list.js +62 -0
  42. package/dist/chat/markdown.d.ts +20 -0
  43. package/dist/chat/markdown.d.ts.map +1 -0
  44. package/dist/chat/markdown.js +188 -0
  45. package/dist/chat/message.d.ts +6 -0
  46. package/dist/chat/message.d.ts.map +1 -0
  47. package/dist/chat/message.js +192 -0
  48. package/dist/chat/messages.d.ts +21 -0
  49. package/dist/chat/messages.d.ts.map +1 -0
  50. package/dist/chat/messages.js +151 -0
  51. package/dist/chat/name.d.ts +6 -0
  52. package/dist/chat/name.d.ts.map +1 -0
  53. package/dist/chat/name.js +37 -0
  54. package/dist/chat/opened.d.ts +11 -0
  55. package/dist/chat/opened.d.ts.map +1 -0
  56. package/dist/chat/opened.js +17 -0
  57. package/dist/chat/overlay.d.ts +31 -0
  58. package/dist/chat/overlay.d.ts.map +1 -0
  59. package/dist/chat/overlay.js +97 -0
  60. package/dist/chat/parts.d.ts +57 -0
  61. package/dist/chat/parts.d.ts.map +1 -0
  62. package/dist/chat/parts.js +119 -0
  63. package/dist/chat/process.d.ts +20 -0
  64. package/dist/chat/process.d.ts.map +1 -0
  65. package/dist/chat/process.js +32 -0
  66. package/dist/chat/provider.d.ts +63 -0
  67. package/dist/chat/provider.d.ts.map +1 -0
  68. package/dist/chat/provider.js +645 -0
  69. package/dist/chat/question.d.ts +9 -0
  70. package/dist/chat/question.d.ts.map +1 -0
  71. package/dist/chat/question.js +80 -0
  72. package/dist/chat/questions.d.ts +31 -0
  73. package/dist/chat/questions.d.ts.map +1 -0
  74. package/dist/chat/questions.js +111 -0
  75. package/dist/chat/read.d.ts +5 -0
  76. package/dist/chat/read.d.ts.map +1 -0
  77. package/dist/chat/read.js +13 -0
  78. package/dist/chat/ready.d.ts +33 -0
  79. package/dist/chat/ready.d.ts.map +1 -0
  80. package/dist/chat/ready.js +91 -0
  81. package/dist/chat/style.d.ts +4 -0
  82. package/dist/chat/style.d.ts.map +1 -0
  83. package/dist/chat/style.js +302 -0
  84. package/dist/chat.d.ts +14 -0
  85. package/dist/chat.d.ts.map +1 -0
  86. package/dist/chat.js +8 -0
  87. package/dist/client.d.ts +49 -33
  88. package/dist/client.d.ts.map +1 -1
  89. package/dist/client.js +164 -183
  90. package/dist/collection.d.ts +9 -17
  91. package/dist/collection.d.ts.map +1 -1
  92. package/dist/collection.js +83 -89
  93. package/dist/connection.d.ts +66 -48
  94. package/dist/connection.d.ts.map +1 -1
  95. package/dist/connection.js +1964 -1173
  96. package/dist/conversation.d.ts +65 -24
  97. package/dist/conversation.d.ts.map +1 -1
  98. package/dist/conversation.js +675 -334
  99. package/dist/durability.js +6 -6
  100. package/dist/entry.d.ts +17 -0
  101. package/dist/entry.d.ts.map +1 -0
  102. package/dist/entry.js +156 -0
  103. package/dist/expectation.d.ts +21 -0
  104. package/dist/expectation.d.ts.map +1 -0
  105. package/dist/expectation.js +70 -0
  106. package/dist/failure.d.ts +13 -0
  107. package/dist/failure.d.ts.map +1 -0
  108. package/dist/failure.js +19 -0
  109. package/dist/fence.d.ts +6 -0
  110. package/dist/fence.d.ts.map +1 -0
  111. package/dist/fence.js +10 -0
  112. package/dist/file.d.ts +2 -2
  113. package/dist/file.d.ts.map +1 -1
  114. package/dist/file.js +48 -71
  115. package/dist/host.d.ts +15 -3
  116. package/dist/host.d.ts.map +1 -1
  117. package/dist/host.js +109 -127
  118. package/dist/identity.d.ts +4 -0
  119. package/dist/identity.d.ts.map +1 -1
  120. package/dist/identity.js +22 -9
  121. package/dist/index.d.ts +17 -11
  122. package/dist/index.d.ts.map +1 -1
  123. package/dist/index.js +4 -3
  124. package/dist/log.js +5 -6
  125. package/dist/memory.d.ts +19 -8
  126. package/dist/memory.d.ts.map +1 -1
  127. package/dist/memory.js +304 -216
  128. package/dist/model.d.ts +16 -30
  129. package/dist/model.d.ts.map +1 -1
  130. package/dist/model.js +21 -21
  131. package/dist/observation.d.ts +27 -0
  132. package/dist/observation.d.ts.map +1 -0
  133. package/dist/observation.js +95 -0
  134. package/dist/open.d.ts +2 -0
  135. package/dist/open.d.ts.map +1 -0
  136. package/dist/open.js +1 -0
  137. package/dist/outcome.d.ts +37 -31
  138. package/dist/outcome.d.ts.map +1 -1
  139. package/dist/outcome.js +36 -18
  140. package/dist/overlay.d.ts +22 -4
  141. package/dist/overlay.d.ts.map +1 -1
  142. package/dist/overlay.js +236 -54
  143. package/dist/paths.d.ts +0 -2
  144. package/dist/paths.d.ts.map +1 -1
  145. package/dist/paths.js +6 -29
  146. package/dist/query.d.ts +219 -10
  147. package/dist/query.d.ts.map +1 -1
  148. package/dist/query.js +462 -15
  149. package/dist/reach.d.ts +42 -0
  150. package/dist/reach.d.ts.map +1 -0
  151. package/dist/reach.js +294 -0
  152. package/dist/react.d.ts +41 -0
  153. package/dist/react.d.ts.map +1 -0
  154. package/dist/react.js +64 -0
  155. package/dist/read-side.d.ts +18 -0
  156. package/dist/read-side.d.ts.map +1 -0
  157. package/dist/read-side.js +440 -0
  158. package/dist/refresh.d.ts +15 -5
  159. package/dist/refresh.d.ts.map +1 -1
  160. package/dist/refresh.js +64 -41
  161. package/dist/replica.d.ts +28 -5
  162. package/dist/replica.d.ts.map +1 -1
  163. package/dist/replica.js +249 -40
  164. package/dist/room-draft.d.ts +144 -0
  165. package/dist/room-draft.d.ts.map +1 -0
  166. package/dist/room-draft.js +438 -0
  167. package/dist/rows.d.ts +46 -0
  168. package/dist/rows.d.ts.map +1 -0
  169. package/dist/rows.js +145 -0
  170. package/dist/save.d.ts +33 -5
  171. package/dist/save.d.ts.map +1 -1
  172. package/dist/save.js +630 -366
  173. package/dist/search.d.ts +62 -0
  174. package/dist/search.d.ts.map +1 -0
  175. package/dist/search.js +125 -0
  176. package/dist/status.d.ts +6 -8
  177. package/dist/status.d.ts.map +1 -1
  178. package/dist/status.js +8 -20
  179. package/dist/store.d.ts +89 -34
  180. package/dist/store.d.ts.map +1 -1
  181. package/dist/store.js +513 -132
  182. package/dist/transfer.d.ts +5 -0
  183. package/dist/transfer.d.ts.map +1 -1
  184. package/dist/transfer.js +1 -5
  185. package/dist/wire.d.ts +59 -19
  186. package/dist/wire.d.ts.map +1 -1
  187. package/dist/wire.js +88 -105
  188. package/package.json +34 -16
  189. package/dist/adapter.js.map +0 -1
  190. package/dist/bootstrap.js.map +0 -1
  191. package/dist/change.js.map +0 -1
  192. package/dist/client.js.map +0 -1
  193. package/dist/collection.js.map +0 -1
  194. package/dist/connection.js.map +0 -1
  195. package/dist/conversation.js.map +0 -1
  196. package/dist/durability.js.map +0 -1
  197. package/dist/file.js.map +0 -1
  198. package/dist/host.js.map +0 -1
  199. package/dist/identity.js.map +0 -1
  200. package/dist/index.js.map +0 -1
  201. package/dist/log.js.map +0 -1
  202. package/dist/memory.js.map +0 -1
  203. package/dist/model.js.map +0 -1
  204. package/dist/outcome.js.map +0 -1
  205. package/dist/overlay.js.map +0 -1
  206. package/dist/paths.js.map +0 -1
  207. package/dist/query.js.map +0 -1
  208. package/dist/refresh.js.map +0 -1
  209. package/dist/replica.js.map +0 -1
  210. package/dist/save.js.map +0 -1
  211. package/dist/status.js.map +0 -1
  212. package/dist/store.js.map +0 -1
  213. package/dist/transfer.js.map +0 -1
  214. package/dist/wire.js.map +0 -1
@@ -0,0 +1,438 @@
1
+ import { apply, equalValue, frontmatterValue, parse, serialize } from '@agentshouse/mdmodel';
2
+ import { runtimeError } from './failure.js';
3
+ import { hexDigest } from './file.js';
4
+ import { binaryLocalId } from './identity.js';
5
+ import { normalize } from './paths.js';
6
+ export function returnedBy(kind) {
7
+ return { kind, code: kind, fields: {}, retryable: false };
8
+ }
9
+ export function staleWrite(current) {
10
+ return { outcome: 'stale', code: 'stale', retryable: false, current };
11
+ }
12
+ function malformed() {
13
+ throw runtimeError('room-draft-malformed');
14
+ }
15
+ function plain(value) {
16
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
17
+ }
18
+ function text(value) {
19
+ return typeof value === 'string' ? value : malformed();
20
+ }
21
+ function optionalText(value) {
22
+ return value === undefined ? undefined : text(value);
23
+ }
24
+ function markdownBase(value) {
25
+ return value === null ? null : optionalText(value);
26
+ }
27
+ function entries(value, each) {
28
+ if (!plain(value))
29
+ malformed();
30
+ const read = {};
31
+ for (const [name, item] of Object.entries(value))
32
+ read[name] = each(item);
33
+ return read;
34
+ }
35
+ function fieldValue(value) {
36
+ if (value === null)
37
+ return null;
38
+ if (!plain(value) || !Object.hasOwn(value, 'value') || !frontmatterValue(value.value)) {
39
+ malformed();
40
+ }
41
+ return { value: value.value };
42
+ }
43
+ function textPart(value) {
44
+ if (!plain(value))
45
+ malformed();
46
+ const base = optionalText(value.base);
47
+ return { value: text(value.value), ...(base === undefined ? {} : { base }) };
48
+ }
49
+ function creationOf(value) {
50
+ if (!plain(value))
51
+ malformed();
52
+ if (Object.hasOwn(value, 'markdown'))
53
+ return { markdown: text(value.markdown) };
54
+ const fields = entries(value.fields, (item) => (frontmatterValue(item) ? item : malformed()));
55
+ return { type: text(value.type), fields, sections: entries(value.sections, text) };
56
+ }
57
+ const PART_NAMES = new Set([
58
+ 'fields',
59
+ 'sections',
60
+ 'preamble',
61
+ 'markdown',
62
+ 'creation',
63
+ 'move',
64
+ 'removal',
65
+ 'file',
66
+ ]);
67
+ export function readParts(value) {
68
+ if (!plain(value) || Object.keys(value).some((name) => !PART_NAMES.has(name)))
69
+ malformed();
70
+ const parts = {};
71
+ if (value.fields !== undefined) {
72
+ parts.fields = entries(value.fields, (item) => {
73
+ if (!plain(item) || !Object.hasOwn(item, 'value') || !frontmatterValue(item.value)) {
74
+ malformed();
75
+ }
76
+ return item.base === undefined
77
+ ? { value: item.value }
78
+ : { value: item.value, base: fieldValue(item.base) };
79
+ });
80
+ }
81
+ if (value.sections !== undefined)
82
+ parts.sections = entries(value.sections, textPart);
83
+ if (value.preamble !== undefined)
84
+ parts.preamble = textPart(value.preamble);
85
+ if (value.markdown !== undefined)
86
+ parts.markdown = textPart(value.markdown);
87
+ if (value.creation !== undefined)
88
+ parts.creation = creationOf(value.creation);
89
+ if (value.move !== undefined) {
90
+ if (!plain(value.move))
91
+ malformed();
92
+ const base = markdownBase(value.move.base);
93
+ parts.move = { to: text(value.move.to), ...(base === undefined ? {} : { base }) };
94
+ }
95
+ if (value.removal !== undefined) {
96
+ if (!plain(value.removal))
97
+ malformed();
98
+ const base = markdownBase(value.removal.base);
99
+ parts.removal = base === undefined ? {} : { base };
100
+ }
101
+ if (value.file !== undefined) {
102
+ if (!plain(value.file) || !(value.file.blob instanceof Blob))
103
+ malformed();
104
+ if (Object.keys(value).length > 1)
105
+ throw runtimeError('room-draft-file-exclusive');
106
+ parts.file = { blob: value.file.blob };
107
+ }
108
+ return parts;
109
+ }
110
+ function named(record) {
111
+ return Object.entries(record ?? {});
112
+ }
113
+ function own(record, name) {
114
+ return record !== undefined && Object.hasOwn(record, name) ? record[name] : undefined;
115
+ }
116
+ function kept(prior, base) {
117
+ if (prior !== undefined)
118
+ return prior.base;
119
+ if (base === undefined)
120
+ throw runtimeError('room-draft-base-missing');
121
+ return base;
122
+ }
123
+ function editsText(parts) {
124
+ return (parts.preamble !== undefined ||
125
+ named(parts.fields).length > 0 ||
126
+ named(parts.sections).length > 0);
127
+ }
128
+ export function held(current, parts, file) {
129
+ if (parts.markdown !== undefined && editsText(parts)) {
130
+ throw runtimeError('room-draft-markdown-exclusive');
131
+ }
132
+ if (file !== undefined) {
133
+ if (current !== undefined && current.file === undefined) {
134
+ throw runtimeError('room-draft-file-exclusive');
135
+ }
136
+ return { file: { ...file, base: current?.file === undefined ? file.base : current.file.base } };
137
+ }
138
+ if (current?.file !== undefined) {
139
+ if (editsText(parts) ||
140
+ parts.markdown !== undefined ||
141
+ parts.creation !== undefined ||
142
+ (parts.move === undefined && parts.removal === undefined)) {
143
+ throw runtimeError('room-draft-file-exclusive');
144
+ }
145
+ return {
146
+ file: current.file,
147
+ ...(parts.move === undefined
148
+ ? {}
149
+ : { move: { to: parts.move.to, base: kept(current.move, parts.move.base) } }),
150
+ ...(parts.removal === undefined
151
+ ? {}
152
+ : { removal: { base: kept(current.removal, parts.removal.base) } }),
153
+ };
154
+ }
155
+ const next = {};
156
+ const fields = named(parts.fields);
157
+ if (fields.length > 0) {
158
+ next.fields = Object.fromEntries(fields.map(([name, part]) => [
159
+ name,
160
+ { value: part.value, base: kept(own(current?.fields, name), part.base) },
161
+ ]));
162
+ }
163
+ const sections = named(parts.sections);
164
+ if (sections.length > 0) {
165
+ next.sections = Object.fromEntries(sections.map(([name, part]) => [
166
+ name,
167
+ { value: part.value, base: kept(own(current?.sections, name), part.base) },
168
+ ]));
169
+ }
170
+ if (parts.preamble !== undefined) {
171
+ next.preamble = {
172
+ value: parts.preamble.value,
173
+ base: kept(current?.preamble, parts.preamble.base),
174
+ };
175
+ }
176
+ if (parts.markdown !== undefined) {
177
+ next.markdown = {
178
+ value: parts.markdown.value,
179
+ base: kept(current?.markdown, parts.markdown.base),
180
+ };
181
+ }
182
+ if (parts.creation !== undefined)
183
+ next.creation = parts.creation;
184
+ if (parts.move !== undefined) {
185
+ next.move = { to: parts.move.to, base: kept(current?.move, parts.move.base) };
186
+ }
187
+ if (parts.removal !== undefined) {
188
+ next.removal = { base: kept(current?.removal, parts.removal.base) };
189
+ }
190
+ if (Object.keys(next).length === 0)
191
+ throw runtimeError('room-draft-empty');
192
+ return next;
193
+ }
194
+ function sameField(left, right) {
195
+ if (left === null || right === null)
196
+ return left === right;
197
+ return equalValue(left.value, right.value);
198
+ }
199
+ export function underneath(parts, path, visible) {
200
+ const markdown = visible?.documents[path] ?? null;
201
+ const document = visible?.collection.document(path);
202
+ const report = {};
203
+ const fields = {};
204
+ for (const [name, part] of named(parts.fields)) {
205
+ const current = document !== undefined && Object.hasOwn(document.fields, name)
206
+ ? { value: document.fields[name] }
207
+ : null;
208
+ if (!sameField(part.base, current))
209
+ fields[name] = current;
210
+ }
211
+ if (Object.keys(fields).length > 0)
212
+ report.fields = fields;
213
+ const sections = {};
214
+ for (const [name, part] of named(parts.sections)) {
215
+ const current = document !== undefined && Object.hasOwn(document.sections, name)
216
+ ? document.sections[name]
217
+ : null;
218
+ if (current !== part.base)
219
+ sections[name] = current;
220
+ }
221
+ if (Object.keys(sections).length > 0)
222
+ report.sections = sections;
223
+ const preamble = document?.preamble ?? null;
224
+ if (parts.preamble !== undefined && preamble !== parts.preamble.base)
225
+ report.preamble = preamble;
226
+ if (parts.markdown !== undefined && markdown !== parts.markdown.base)
227
+ report.markdown = markdown;
228
+ if (parts.creation !== undefined && markdown !== null)
229
+ report.creation = markdown;
230
+ if (parts.move !== undefined && markdown !== parts.move.base)
231
+ report.move = markdown;
232
+ if (parts.removal !== undefined && markdown !== parts.removal.base)
233
+ report.removal = markdown;
234
+ if (parts.file !== undefined) {
235
+ const file = binaryBase(visible, path);
236
+ if (file !== parts.file.base || markdown !== null)
237
+ report.file = file;
238
+ }
239
+ return report;
240
+ }
241
+ const UNTYPED_MEDIA_TYPE = 'application/octet-stream';
242
+ export async function heldFile(blob, base) {
243
+ const bytes = await blob.arrayBuffer();
244
+ return {
245
+ blob,
246
+ localId: binaryLocalId(),
247
+ byteLength: bytes.byteLength,
248
+ mediaType: blob.type === '' ? UNTYPED_MEDIA_TYPE : blob.type,
249
+ sha256: hexDigest(await crypto.subtle.digest('SHA-256', bytes)),
250
+ base,
251
+ };
252
+ }
253
+ export function binaryBase(visible, path) {
254
+ return visible?.binaries[path] === undefined ? null : (visible.revisions[path] ?? null);
255
+ }
256
+ export function changedParts(path, report) {
257
+ return [
258
+ ...(report.creation === undefined ? [] : [{ path, part: 'creation' }]),
259
+ ...named(report.fields).map(([name]) => ({ path, part: 'field', name })),
260
+ ...named(report.sections).map(([name]) => ({ path, part: 'section', name })),
261
+ ...(report.preamble === undefined ? [] : [{ path, part: 'preamble' }]),
262
+ ...(report.markdown === undefined ? [] : [{ path, part: 'markdown' }]),
263
+ ...(report.move === undefined ? [] : [{ path, part: 'move' }]),
264
+ ...(report.removal === undefined ? [] : [{ path, part: 'removal' }]),
265
+ ...(report.file === undefined ? [] : [{ path, part: 'file' }]),
266
+ ];
267
+ }
268
+ function joined(existing, returned) {
269
+ const merged = { ...existing };
270
+ for (const [name, part] of named(returned))
271
+ if (!Object.hasOwn(merged, name))
272
+ merged[name] = part;
273
+ return Object.keys(merged).length === 0 ? undefined : merged;
274
+ }
275
+ function ended(text) {
276
+ return text === '' || text.endsWith('\n') ? text : `${text}\n`;
277
+ }
278
+ function frontmatterOf(fields) {
279
+ const opened = parse('---\n---\n');
280
+ if (!opened.ok)
281
+ return malformed();
282
+ let document = opened.document;
283
+ for (const [name, part] of fields) {
284
+ const next = apply(document, { kind: 'field', name, value: part.value, prior: undefined });
285
+ if (next.outcome !== 'applied')
286
+ return malformed();
287
+ document = next.document;
288
+ }
289
+ return serialize(document);
290
+ }
291
+ function concatenated(markdown, parts) {
292
+ const fields = named(parts.fields);
293
+ const pieces = [
294
+ ...(fields.length === 0 ? [] : [frontmatterOf(fields)]),
295
+ ...(parts.preamble === undefined ? [] : [parts.preamble.value]),
296
+ ...named(parts.sections).map(([name, part]) => `## ${name}\n${part.value}`),
297
+ ];
298
+ if (pieces.length === 0)
299
+ return markdown;
300
+ return `${ended(markdown)}\n${pieces.map(ended).join('')}`;
301
+ }
302
+ export function returnedInto(existing, returned) {
303
+ if (existing === undefined)
304
+ return returned;
305
+ const next = { ...existing };
306
+ const whole = existing.markdown ?? returned.markdown;
307
+ if (whole === undefined) {
308
+ const fields = joined(existing.fields, returned.fields);
309
+ const sections = joined(existing.sections, returned.sections);
310
+ if (fields !== undefined)
311
+ next.fields = fields;
312
+ if (sections !== undefined)
313
+ next.sections = sections;
314
+ const preamble = existing.preamble ?? returned.preamble;
315
+ if (preamble !== undefined)
316
+ next.preamble = preamble;
317
+ }
318
+ else {
319
+ delete next.fields;
320
+ delete next.sections;
321
+ delete next.preamble;
322
+ const value = concatenated(whole.value, existing.markdown === undefined ? existing : returned);
323
+ next.markdown = { value, base: whole.base };
324
+ }
325
+ const creation = existing.creation ?? returned.creation;
326
+ const move = existing.move ?? returned.move;
327
+ const removal = existing.removal ?? returned.removal;
328
+ const file = existing.file ?? returned.file;
329
+ if (creation !== undefined)
330
+ next.creation = creation;
331
+ if (move !== undefined)
332
+ next.move = move;
333
+ if (removal !== undefined)
334
+ next.removal = removal;
335
+ if (file !== undefined)
336
+ next.file = file;
337
+ return next;
338
+ }
339
+ export function moveTarget(move) {
340
+ const to = normalize(move.to);
341
+ return to === null || to === '' ? move.to : to;
342
+ }
343
+ export function changesOf(path, parts) {
344
+ const changes = [];
345
+ const bases = [];
346
+ const creation = parts.creation;
347
+ if (creation !== undefined) {
348
+ changes.push('markdown' in creation
349
+ ? { kind: 'create', path, content: creation.markdown }
350
+ : {
351
+ kind: 'create',
352
+ path,
353
+ fields: { type: creation.type, ...creation.fields },
354
+ sections: creation.sections,
355
+ });
356
+ }
357
+ for (const [name, part] of named(parts.fields)) {
358
+ changes.push({ kind: 'field', path, name, value: part.value });
359
+ bases.push({ kind: 'field', path, name, value: part.base?.value });
360
+ }
361
+ for (const [name, part] of named(parts.sections)) {
362
+ changes.push({ kind: 'section', path, name, value: part.value });
363
+ bases.push({ kind: 'section', path, name, value: part.base });
364
+ }
365
+ if (parts.preamble !== undefined) {
366
+ changes.push({ kind: 'preamble', path, value: parts.preamble.value });
367
+ bases.push({ kind: 'preamble', path, value: parts.preamble.base });
368
+ }
369
+ if (parts.markdown !== undefined) {
370
+ changes.push({ kind: 'replace', path, content: parts.markdown.value });
371
+ }
372
+ if (parts.file !== undefined && parts.removal === undefined) {
373
+ const { base, ...file } = parts.file;
374
+ const at = parts.move === undefined ? path : moveTarget(parts.move);
375
+ if (base !== null && at !== path)
376
+ changes.push({ kind: 'move', path, to: at });
377
+ changes.push({ kind: 'binary', path: at, ...file });
378
+ if (base === null && at !== path)
379
+ bases.push({ kind: 'absent', path: at });
380
+ return { changes, bases };
381
+ }
382
+ if (parts.file?.base === null)
383
+ return { changes, bases };
384
+ if (parts.move !== undefined)
385
+ changes.push({ kind: 'move', path, to: moveTarget(parts.move) });
386
+ if (parts.removal !== undefined)
387
+ changes.push({ kind: 'remove', path });
388
+ return { changes, bases };
389
+ }
390
+ function rebased(before, after) {
391
+ return before === undefined ? after : { ...after, base: before.base };
392
+ }
393
+ function rebasedAll(before, after) {
394
+ return {
395
+ ...before,
396
+ ...Object.fromEntries(Object.entries(after).map(([name, part]) => [name, rebased(before?.[name], part)])),
397
+ };
398
+ }
399
+ export function joinedDraft(carried, saved) {
400
+ const draft = saved.find((item) => item.path === carried.path);
401
+ if (draft === undefined)
402
+ return carried;
403
+ const held = carried.parts;
404
+ const next = draft.parts;
405
+ return {
406
+ path: carried.path,
407
+ parts: {
408
+ ...held,
409
+ ...next,
410
+ ...(next.fields === undefined ? {} : { fields: rebasedAll(held.fields, next.fields) }),
411
+ ...(next.sections === undefined
412
+ ? {}
413
+ : { sections: rebasedAll(held.sections, next.sections) }),
414
+ ...(next.preamble === undefined ? {} : { preamble: rebased(held.preamble, next.preamble) }),
415
+ ...(next.markdown === undefined ? {} : { markdown: rebased(held.markdown, next.markdown) }),
416
+ },
417
+ };
418
+ }
419
+ export function withdrawnDrafts(drafts, changes) {
420
+ return drafts.flatMap((draft) => {
421
+ const file = draft.parts.file;
422
+ const at = draft.parts.move?.to ?? draft.path;
423
+ const change = changes.find((item) => (item.kind === 'move' || item.kind === 'remove') && item.path === at);
424
+ if (file === undefined || change === undefined)
425
+ return [draft];
426
+ if (change.kind === 'move') {
427
+ return [
428
+ {
429
+ path: draft.path,
430
+ parts: change.to === draft.path ? { file } : { file, move: { to: change.to, base: null } },
431
+ },
432
+ ];
433
+ }
434
+ return file.base === null
435
+ ? []
436
+ : [{ path: draft.path, parts: { file, removal: { base: null } } }];
437
+ });
438
+ }
package/dist/rows.d.ts ADDED
@@ -0,0 +1,46 @@
1
+ import { type Document } from '@agentshouse/mdmodel';
2
+ import type { HeadChange } from './adapter.js';
3
+ import type { BinaryFileRecord } from './binary.js';
4
+ import { type Composition } from './collection.js';
5
+ type Fields = Readonly<Record<string, unknown>>;
6
+ export type DocumentRow = {
7
+ key: string;
8
+ kind: 'document';
9
+ room: string;
10
+ path: string;
11
+ type: string | null;
12
+ fields: Fields;
13
+ values: Fields;
14
+ reached: Readonly<Record<string, readonly unknown[]>>;
15
+ directories: readonly string[];
16
+ body: string;
17
+ change: HeadChange | null;
18
+ binary: BinaryFileRecord | null;
19
+ };
20
+ export type BodyRow = {
21
+ key: string;
22
+ kind: 'body';
23
+ room: string;
24
+ path: string;
25
+ preamble: string;
26
+ sections: Readonly<Record<string, string>>;
27
+ markdown: string;
28
+ };
29
+ export type ReferenceRow = {
30
+ key: string;
31
+ kind: 'reference';
32
+ room: string;
33
+ source: string;
34
+ target: string;
35
+ field: string;
36
+ forms: readonly string[];
37
+ path: string;
38
+ type: string;
39
+ };
40
+ export type Row = DocumentRow | BodyRow | ReferenceRow;
41
+ export declare function documentKey(room: string, path: string): string;
42
+ export declare function roomOfKey(key: string): string;
43
+ export declare function markdownPath(path: string): boolean;
44
+ export declare function rowsOf(room: string, path: string, document: Document | null, composition: Composition, change: HeadChange | null, binary: BinaryFileRecord | null): readonly Row[];
45
+ export {};
46
+ //# sourceMappingURL=rows.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rows.d.ts","sourceRoot":"","sources":["../src/rows.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,QAAQ,EACd,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAAa,KAAK,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAG9D,KAAK,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAEhD,MAAM,MAAM,WAAW,GAAG;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,UAAU,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,OAAO,EAAE,CAAC,CAAC,CAAC;IACtD,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,UAAU,GAAG,IAAI,CAAC;IAC1B,MAAM,EAAE,gBAAgB,GAAG,IAAI,CAAC;CACjC,CAAC;AAEF,MAAM,MAAM,OAAO,GAAG;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAC3C,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,WAAW,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,GAAG,GAAG,WAAW,GAAG,OAAO,GAAG,YAAY,CAAC;AAMvD,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAE9D;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE7C;AAmFD,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAElD;AAED,wBAAgB,MAAM,CACpB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,QAAQ,GAAG,IAAI,EACzB,WAAW,EAAE,WAAW,EACxB,MAAM,EAAE,UAAU,GAAG,IAAI,EACzB,MAAM,EAAE,gBAAgB,GAAG,IAAI,GAC9B,SAAS,GAAG,EAAE,CAyEhB"}
package/dist/rows.js ADDED
@@ -0,0 +1,145 @@
1
+ import { ARRAY_INDEX, escapePointer, references, target, } from '@agentshouse/mdmodel';
2
+ import { governing } from './collection.js';
3
+ import { definitionOf } from './model.js';
4
+ function keyOf(...parts) {
5
+ return `${parts.join('\u0000')}\u0000`;
6
+ }
7
+ export function documentKey(room, path) {
8
+ return keyOf('d', room, path);
9
+ }
10
+ export function roomOfKey(key) {
11
+ return key.split('\u0000')[1];
12
+ }
13
+ function plainObject(value) {
14
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
15
+ }
16
+ function compared(reading, exact, value) {
17
+ if (!reading.references.has(exact))
18
+ return value;
19
+ return (typeof value === 'string' ? target(reading.path, value) : null) ?? undefined;
20
+ }
21
+ function record(reading, node, forms, exact) {
22
+ const value = Array.isArray(node)
23
+ ? node.map((item, index) => compared(reading, `${exact}/${index}`, item))
24
+ : compared(reading, exact, node);
25
+ for (const form of forms) {
26
+ if (form.pending)
27
+ continue;
28
+ (reading.reached[form.text] ??= []).push(value);
29
+ if (!form.through && value !== undefined && !plainObject(value)) {
30
+ reading.values[form.text] = value;
31
+ }
32
+ }
33
+ if (Array.isArray(node) || plainObject(node) || !reading.references.has(exact))
34
+ return;
35
+ const found = compared(reading, exact, node);
36
+ if (typeof found !== 'string')
37
+ return;
38
+ const field = forms
39
+ .map((form) => form.text)
40
+ .reduce((left, right) => (right.length < left.length ? right : left));
41
+ const known = reading.targets.get(`${field}\u0000${found}`);
42
+ const kept = known ?? { field, target: found, forms: new Set() };
43
+ for (const form of forms)
44
+ kept.forms.add(form.text);
45
+ reading.targets.set(`${field}\u0000${found}`, kept);
46
+ }
47
+ function walk(reading, node, forms, exact) {
48
+ record(reading, node, forms, exact);
49
+ if (Array.isArray(node)) {
50
+ node.forEach((item, index) => walk(reading, item, forms.flatMap((form) => [
51
+ ...(form.pending
52
+ ? []
53
+ : [{ text: `${form.text}/${index}`, through: form.through, pending: false }]),
54
+ { text: form.text, through: true, pending: true },
55
+ ]), `${exact}/${index}`));
56
+ return;
57
+ }
58
+ if (!plainObject(node))
59
+ return;
60
+ for (const [key, value] of Object.entries(node)) {
61
+ const segment = escapePointer(key);
62
+ walk(reading, value, forms.flatMap((form) => form.pending && ARRAY_INDEX.test(key)
63
+ ? []
64
+ : [{ text: `${form.text}/${segment}`, through: form.through, pending: false }]), `${exact}/${segment}`);
65
+ }
66
+ }
67
+ function directoriesOf(path) {
68
+ const parts = path.split('/').slice(0, -1);
69
+ return ['', ...parts.map((_, index) => parts.slice(0, index + 1).join('/'))];
70
+ }
71
+ export function markdownPath(path) {
72
+ return path.endsWith('.md');
73
+ }
74
+ export function rowsOf(room, path, document, composition, change, binary) {
75
+ const key = documentKey(room, path);
76
+ const body = keyOf('b', room, path);
77
+ if (document === null || !markdownPath(path)) {
78
+ return [
79
+ {
80
+ key,
81
+ kind: 'document',
82
+ room,
83
+ path,
84
+ type: null,
85
+ fields: {},
86
+ values: {},
87
+ reached: {},
88
+ directories: directoriesOf(path),
89
+ body,
90
+ change,
91
+ binary,
92
+ },
93
+ ];
94
+ }
95
+ const type = document.type ?? 'any';
96
+ const definition = definitionOf(governing(composition, path), document.type);
97
+ const reading = {
98
+ path,
99
+ references: new Set(definition === undefined ? [] : references(document, definition).map((one) => one.pointer)),
100
+ values: {},
101
+ reached: {},
102
+ targets: new Map(),
103
+ };
104
+ for (const [name, value] of Object.entries(document.fields)) {
105
+ const segment = escapePointer(name);
106
+ walk(reading, value, [{ text: `/${segment}`, through: false, pending: false }], `/${segment}`);
107
+ }
108
+ const { values, reached } = reading;
109
+ return [
110
+ {
111
+ key,
112
+ kind: 'document',
113
+ room,
114
+ path,
115
+ type,
116
+ fields: document.fields,
117
+ values,
118
+ reached,
119
+ directories: directoriesOf(path),
120
+ body,
121
+ change,
122
+ binary,
123
+ },
124
+ {
125
+ key: body,
126
+ kind: 'body',
127
+ room,
128
+ path,
129
+ preamble: document.preamble,
130
+ sections: document.sections,
131
+ markdown: document.content,
132
+ },
133
+ ...[...reading.targets.values()].map(({ field, target: found, forms }) => ({
134
+ key: keyOf('r', room, path, field, found),
135
+ kind: 'reference',
136
+ room,
137
+ source: key,
138
+ target: documentKey(room, found),
139
+ field,
140
+ forms: [...forms].sort(),
141
+ path,
142
+ type,
143
+ })),
144
+ ];
145
+ }
package/dist/save.d.ts CHANGED
@@ -1,24 +1,29 @@
1
- import type { AttachedModelBinding } from './model.js';
2
1
  import type { ProviderLimit } from './adapter.js';
3
- import { type Change, type ChangeBatch } from './change.js';
2
+ import { type Change, type ChangeBatch, type Precondition, type WrittenBy } from './change.js';
4
3
  import { type Composition } from './collection.js';
5
4
  import type { VisibleLayer } from './replica.js';
5
+ import type { ChangedPart } from './room-draft.js';
6
6
  export type SaveRefusal = {
7
- source?: 'model';
8
- rule: string;
7
+ code: string;
8
+ retryable: boolean;
9
9
  file?: string;
10
10
  location?: string;
11
11
  referrers?: readonly string[];
12
+ parts?: readonly ChangedPart[];
12
13
  };
13
14
  export type SaveInput = {
15
+ local: boolean;
14
16
  composition: Composition;
15
17
  baseRoomVersion: string;
16
18
  operations: import('./adapter.js').OperationScopes;
17
- binding: AttachedModelBinding | null;
18
19
  providerOwnedPaths: readonly string[];
19
20
  providerLimits: readonly ProviderLimit[];
20
21
  visible: VisibleLayer;
22
+ handle: string;
21
23
  changes: readonly Change[];
24
+ bases: readonly Precondition[];
25
+ queued?: ReadonlySet<string>;
26
+ writtenBy?: WrittenBy;
22
27
  };
23
28
  export type BatchResult = {
24
29
  kind: 'batch';
@@ -27,5 +32,28 @@ export type BatchResult = {
27
32
  kind: 'refused';
28
33
  refusal: SaveRefusal;
29
34
  };
35
+ export declare function providerRewritten(batch: ChangeBatch, visible: VisibleLayer): ChangeBatch;
36
+ export type Requeued = {
37
+ kind: 'requeued';
38
+ queue: readonly ChangeBatch[];
39
+ changes: readonly Change[];
40
+ queued: ReadonlySet<string>;
41
+ withdrawn: ReadonlyMap<string, ChangeBatch | null>;
42
+ owners: ReadonlySet<string>;
43
+ } | {
44
+ kind: 'refused';
45
+ refusal: SaveRefusal;
46
+ };
47
+ export type Joining = {
48
+ kind: 'joined';
49
+ batch: ChangeBatch;
50
+ queue: readonly ChangeBatch[];
51
+ } | {
52
+ kind: 'refused';
53
+ refusal: SaveRefusal;
54
+ };
55
+ export declare function requeued(queue: readonly ChangeBatch[], changes: readonly Change[], visible: VisibleLayer, changeable: (batch: ChangeBatch) => boolean, state: (queue: readonly ChangeBatch[]) => VisibleLayer): Requeued;
56
+ export declare function joined(queue: readonly ChangeBatch[], owner: ChangeBatch, extra: ChangeBatch): Joining;
57
+ export declare function limitedIn(queue: readonly ChangeBatch[], batch: ChangeBatch, limits: readonly ProviderLimit[], state: (queue: readonly ChangeBatch[]) => VisibleLayer): SaveRefusal | null;
30
58
  export declare function buildBatch(input: SaveInput): BatchResult;
31
59
  //# sourceMappingURL=save.d.ts.map