@agentshouse/mdruntime 0.1.0-c3903a68dcbbf → 0.1.0-c435c7a7c4f4e

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 (200) hide show
  1. package/AGENTS.md +270 -178
  2. package/README.md +22 -12
  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 +157 -67
  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 -38
  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 +50 -21
  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 -20
  91. package/dist/client.d.ts.map +1 -1
  92. package/dist/client.js +71 -32
  93. package/dist/collection.d.ts +21 -14
  94. package/dist/collection.d.ts.map +1 -1
  95. package/dist/collection.js +270 -32
  96. package/dist/connection.d.ts +48 -38
  97. package/dist/connection.d.ts.map +1 -1
  98. package/dist/connection.js +1996 -846
  99. package/dist/conversation.d.ts +45 -19
  100. package/dist/conversation.d.ts.map +1 -1
  101. package/dist/conversation.js +592 -216
  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 +20 -3
  117. package/dist/host.d.ts.map +1 -1
  118. package/dist/host.js +91 -68
  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 -9
  123. package/dist/index.d.ts.map +1 -1
  124. package/dist/index.js +5 -1
  125. package/dist/memory.d.ts +17 -7
  126. package/dist/memory.d.ts.map +1 -1
  127. package/dist/memory.js +159 -47
  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 +35 -22
  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 +44 -6
  147. package/dist/query.d.ts.map +1 -1
  148. package/dist/query.js +81 -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 +131 -79
  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 -93
  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/shared.d.ts +11 -0
  183. package/dist/shared.d.ts.map +1 -0
  184. package/dist/shared.js +368 -0
  185. package/dist/status.d.ts +6 -2
  186. package/dist/status.d.ts.map +1 -1
  187. package/dist/status.js +0 -8
  188. package/dist/store.d.ts +79 -32
  189. package/dist/store.d.ts.map +1 -1
  190. package/dist/store.js +542 -225
  191. package/dist/turn.d.ts +7 -0
  192. package/dist/turn.d.ts.map +1 -0
  193. package/dist/turn.js +45 -0
  194. package/dist/wire.d.ts +162 -19
  195. package/dist/wire.d.ts.map +1 -1
  196. package/dist/wire.js +82 -67
  197. package/package.json +31 -7
  198. package/dist/attachment.d.ts +0 -3
  199. package/dist/attachment.d.ts.map +0 -1
  200. package/dist/attachment.js +0 -8
package/dist/memory.js CHANGED
@@ -1,9 +1,7 @@
1
- import { ROOM_CONTRACT_VERSION, } from './adapter.js';
2
- import { MD_MODEL } from './collection.js';
3
1
  import { sameChange } from './overlay.js';
4
2
  const FIXTURE_RETENTION = {
5
3
  history: { kind: 'duration', seconds: 1 },
6
- outcome: 1,
4
+ outcome: 604_800,
7
5
  refreshHorizon: 1,
8
6
  };
9
7
  function declaredRetention(value) {
@@ -15,6 +13,19 @@ function declaredRetention(value) {
15
13
  }
16
14
  throw new Error('history retention duration must be a finite positive number of seconds');
17
15
  }
16
+ const MODEL_ABSENT = { kind: 'model-unavailable', reason: 'not-readable' };
17
+ function modelsOf(room) {
18
+ return {
19
+ collection: room.model ?? MODEL_ABSENT,
20
+ ...(room.typedSelection === undefined
21
+ ? {}
22
+ : { selection: room.selectionModel ?? MODEL_ABSENT }),
23
+ };
24
+ }
25
+ function changedModels(room, from) {
26
+ const now = modelsOf(room);
27
+ return JSON.stringify(now) === JSON.stringify(modelsOf(from)) ? undefined : now;
28
+ }
18
29
  function named(source, paths) {
19
30
  const taken = {};
20
31
  for (const path of paths) {
@@ -31,8 +42,6 @@ async function resolveScript(answer, fallback, request) {
31
42
  }
32
43
  export function createMemoryAdapter(options = {}) {
33
44
  let rooms = options.rooms === undefined ? [] : [...options.rooms];
34
- const mdModel = options.mdModel ?? MD_MODEL;
35
- const supportedVersions = options.supportedVersions ?? [ROOM_CONTRACT_VERSION];
36
45
  const capabilities = options.capabilities ?? [];
37
46
  const providerLimits = options.providerLimits ?? [];
38
47
  const retention = declaredRetention(options.retention);
@@ -46,8 +55,11 @@ export function createMemoryAdapter(options = {}) {
46
55
  const scripted = new Map();
47
56
  const unsealed = new Map();
48
57
  const sealed = new Set();
58
+ const sentTurns = new Map();
49
59
  const historyPageSize = options.historyPageSize;
50
- let selected = [];
60
+ let held = [];
61
+ let heldDemand = null;
62
+ let expiring = [];
51
63
  let replicated = [];
52
64
  let issued = 0;
53
65
  function scriptConversations(entries) {
@@ -89,38 +101,43 @@ export function createMemoryAdapter(options = {}) {
89
101
  return {
90
102
  name: room.name,
91
103
  locator: room.locator,
104
+ handle: room.handle,
92
105
  roomVersion: room.roomVersion,
93
106
  operations: room.operations,
94
107
  replica: room.replica,
95
- attachedModel: room.attachedModel,
96
108
  ...(room.typedSelection === undefined ? {} : { typedSelection: room.typedSelection }),
97
- ...(room.untypedFolders === undefined ? {} : { untypedFolders: room.untypedFolders }),
98
109
  providerOwnedPaths: room.providerOwnedPaths,
99
110
  };
100
111
  }
101
- function page(room, from) {
112
+ function page(room, after) {
102
113
  const paths = Object.keys(room.documents ?? {}).sort();
114
+ const opening = after === undefined;
115
+ const from = opening ? 0 : paths.filter((path) => path <= after).length;
103
116
  const size = pageSize ?? paths.length;
104
117
  const taken = paths.slice(from, from + size);
105
118
  const next = from + taken.length;
106
- const targets = from === 0 ? (room.binaryTargets ?? []) : [];
119
+ const targets = opening ? Object.keys(room.binaryFiles ?? {}) : [];
107
120
  return {
108
121
  kind: 'bootstrap',
109
122
  roomVersion: room.roomVersion,
123
+ ...(opening ? { models: modelsOf(room) } : {}),
110
124
  documents: named(room.documents ?? {}, taken),
111
- revisions: named(room.revisions ?? {}, taken),
125
+ revisions: named(room.revisions ?? {}, [...taken, ...targets]),
126
+ ...(room.fileIds === undefined
127
+ ? {}
128
+ : { fileIds: named(room.fileIds, [...taken, ...targets]) }),
112
129
  changes: named(room.changes ?? {}, [...taken, ...targets]),
113
- binaryTargets: targets,
114
- ...(from === 0 && room.attachments !== undefined ? { attachments: room.attachments } : {}),
115
- ...(next < paths.length ? { pageToken: String(next) } : {}),
130
+ binaryFiles: named(room.binaryFiles ?? {}, targets),
131
+ ...(next < paths.length ? { pageToken: taken.at(-1) } : {}),
116
132
  };
117
133
  }
118
134
  function advancement(room, from, at) {
119
135
  const current = room.documents ?? {};
120
136
  const previous = from.documents ?? {};
121
- const currentTargets = new Set(room.binaryTargets ?? []);
122
- const previousTargets = new Set(from.binaryTargets ?? []);
137
+ const currentTargets = new Set(Object.keys(room.binaryFiles ?? {}));
138
+ const previousTargets = new Set(Object.keys(from.binaryFiles ?? {}));
123
139
  const headMoved = (path) => room.revisions?.[path] !== from.revisions?.[path] ||
140
+ room.fileIds?.[path] !== from.fileIds?.[path] ||
124
141
  !sameChange(room.changes?.[path], from.changes?.[path]);
125
142
  const changed = Object.keys(current)
126
143
  .filter((path) => current[path] !== previous[path] || headMoved(path))
@@ -135,22 +152,24 @@ export function createMemoryAdapter(options = {}) {
135
152
  const targets = opening
136
153
  ? [...currentTargets].filter((target) => !previousTargets.has(target) || headMoved(target))
137
154
  : [];
138
- const attachments = room.attachments ?? [];
139
- const declared = opening && JSON.stringify(attachments) !== JSON.stringify(from.attachments ?? []);
155
+ const models = opening ? changedModels(room, from) : undefined;
140
156
  return {
141
157
  kind: 'net-advancement',
142
158
  roomVersion: room.roomVersion,
159
+ ...(models === undefined ? {} : { models }),
143
160
  documents: named(current, taken),
144
- revisions: named(room.revisions ?? {}, taken),
161
+ revisions: named(room.revisions ?? {}, [...taken, ...targets]),
162
+ ...(room.fileIds === undefined
163
+ ? {}
164
+ : { fileIds: named(room.fileIds, [...taken, ...targets]) }),
145
165
  changes: named(room.changes ?? {}, [...taken, ...targets]),
146
- binaryTargets: targets,
166
+ binaryFiles: named(room.binaryFiles ?? {}, targets),
147
167
  removals: opening
148
168
  ? [
149
169
  ...Object.keys(previous).filter((path) => !(path in current) && !currentTargets.has(path)),
150
170
  ...[...previousTargets].filter((target) => !currentTargets.has(target) && !(target in current)),
151
171
  ].sort()
152
172
  : [],
153
- ...(declared ? { attachments } : {}),
154
173
  ...(pageToken === undefined ? {} : { pageToken }),
155
174
  };
156
175
  }
@@ -171,7 +190,7 @@ export function createMemoryAdapter(options = {}) {
171
190
  function historiesOf(input) {
172
191
  const asked = input?.conversations;
173
192
  if (!Array.isArray(asked))
174
- return { kind: 'rejected', code: 'invalid_request', fields: {} };
193
+ return { kind: 'rejected', code: 'invalid_request', fields: {}, retryable: false };
175
194
  const histories = [];
176
195
  for (const entry of asked) {
177
196
  const conversation = typeof entry.conversation === 'string' ? scripted.get(entry.conversation) : undefined;
@@ -199,21 +218,32 @@ export function createMemoryAdapter(options = {}) {
199
218
  const named = typeof input.conversation === 'string' ? input.conversation : '';
200
219
  const conversation = scripted.get(named);
201
220
  if (conversation === undefined) {
202
- return { kind: 'rejected', code: 'unknown-conversation', fields: { conversation: named } };
221
+ return {
222
+ kind: 'rejected',
223
+ code: 'unknown-conversation',
224
+ fields: { conversation: named },
225
+ retryable: false,
226
+ };
203
227
  }
204
228
  const outcome = conversation.outcomes?.[name];
205
229
  if (outcome !== undefined)
206
230
  return outcome;
207
231
  if (name === 'conversation.send') {
232
+ const operation = typeof input.operation_id === 'string' ? input.operation_id : null;
233
+ const answered = operation === null ? undefined : sentTurns.get(operation);
234
+ if (answered !== undefined)
235
+ return { kind: 'action', result: { turn: answered } };
208
236
  issued += 1;
209
237
  const turn = `turn-${issued}`;
210
238
  unsealed.set(turn, named);
239
+ if (operation !== null)
240
+ sentTurns.set(operation, turn);
211
241
  return { kind: 'action', result: { turn } };
212
242
  }
213
243
  if (name === 'conversation.cancel') {
214
244
  const turn = typeof input.turn === 'string' ? input.turn : '';
215
245
  if (sealed.has(turn) || !unsealed.has(turn)) {
216
- return { kind: 'rejected', code: 'turn-sealed', fields: { turn } };
246
+ return { kind: 'rejected', code: 'turn-sealed', fields: { turn }, retryable: false };
217
247
  }
218
248
  unsealed.delete(turn);
219
249
  return { kind: 'action', result: { turn, cancelled: true } };
@@ -222,18 +252,23 @@ export function createMemoryAdapter(options = {}) {
222
252
  scripted.delete(named);
223
253
  return { kind: 'action', result: { conversation: named } };
224
254
  }
255
+ function announceList() {
256
+ const entries = [...scripted.values()].flatMap(({ conversation, listed }) => listed === undefined ? [] : [{ conversation, ...listed }]);
257
+ for (const listener of listeners)
258
+ listener({ kind: 'list', entries });
259
+ }
225
260
  function discovery() {
226
261
  return {
227
262
  kind: 'discovery',
228
- supportedVersions,
229
- mdModel,
230
263
  rooms: rooms.map(declared),
231
264
  capabilities,
232
265
  providerLimits,
233
266
  retention,
267
+ ...(options.member === undefined ? {} : { member: options.member }),
234
268
  };
235
269
  }
236
270
  return {
271
+ ...(options.exhaustiveDiscovery === undefined ? {} : { exhaustiveDiscovery: true }),
237
272
  setRooms(next) {
238
273
  rooms = [...next];
239
274
  retain(rooms);
@@ -241,9 +276,21 @@ export function createMemoryAdapter(options = {}) {
241
276
  setConversations(next) {
242
277
  scriptConversations(next);
243
278
  },
244
- notifyRoomVersion(notification) {
279
+ open() {
280
+ announceList();
281
+ for (const observer of observers)
282
+ observer({ kind: 'opened' });
283
+ },
284
+ close(revoked = false) {
245
285
  for (const observer of observers)
246
- observer(notification);
286
+ observer({ kind: 'closed', revoked });
287
+ },
288
+ expire(locators) {
289
+ expiring = [...locators];
290
+ },
291
+ notifyRoomVersion({ locator, roomVersion }) {
292
+ for (const observer of observers)
293
+ observer({ kind: 'room-version', locator, roomVersion });
247
294
  },
248
295
  notifyConversation(event) {
249
296
  if (event.kind === 'accepted-message') {
@@ -257,8 +304,11 @@ export function createMemoryAdapter(options = {}) {
257
304
  for (const observer of modeObservers)
258
305
  observer(mode);
259
306
  },
260
- selectedRooms() {
261
- return selected;
307
+ heldRooms() {
308
+ return held;
309
+ },
310
+ heldDemand() {
311
+ return heldDemand;
262
312
  },
263
313
  replicatedConversations() {
264
314
  return replicated;
@@ -282,12 +332,47 @@ export function createMemoryAdapter(options = {}) {
282
332
  };
283
333
  },
284
334
  },
285
- roomVersions: {
286
- select(locators) {
287
- selected = [...locators];
335
+ roomStream: {
336
+ hold(asked, demand) {
337
+ held = asked.map((room) => room.locator);
338
+ heldDemand = demand;
339
+ const expired = expiring.filter((locator) => held.includes(locator));
340
+ expiring = [];
341
+ const moved = asked.flatMap((room) => {
342
+ const current = rooms.find((entry) => entry.locator === room.locator);
343
+ return current === undefined ||
344
+ expired.includes(room.locator) ||
345
+ current.roomVersion === room.roomVersion
346
+ ? []
347
+ : [{ locator: room.locator, roomVersion: current.roomVersion }];
348
+ });
349
+ queueMicrotask(() => {
350
+ for (const observer of observers) {
351
+ for (const room of moved)
352
+ observer({ kind: 'room-version', ...room });
353
+ observer({ kind: 'opening', expired });
354
+ }
355
+ });
356
+ },
357
+ reopen() {
358
+ queueMicrotask(() => {
359
+ for (const observer of observers)
360
+ observer({ kind: 'closed', revoked: false });
361
+ });
288
362
  },
363
+ published() { },
289
364
  observe(notify) {
290
365
  observers.add(notify);
366
+ queueMicrotask(() => {
367
+ if (!observers.has(notify))
368
+ return;
369
+ if (options.unopened === true) {
370
+ notify({ kind: 'closed', revoked: false });
371
+ return;
372
+ }
373
+ announceList();
374
+ notify({ kind: 'opened' });
375
+ });
291
376
  return () => {
292
377
  observers.delete(notify);
293
378
  };
@@ -299,13 +384,6 @@ export function createMemoryAdapter(options = {}) {
299
384
  else
300
385
  scripts[operation] = answer;
301
386
  },
302
- async model(_authorization, request) {
303
- return resolveScript(scripts.model, () => {
304
- const room = rooms.find((entry) => entry.locator === request.locator);
305
- const answer = request.attached === 'collection' ? room?.model : room?.selectionModel;
306
- return answer ?? { kind: 'model-unavailable', reason: 'not-readable' };
307
- }, request);
308
- },
309
387
  async authorize() {
310
388
  return resolveScript(scripts.authorize, () => ({}), undefined);
311
389
  },
@@ -318,13 +396,21 @@ export function createMemoryAdapter(options = {}) {
318
396
  });
319
397
  },
320
398
  reached,
399
+ follows(locator, roomVersion, next) {
400
+ const scripted = scripts.follows;
401
+ if (typeof scripted === 'boolean')
402
+ return scripted;
403
+ if (scripted !== undefined)
404
+ return scripted(locator, roomVersion, next);
405
+ return true;
406
+ },
321
407
  async bootstrap(_authorization, request) {
322
408
  return resolveScript(scripts.bootstrap, () => {
323
409
  const room = rooms.find((entry) => entry.locator === request.locator);
324
410
  if (room === undefined) {
325
411
  return { kind: 'room-not-readable', locator: request.locator };
326
412
  }
327
- return page(room, request.pageToken === undefined ? 0 : Number(request.pageToken));
413
+ return page(room, request.pageToken);
328
414
  }, request).then((answer) => note(request.locator, answer));
329
415
  },
330
416
  async refresh(_authorization, request) {
@@ -342,10 +428,6 @@ export function createMemoryAdapter(options = {}) {
342
428
  }
343
429
  return advancement(continuation.room, continuation.from, continuation.at);
344
430
  }
345
- if (request.minPosition !== undefined &&
346
- !reached(room.locator, room.roomVersion, request.minPosition)) {
347
- return { kind: 'retryable-refusal', code: 'replica_behind', fields: {} };
348
- }
349
431
  if (request.fromRoomVersion === room.roomVersion) {
350
432
  return { kind: 'still-current' };
351
433
  }
@@ -361,7 +443,13 @@ export function createMemoryAdapter(options = {}) {
361
443
  if (room === undefined) {
362
444
  return { kind: 'room-not-readable', locator: request.locator };
363
445
  }
364
- return { kind: 'rejected', code: 'unscripted-mutation', fields: {}, affected: {} };
446
+ return {
447
+ kind: 'rejected',
448
+ code: 'unscripted-mutation',
449
+ fields: {},
450
+ retryable: false,
451
+ affected: {},
452
+ };
365
453
  }, request).then((answer) => note(request.locator, answer));
366
454
  },
367
455
  async history(_authorization, request) {
@@ -376,11 +464,35 @@ export function createMemoryAdapter(options = {}) {
376
464
  };
377
465
  }, request);
378
466
  },
467
+ async binary(_authorization, request) {
468
+ return resolveScript(scripts.binary, () => {
469
+ if (request.kind === 'reference') {
470
+ return { kind: 'rejected', code: 'unscripted-binary', fields: {}, retryable: false };
471
+ }
472
+ const room = rooms.find((entry) => entry.locator === request.locator);
473
+ if (room === undefined)
474
+ return { kind: 'room-not-readable', locator: request.locator };
475
+ const blob = room.blobs?.[request.path];
476
+ return blob === undefined
477
+ ? { kind: 'rejected', code: 'file_not_found', fields: {}, retryable: false }
478
+ : { kind: 'binary', blob };
479
+ }, request);
480
+ },
481
+ referrerScope(referrer) {
482
+ if (typeof referrer !== 'object' || referrer === null)
483
+ return null;
484
+ const { room, path, message } = referrer;
485
+ if (typeof message === 'string')
486
+ return { kind: 'conversations' };
487
+ if (typeof room !== 'string')
488
+ return null;
489
+ return { kind: 'room', locator: room, ...(typeof path === 'string' ? { path } : {}) };
490
+ },
379
491
  async action(_authorization, request) {
380
492
  return resolveScript(scripts.action, () => {
381
493
  if (request.name.startsWith('conversation.'))
382
494
  return conversationAction(request);
383
- return { kind: 'rejected', code: 'unscripted-action', fields: {} };
495
+ return { kind: 'rejected', code: 'unscripted-action', fields: {}, retryable: false };
384
496
  }, request).then((answer) => {
385
497
  if (answer.kind === 'action' && answer.at !== undefined)
386
498
  note(answer.at.locator, answer.at);
package/dist/model.d.ts CHANGED
@@ -1,31 +1,27 @@
1
- import type { Definition, FieldSpec, SectionSpec, VerifiedModel } from '@agentshouse/mdmodel';
1
+ import type { Definition, ValueFacts, VerifiedModel } from '@agentshouse/mdmodel';
2
+ import type { Open } from './open.js';
2
3
  export type ModelIdentity = {
3
4
  identity: string;
4
5
  version: string;
5
6
  };
6
7
  export type ModelSnapshot = ModelIdentity & {
7
8
  schema: string | null;
9
+ revision: string | null;
8
10
  };
9
- export type ModelUnavailableReason = 'not-readable' | 'revoked' | 'not-acquired' | 'version-changed' | 'invalid';
11
+ export type ModelUnavailableReason = Open<'not-readable' | 'revoked' | 'not-acquired' | 'invalid'>;
10
12
  export type ModelResult = {
11
13
  kind: 'model';
12
14
  snapshot: ModelSnapshot;
13
15
  } | {
14
16
  kind: 'model-unavailable';
15
17
  reason: ModelUnavailableReason;
16
- } | {
17
- kind: 'transport-uncertainty';
18
- } | {
19
- kind: 'revoked-authorization';
20
- };
21
- export type MdType = {
22
- name: string;
23
- fields: Readonly<Record<string, FieldSpec>>;
24
- sections: Readonly<Record<string, SectionSpec>>;
25
18
  };
19
+ export type MdType = Definition;
26
20
  export type ModelState = {
27
21
  status: 'unavailable';
28
22
  reason: ModelUnavailableReason;
23
+ schema?: string | null;
24
+ revision?: string | null;
29
25
  } | {
30
26
  status: 'current' | 'cached';
31
27
  snapshot: ModelSnapshot;
@@ -33,23 +29,30 @@ export type ModelState = {
33
29
  types: VerifiedModel['types'];
34
30
  };
35
31
  export type TypesResult = {
36
- status: 'unavailable';
32
+ status: Open<'unavailable'>;
37
33
  reason: ModelUnavailableReason;
34
+ types: null;
35
+ schema?: string | null;
36
+ revision?: string | null;
38
37
  } | {
39
- status: 'current' | 'cached';
38
+ status: Open<'current' | 'cached'>;
40
39
  identity: string;
41
40
  version: string;
42
41
  verifiedWith: ModelIdentity;
43
42
  types: readonly MdType[];
43
+ schema: string | null;
44
+ revision: string | null;
44
45
  };
45
46
  export type ValidationReceipt = {
46
47
  identity: string;
47
48
  version: string;
48
- source: 'current' | 'cached';
49
+ source: Open<'current' | 'cached'>;
49
50
  };
50
51
  export declare const NO_MODEL: ModelState;
51
52
  export declare function definitionOf(state: ModelState, type: string | undefined): Definition | undefined;
52
53
  export declare function modelTypes(state: ModelState): TypesResult;
53
54
  export declare function cached(state: ModelState): ModelState;
54
55
  export declare function sameModel(left: ModelIdentity, right: ModelIdentity): boolean;
56
+ export declare function sameTyping(left: ModelState, right: ModelState): boolean;
57
+ export declare function referenceField(facts: ValueFacts | undefined): boolean;
55
58
  //# sourceMappingURL=model.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../src/model.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAE9F,MAAM,MAAM,aAAa,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAClE,MAAM,MAAM,aAAa,GAAG,aAAa,GAAG;IAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC;AACtE,MAAM,MAAM,sBAAsB,GAC9B,cAAc,GACd,SAAS,GACT,cAAc,GACd,iBAAiB,GACjB,SAAS,CAAC;AACd,MAAM,MAAM,WAAW,GACnB;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,aAAa,CAAA;CAAE,GAC1C;IAAE,IAAI,EAAE,mBAAmB,CAAC;IAAC,MAAM,EAAE,sBAAsB,CAAA;CAAE,GAC7D;IAAE,IAAI,EAAE,uBAAuB,CAAA;CAAE,GACjC;IAAE,IAAI,EAAE,uBAAuB,CAAA;CAAE,CAAC;AACtC,MAAM,MAAM,MAAM,GAAG;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC;IAC5C,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC;CACjD,CAAC;AACF,MAAM,MAAM,UAAU,GAClB;IAAE,MAAM,EAAE,aAAa,CAAC;IAAC,MAAM,EAAE,sBAAsB,CAAA;CAAE,GACzD;IACE,MAAM,EAAE,SAAS,GAAG,QAAQ,CAAC;IAC7B,QAAQ,EAAE,aAAa,CAAC;IACxB,YAAY,EAAE,aAAa,CAAC;IAC5B,KAAK,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC;CAC/B,CAAC;AACN,MAAM,MAAM,WAAW,GACnB;IAAE,MAAM,EAAE,aAAa,CAAC;IAAC,MAAM,EAAE,sBAAsB,CAAA;CAAE,GACzD;IACE,MAAM,EAAE,SAAS,GAAG,QAAQ,CAAC;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,aAAa,CAAC;IAC5B,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;CAC1B,CAAC;AACN,MAAM,MAAM,iBAAiB,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,SAAS,GAAG,QAAQ,CAAA;CAAE,CAAC;AACpG,eAAO,MAAM,QAAQ,EAAE,UAA8D,CAAC;AACtF,wBAAgB,YAAY,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,CAGhG;AACD,wBAAgB,UAAU,CAAC,KAAK,EAAE,UAAU,GAAG,WAAW,CAczD;AACD,wBAAgB,MAAM,CAAC,KAAK,EAAE,UAAU,GAAG,UAAU,CAEpD;AACD,wBAAgB,SAAS,CAAC,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,aAAa,GAAG,OAAO,CAE5E"}
1
+ {"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../src/model.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAClF,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEtC,MAAM,MAAM,aAAa,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAClE,MAAM,MAAM,aAAa,GAAG,aAAa,GAAG;IAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC;AAC/F,MAAM,MAAM,sBAAsB,GAAG,IAAI,CAAC,cAAc,GAAG,SAAS,GAAG,cAAc,GAAG,SAAS,CAAC,CAAC;AACnG,MAAM,MAAM,WAAW,GACnB;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,aAAa,CAAA;CAAE,GAC1C;IAAE,IAAI,EAAE,mBAAmB,CAAC;IAAC,MAAM,EAAE,sBAAsB,CAAA;CAAE,CAAC;AAClE,MAAM,MAAM,MAAM,GAAG,UAAU,CAAC;AAChC,MAAM,MAAM,UAAU,GAClB;IACE,MAAM,EAAE,aAAa,CAAC;IACtB,MAAM,EAAE,sBAAsB,CAAC;IAC/B,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B,GACD;IACE,MAAM,EAAE,SAAS,GAAG,QAAQ,CAAC;IAC7B,QAAQ,EAAE,aAAa,CAAC;IACxB,YAAY,EAAE,aAAa,CAAC;IAC5B,KAAK,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC;CAC/B,CAAC;AACN,MAAM,MAAM,WAAW,GACnB;IACE,MAAM,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC;IAC5B,MAAM,EAAE,sBAAsB,CAAC;IAC/B,KAAK,EAAE,IAAI,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B,GACD;IACE,MAAM,EAAE,IAAI,CAAC,SAAS,GAAG,QAAQ,CAAC,CAAC;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,aAAa,CAAC;IAC5B,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB,CAAC;AACN,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,IAAI,CAAC,SAAS,GAAG,QAAQ,CAAC,CAAC;CACpC,CAAC;AACF,eAAO,MAAM,QAAQ,EAAE,UAA8D,CAAC;AACtF,wBAAgB,YAAY,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,CAGhG;AACD,wBAAgB,UAAU,CAAC,KAAK,EAAE,UAAU,GAAG,WAAW,CAgBzD;AACD,wBAAgB,MAAM,CAAC,KAAK,EAAE,UAAU,GAAG,UAAU,CAEpD;AACD,wBAAgB,SAAS,CAAC,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,aAAa,GAAG,OAAO,CAE5E;AACD,wBAAgB,UAAU,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,GAAG,OAAO,CASvE;AACD,wBAAgB,cAAc,CAAC,KAAK,EAAE,UAAU,GAAG,SAAS,GAAG,OAAO,CAErE"}
package/dist/model.js CHANGED
@@ -6,15 +6,15 @@ export function definitionOf(state, type) {
6
6
  }
7
7
  export function modelTypes(state) {
8
8
  return state.status === 'unavailable'
9
- ? { ...state }
9
+ ? { ...state, types: null }
10
10
  : {
11
11
  status: state.status,
12
12
  identity: state.snapshot.identity,
13
13
  version: state.snapshot.version,
14
14
  verifiedWith: { ...state.verifiedWith },
15
- types: structuredClone(Object.values(state.types)
16
- .sort((left, right) => (left.name < right.name ? -1 : left.name > right.name ? 1 : 0))
17
- .map(({ name, fields, sections }) => ({ name, fields, sections }))),
15
+ schema: state.snapshot.schema,
16
+ revision: state.snapshot.revision,
17
+ types: structuredClone(Object.values(state.types).sort((left, right) => left.name < right.name ? -1 : left.name > right.name ? 1 : 0)),
18
18
  };
19
19
  }
20
20
  export function cached(state) {
@@ -23,3 +23,15 @@ export function cached(state) {
23
23
  export function sameModel(left, right) {
24
24
  return left.identity === right.identity && left.version === right.version;
25
25
  }
26
+ export function sameTyping(left, right) {
27
+ if (left === right)
28
+ return true;
29
+ if (left.status === 'unavailable' || right.status === 'unavailable') {
30
+ return left.status === right.status;
31
+ }
32
+ return (left.snapshot.schema === right.snapshot.schema &&
33
+ sameModel(left.verifiedWith, right.verifiedWith));
34
+ }
35
+ export function referenceField(facts) {
36
+ return facts?.reference !== undefined || facts?.items?.reference !== undefined;
37
+ }
@@ -1,4 +1,4 @@
1
- import type { QueryDocument, QueryGroup, QueryResult } from './query.js';
1
+ import type { QueryCoverage, QueryDocument, QueryGroup, QueryResult } from './query.js';
2
2
  export type Observation<T> = {
3
3
  current(): T;
4
4
  subscribe(listener: (value: T) => void): () => void;
@@ -13,13 +13,18 @@ export type QueryChange = {
13
13
  changed: readonly (readonly [RowKey, QueryDocument | QueryGroup])[];
14
14
  removed: readonly RowKey[];
15
15
  order: readonly RowKey[] | null;
16
+ coverage?: QueryCoverage;
16
17
  };
18
+ export type QueryMessage = KeyedResult | QueryChange;
17
19
  export type Observed = {
18
20
  observation: Observation<unknown>;
19
21
  receive(message: unknown): void;
20
22
  end(): void;
21
23
  };
22
24
  export declare function rowsOf(result: QueryResult): readonly (QueryDocument | QueryGroup)[];
25
+ export declare function whole(message: QueryMessage): message is KeyedResult;
26
+ export declare function applied(keyed: KeyedResult, message: QueryMessage): KeyedResult;
23
27
  export declare function observed(first: unknown, stop: () => void, keyed: boolean): Observed;
24
28
  export declare function opened(watch: (deliver: (message: unknown) => void) => () => void, keyed: boolean): Promise<Observation<unknown>>;
29
+ export declare function observing<Topic extends string>(watch: (topic: Topic, input: readonly unknown[], deliver: (message: unknown) => void) => () => void): (topic: Topic, ...input: unknown[]) => Promise<Observation<unknown>>;
25
30
  //# sourceMappingURL=observation.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"observation.d.ts","sourceRoot":"","sources":["../src/observation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEzE,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI;IAC3B,OAAO,IAAI,CAAC,CAAC;IACb,SAAS,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IACpD,KAAK,IAAI,IAAI,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;AAErC,MAAM,MAAM,WAAW,GAAG;IAAE,MAAM,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,CAAC;AAE3E,MAAM,MAAM,WAAW,GAAG;IACxB,OAAO,EAAE,SAAS,CAAC,SAAS,CAAC,MAAM,EAAE,aAAa,GAAG,UAAU,CAAC,CAAC,EAAE,CAAC;IACpE,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3B,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,CAAC;CACjC,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG;IACrB,WAAW,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC;IAClC,OAAO,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;IAChC,GAAG,IAAI,IAAI,CAAC;CACb,CAAC;AAEF,wBAAgB,MAAM,CAAC,MAAM,EAAE,WAAW,GAAG,SAAS,CAAC,aAAa,GAAG,UAAU,CAAC,EAAE,CAEnF;AA0BD,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,IAAI,EAAE,KAAK,EAAE,OAAO,GAAG,QAAQ,CAkCnF;AAED,wBAAgB,MAAM,CACpB,KAAK,EAAE,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,KAAK,MAAM,IAAI,EAC1D,KAAK,EAAE,OAAO,GACb,OAAO,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAiB/B"}
1
+ {"version":3,"file":"observation.d.ts","sourceRoot":"","sources":["../src/observation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAExF,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI;IAC3B,OAAO,IAAI,CAAC,CAAC;IACb,SAAS,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IACpD,KAAK,IAAI,IAAI,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;AAErC,MAAM,MAAM,WAAW,GAAG;IAAE,MAAM,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,CAAC;AAE3E,MAAM,MAAM,WAAW,GAAG;IACxB,OAAO,EAAE,SAAS,CAAC,SAAS,CAAC,MAAM,EAAE,aAAa,GAAG,UAAU,CAAC,CAAC,EAAE,CAAC;IACpE,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3B,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,CAAC;IAChC,QAAQ,CAAC,EAAE,aAAa,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG,WAAW,GAAG,WAAW,CAAC;AAErD,MAAM,MAAM,QAAQ,GAAG;IACrB,WAAW,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC;IAClC,OAAO,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;IAChC,GAAG,IAAI,IAAI,CAAC;CACb,CAAC;AAEF,wBAAgB,MAAM,CAAC,MAAM,EAAE,WAAW,GAAG,SAAS,CAAC,aAAa,GAAG,UAAU,CAAC,EAAE,CAGnF;AAED,wBAAgB,KAAK,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,IAAI,WAAW,CAEnE;AAED,wBAAgB,OAAO,CAAC,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,YAAY,GAAG,WAAW,CAkB9E;AAiBD,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,IAAI,EAAE,KAAK,EAAE,OAAO,GAAG,QAAQ,CAkCnF;AAED,wBAAgB,MAAM,CACpB,KAAK,EAAE,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,KAAK,MAAM,IAAI,EAC1D,KAAK,EAAE,OAAO,GACb,OAAO,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAiB/B;AAED,wBAAgB,SAAS,CAAC,KAAK,SAAS,MAAM,EAC5C,KAAK,EAAE,CACL,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,SAAS,OAAO,EAAE,EACzB,OAAO,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,KAChC,MAAM,IAAI,WAEA,KAAK,YAAY,OAAO,EAAE,KAAG,OAAO,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAQ1E"}
@@ -1,32 +1,48 @@
1
1
  export function rowsOf(result) {
2
- return 'groups' in result ? result.groups : result.documents;
2
+ if ('groups' in result)
3
+ return result.groups;
4
+ return 'documents' in result ? result.documents : [];
5
+ }
6
+ export function whole(message) {
7
+ return 'result' in message;
8
+ }
9
+ export function applied(keyed, message) {
10
+ if (whole(message))
11
+ return message;
12
+ const result = keyed.result;
13
+ if ('pending' in result)
14
+ return keyed;
15
+ const rows = rowsOf(result);
16
+ const byKey = new Map(keyed.keys.map((key, index) => [key, rows[index]]));
17
+ for (const key of message.removed)
18
+ byKey.delete(key);
19
+ for (const [key, row] of message.changed)
20
+ byKey.set(key, row);
21
+ const keys = message.order ?? keyed.keys.filter((key) => byKey.has(key));
22
+ const next = keys.map((key) => byKey.get(key));
23
+ const coverage = message.coverage ?? result.coverage;
24
+ return {
25
+ result: 'groups' in result
26
+ ? { groups: next, coverage }
27
+ : { documents: next, coverage },
28
+ keys,
29
+ };
3
30
  }
4
31
  function keyedResult(first) {
5
- const grouped = 'groups' in first.result;
6
- const rows = rowsOf(first.result);
7
- const byKey = new Map(first.keys.map((key, index) => [key, rows[index]]));
8
- let keys = first.keys;
32
+ let keyed = first;
9
33
  return {
10
34
  value: first.result,
11
35
  next(message) {
12
- const change = message;
13
- for (const key of change.removed)
14
- byKey.delete(key);
15
- for (const [key, row] of change.changed)
16
- byKey.set(key, row);
17
- keys = change.order ?? keys.filter((key) => byKey.has(key));
18
- const next = keys.map((key) => byKey.get(key));
19
- return grouped
20
- ? { groups: next }
21
- : { documents: next };
36
+ keyed = applied(keyed, message);
37
+ return keyed.result;
22
38
  },
23
39
  };
24
40
  }
25
- function whole(first) {
41
+ function replaced(first) {
26
42
  return { value: first, next: (message) => message };
27
43
  }
28
44
  export function observed(first, stop, keyed) {
29
- const reducer = keyed ? keyedResult(first) : whole(first);
45
+ const reducer = keyed ? keyedResult(first) : replaced(first);
30
46
  let value = reducer.value;
31
47
  let open = true;
32
48
  const listeners = new Set();
@@ -83,3 +99,8 @@ export function opened(watch, keyed) {
83
99
  }
84
100
  });
85
101
  }
102
+ export function observing(watch) {
103
+ return (topic, ...input) => opened((deliver) => watch(topic, input, (message) => {
104
+ deliver(structuredClone(message));
105
+ }), topic === 'query');
106
+ }
package/dist/open.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export type Open<Value extends string> = Value | (string & {});
2
+ //# sourceMappingURL=open.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"open.d.ts","sourceRoot":"","sources":["../src/open.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,IAAI,CAAC,KAAK,SAAS,MAAM,IAAI,KAAK,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC"}
package/dist/open.js ADDED
@@ -0,0 +1 @@
1
+ export {};