@agentshouse/mdruntime 0.1.0-ce531b51ff088 → 0.1.0-ceb97106cc2fd

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 (203) hide show
  1. package/AGENTS.md +273 -175
  2. package/README.md +21 -11
  3. package/dist/acquisition.d.ts +14 -0
  4. package/dist/acquisition.d.ts.map +1 -0
  5. package/dist/acquisition.js +24 -0
  6. package/dist/adapter.d.ts +129 -41
  7. package/dist/adapter.d.ts.map +1 -1
  8. package/dist/adapter.js +1 -9
  9. package/dist/attached-model.d.ts +11 -0
  10. package/dist/attached-model.d.ts.map +1 -0
  11. package/dist/attached-model.js +47 -0
  12. package/dist/attempt.d.ts +16 -0
  13. package/dist/attempt.d.ts.map +1 -0
  14. package/dist/attempt.js +70 -0
  15. package/dist/binary.d.ts +23 -0
  16. package/dist/binary.d.ts.map +1 -0
  17. package/dist/binary.js +8 -0
  18. package/dist/bootstrap.d.ts +20 -6
  19. package/dist/bootstrap.d.ts.map +1 -1
  20. package/dist/bootstrap.js +114 -37
  21. package/dist/change-set.d.ts +2 -0
  22. package/dist/change-set.d.ts.map +1 -1
  23. package/dist/change-set.js +49 -16
  24. package/dist/change.d.ts +15 -0
  25. package/dist/change.d.ts.map +1 -1
  26. package/dist/change.js +26 -1
  27. package/dist/chat/acts.d.ts +33 -0
  28. package/dist/chat/acts.d.ts.map +1 -0
  29. package/dist/chat/acts.js +36 -0
  30. package/dist/chat/context.d.ts +3 -0
  31. package/dist/chat/context.d.ts.map +1 -0
  32. package/dist/chat/context.js +10 -0
  33. package/dist/chat/input.d.ts +9 -0
  34. package/dist/chat/input.d.ts.map +1 -0
  35. package/dist/chat/input.js +99 -0
  36. package/dist/chat/kit.d.ts +61 -0
  37. package/dist/chat/kit.d.ts.map +1 -0
  38. package/dist/chat/kit.js +123 -0
  39. package/dist/chat/lasting.d.ts +6 -0
  40. package/dist/chat/lasting.d.ts.map +1 -0
  41. package/dist/chat/lasting.js +19 -0
  42. package/dist/chat/list.d.ts +27 -0
  43. package/dist/chat/list.d.ts.map +1 -0
  44. package/dist/chat/list.js +63 -0
  45. package/dist/chat/markdown.d.ts +20 -0
  46. package/dist/chat/markdown.d.ts.map +1 -0
  47. package/dist/chat/markdown.js +188 -0
  48. package/dist/chat/message.d.ts +6 -0
  49. package/dist/chat/message.d.ts.map +1 -0
  50. package/dist/chat/message.js +192 -0
  51. package/dist/chat/messages.d.ts +22 -0
  52. package/dist/chat/messages.d.ts.map +1 -0
  53. package/dist/chat/messages.js +155 -0
  54. package/dist/chat/name.d.ts +6 -0
  55. package/dist/chat/name.d.ts.map +1 -0
  56. package/dist/chat/name.js +37 -0
  57. package/dist/chat/opened.d.ts +11 -0
  58. package/dist/chat/opened.d.ts.map +1 -0
  59. package/dist/chat/opened.js +17 -0
  60. package/dist/chat/overlay.d.ts +31 -0
  61. package/dist/chat/overlay.d.ts.map +1 -0
  62. package/dist/chat/overlay.js +97 -0
  63. package/dist/chat/parts.d.ts +57 -0
  64. package/dist/chat/parts.d.ts.map +1 -0
  65. package/dist/chat/parts.js +119 -0
  66. package/dist/chat/process.d.ts +20 -0
  67. package/dist/chat/process.d.ts.map +1 -0
  68. package/dist/chat/process.js +32 -0
  69. package/dist/chat/provider.d.ts +63 -0
  70. package/dist/chat/provider.d.ts.map +1 -0
  71. package/dist/chat/provider.js +629 -0
  72. package/dist/chat/question.d.ts +9 -0
  73. package/dist/chat/question.d.ts.map +1 -0
  74. package/dist/chat/question.js +80 -0
  75. package/dist/chat/questions.d.ts +31 -0
  76. package/dist/chat/questions.d.ts.map +1 -0
  77. package/dist/chat/questions.js +111 -0
  78. package/dist/chat/read.d.ts +5 -0
  79. package/dist/chat/read.d.ts.map +1 -0
  80. package/dist/chat/read.js +13 -0
  81. package/dist/chat/ready.d.ts +34 -0
  82. package/dist/chat/ready.d.ts.map +1 -0
  83. package/dist/chat/ready.js +91 -0
  84. package/dist/chat/style.d.ts +4 -0
  85. package/dist/chat/style.d.ts.map +1 -0
  86. package/dist/chat/style.js +302 -0
  87. package/dist/chat.d.ts +14 -0
  88. package/dist/chat.d.ts.map +1 -0
  89. package/dist/chat.js +8 -0
  90. package/dist/client.d.ts +28 -14
  91. package/dist/client.d.ts.map +1 -1
  92. package/dist/client.js +71 -26
  93. package/dist/collection.d.ts +21 -12
  94. package/dist/collection.d.ts.map +1 -1
  95. package/dist/collection.js +271 -23
  96. package/dist/connection.d.ts +50 -23
  97. package/dist/connection.d.ts.map +1 -1
  98. package/dist/connection.js +2074 -735
  99. package/dist/conversation.d.ts +41 -14
  100. package/dist/conversation.d.ts.map +1 -1
  101. package/dist/conversation.js +546 -214
  102. package/dist/durability.d.ts +7 -1
  103. package/dist/durability.d.ts.map +1 -1
  104. package/dist/entry.d.ts +17 -0
  105. package/dist/entry.d.ts.map +1 -0
  106. package/dist/entry.js +156 -0
  107. package/dist/expectation.d.ts +21 -0
  108. package/dist/expectation.d.ts.map +1 -0
  109. package/dist/expectation.js +70 -0
  110. package/dist/failure.d.ts +13 -0
  111. package/dist/failure.d.ts.map +1 -0
  112. package/dist/failure.js +19 -0
  113. package/dist/fence.d.ts +6 -0
  114. package/dist/fence.d.ts.map +1 -0
  115. package/dist/fence.js +10 -0
  116. package/dist/host.d.ts +24 -2
  117. package/dist/host.d.ts.map +1 -1
  118. package/dist/host.js +126 -45
  119. package/dist/identity.d.ts +5 -0
  120. package/dist/identity.d.ts.map +1 -1
  121. package/dist/identity.js +19 -1
  122. package/dist/index.d.ts +16 -8
  123. package/dist/index.d.ts.map +1 -1
  124. package/dist/index.js +5 -0
  125. package/dist/memory.d.ts +17 -7
  126. package/dist/memory.d.ts.map +1 -1
  127. package/dist/memory.js +159 -46
  128. package/dist/model.d.ts +17 -14
  129. package/dist/model.d.ts.map +1 -1
  130. package/dist/model.js +16 -4
  131. package/dist/observation.d.ts +6 -1
  132. package/dist/observation.d.ts.map +1 -1
  133. package/dist/observation.js +38 -17
  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 +32 -3
  138. package/dist/outcome.d.ts.map +1 -1
  139. package/dist/outcome.js +42 -1
  140. package/dist/overlay.d.ts +16 -3
  141. package/dist/overlay.d.ts.map +1 -1
  142. package/dist/overlay.js +118 -77
  143. package/dist/paths.d.ts +0 -1
  144. package/dist/paths.d.ts.map +1 -1
  145. package/dist/paths.js +0 -16
  146. package/dist/query.d.ts +46 -6
  147. package/dist/query.d.ts.map +1 -1
  148. package/dist/query.js +85 -37
  149. package/dist/reach.d.ts +45 -0
  150. package/dist/reach.d.ts.map +1 -0
  151. package/dist/reach.js +306 -0
  152. package/dist/react.d.ts +44 -0
  153. package/dist/react.d.ts.map +1 -0
  154. package/dist/react.js +66 -0
  155. package/dist/read-side.d.ts +16 -5
  156. package/dist/read-side.d.ts.map +1 -1
  157. package/dist/read-side.js +408 -137
  158. package/dist/readiness.d.ts +53 -0
  159. package/dist/readiness.d.ts.map +1 -0
  160. package/dist/readiness.js +33 -0
  161. package/dist/recovery.d.ts +25 -0
  162. package/dist/recovery.d.ts.map +1 -0
  163. package/dist/recovery.js +197 -0
  164. package/dist/refresh.d.ts +12 -1
  165. package/dist/refresh.d.ts.map +1 -1
  166. package/dist/refresh.js +51 -36
  167. package/dist/replica.d.ts +10 -7
  168. package/dist/replica.d.ts.map +1 -1
  169. package/dist/replica.js +129 -73
  170. package/dist/room-draft.d.ts +41 -9
  171. package/dist/room-draft.d.ts.map +1 -1
  172. package/dist/room-draft.js +139 -28
  173. package/dist/rows.d.ts +11 -14
  174. package/dist/rows.d.ts.map +1 -1
  175. package/dist/rows.js +87 -50
  176. package/dist/save.d.ts +32 -3
  177. package/dist/save.d.ts.map +1 -1
  178. package/dist/save.js +465 -92
  179. package/dist/search.d.ts +37 -8
  180. package/dist/search.d.ts.map +1 -1
  181. package/dist/search.js +116 -45
  182. package/dist/seed.d.ts +16 -0
  183. package/dist/seed.d.ts.map +1 -0
  184. package/dist/seed.js +61 -0
  185. package/dist/shared.d.ts +11 -0
  186. package/dist/shared.d.ts.map +1 -0
  187. package/dist/shared.js +368 -0
  188. package/dist/status.d.ts +6 -2
  189. package/dist/status.d.ts.map +1 -1
  190. package/dist/status.js +0 -8
  191. package/dist/store.d.ts +76 -19
  192. package/dist/store.d.ts.map +1 -1
  193. package/dist/store.js +539 -200
  194. package/dist/turn.d.ts +7 -0
  195. package/dist/turn.d.ts.map +1 -0
  196. package/dist/turn.js +45 -0
  197. package/dist/wire.d.ts +162 -19
  198. package/dist/wire.d.ts.map +1 -1
  199. package/dist/wire.js +82 -61
  200. package/package.json +31 -7
  201. package/dist/attachment.d.ts +0 -3
  202. package/dist/attachment.d.ts.map +0 -1
  203. package/dist/attachment.js +0 -8
package/dist/query.js CHANGED
@@ -1,16 +1,22 @@
1
+ import { pointerFacts, } from '@agentshouse/mdmodel';
2
+ import { referenceField } from './model.js';
1
3
  import { normalize } from './paths.js';
2
4
  export class QueryError extends Error {
5
+ code;
6
+ retryable = false;
3
7
  constructor(code, at, problem) {
4
8
  super(`${code} at ${at}: ${problem}`);
9
+ this.code = code;
5
10
  }
6
11
  }
7
12
  const RANGE = ['$gt', '$gte', '$lt', '$lte'];
8
13
  const SINGLE = ['$eq', '$ne', '$in', '$nin', '$all', '$size', '$exists'];
9
14
  const FILTER = ['rooms', 'types', 'where', 'pathPrefix', 'hop', 'backlinks'];
10
15
  const RELATED = ['types', 'where', 'pathPrefix', 'field'];
11
- const PARTS = ['fields', 'sections', 'preamble', 'markdown', 'backlinks', 'change'];
16
+ const PARTS = ['fields', 'sections', 'preamble', 'markdown', 'backlinks', 'change', 'fileId'];
12
17
  const ORDER = ['by', 'direction', 'nulls', 'stringSort', 'locale', 'localeOptions'];
13
18
  const CALENDAR_DATE = /^(\d{4})-(\d{2})-(\d{2})$/;
19
+ const STRAY_ESCAPE = /~(?![01])/u;
14
20
  export function invalid(at, problem) {
15
21
  throw new QueryError('invalid-query', at, problem);
16
22
  }
@@ -68,23 +74,29 @@ function calendarDate(value) {
68
74
  const date = new Date(Date.UTC(year, month - 1, day));
69
75
  return (date.getUTCFullYear() === year && date.getUTCMonth() === month - 1 && date.getUTCDate() === day);
70
76
  }
71
- function fits(spec, value) {
72
- const item = spec.kind === 'list' ? spec.of : spec;
73
- if (item.kind === 'number')
77
+ function itemOf(facts) {
78
+ return facts.type === 'array' ? facts.items : facts;
79
+ }
80
+ function fits(facts, value) {
81
+ const item = itemOf(facts);
82
+ if (item?.type === undefined)
83
+ return true;
84
+ if (item.values !== undefined)
85
+ return item.values.includes(value);
86
+ if (item.type === 'number')
74
87
  return typeof value === 'number';
75
- if (typeof value !== 'string')
88
+ if (item.type === 'integer')
89
+ return Number.isSafeInteger(value);
90
+ if (item.type === 'boolean')
91
+ return typeof value === 'boolean';
92
+ if (item.type !== 'string' || typeof value !== 'string')
76
93
  return false;
77
- if (item.kind === 'enum')
78
- return item.values.includes(value);
79
- if (item.kind === 'date')
80
- return calendarDate(value);
81
- if (item.kind === 'reference')
94
+ if (item.reference !== undefined)
82
95
  return value !== '' && normalize(value) === value;
96
+ if (item.format === 'date')
97
+ return calendarDate(value);
83
98
  return true;
84
99
  }
85
- function reference(spec) {
86
- return spec.kind === 'reference' || (spec.kind === 'list' && spec.of.kind === 'reference');
87
- }
88
100
  function typing(facts, rooms, types, at) {
89
101
  const read = rooms === null ? facts.rooms : facts.rooms.filter((room) => rooms.includes(room.locator));
90
102
  const acquiring = !facts.listed &&
@@ -102,22 +114,46 @@ function typing(facts, rooms, types, at) {
102
114
  types: types === null ? declared : declared.filter((type) => types.includes(type.name)),
103
115
  };
104
116
  }
105
- function specs(typed, field, at) {
106
- const declared = typed.types.flatMap((type) => Object.hasOwn(type.fields, field) ? [type.fields[field]] : []);
117
+ function pointer(input, at) {
118
+ if (typeof input !== 'string' || !input.startsWith('/') || STRAY_ESCAPE.test(input)) {
119
+ invalid(at, 'expects a JSON Pointer');
120
+ }
121
+ return input;
122
+ }
123
+ function specs(typed, input, at) {
124
+ const field = pointer(input, at);
125
+ const declared = typed.types.flatMap((type) => {
126
+ const spec = pointerFacts(type, field);
127
+ return spec === undefined ? [] : [spec];
128
+ });
107
129
  if (declared.length === 0 && !typed.untyped) {
108
130
  throw new QueryError('unknown-field', at, `no MD Type it reads declares ${field}`);
109
131
  }
110
- return declared;
132
+ if (!declared.some((spec) => spec.conditional))
133
+ return declared;
134
+ return declared.map((spec) => ({
135
+ facts: { prose: false },
136
+ through: several(spec),
137
+ conditional: true,
138
+ }));
139
+ }
140
+ function several(spec) {
141
+ return spec.through || spec.facts.type === 'array';
142
+ }
143
+ function single(spec) {
144
+ return spec.facts.type !== undefined && spec.facts.type !== 'array';
145
+ }
146
+ function every(declared, holds) {
147
+ return declared.length > 0 && declared.every(holds);
111
148
  }
112
149
  function kind(declared, checked, at) {
113
- if (checked === null || declared.length === 0 || declared.some((spec) => fits(spec, checked))) {
150
+ if (checked === null ||
151
+ declared.length === 0 ||
152
+ declared.some((spec) => fits(spec.facts, checked))) {
114
153
  return;
115
154
  }
116
155
  throw new QueryError('wrong-kind', at, `${JSON.stringify(checked)} is not of its field's kind`);
117
156
  }
118
- function listed(declared, list) {
119
- return declared.length > 0 && declared.every((spec) => (spec.kind === 'list') === list);
120
- }
121
157
  function test(declared, input, at) {
122
158
  if (!plain(input)) {
123
159
  const equal = value(input, at);
@@ -131,8 +167,11 @@ function test(declared, input, at) {
131
167
  const stray = operators.find((operator) => !RANGE.includes(operator));
132
168
  if (stray !== undefined)
133
169
  invalid(place(at, stray), 'cannot join a range');
134
- if (listed(declared, true))
170
+ if (every(declared, (spec) => spec.facts.type === 'array'))
135
171
  invalid(at, 'a range on a list');
172
+ if (every(declared, (spec) => spec.facts.type === 'boolean' || spec.facts.type === 'object')) {
173
+ throw new QueryError('wrong-kind', at, 'its field does not compare in order');
174
+ }
136
175
  return {
137
176
  op: 'range',
138
177
  bounds: RANGE.filter((operator) => operators.includes(operator)).map((operator) => {
@@ -165,7 +204,7 @@ function test(declared, input, at) {
165
204
  return { op: 'nin', values: compared };
166
205
  if (compared.includes(null))
167
206
  invalid(where, 'a list holds no null');
168
- if (listed(declared, false))
207
+ if (every(declared, single))
169
208
  invalid(where, 'the field is not a list');
170
209
  return { op: 'all', values: compared };
171
210
  }
@@ -173,7 +212,7 @@ function test(declared, input, at) {
173
212
  if (!Number.isSafeInteger(operand) || operand < 0) {
174
213
  invalid(where, 'expects a nonnegative integer');
175
214
  }
176
- if (listed(declared, false))
215
+ if (every(declared, single))
177
216
  invalid(where, 'the field is not a list');
178
217
  return { op: 'size', size: operand };
179
218
  }
@@ -186,7 +225,7 @@ function condition(typed, input, at) {
186
225
  invalid(at, 'expects an object of field predicates');
187
226
  const conditions = [];
188
227
  for (const name of Object.keys(input).sort()) {
189
- const where = place(at, name);
228
+ const where = name.startsWith('/') ? `${at}${name}` : place(at, name);
190
229
  const entry = input[name];
191
230
  if (name === '$and' || name === '$or') {
192
231
  const nested = list(entry, where).map((item, index) => condition(typed, item, `${where}[${index}]`));
@@ -215,19 +254,19 @@ function scope(facts, rooms, input, at) {
215
254
  pathPrefix: input.pathPrefix === undefined ? null : directory(input.pathPrefix, place(at, 'pathPrefix')),
216
255
  };
217
256
  }
218
- function referenceField(typed, field, at) {
219
- if (typeof field !== 'string')
220
- invalid(at, 'expects a field name');
257
+ function referenceName(typed, field, at) {
221
258
  const declared = specs(typed, field, at);
222
- if (declared.length > 0 && !declared.some(reference))
259
+ if (declared.length > 0 &&
260
+ !declared.some((spec) => spec.conditional || referenceField(spec.facts))) {
223
261
  invalid(at, 'not a reference field');
262
+ }
224
263
  return field;
225
264
  }
226
265
  function hopOf(facts, rooms, root, input) {
227
266
  if (!plain(input))
228
267
  invalid('hop', 'expects an object');
229
268
  known(input, RELATED, 'hop');
230
- const field = referenceField(root, input.field, 'hop.field');
269
+ const field = referenceName(root, input.field, 'hop.field');
231
270
  const { types, where, pathPrefix } = scope(facts, rooms, input, 'hop');
232
271
  return { types, where, pathPrefix, field };
233
272
  }
@@ -236,14 +275,16 @@ function backlinksOf(facts, rooms, input) {
236
275
  invalid('backlinks', 'expects an object');
237
276
  known(input, RELATED, 'backlinks');
238
277
  const { typed, types, where, pathPrefix } = scope(facts, rooms, input, 'backlinks');
239
- const field = input.field === undefined ? null : referenceField(typed, input.field, 'backlinks.field');
278
+ const field = input.field === undefined ? null : referenceName(typed, input.field, 'backlinks.field');
240
279
  return { types, where, pathPrefix, field };
241
280
  }
242
281
  function keyPlan(typed, input, at, path) {
243
282
  if (input === 'room' || input === 'type' || (path && input === 'path'))
244
283
  return { of: input };
245
- if (plain(input) && typeof input.field === 'string' && Object.keys(input).length === 1) {
246
- specs(typed, input.field, place(at, 'field'));
284
+ if (plain(input) && Object.hasOwn(input, 'field') && Object.keys(input).length === 1) {
285
+ if (specs(typed, input.field, place(at, 'field')).some(several)) {
286
+ invalid(place(at, 'field'), 'a path that can hold several values');
287
+ }
247
288
  return { of: 'field', field: input.field };
248
289
  }
249
290
  return invalid(at, path ? 'expects room, path, type or a field' : 'expects room, type or a field');
@@ -301,10 +342,10 @@ function parts(typed, input, at, fields) {
301
342
  return 'all';
302
343
  const named = list(input, at);
303
344
  named.forEach((name, index) => {
304
- if (typeof name !== 'string')
305
- invalid(`${at}[${index}]`, 'expects a name');
306
345
  if (fields)
307
346
  specs(typed, name, `${at}[${index}]`);
347
+ else if (typeof name !== 'string')
348
+ invalid(`${at}[${index}]`, 'expects a name');
308
349
  });
309
350
  return named;
310
351
  }
@@ -336,6 +377,7 @@ function documentShape(typed, input) {
336
377
  markdown: true,
337
378
  backlinks: true,
338
379
  change: true,
380
+ fileId: true,
339
381
  orderBy,
340
382
  };
341
383
  }
@@ -350,6 +392,7 @@ function documentShape(typed, input) {
350
392
  markdown: flag(select.markdown, 'select.markdown'),
351
393
  backlinks: flag(select.backlinks, 'select.backlinks'),
352
394
  change: flag(select.change, 'select.change'),
395
+ fileId: flag(select.fileId, 'select.fileId'),
353
396
  orderBy,
354
397
  };
355
398
  }
@@ -372,7 +415,7 @@ function groupShape(typed, input) {
372
415
  });
373
416
  return { kind: 'groups', groupBy, orderBy };
374
417
  }
375
- export function planOf(input, facts) {
418
+ export function planOf(input, facts, reach = null) {
376
419
  if (!plain(input))
377
420
  invalid('query', 'expects a query object');
378
421
  const grouped = input.groupBy !== undefined;
@@ -385,7 +428,9 @@ export function planOf(input, facts) {
385
428
  const rooms = input.rooms === undefined ? null : names(input.rooms, 'rooms');
386
429
  const { typed, ...root } = scope(facts, rooms, input, '');
387
430
  return {
431
+ key: queryKey(input),
388
432
  rooms,
433
+ reach,
389
434
  root,
390
435
  hop: input.hop === undefined ? null : hopOf(facts, rooms, typed, input.hop),
391
436
  backlinks: input.backlinks === undefined ? null : backlinksOf(facts, rooms, input.backlinks),
@@ -401,11 +446,14 @@ export function limitOf(input) {
401
446
  }
402
447
  return input;
403
448
  }
404
- export function filterOf(input, facts) {
449
+ export function filterOf(input, facts, reach = null) {
405
450
  if (!plain(input))
406
451
  invalid('documents', 'expects a filter object');
407
452
  known(input, FILTER, '');
408
- return planOf({ ...input, select: {} }, facts);
453
+ return planOf({ ...input, select: {} }, facts, reach);
454
+ }
455
+ export function queryKey(query) {
456
+ return canonical(query);
409
457
  }
410
458
  function canonical(value) {
411
459
  if (Array.isArray(value))
@@ -0,0 +1,45 @@
1
+ import type { ReferrerScope } from './adapter.js';
2
+ import type { MdRuntime, ReadinessPort, RuntimeTopic } from './connection.js';
3
+ import type { Plan, PlanReach, QueryResult } from './query.js';
4
+ import type { Readiness } from './readiness.js';
5
+ import { type SearchResult } from './search.js';
6
+ export type ReachedDocument = {
7
+ readonly room: string;
8
+ readonly path: string;
9
+ };
10
+ export type PortReach = {
11
+ readonly rooms: readonly string[];
12
+ readonly documents: readonly ReachedDocument[];
13
+ readonly conversations: boolean;
14
+ };
15
+ export type Deliver = (message: unknown) => void;
16
+ export type Watch = (topic: RuntimeTopic, input: readonly unknown[], deliver: Deliver) => () => void;
17
+ export type RuntimeView = {
18
+ md: MdRuntime;
19
+ watch: Watch;
20
+ };
21
+ export type NarrowedView = RuntimeView & {
22
+ narrow(reach: PortReach | null): void;
23
+ facts(port: ReadinessPort): void;
24
+ };
25
+ export type Reachable = RuntimeView & {
26
+ plan(input: unknown, reach: PortReach | null): Plan;
27
+ query(plan: Plan): Promise<QueryResult>;
28
+ watchPlan(plan: Plan, deliver: (message: unknown) => void): () => void;
29
+ search(input: unknown, reach: PortReach | null): Promise<SearchResult>;
30
+ watchReadiness(port: ReadinessPort, reach: PortReach | null, deliver: (readiness: Readiness) => void): () => void;
31
+ referrerScope(referrer: unknown): ReferrerScope | null;
32
+ };
33
+ export declare function holds(reach: PortReach | null, room: string): boolean;
34
+ export declare function governs(reach: PortReach | null, room: {
35
+ readonly locator: string;
36
+ readonly typedSelection: {
37
+ roots: readonly string[];
38
+ } | null;
39
+ }): {
40
+ readonly collection: boolean;
41
+ readonly selection: boolean;
42
+ };
43
+ export declare function planReach(reach: PortReach | null): PlanReach | null;
44
+ export declare function narrowed(runtime: Reachable, given: PortReach | null, told: ReadinessPort): NarrowedView;
45
+ //# sourceMappingURL=reach.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reach.d.ts","sourceRoot":"","sources":["../src/reach.ts"],"names":[],"mappings":"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"}
package/dist/reach.js ADDED
@@ -0,0 +1,306 @@
1
+ import { unheldView } from './conversation.js';
2
+ import { modelTypes, NO_MODEL } from './model.js';
3
+ import { applied, observing } from './observation.js';
4
+ import { runtimeError } from './failure.js';
5
+ import { failure, undeclared, } from './outcome.js';
6
+ import { beneath } from './paths.js';
7
+ import { reconciled } from './read-side.js';
8
+ import { moveTarget, readParts, staleWrite, } from './room-draft.js';
9
+ import { documentKey } from './rows.js';
10
+ import { undeclaredSearch } from './search.js';
11
+ const UNHELD_TYPES = { collection: modelTypes(NO_MODEL), typedSelection: null };
12
+ function unconversed(name) {
13
+ return () => Promise.resolve(undeclared(name));
14
+ }
15
+ const NO_CONVERSATION = {
16
+ list: () => Promise.resolve([]),
17
+ read: (conversation) => Promise.resolve(unheldView(conversation)),
18
+ create: unconversed('conversation.create'),
19
+ send: unconversed('conversation.send'),
20
+ answer: unconversed('conversation.answer'),
21
+ interrupt: unconversed('conversation.interrupt'),
22
+ kill: unconversed('conversation.kill'),
23
+ cancel: unconversed('conversation.cancel'),
24
+ delete: unconversed('conversation.delete'),
25
+ };
26
+ const NO_CHAT_DRAFT = {
27
+ write: () => Promise.resolve(),
28
+ discard: () => Promise.resolve(),
29
+ };
30
+ export function holds(reach, room) {
31
+ return (reach === null ||
32
+ reach.rooms.includes(room) ||
33
+ reach.documents.some((document) => document.room === room));
34
+ }
35
+ function wholly(reach, room) {
36
+ return reach === null || reach.rooms.includes(room);
37
+ }
38
+ export function governs(reach, room) {
39
+ if (wholly(reach, room.locator)) {
40
+ return { collection: true, selection: room.typedSelection !== null };
41
+ }
42
+ const selected = (path) => room.typedSelection?.roots.some((root) => beneath(root, path)) ?? false;
43
+ const paths = reach.documents
44
+ .filter((document) => document.room === room.locator)
45
+ .map(({ path }) => path);
46
+ return { collection: paths.some((path) => !selected(path)), selection: paths.some(selected) };
47
+ }
48
+ function covers(reach, room, path) {
49
+ return (reach === null ||
50
+ reach.rooms.includes(room) ||
51
+ reach.documents.some((document) => document.room === room && document.path === path));
52
+ }
53
+ function conversing(reach) {
54
+ return reach === null || reach.conversations;
55
+ }
56
+ function refers(reach, scope) {
57
+ if (reach === null)
58
+ return true;
59
+ if (scope === null)
60
+ return false;
61
+ if (scope.kind === 'conversations')
62
+ return reach.conversations;
63
+ return scope.path === undefined
64
+ ? wholly(reach, scope.locator)
65
+ : covers(reach, scope.locator, scope.path);
66
+ }
67
+ function unreachedReference(fileRef) {
68
+ return { kind: 'absent', code: 'binary-file-absent', fields: { fileRef }, retryable: false };
69
+ }
70
+ export function planReach(reach) {
71
+ return reach === null
72
+ ? null
73
+ : {
74
+ rooms: [...new Set(reach.rooms)].sort(),
75
+ documents: [
76
+ ...new Set(reach.documents.map(({ room, path }) => documentKey(room, path))),
77
+ ].sort(),
78
+ };
79
+ }
80
+ function roomsWithin(reach, rooms) {
81
+ return rooms.filter((room) => wholly(reach, room.locator));
82
+ }
83
+ function typesWithin(reach, room, types) {
84
+ if (room === undefined)
85
+ return structuredClone(UNHELD_TYPES);
86
+ const governed = governs(reach, room);
87
+ return {
88
+ collection: governed.collection ? types.collection : modelTypes(NO_MODEL),
89
+ typedSelection: governed.selection ? types.typedSelection : null,
90
+ };
91
+ }
92
+ function moves(reach, room, move) {
93
+ return move === undefined || covers(reach, room, moveTarget(move));
94
+ }
95
+ function draftWithin(reach, draft) {
96
+ return covers(reach, draft.room, draft.path) && moves(reach, draft.room, draft.parts.move);
97
+ }
98
+ function draftsWithin(reach, drafts) {
99
+ return drafts.filter((draft) => draftWithin(reach, draft));
100
+ }
101
+ function shownDraft(reach, draft) {
102
+ return draft !== null && draftWithin(reach, draft) ? draft : null;
103
+ }
104
+ function writeWithin(reach, write) {
105
+ return write.outcome === 'written'
106
+ ? { outcome: 'written', roomDraft: shownDraft(reach, write.roomDraft) }
107
+ : staleWrite(shownDraft(reach, write.current));
108
+ }
109
+ function savedWithin(reach, room, result) {
110
+ if (reach === null || result.outcome === 'saved')
111
+ return result;
112
+ const { file, location, referrers, ...refusal } = result;
113
+ const hidden = file !== undefined && !covers(reach, room, file);
114
+ return {
115
+ ...refusal,
116
+ ...(file === undefined || hidden ? {} : { file }),
117
+ ...(location === undefined || hidden ? {} : { location }),
118
+ ...(referrers === undefined
119
+ ? {}
120
+ : { referrers: referrers.filter((path) => covers(reach, room, path)) }),
121
+ };
122
+ }
123
+ function statusWithin(reach, status) {
124
+ return {
125
+ ...status,
126
+ attentionRequired: status.attentionRequired.filter((entry) => 'refusal' in entry ? wholly(reach, entry.room) : covers(reach, entry.room, entry.path)),
127
+ };
128
+ }
129
+ function queueWithin(reach, room, entries) {
130
+ return entries.filter(({ batch }) => batch.changes.every((change) => covers(reach, room, change.path) &&
131
+ (change.kind !== 'move' || covers(reach, room, change.to))));
132
+ }
133
+ function unreached(room) {
134
+ return runtimeError('room-not-held', `No Local Replica for ${room}`);
135
+ }
136
+ export function narrowed(runtime, given, told) {
137
+ const { md, watch } = runtime;
138
+ let reach = given;
139
+ let port = told;
140
+ const open = new Set();
141
+ function following(begin) {
142
+ let stop = begin();
143
+ const restart = () => {
144
+ stop();
145
+ stop = begin();
146
+ };
147
+ open.add(restart);
148
+ return () => {
149
+ open.delete(restart);
150
+ stop();
151
+ };
152
+ }
153
+ const within = (topic, input, deliver) => {
154
+ if (topic === 'rooms') {
155
+ return following(() => watch(topic, input, (rooms) => deliver(roomsWithin(reach, rooms))));
156
+ }
157
+ if (topic === 'readiness') {
158
+ return following(() => runtime.watchReadiness(port, reach, deliver));
159
+ }
160
+ if (topic === 'sync-status') {
161
+ return following(() => watch(topic, input, (status) => deliver(statusWithin(reach, status))));
162
+ }
163
+ if (topic === 'room-drafts') {
164
+ return following(() => watch(topic, input, (drafts) => deliver(draftsWithin(reach, drafts))));
165
+ }
166
+ if (topic === 'queue') {
167
+ const room = input[0];
168
+ return following(() => {
169
+ if (!holds(reach, room)) {
170
+ deliver([]);
171
+ return () => { };
172
+ }
173
+ return watch(topic, input, (entries) => deliver(queueWithin(reach, room, entries)));
174
+ });
175
+ }
176
+ if (topic === 'query') {
177
+ const plan = runtime.plan(input[0], reach);
178
+ let held = null;
179
+ return following(() => {
180
+ let first = true;
181
+ return runtime.watchPlan({ ...plan, reach: planReach(reach) }, (message) => {
182
+ if (!first) {
183
+ held = applied(held, message);
184
+ deliver(message);
185
+ return;
186
+ }
187
+ first = false;
188
+ if (held === null) {
189
+ held = message;
190
+ deliver(message);
191
+ return;
192
+ }
193
+ const next = reconciled(held, message);
194
+ if (next === null)
195
+ return;
196
+ held = next.keyed;
197
+ deliver(next.message);
198
+ });
199
+ });
200
+ }
201
+ if (topic === 'conversation') {
202
+ return following(() => {
203
+ if (conversing(reach))
204
+ return watch(topic, input, deliver);
205
+ deliver(unheldView(input[0]));
206
+ return () => { };
207
+ });
208
+ }
209
+ if (topic === 'conversations' || topic === 'agents') {
210
+ return following(() => {
211
+ if (conversing(reach))
212
+ return watch(topic, input, deliver);
213
+ deliver([]);
214
+ return () => { };
215
+ });
216
+ }
217
+ return watch(topic, input, deliver);
218
+ };
219
+ const conversation = () => (conversing(reach) ? md.conversation : NO_CONVERSATION);
220
+ const chatDraft = () => (conversing(reach) ? md.chatDraft : NO_CHAT_DRAFT);
221
+ const view = {
222
+ ...md,
223
+ rooms: () => roomsWithin(reach, md.rooms()),
224
+ query: ((input) => runtime.query(runtime.plan(input, reach))),
225
+ search: (input) => port.search === 'required'
226
+ ? runtime.search(input, reach)
227
+ : Promise.resolve(undeclaredSearch()),
228
+ types(room) {
229
+ if (wholly(reach, room))
230
+ return md.types(room);
231
+ if (!holds(reach, room))
232
+ return structuredClone(UNHELD_TYPES);
233
+ const listed = md.rooms().find(({ locator }) => locator === room);
234
+ return typesWithin(reach, listed, md.types(room));
235
+ },
236
+ async save(room, roomDrafts, writtenBy) {
237
+ if (!holds(reach, room))
238
+ throw unreached(room);
239
+ const outside = roomDrafts.find(({ path }) => !covers(reach, room, path) ||
240
+ !moves(reach, room, md.roomDraft.read(room, path)?.parts.move));
241
+ if (outside !== undefined) {
242
+ return { outcome: 'refused', code: 'stale', retryable: false, file: outside.path };
243
+ }
244
+ return savedWithin(reach, room, await md.save(room, roomDrafts, writtenBy));
245
+ },
246
+ history: (room, path, query) => covers(reach, room, path)
247
+ ? md.history(room, path, query)
248
+ : Promise.resolve(failure({ kind: 'room-not-readable', locator: room })),
249
+ binaryFile: {
250
+ read: (room, path, revision) => covers(reach, room, path)
251
+ ? md.binaryFile.read(room, path, revision)
252
+ : Promise.resolve(failure({ kind: 'room-not-readable', locator: room })),
253
+ readReference: (reference) => refers(reach, runtime.referrerScope(reference.referrer))
254
+ ? md.binaryFile.readReference(reference)
255
+ : Promise.resolve(unreachedReference(reference.fileRef)),
256
+ },
257
+ reached: (room, roomVersion, path) => (path === undefined ? wholly(reach, room) : covers(reach, room, path)) &&
258
+ md.reached(room, roomVersion, path),
259
+ log: () => (reach === null ? md.log() : ''),
260
+ roomDraft: {
261
+ list: () => md.roomDraft.list().then((drafts) => draftsWithin(reach, drafts)),
262
+ read: (room, path) => covers(reach, room, path) ? shownDraft(reach, md.roomDraft.read(room, path)) : null,
263
+ async write(room, path, revision, parts) {
264
+ if (!covers(reach, room, path) || !moves(reach, room, readParts(parts).move)) {
265
+ throw unreached(room);
266
+ }
267
+ return writeWithin(reach, await md.roomDraft.write(room, path, revision, parts));
268
+ },
269
+ async discard(room, path, revision) {
270
+ if (!covers(reach, room, path))
271
+ return staleWrite(null);
272
+ return writeWithin(reach, await md.roomDraft.discard(room, path, revision));
273
+ },
274
+ },
275
+ conversation: {
276
+ list: () => conversation().list(),
277
+ read: (id, options) => conversation().read(id, options),
278
+ create: (input) => conversation().create(input),
279
+ send: (id, operation, draft) => conversation().send(id, operation, draft),
280
+ answer: (id, answer) => conversation().answer(id, answer),
281
+ interrupt: (id) => conversation().interrupt(id),
282
+ kill: (id) => conversation().kill(id),
283
+ cancel: (id, turn) => conversation().cancel(id, turn),
284
+ delete: (id) => conversation().delete(id),
285
+ },
286
+ chatDraft: {
287
+ write: (id, draft, revision) => chatDraft().write(id, draft, revision),
288
+ discard: (id) => chatDraft().discard(id),
289
+ },
290
+ observe: observing(within),
291
+ };
292
+ return {
293
+ md: view,
294
+ watch: within,
295
+ narrow(next) {
296
+ reach = next;
297
+ for (const restart of [...open])
298
+ restart();
299
+ },
300
+ facts(next) {
301
+ port = next;
302
+ for (const restart of [...open])
303
+ restart();
304
+ },
305
+ };
306
+ }
@@ -0,0 +1,44 @@
1
+ import type { MdRuntimeClient } from './client.js';
2
+ import type { AppRoom, ConnectionRefusal, SavedRoomDraft, SaveResult } from './connection.js';
3
+ import type { Member, ServiceMode } from './adapter.js';
4
+ import type { ConversationAgent, ConversationEntry, ConversationOptions, ConversationView } from './conversation.js';
5
+ import { type RuntimeError } from './failure.js';
6
+ import type { QueueEntry } from './outcome.js';
7
+ import type { DocumentQuery, DocumentResult, GroupQuery, GroupResult, PendingResult } from './query.js';
8
+ import type { Readiness } from './readiness.js';
9
+ import type { RoomDraft, RoomDraftParts } from './room-draft.js';
10
+ import type { SyncStatus } from './status.js';
11
+ type Client = MdRuntimeClient | null;
12
+ export type Observed<T> = {
13
+ readonly state: 'loading';
14
+ } | {
15
+ readonly state: 'ready';
16
+ readonly value: T;
17
+ };
18
+ export declare function useObserve(client: Client, topic: 'deployment-facts'): Observed<unknown>;
19
+ export declare function useObserve(client: Client, topic: 'rooms'): Observed<readonly AppRoom[]>;
20
+ export declare function useObserve(client: Client, topic: 'readiness'): Observed<Readiness>;
21
+ export declare function useObserve(client: Client, topic: 'connection-refusal'): Observed<ConnectionRefusal | null>;
22
+ export declare function useObserve(client: Client, topic: 'storage-loss'): Observed<string | null>;
23
+ export declare function useObserve(client: Client, topic: 'sync-status'): Observed<SyncStatus>;
24
+ export declare function useObserve(client: Client, topic: 'service-mode'): Observed<ServiceMode>;
25
+ export declare function useObserve(client: Client, topic: 'member'): Observed<Member | null>;
26
+ export declare function useObserve(client: Client, topic: 'query', query: DocumentQuery): Observed<DocumentResult | PendingResult>;
27
+ export declare function useObserve(client: Client, topic: 'query', query: GroupQuery): Observed<GroupResult | PendingResult>;
28
+ export declare function useObserve(client: Client, topic: 'queue', locator: string): Observed<readonly QueueEntry[]>;
29
+ export declare function useObserve(client: Client, topic: 'room-drafts'): Observed<readonly RoomDraft[]>;
30
+ export declare function useObserve(client: Client, topic: 'presentation'): Observed<unknown>;
31
+ export declare function useObserve(client: Client, topic: 'conversation', conversation: string, options: ConversationOptions): Observed<ConversationView>;
32
+ export declare function useObserve(client: Client, topic: 'conversations'): Observed<readonly ConversationEntry[]>;
33
+ export declare function useObserve(client: Client, topic: 'agents'): Observed<readonly ConversationAgent[]>;
34
+ export type Saving = {
35
+ readonly busy: boolean;
36
+ readonly outcome: SaveResult | null;
37
+ readonly failure: RuntimeError | null;
38
+ save(room: string, roomDrafts: ReadonlyMap<string, RoomDraftParts>): Promise<SaveResult>;
39
+ saveWritten(room: string, roomDrafts: readonly SavedRoomDraft[]): Promise<SaveResult>;
40
+ };
41
+ export declare function saveRoomDrafts(client: MdRuntimeClient, room: string, roomDrafts: ReadonlyMap<string, RoomDraftParts>): Promise<SaveResult>;
42
+ export declare function useSave(client: MdRuntimeClient): Saving;
43
+ export {};
44
+ //# sourceMappingURL=react.d.ts.map