@agentshouse/mdruntime 0.1.0-cd3486bd400ed → 0.1.0-cd37f80239af0

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 (201) hide show
  1. package/README.md +21 -9
  2. package/dist/adapter.d.ts +149 -46
  3. package/dist/adapter.d.ts.map +1 -1
  4. package/dist/attached-model.d.ts +5 -1
  5. package/dist/attached-model.d.ts.map +1 -1
  6. package/dist/attached-model.js +38 -4
  7. package/dist/attempt.d.ts.map +1 -1
  8. package/dist/attempt.js +14 -1
  9. package/dist/binary.d.ts +18 -0
  10. package/dist/binary.d.ts.map +1 -0
  11. package/dist/binary.js +8 -0
  12. package/dist/bootstrap.d.ts +27 -11
  13. package/dist/bootstrap.d.ts.map +1 -1
  14. package/dist/bootstrap.js +184 -84
  15. package/dist/chain.d.ts +11 -0
  16. package/dist/chain.d.ts.map +1 -0
  17. package/dist/chain.js +64 -0
  18. package/dist/change-set.d.ts +9 -14
  19. package/dist/change-set.d.ts.map +1 -1
  20. package/dist/change-set.js +11 -62
  21. package/dist/change.d.ts +41 -0
  22. package/dist/change.d.ts.map +1 -1
  23. package/dist/change.js +34 -0
  24. package/dist/client.d.ts +31 -13
  25. package/dist/client.d.ts.map +1 -1
  26. package/dist/client.js +103 -7
  27. package/dist/collection.d.ts +10 -7
  28. package/dist/collection.d.ts.map +1 -1
  29. package/dist/collection.js +38 -79
  30. package/dist/compile.d.ts +16 -0
  31. package/dist/compile.d.ts.map +1 -0
  32. package/dist/compile.js +786 -0
  33. package/dist/connection.d.ts +50 -16
  34. package/dist/connection.d.ts.map +1 -1
  35. package/dist/connection.js +3786 -931
  36. package/dist/conversation.d.ts +37 -16
  37. package/dist/conversation.d.ts.map +1 -1
  38. package/dist/conversation.js +700 -257
  39. package/dist/digest.d.ts +7 -0
  40. package/dist/digest.d.ts.map +1 -0
  41. package/dist/digest.js +113 -0
  42. package/dist/durability.d.ts +4 -1
  43. package/dist/durability.d.ts.map +1 -1
  44. package/dist/durability.js +5 -0
  45. package/dist/entry.d.ts +20 -0
  46. package/dist/entry.d.ts.map +1 -0
  47. package/dist/entry.js +151 -0
  48. package/dist/fence.d.ts +6 -0
  49. package/dist/fence.d.ts.map +1 -0
  50. package/dist/fence.js +10 -0
  51. package/dist/file.d.ts +2 -19
  52. package/dist/file.d.ts.map +1 -1
  53. package/dist/file.js +7 -122
  54. package/dist/fulltext.d.ts +27 -0
  55. package/dist/fulltext.d.ts.map +1 -0
  56. package/dist/fulltext.js +370 -0
  57. package/dist/host.d.ts +13 -3
  58. package/dist/host.d.ts.map +1 -1
  59. package/dist/host.js +130 -17
  60. package/dist/identity.d.ts +2 -0
  61. package/dist/identity.d.ts.map +1 -1
  62. package/dist/identity.js +6 -0
  63. package/dist/index.d.ts +17 -9
  64. package/dist/index.d.ts.map +1 -1
  65. package/dist/index.js +7 -0
  66. package/dist/live.d.ts +22 -0
  67. package/dist/live.d.ts.map +1 -0
  68. package/dist/live.js +123 -0
  69. package/dist/media.d.ts +8 -0
  70. package/dist/media.d.ts.map +1 -0
  71. package/dist/media.js +218 -0
  72. package/dist/memory.d.ts +17 -7
  73. package/dist/memory.d.ts.map +1 -1
  74. package/dist/memory.js +231 -91
  75. package/dist/messages.d.ts +28 -0
  76. package/dist/messages.d.ts.map +1 -0
  77. package/dist/messages.js +90 -0
  78. package/dist/model.d.ts +9 -2
  79. package/dist/model.d.ts.map +1 -1
  80. package/dist/model.js +8 -2
  81. package/dist/name.d.ts +4 -0
  82. package/dist/name.d.ts.map +1 -0
  83. package/dist/name.js +122 -0
  84. package/dist/observation.d.ts +20 -2
  85. package/dist/observation.d.ts.map +1 -1
  86. package/dist/observation.js +113 -12
  87. package/dist/opened.d.ts +22 -0
  88. package/dist/opened.d.ts.map +1 -0
  89. package/dist/opened.js +107 -0
  90. package/dist/outcome.d.ts +52 -4
  91. package/dist/outcome.d.ts.map +1 -1
  92. package/dist/outcome.js +46 -0
  93. package/dist/overlay.d.ts +15 -5
  94. package/dist/overlay.d.ts.map +1 -1
  95. package/dist/overlay.js +129 -177
  96. package/dist/paths.d.ts +1 -0
  97. package/dist/paths.d.ts.map +1 -1
  98. package/dist/paths.js +4 -0
  99. package/dist/query.d.ts +68 -4
  100. package/dist/query.d.ts.map +1 -1
  101. package/dist/query.js +48 -7
  102. package/dist/reach.d.ts +18 -4
  103. package/dist/reach.d.ts.map +1 -1
  104. package/dist/reach.js +224 -55
  105. package/dist/react.d.ts +9 -6
  106. package/dist/react.d.ts.map +1 -1
  107. package/dist/react.js +7 -17
  108. package/dist/readiness.d.ts +52 -0
  109. package/dist/readiness.d.ts.map +1 -0
  110. package/dist/readiness.js +27 -0
  111. package/dist/recovery.d.ts +26 -0
  112. package/dist/recovery.d.ts.map +1 -0
  113. package/dist/recovery.js +224 -0
  114. package/dist/refresh.d.ts +17 -14
  115. package/dist/refresh.d.ts.map +1 -1
  116. package/dist/refresh.js +136 -56
  117. package/dist/replica.d.ts +58 -33
  118. package/dist/replica.d.ts.map +1 -1
  119. package/dist/replica.js +307 -159
  120. package/dist/room-draft.d.ts +51 -4
  121. package/dist/room-draft.d.ts.map +1 -1
  122. package/dist/room-draft.js +210 -18
  123. package/dist/rows.d.ts +37 -35
  124. package/dist/rows.d.ts.map +1 -1
  125. package/dist/rows.js +173 -102
  126. package/dist/save.d.ts +70 -3
  127. package/dist/save.d.ts.map +1 -1
  128. package/dist/save.js +805 -147
  129. package/dist/search.d.ts +39 -28
  130. package/dist/search.d.ts.map +1 -1
  131. package/dist/search.js +37 -105
  132. package/dist/seed.d.ts +16 -0
  133. package/dist/seed.d.ts.map +1 -0
  134. package/dist/seed.js +75 -0
  135. package/dist/shared.d.ts +15 -0
  136. package/dist/shared.d.ts.map +1 -0
  137. package/dist/shared.js +421 -0
  138. package/dist/status.d.ts +5 -0
  139. package/dist/status.d.ts.map +1 -1
  140. package/dist/status.js +3 -1
  141. package/dist/store.d.ts +107 -80
  142. package/dist/store.d.ts.map +1 -1
  143. package/dist/store.js +325 -461
  144. package/dist/stored.d.ts +130 -0
  145. package/dist/stored.d.ts.map +1 -0
  146. package/dist/stored.js +560 -0
  147. package/dist/tables.d.ts +325 -0
  148. package/dist/tables.d.ts.map +1 -0
  149. package/dist/tables.js +578 -0
  150. package/dist/transfer.d.ts +0 -5
  151. package/dist/transfer.d.ts.map +1 -1
  152. package/dist/turn.d.ts +9 -0
  153. package/dist/turn.d.ts.map +1 -0
  154. package/dist/turn.js +75 -0
  155. package/dist/validation.d.ts +29 -0
  156. package/dist/validation.d.ts.map +1 -0
  157. package/dist/validation.js +101 -0
  158. package/dist/windows.d.ts +22 -0
  159. package/dist/windows.d.ts.map +1 -0
  160. package/dist/windows.js +89 -0
  161. package/dist/wire.d.ts +165 -30
  162. package/dist/wire.d.ts.map +1 -1
  163. package/dist/wire.js +56 -29
  164. package/dist/words.d.ts +19 -0
  165. package/dist/words.d.ts.map +1 -0
  166. package/dist/words.js +156 -0
  167. package/package.json +26 -15
  168. package/AGENTS.md +0 -290
  169. package/dist/attachment.d.ts +0 -3
  170. package/dist/attachment.d.ts.map +0 -1
  171. package/dist/attachment.js +0 -8
  172. package/dist/chat/acts.d.ts +0 -34
  173. package/dist/chat/acts.d.ts.map +0 -1
  174. package/dist/chat/acts.js +0 -36
  175. package/dist/chat/messages.d.ts +0 -21
  176. package/dist/chat/messages.d.ts.map +0 -1
  177. package/dist/chat/messages.js +0 -151
  178. package/dist/chat/overlay.d.ts +0 -31
  179. package/dist/chat/overlay.d.ts.map +0 -1
  180. package/dist/chat/overlay.js +0 -97
  181. package/dist/chat/parts.d.ts +0 -57
  182. package/dist/chat/parts.d.ts.map +0 -1
  183. package/dist/chat/parts.js +0 -119
  184. package/dist/chat/process.d.ts +0 -20
  185. package/dist/chat/process.d.ts.map +0 -1
  186. package/dist/chat/process.js +0 -32
  187. package/dist/chat/provider.d.ts +0 -57
  188. package/dist/chat/provider.d.ts.map +0 -1
  189. package/dist/chat/provider.js +0 -637
  190. package/dist/chat/questions.d.ts +0 -31
  191. package/dist/chat/questions.d.ts.map +0 -1
  192. package/dist/chat/questions.js +0 -111
  193. package/dist/chat/read.d.ts +0 -5
  194. package/dist/chat/read.d.ts.map +0 -1
  195. package/dist/chat/read.js +0 -13
  196. package/dist/chat.d.ts +0 -7
  197. package/dist/chat.d.ts.map +0 -1
  198. package/dist/chat.js +0 -2
  199. package/dist/read-side.d.ts +0 -18
  200. package/dist/read-side.d.ts.map +0 -1
  201. package/dist/read-side.js +0 -433
package/dist/query.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { pointerFacts, } from '@agentshouse/mdmodel';
2
2
  import { referenceField } from './model.js';
3
3
  import { normalize } from './paths.js';
4
+ import { decodedCursor } from './windows.js';
4
5
  export class QueryError extends Error {
5
6
  code;
6
7
  retryable = false;
@@ -11,9 +12,18 @@ export class QueryError extends Error {
11
12
  }
12
13
  const RANGE = ['$gt', '$gte', '$lt', '$lte'];
13
14
  const SINGLE = ['$eq', '$ne', '$in', '$nin', '$all', '$size', '$exists'];
14
- const FILTER = ['rooms', 'types', 'where', 'pathPrefix', 'hop', 'backlinks'];
15
+ const FILTER = ['rooms', 'types', 'where', 'pathPrefix', 'folder', 'paths', 'hop', 'backlinks'];
15
16
  const RELATED = ['types', 'where', 'pathPrefix', 'field'];
16
- const PARTS = ['fields', 'sections', 'preamble', 'markdown', 'backlinks', 'change'];
17
+ const PARTS = [
18
+ 'fields',
19
+ 'sections',
20
+ 'preamble',
21
+ 'markdown',
22
+ 'backlinks',
23
+ 'change',
24
+ 'fileId',
25
+ 'revision',
26
+ ];
17
27
  const ORDER = ['by', 'direction', 'nulls', 'stringSort', 'locale', 'localeOptions'];
18
28
  const CALENDAR_DATE = /^(\d{4})-(\d{2})-(\d{2})$/;
19
29
  const STRAY_ESCAPE = /~(?![01])/u;
@@ -60,6 +70,13 @@ function names(input, at) {
60
70
  invalid(at, 'expects a list of names');
61
71
  return [...new Set(listed)].sort();
62
72
  }
73
+ function pathsOf(input) {
74
+ const listed = list(input, 'paths');
75
+ if (listed.some((item) => typeof item !== 'string' || item === '' || normalize(item) !== item)) {
76
+ invalid('paths', 'expects a list of Paths');
77
+ }
78
+ return [...new Set(listed)].sort();
79
+ }
63
80
  function directory(input, at) {
64
81
  if (typeof input !== 'string' || normalize(input) !== input) {
65
82
  invalid(at, 'expects a normalized directory');
@@ -279,15 +296,16 @@ function backlinksOf(facts, rooms, input) {
279
296
  return { types, where, pathPrefix, field };
280
297
  }
281
298
  function keyPlan(typed, input, at, path) {
282
- if (input === 'room' || input === 'type' || (path && input === 'path'))
299
+ if (input === 'room' || input === 'type' || input === 'folder' || (path && input === 'path')) {
283
300
  return { of: input };
301
+ }
284
302
  if (plain(input) && Object.hasOwn(input, 'field') && Object.keys(input).length === 1) {
285
303
  if (specs(typed, input.field, place(at, 'field')).some(several)) {
286
304
  invalid(place(at, 'field'), 'a path that can hold several values');
287
305
  }
288
306
  return { of: 'field', field: input.field };
289
307
  }
290
- return invalid(at, path ? 'expects room, path, type or a field' : 'expects room, type or a field');
308
+ return invalid(at, path ? 'expects room, path, type, folder or a field' : 'expects room, type, folder or a field');
291
309
  }
292
310
  function direction(input, at) {
293
311
  if (input === undefined)
@@ -349,7 +367,7 @@ function parts(typed, input, at, fields) {
349
367
  });
350
368
  return named;
351
369
  }
352
- function flag(input, at) {
370
+ export function flag(input, at) {
353
371
  if (input === undefined)
354
372
  return false;
355
373
  if (input !== true)
@@ -377,6 +395,8 @@ function documentShape(typed, input) {
377
395
  markdown: true,
378
396
  backlinks: true,
379
397
  change: true,
398
+ fileId: true,
399
+ revision: true,
380
400
  orderBy,
381
401
  };
382
402
  }
@@ -391,6 +411,8 @@ function documentShape(typed, input) {
391
411
  markdown: flag(select.markdown, 'select.markdown'),
392
412
  backlinks: flag(select.backlinks, 'select.backlinks'),
393
413
  change: flag(select.change, 'select.change'),
414
+ fileId: flag(select.fileId, 'select.fileId'),
415
+ revision: flag(select.revision, 'select.revision'),
394
416
  orderBy,
395
417
  };
396
418
  }
@@ -421,20 +443,36 @@ export function planOf(input, facts, reach = null) {
421
443
  invalid('select', 'a grouped query selects nothing');
422
444
  if (!grouped && input.select === undefined)
423
445
  invalid('select', 'a Document query must select');
424
- known(input, [...FILTER, 'orderBy', 'limit', grouped ? 'groupBy' : 'select'], '');
446
+ known(input, [...FILTER, 'orderBy', 'limit', 'after', 'total', grouped ? 'groupBy' : 'select'], '');
425
447
  const limit = limitOf(input.limit);
426
448
  const rooms = input.rooms === undefined ? null : names(input.rooms, 'rooms');
427
449
  const { typed, ...root } = scope(facts, rooms, input, '');
450
+ const shape = grouped ? groupShape(typed, input) : documentShape(typed, input);
428
451
  return {
452
+ key: queryKey(input),
429
453
  rooms,
430
454
  reach,
455
+ locked: [],
431
456
  root,
457
+ folder: input.folder === undefined ? null : directory(input.folder, 'folder'),
458
+ paths: input.paths === undefined ? null : pathsOf(input.paths),
432
459
  hop: input.hop === undefined ? null : hopOf(facts, rooms, typed, input.hop),
433
460
  backlinks: input.backlinks === undefined ? null : backlinksOf(facts, rooms, input.backlinks),
434
- shape: grouped ? groupShape(typed, input) : documentShape(typed, input),
461
+ shape,
435
462
  limit,
463
+ after: continuationOf(input.after, shape.orderBy.length + 1),
464
+ total: flag(input.total, 'total'),
436
465
  };
437
466
  }
467
+ function continuationOf(input, arity) {
468
+ if (input === undefined)
469
+ return null;
470
+ const values = typeof input === 'string' ? decodedCursor(input) : null;
471
+ if (values === null || (values.length !== 0 && values.length !== arity)) {
472
+ invalid('after', 'expects the next an earlier result of this query returned');
473
+ }
474
+ return { text: input, values: values.length === 0 ? null : values };
475
+ }
438
476
  export function limitOf(input) {
439
477
  if (input === undefined)
440
478
  return null;
@@ -449,6 +487,9 @@ export function filterOf(input, facts, reach = null) {
449
487
  known(input, FILTER, '');
450
488
  return planOf({ ...input, select: {} }, facts, reach);
451
489
  }
490
+ export function queryKey(query) {
491
+ return canonical(query);
492
+ }
452
493
  function canonical(value) {
453
494
  if (Array.isArray(value))
454
495
  return `[${value.map(canonical).join(',')}]`;
package/dist/reach.d.ts CHANGED
@@ -1,13 +1,20 @@
1
- import type { MdRuntime, RuntimeTopic } from './connection.js';
1
+ import type { AppRoom, MdRuntime, ReadinessPort, RuntimeTopic } from './connection.js';
2
2
  import type { Plan, PlanReach, QueryResult } from './query.js';
3
- import type { SearchResult } from './search.js';
3
+ import type { Readiness } from './readiness.js';
4
+ import { type SearchResult } from './search.js';
4
5
  export type ReachedDocument = {
5
6
  readonly room: string;
6
7
  readonly path: string;
7
8
  };
9
+ export type ReachedArea = {
10
+ readonly room: string;
11
+ readonly path: string;
12
+ readonly open: readonly string[];
13
+ };
8
14
  export type PortReach = {
9
15
  readonly rooms: readonly string[];
10
16
  readonly documents: readonly ReachedDocument[];
17
+ readonly areas?: readonly ReachedArea[];
11
18
  readonly conversations: boolean;
12
19
  };
13
20
  export type Deliver = (message: unknown) => void;
@@ -18,12 +25,19 @@ export type RuntimeView = {
18
25
  };
19
26
  export type NarrowedView = RuntimeView & {
20
27
  narrow(reach: PortReach | null): void;
28
+ facts(port: ReadinessPort): void;
21
29
  };
22
30
  export type Reachable = RuntimeView & {
23
31
  plan(input: unknown, reach: PortReach | null): Plan;
24
32
  query(plan: Plan): Promise<QueryResult>;
25
33
  watchPlan(plan: Plan, deliver: (message: unknown) => void): () => void;
26
- search(input: unknown, reach: PortReach | null): Promise<SearchResult>;
34
+ search(input: unknown, reach: PortReach | null, locked: readonly string[]): Promise<SearchResult>;
35
+ watchReadiness(port: ReadinessPort, reach: PortReach | null, deliver: (readiness: Readiness) => void): () => void;
36
+ locked(room: string): boolean;
37
+ lockedRooms(): readonly string[];
38
+ watchLocks(notify: () => void): () => void;
39
+ located(room: string, path: string, destination: string): ReachedDocument | null;
40
+ room(locator: string): AppRoom | undefined;
27
41
  };
28
42
  export declare function holds(reach: PortReach | null, room: string): boolean;
29
43
  export declare function governs(reach: PortReach | null, room: {
@@ -36,5 +50,5 @@ export declare function governs(reach: PortReach | null, room: {
36
50
  readonly selection: boolean;
37
51
  };
38
52
  export declare function planReach(reach: PortReach | null): PlanReach | null;
39
- export declare function narrowed(runtime: Reachable, given: PortReach | null): NarrowedView;
53
+ export declare function narrowed(runtime: Reachable, given: PortReach | null, told: ReadinessPort): NarrowedView;
40
54
  //# sourceMappingURL=reach.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"reach.d.ts","sourceRoot":"","sources":["../src/reach.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAW,SAAS,EAAe,YAAY,EAAc,MAAM,iBAAiB,CAAC;AAOjG,OAAO,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAU/D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAGhD,MAAM,MAAM,eAAe,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAE/E,MAAM,MAAM,SAAS,GAAG;IACtB,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,QAAQ,CAAC,SAAS,EAAE,SAAS,eAAe,EAAE,CAAC;IAC/C,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;CACjC,CAAC;AAEF,MAAM,MAAM,OAAO,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;AAEjD,MAAM,MAAM,KAAK,GAAG,CAClB,KAAK,EAAE,YAAY,EACnB,KAAK,EAAE,SAAS,OAAO,EAAE,EACzB,OAAO,EAAE,OAAO,KACb,MAAM,IAAI,CAAC;AAEhB,MAAM,MAAM,WAAW,GAAG;IAAE,EAAE,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,KAAK,CAAA;CAAE,CAAC;AAE1D,MAAM,MAAM,YAAY,GAAG,WAAW,GAAG;IAAE,MAAM,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI,GAAG,IAAI,CAAA;CAAE,CAAC;AAEnF,MAAM,MAAM,SAAS,GAAG,WAAW,GAAG;IACpC,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,GAAG,IAAI,GAAG,IAAI,CAAC;IACpD,KAAK,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IACxC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IACvE,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,GAAG,IAAI,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;CACxE,CAAC;AAyBF,wBAAgB,KAAK,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAMpE;AAMD,wBAAgB,OAAO,CACrB,KAAK,EAAE,SAAS,GAAG,IAAI,EACvB,IAAI,EAAE;IAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,cAAc,EAAE;QAAE,KAAK,EAAE,SAAS,MAAM,EAAE,CAAA;KAAE,GAAG,IAAI,CAAA;CAAE,GAC/F;IAAE,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CAU/D;AAcD,wBAAgB,SAAS,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI,GAAG,SAAS,GAAG,IAAI,CASnE;AA8ED,wBAAgB,QAAQ,CAAC,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,GAAG,IAAI,GAAG,YAAY,CAkKlF"}
1
+ {"version":3,"file":"reach.d.ts","sourceRoot":"","sources":["../src/reach.ts"],"names":[],"mappings":"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"}
package/dist/reach.js CHANGED
@@ -1,12 +1,12 @@
1
1
  import { unheldView } from './conversation.js';
2
2
  import { modelTypes, NO_MODEL } from './model.js';
3
- import { applied, observing } from './observation.js';
3
+ import { changeOf, digestApplied, digestOf, observing, } from './observation.js';
4
4
  import { runtimeError } from './failure.js';
5
- import { failure, undeclared } from './outcome.js';
5
+ import { failure, lockedRoom, ROOM_LOCKED, undeclared, UNREADABLE, } from './outcome.js';
6
6
  import { beneath } from './paths.js';
7
- import { reconciled } from './read-side.js';
8
7
  import { moveTarget, readParts, staleWrite, } from './room-draft.js';
9
8
  import { documentKey } from './rows.js';
9
+ import { undeclaredSearch } from './search.js';
10
10
  const UNHELD_TYPES = { collection: modelTypes(NO_MODEL), typedSelection: null };
11
11
  function unconversed(name) {
12
12
  return () => Promise.resolve(undeclared(name));
@@ -14,18 +14,26 @@ function unconversed(name) {
14
14
  const NO_CONVERSATION = {
15
15
  list: () => Promise.resolve([]),
16
16
  read: (conversation) => Promise.resolve(unheldView(conversation)),
17
- create: unconversed('conversation.create'),
18
- send: unconversed('conversation.send'),
19
- answer: unconversed('conversation.answer'),
20
- interrupt: unconversed('conversation.interrupt'),
21
- kill: unconversed('conversation.kill'),
22
- cancel: unconversed('conversation.cancel'),
23
- delete: unconversed('conversation.delete'),
17
+ create: unconversed('conversations/create'),
18
+ send: unconversed('conversations/send'),
19
+ answer: unconversed('conversations/interaction/answer'),
20
+ interrupt: unconversed('conversations/interrupt'),
21
+ kill: unconversed('conversations/kill'),
22
+ delete: unconversed('conversations/delete'),
24
23
  };
25
24
  const NO_CHAT_DRAFT = {
26
25
  write: () => Promise.resolve(),
26
+ prepend: () => Promise.resolve(),
27
27
  discard: () => Promise.resolve(),
28
28
  };
29
+ function areasOf(reach) {
30
+ return reach.areas ?? [];
31
+ }
32
+ function fenced(reach, room, path) {
33
+ return areasOf(reach).some((area) => area.room === room &&
34
+ beneath(area.path, path) &&
35
+ !area.open.some((open) => beneath(open, path)));
36
+ }
29
37
  export function holds(reach, room) {
30
38
  return (reach === null ||
31
39
  reach.rooms.includes(room) ||
@@ -46,8 +54,9 @@ export function governs(reach, room) {
46
54
  }
47
55
  function covers(reach, room, path) {
48
56
  return (reach === null ||
49
- reach.rooms.includes(room) ||
50
- reach.documents.some((document) => document.room === room && document.path === path));
57
+ (!fenced(reach, room, path) &&
58
+ (reach.rooms.includes(room) ||
59
+ reach.documents.some((document) => document.room === room && document.path === path))));
51
60
  }
52
61
  function conversing(reach) {
53
62
  return reach === null || reach.conversations;
@@ -60,6 +69,9 @@ export function planReach(reach) {
60
69
  documents: [
61
70
  ...new Set(reach.documents.map(({ room, path }) => documentKey(room, path))),
62
71
  ].sort(),
72
+ areas: areasOf(reach)
73
+ .map(({ room, path, open }) => ({ room, path, open: [...new Set(open)].sort() }))
74
+ .sort((left, right) => documentKey(left.room, left.path) < documentKey(right.room, right.path) ? -1 : 1),
63
75
  };
64
76
  }
65
77
  function roomsWithin(reach, rooms) {
@@ -75,7 +87,7 @@ function typesWithin(reach, room, types) {
75
87
  };
76
88
  }
77
89
  function moves(reach, room, move) {
78
- return move === undefined || covers(reach, room, moveTarget(move));
90
+ return move === undefined || covers(reach, move.room ?? room, moveTarget(move));
79
91
  }
80
92
  function draftWithin(reach, draft) {
81
93
  return covers(reach, draft.room, draft.path) && moves(reach, draft.room, draft.parts.move);
@@ -109,75 +121,127 @@ function statusWithin(reach, status) {
109
121
  return {
110
122
  ...status,
111
123
  attentionRequired: status.attentionRequired.filter((entry) => covers(reach, entry.room, entry.path)),
124
+ unsent: status.unsent.filter((change) => covers(reach, change.room, change.path)),
112
125
  };
113
126
  }
114
127
  function queueWithin(reach, room, entries) {
115
- return entries.filter(({ batch }) => batch.changes.every((change) => covers(reach, room, change.path) &&
116
- (change.kind !== 'move' || covers(reach, room, change.to))));
128
+ return entries
129
+ .filter(({ batch }) => batch.changes.every((change) => covers(reach, room, change.path) &&
130
+ (change.kind !== 'move' || covers(reach, room, change.to))))
131
+ .map((entry) => {
132
+ const { between, ...batch } = entry.batch;
133
+ if (between === undefined)
134
+ return entry;
135
+ return wholly(reach, between.source.room) && wholly(reach, between.destination.room)
136
+ ? entry
137
+ : { ...entry, batch };
138
+ });
117
139
  }
118
140
  function unreached(room) {
119
- return runtimeError('room-not-held', `No Local Replica for ${room}`);
141
+ return runtimeError('unknown-room', `Unknown Room ${room}`);
142
+ }
143
+ function lockedError(room) {
144
+ return runtimeError(ROOM_LOCKED, `${room} is locked until House confirms its access`, true);
145
+ }
146
+ function deviceUnreached() {
147
+ return runtimeError('device-unreached', 'Only the panel cleans up this device');
120
148
  }
121
- export function narrowed(runtime, given) {
149
+ const ATTACH_REQUIRED = 'attach-required';
150
+ function attachRequired() {
151
+ return runtimeError(ATTACH_REQUIRED, 'App code takes a file in with binaryFile.attach');
152
+ }
153
+ function carriesFile(parts) {
154
+ return parts !== null && typeof parts === 'object' && Object.hasOwn(parts, 'file');
155
+ }
156
+ export function narrowed(runtime, given, told) {
122
157
  const { md, watch } = runtime;
123
158
  let reach = given;
159
+ let port = told;
124
160
  const open = new Set();
125
- function following(begin) {
161
+ function following(begin, locking = false) {
126
162
  let stop = begin();
127
163
  const restart = () => {
128
164
  stop();
129
165
  stop = begin();
130
166
  };
131
167
  open.add(restart);
168
+ const unwatch = locking ? runtime.watchLocks(restart) : () => { };
132
169
  return () => {
133
170
  open.delete(restart);
171
+ unwatch();
134
172
  stop();
135
173
  };
136
174
  }
175
+ const unlocking = (rooms) => {
176
+ const locked = (rooms ?? []).filter((room) => runtime.locked(room));
177
+ return locked.length === 0
178
+ ? null
179
+ : { pending: locked.map((room) => ({ room, awaits: 'unlock' })) };
180
+ };
181
+ const unlocked = (entries) => entries.filter((entry) => !runtime.locked(entry.room));
182
+ const intoLocked = (held) => held.some((move) => move?.room !== undefined && runtime.locked(move.room));
137
183
  const within = (topic, input, deliver) => {
138
184
  if (topic === 'rooms') {
139
185
  return following(() => watch(topic, input, (rooms) => deliver(roomsWithin(reach, rooms))));
140
186
  }
187
+ if (topic === 'readiness') {
188
+ return following(() => runtime.watchReadiness(port, reach, deliver));
189
+ }
141
190
  if (topic === 'sync-status') {
142
- return following(() => watch(topic, input, (status) => deliver(statusWithin(reach, status))));
191
+ return following(() => watch(topic, input, (status) => {
192
+ const within = statusWithin(reach, status);
193
+ deliver({ ...within, attentionRequired: unlocked(within.attentionRequired) });
194
+ }), true);
143
195
  }
144
196
  if (topic === 'room-drafts') {
145
- return following(() => watch(topic, input, (drafts) => deliver(draftsWithin(reach, drafts))));
197
+ return following(() => watch(topic, input, (drafts) => deliver(unlocked(draftsWithin(reach, drafts)))), true);
146
198
  }
147
199
  if (topic === 'queue') {
148
200
  const room = input[0];
149
201
  return following(() => {
150
- if (!holds(reach, room)) {
202
+ if (!holds(reach, room) || runtime.locked(room)) {
151
203
  deliver([]);
152
204
  return () => { };
153
205
  }
154
206
  return watch(topic, input, (entries) => deliver(queueWithin(reach, room, entries)));
155
- });
207
+ }, true);
156
208
  }
157
209
  if (topic === 'query') {
158
210
  const plan = runtime.plan(input[0], reach);
159
211
  let held = null;
160
212
  return following(() => {
213
+ const locked = unlocking(plan.rooms);
214
+ if (locked !== null) {
215
+ const answer = { result: locked, keys: [] };
216
+ if (held === null || changeOf(held, answer) !== null)
217
+ deliver(answer);
218
+ held = digestOf(answer);
219
+ return () => { };
220
+ }
161
221
  let first = true;
162
- return runtime.watchPlan({ ...plan, reach: planReach(reach) }, (message) => {
222
+ let open = true;
223
+ const heard = (message) => {
163
224
  if (!first) {
164
- held = applied(held, message);
225
+ held = digestApplied(held, message);
165
226
  deliver(message);
166
227
  return;
167
228
  }
168
229
  first = false;
169
- if (held === null) {
170
- held = message;
171
- deliver(message);
172
- return;
173
- }
174
- const next = reconciled(held, message);
175
- if (next === null)
176
- return;
177
- held = next.keyed;
178
- deliver(next.change);
230
+ const change = held === null ? message : changeOf(held, message);
231
+ held = digestOf(message);
232
+ if (change !== null)
233
+ deliver(change);
234
+ };
235
+ const current = { ...plan, reach: planReach(reach), locked: runtime.lockedRooms() };
236
+ const stop = runtime.watchPlan(current, (message) => {
237
+ if (open)
238
+ heard(message);
179
239
  });
180
- });
240
+ return () => {
241
+ open = false;
242
+ stop();
243
+ };
244
+ }, true);
181
245
  }
182
246
  if (topic === 'conversation') {
183
247
  return following(() => {
@@ -197,49 +261,149 @@ export function narrowed(runtime, given) {
197
261
  }
198
262
  return watch(topic, input, deliver);
199
263
  };
264
+ const files = new Map();
265
+ let handed = 0;
266
+ async function hand(reading) {
267
+ const answer = await reading;
268
+ if (answer.kind !== 'binary')
269
+ return answer;
270
+ handed += 1;
271
+ const file = String(handed);
272
+ files.set(file, answer.file);
273
+ return { ...answer, file };
274
+ }
200
275
  const conversation = () => (conversing(reach) ? md.conversation : NO_CONVERSATION);
201
276
  const chatDraft = () => (conversing(reach) ? md.chatDraft : NO_CHAT_DRAFT);
202
277
  const view = {
203
278
  ...md,
204
- rooms: () => roomsWithin(reach, md.rooms()),
205
- query: ((input) => runtime.query(runtime.plan(input, reach))),
206
- search: (input) => runtime.search(input, reach),
279
+ rooms: () => md.rooms().then((rooms) => roomsWithin(reach, rooms)),
280
+ query: ((input) => {
281
+ const plan = runtime.plan(input, reach);
282
+ return Promise.resolve(unlocking(plan.rooms) ?? runtime.query({ ...plan, locked: runtime.lockedRooms() }));
283
+ }),
284
+ search: (input) => port.search === 'required'
285
+ ? runtime.search(input, reach, runtime.lockedRooms())
286
+ : Promise.resolve(undeclaredSearch()),
207
287
  types(room) {
288
+ if (holds(reach, room) && runtime.locked(room))
289
+ throw lockedError(room);
208
290
  if (wholly(reach, room))
209
291
  return md.types(room);
210
292
  if (!holds(reach, room))
211
293
  return structuredClone(UNHELD_TYPES);
212
- const listed = md.rooms().find(({ locator }) => locator === room);
213
- return typesWithin(reach, listed, md.types(room));
294
+ return typesWithin(reach, runtime.room(room), md.types(room));
214
295
  },
215
296
  async save(room, roomDrafts, writtenBy) {
216
297
  if (!holds(reach, room))
217
298
  throw unreached(room);
218
- const outside = roomDrafts.find(({ path }) => !covers(reach, room, path) ||
219
- !moves(reach, room, md.roomDraft.read(room, path)?.parts.move));
299
+ if (runtime.locked(room))
300
+ return { outcome: 'refused', code: ROOM_LOCKED, retryable: true };
301
+ const held = (await Promise.all(roomDrafts.map(({ path }) => md.roomDraft.read(room, path)))).map((draft) => draft?.parts.move);
302
+ const outside = roomDrafts.find(({ path }, at) => !covers(reach, room, path) || !moves(reach, room, held[at]));
220
303
  if (outside !== undefined) {
221
304
  return { outcome: 'refused', code: 'stale', retryable: false, file: outside.path };
222
305
  }
306
+ if (intoLocked(held))
307
+ return { outcome: 'refused', code: ROOM_LOCKED, retryable: true };
223
308
  return savedWithin(reach, room, await md.save(room, roomDrafts, writtenBy));
224
309
  },
225
- history: (room, path, query) => covers(reach, room, path)
226
- ? md.history(room, path, query)
227
- : Promise.resolve(failure({ kind: 'room-not-readable', locator: room })),
228
- reached: (room, roomVersion, path) => (path === undefined ? wholly(reach, room) : covers(reach, room, path)) &&
229
- md.reached(room, roomVersion, path),
310
+ async saveParts(room, parts, writtenBy) {
311
+ if (!holds(reach, room))
312
+ throw unreached(room);
313
+ const filed = reach === null ? undefined : parts.find(({ parts: one }) => carriesFile(one));
314
+ if (filed !== undefined) {
315
+ return { outcome: 'refused', code: ATTACH_REQUIRED, retryable: false, file: filed.path };
316
+ }
317
+ if (runtime.locked(room))
318
+ return { outcome: 'refused', code: ROOM_LOCKED, retryable: true };
319
+ const held = parts.map(({ parts: one }) => readParts(one).move);
320
+ if (parts.some(({ path }, at) => !covers(reach, room, path) || !moves(reach, room, held[at]))) {
321
+ throw unreached(room);
322
+ }
323
+ if (intoLocked(held))
324
+ return { outcome: 'refused', code: ROOM_LOCKED, retryable: true };
325
+ return savedWithin(reach, room, await md.saveParts(room, parts, writtenBy));
326
+ },
327
+ resolve(room, path, destination) {
328
+ const at = runtime.located(room, path, destination);
329
+ return at !== null && !covers(reach, at.room, at.path)
330
+ ? Promise.resolve(UNREADABLE)
331
+ : md.resolve(room, path, destination);
332
+ },
333
+ history: (room, path, query) => !covers(reach, room, path)
334
+ ? Promise.resolve(failure({ kind: 'room-not-readable', locator: room }))
335
+ : runtime.locked(room)
336
+ ? Promise.resolve(lockedRoom(room))
337
+ : md.history(room, path, query),
338
+ binaryFile: {
339
+ read: (room, path, revision) => !covers(reach, room, path)
340
+ ? Promise.resolve(failure({ kind: 'room-not-readable', locator: room }))
341
+ : runtime.locked(room)
342
+ ? Promise.resolve(lockedRoom(room))
343
+ : hand(md.binaryFile.read(room, path, revision)),
344
+ link: (room, path, revision) => !covers(reach, room, path)
345
+ ? Promise.resolve(failure({ kind: 'room-not-readable', locator: room }))
346
+ : runtime.locked(room)
347
+ ? Promise.resolve(lockedRoom(room))
348
+ : md.binaryFile.link(room, path, revision),
349
+ show: (room, path, revision) => !covers(reach, room, path)
350
+ ? Promise.resolve(failure({ kind: 'room-not-readable', locator: room }))
351
+ : runtime.locked(room)
352
+ ? Promise.resolve(lockedRoom(room))
353
+ : hand(md.binaryFile.show(room, path, revision)),
354
+ attach: (room, file) => !wholly(reach, room)
355
+ ? Promise.resolve(failure({ kind: 'room-not-readable', locator: room }))
356
+ : runtime.locked(room)
357
+ ? Promise.resolve(lockedRoom(room))
358
+ : md.binaryFile.attach(room, file),
359
+ async part(file, part) {
360
+ const key = files.get(file);
361
+ if (key === undefined)
362
+ return null;
363
+ const bytes = await md.binaryFile.part(key, part);
364
+ if (bytes === null)
365
+ files.delete(file);
366
+ return bytes;
367
+ },
368
+ },
369
+ reached: async (room, roomVersion, path) => (path === undefined ? wholly(reach, room) : covers(reach, room, path)) &&
370
+ !runtime.locked(room) &&
371
+ (await md.reached(room, roomVersion, path)),
230
372
  log: () => (reach === null ? md.log() : ''),
373
+ cleanUp: () => (reach === null ? md.cleanUp() : Promise.reject(deviceUnreached())),
231
374
  roomDraft: {
232
- list: () => md.roomDraft.list().then((drafts) => draftsWithin(reach, drafts)),
233
- read: (room, path) => covers(reach, room, path) ? shownDraft(reach, md.roomDraft.read(room, path)) : null,
234
- async write(room, path, revision, parts) {
235
- if (!covers(reach, room, path) || !moves(reach, room, readParts(parts).move)) {
236
- throw unreached(room);
375
+ list: () => md.roomDraft.list().then((drafts) => unlocked(draftsWithin(reach, drafts))),
376
+ async read(room, path) {
377
+ if (!covers(reach, room, path))
378
+ return null;
379
+ if (runtime.locked(room))
380
+ throw lockedError(room);
381
+ return shownDraft(reach, await md.roomDraft.read(room, path));
382
+ },
383
+ write(room, path, revision, parts, next) {
384
+ try {
385
+ if (reach !== null && carriesFile(parts))
386
+ throw attachRequired();
387
+ const move = readParts(parts).move;
388
+ if (!covers(reach, room, path) || !moves(reach, room, move))
389
+ throw unreached(room);
390
+ if (runtime.locked(room))
391
+ throw lockedError(room);
392
+ if (intoLocked([move]))
393
+ throw lockedError(move.room);
237
394
  }
238
- return writeWithin(reach, await md.roomDraft.write(room, path, revision, parts));
395
+ catch (error) {
396
+ return Promise.reject(error);
397
+ }
398
+ return md.roomDraft
399
+ .write(room, path, revision, parts, next)
400
+ .then((written) => writeWithin(reach, written));
239
401
  },
240
402
  async discard(room, path, revision) {
241
403
  if (!covers(reach, room, path))
242
404
  return staleWrite(null);
405
+ if (runtime.locked(room))
406
+ throw lockedError(room);
243
407
  return writeWithin(reach, await md.roomDraft.discard(room, path, revision));
244
408
  },
245
409
  },
@@ -247,15 +411,15 @@ export function narrowed(runtime, given) {
247
411
  list: () => conversation().list(),
248
412
  read: (id, options) => conversation().read(id, options),
249
413
  create: (input) => conversation().create(input),
250
- send: (id) => conversation().send(id),
414
+ send: (id, operation, draft) => conversation().send(id, operation, draft),
251
415
  answer: (id, answer) => conversation().answer(id, answer),
252
416
  interrupt: (id) => conversation().interrupt(id),
253
417
  kill: (id) => conversation().kill(id),
254
- cancel: (id, turn) => conversation().cancel(id, turn),
255
418
  delete: (id) => conversation().delete(id),
256
419
  },
257
420
  chatDraft: {
258
421
  write: (id, draft, revision) => chatDraft().write(id, draft, revision),
422
+ prepend: (id, draft) => chatDraft().prepend(id, draft),
259
423
  discard: (id) => chatDraft().discard(id),
260
424
  },
261
425
  observe: observing(within),
@@ -268,5 +432,10 @@ export function narrowed(runtime, given) {
268
432
  for (const restart of [...open])
269
433
  restart();
270
434
  },
435
+ facts(next) {
436
+ port = next;
437
+ for (const restart of [...open])
438
+ restart();
439
+ },
271
440
  };
272
441
  }