@agentshouse/mdruntime 0.1.0-c389df724fd2f → 0.1.0-c38dcfe165d81

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 (184) hide show
  1. package/README.md +24 -11
  2. package/dist/adapter.d.ts +175 -54
  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 +16 -0
  8. package/dist/attempt.d.ts.map +1 -0
  9. package/dist/attempt.js +70 -0
  10. package/dist/binary.d.ts +18 -0
  11. package/dist/binary.d.ts.map +1 -0
  12. package/dist/binary.js +8 -0
  13. package/dist/bootstrap.d.ts +33 -5
  14. package/dist/bootstrap.d.ts.map +1 -1
  15. package/dist/bootstrap.js +202 -48
  16. package/dist/chain.d.ts +12 -0
  17. package/dist/chain.d.ts.map +1 -0
  18. package/dist/chain.js +64 -0
  19. package/dist/change-set.d.ts +9 -14
  20. package/dist/change-set.d.ts.map +1 -1
  21. package/dist/change-set.js +11 -56
  22. package/dist/change.d.ts +45 -0
  23. package/dist/change.d.ts.map +1 -1
  24. package/dist/change.js +34 -0
  25. package/dist/client.d.ts +37 -17
  26. package/dist/client.d.ts.map +1 -1
  27. package/dist/client.js +123 -22
  28. package/dist/collection.d.ts +13 -14
  29. package/dist/collection.d.ts.map +1 -1
  30. package/dist/collection.js +52 -56
  31. package/dist/compile.d.ts +11 -0
  32. package/dist/compile.d.ts.map +1 -0
  33. package/dist/compile.js +586 -0
  34. package/dist/connection.d.ts +59 -20
  35. package/dist/connection.d.ts.map +1 -1
  36. package/dist/connection.js +3810 -905
  37. package/dist/context.d.ts +2 -0
  38. package/dist/context.d.ts.map +1 -0
  39. package/dist/context.js +7 -0
  40. package/dist/conversation.d.ts +50 -20
  41. package/dist/conversation.d.ts.map +1 -1
  42. package/dist/conversation.js +714 -242
  43. package/dist/digest.d.ts +7 -0
  44. package/dist/digest.d.ts.map +1 -0
  45. package/dist/digest.js +113 -0
  46. package/dist/durability.d.ts +4 -1
  47. package/dist/durability.d.ts.map +1 -1
  48. package/dist/durability.js +5 -0
  49. package/dist/entry.d.ts +20 -0
  50. package/dist/entry.d.ts.map +1 -0
  51. package/dist/entry.js +159 -0
  52. package/dist/expectation.d.ts +21 -0
  53. package/dist/expectation.d.ts.map +1 -0
  54. package/dist/expectation.js +70 -0
  55. package/dist/failure.d.ts +13 -0
  56. package/dist/failure.d.ts.map +1 -0
  57. package/dist/failure.js +19 -0
  58. package/dist/fence.d.ts +6 -0
  59. package/dist/fence.d.ts.map +1 -0
  60. package/dist/fence.js +10 -0
  61. package/dist/file.d.ts +2 -19
  62. package/dist/file.d.ts.map +1 -1
  63. package/dist/file.js +7 -122
  64. package/dist/host.d.ts +11 -3
  65. package/dist/host.d.ts.map +1 -1
  66. package/dist/host.js +128 -25
  67. package/dist/identity.d.ts +3 -0
  68. package/dist/identity.d.ts.map +1 -1
  69. package/dist/identity.js +13 -1
  70. package/dist/index.d.ts +23 -9
  71. package/dist/index.d.ts.map +1 -1
  72. package/dist/index.js +11 -0
  73. package/dist/job.d.ts +14 -0
  74. package/dist/job.d.ts.map +1 -0
  75. package/dist/job.js +28 -0
  76. package/dist/live.d.ts +23 -0
  77. package/dist/live.d.ts.map +1 -0
  78. package/dist/live.js +127 -0
  79. package/dist/media.d.ts +8 -0
  80. package/dist/media.d.ts.map +1 -0
  81. package/dist/media.js +218 -0
  82. package/dist/memory.d.ts +19 -7
  83. package/dist/memory.d.ts.map +1 -1
  84. package/dist/memory.js +251 -98
  85. package/dist/messages.d.ts +28 -0
  86. package/dist/messages.d.ts.map +1 -0
  87. package/dist/messages.js +89 -0
  88. package/dist/model.d.ts +13 -6
  89. package/dist/model.d.ts.map +1 -1
  90. package/dist/model.js +8 -5
  91. package/dist/name.d.ts +4 -0
  92. package/dist/name.d.ts.map +1 -0
  93. package/dist/name.js +122 -0
  94. package/dist/observation.d.ts +22 -4
  95. package/dist/observation.d.ts.map +1 -1
  96. package/dist/observation.js +120 -14
  97. package/dist/open.d.ts +2 -0
  98. package/dist/open.d.ts.map +1 -0
  99. package/dist/open.js +1 -0
  100. package/dist/opened.d.ts +22 -0
  101. package/dist/opened.d.ts.map +1 -0
  102. package/dist/opened.js +107 -0
  103. package/dist/outcome.d.ts +72 -5
  104. package/dist/outcome.d.ts.map +1 -1
  105. package/dist/outcome.js +81 -1
  106. package/dist/overlay.d.ts +15 -5
  107. package/dist/overlay.d.ts.map +1 -1
  108. package/dist/overlay.js +136 -180
  109. package/dist/paths.d.ts +1 -0
  110. package/dist/paths.d.ts.map +1 -1
  111. package/dist/paths.js +4 -0
  112. package/dist/query.d.ts +42 -181
  113. package/dist/query.d.ts.map +1 -1
  114. package/dist/query.js +3 -461
  115. package/dist/reach.d.ts +18 -4
  116. package/dist/reach.d.ts.map +1 -1
  117. package/dist/reach.js +234 -69
  118. package/dist/react.d.ts +14 -8
  119. package/dist/react.d.ts.map +1 -1
  120. package/dist/react.js +8 -17
  121. package/dist/readiness.d.ts +50 -0
  122. package/dist/readiness.d.ts.map +1 -0
  123. package/dist/readiness.js +27 -0
  124. package/dist/recovery.d.ts +26 -0
  125. package/dist/recovery.d.ts.map +1 -0
  126. package/dist/recovery.js +224 -0
  127. package/dist/refresh.d.ts +20 -7
  128. package/dist/refresh.d.ts.map +1 -1
  129. package/dist/refresh.js +148 -59
  130. package/dist/replica.d.ts +59 -33
  131. package/dist/replica.d.ts.map +1 -1
  132. package/dist/replica.js +320 -157
  133. package/dist/room-draft.d.ts +59 -11
  134. package/dist/room-draft.d.ts.map +1 -1
  135. package/dist/room-draft.js +276 -80
  136. package/dist/rows.d.ts +23 -35
  137. package/dist/rows.d.ts.map +1 -1
  138. package/dist/rows.js +61 -117
  139. package/dist/save.d.ts +73 -4
  140. package/dist/save.d.ts.map +1 -1
  141. package/dist/save.js +825 -150
  142. package/dist/search.d.ts +15 -54
  143. package/dist/search.d.ts.map +1 -1
  144. package/dist/search.js +10 -121
  145. package/dist/seed.d.ts +16 -0
  146. package/dist/seed.d.ts.map +1 -0
  147. package/dist/seed.js +75 -0
  148. package/dist/shared.d.ts +15 -0
  149. package/dist/shared.d.ts.map +1 -0
  150. package/dist/shared.js +421 -0
  151. package/dist/status.d.ts +7 -2
  152. package/dist/status.d.ts.map +1 -1
  153. package/dist/status.js +3 -9
  154. package/dist/store.d.ts +107 -62
  155. package/dist/store.d.ts.map +1 -1
  156. package/dist/store.js +318 -354
  157. package/dist/stored.d.ts +129 -0
  158. package/dist/stored.d.ts.map +1 -0
  159. package/dist/stored.js +495 -0
  160. package/dist/tables.d.ts +326 -0
  161. package/dist/tables.d.ts.map +1 -0
  162. package/dist/tables.js +574 -0
  163. package/dist/transfer.d.ts +0 -5
  164. package/dist/transfer.d.ts.map +1 -1
  165. package/dist/turn.d.ts +9 -0
  166. package/dist/turn.d.ts.map +1 -0
  167. package/dist/turn.js +75 -0
  168. package/dist/validation.d.ts +29 -0
  169. package/dist/validation.d.ts.map +1 -0
  170. package/dist/validation.js +104 -0
  171. package/dist/windows.d.ts +11 -0
  172. package/dist/windows.d.ts.map +1 -0
  173. package/dist/windows.js +16 -0
  174. package/dist/wire.d.ts +169 -33
  175. package/dist/wire.d.ts.map +1 -1
  176. package/dist/wire.js +68 -31
  177. package/package.json +26 -15
  178. package/AGENTS.md +0 -253
  179. package/dist/attachment.d.ts +0 -3
  180. package/dist/attachment.d.ts.map +0 -1
  181. package/dist/attachment.js +0 -8
  182. package/dist/read-side.d.ts +0 -18
  183. package/dist/read-side.d.ts.map +0 -1
  184. package/dist/read-side.js +0 -433
package/dist/memory.js CHANGED
@@ -1,7 +1,10 @@
1
1
  import { sameChange } from './overlay.js';
2
+ const ATTACHMENTS = 'attachments';
3
+ function lastName(path) {
4
+ return path.slice(path.lastIndexOf('/') + 1);
5
+ }
2
6
  const FIXTURE_RETENTION = {
3
7
  history: { kind: 'duration', seconds: 1 },
4
- outcome: 1,
5
8
  refreshHorizon: 1,
6
9
  };
7
10
  function declaredRetention(value) {
@@ -15,12 +18,7 @@ function declaredRetention(value) {
15
18
  }
16
19
  const MODEL_ABSENT = { kind: 'model-unavailable', reason: 'not-readable' };
17
20
  function modelsOf(room) {
18
- return {
19
- collection: room.model ?? MODEL_ABSENT,
20
- ...(room.typedSelection === undefined
21
- ? {}
22
- : { selection: room.selectionModel ?? MODEL_ABSENT }),
23
- };
21
+ return { collection: room.model ?? MODEL_ABSENT };
24
22
  }
25
23
  function changedModels(room, from) {
26
24
  const now = modelsOf(room);
@@ -53,12 +51,16 @@ export function createMemoryAdapter(options = {}) {
53
51
  const listeners = new Set();
54
52
  const modeObservers = new Set();
55
53
  const scripted = new Map();
56
- const unsealed = new Map();
57
- const sealed = new Set();
54
+ const sentTurns = new Map();
58
55
  const historyPageSize = options.historyPageSize;
59
- let held = [];
56
+ const attached = new Map();
57
+ const linked = new WeakMap();
58
+ const carried = new Map();
60
59
  let expiring = [];
60
+ let openings = 0;
61
+ const declaredRooms = new Set();
61
62
  let replicated = [];
63
+ let heldCursor = null;
62
64
  let issued = 0;
63
65
  function scriptConversations(entries) {
64
66
  scripted.clear();
@@ -86,6 +88,19 @@ export function createMemoryAdapter(options = {}) {
86
88
  const wanted = ordinals.get(`${locator}\u0000${target}`);
87
89
  return held !== undefined && wanted !== undefined && held >= wanted;
88
90
  }
91
+ function heldBlob(room, path) {
92
+ return attached.get(`${room.locator}\u0000${path}`) ?? room.blobs?.[path];
93
+ }
94
+ function freePath(room, name) {
95
+ const dot = name.lastIndexOf('.');
96
+ const stem = dot > 0 ? name.slice(0, dot) : name;
97
+ const extension = dot > 0 ? name.slice(dot) : '';
98
+ for (let count = 1;; count += 1) {
99
+ const path = `${ATTACHMENTS}/${count === 1 ? stem : `${stem}-${count}`}${extension}`;
100
+ if (heldBlob(room, path) === undefined && room.documents?.[path] === undefined)
101
+ return path;
102
+ }
103
+ }
89
104
  function retain(entries) {
90
105
  for (const room of entries) {
91
106
  note(room.locator, room);
@@ -107,35 +122,51 @@ export function createMemoryAdapter(options = {}) {
107
122
  providerOwnedPaths: room.providerOwnedPaths,
108
123
  };
109
124
  }
110
- function page(room, from) {
125
+ function page(room, after) {
111
126
  const paths = Object.keys(room.documents ?? {}).sort();
127
+ const opening = after === undefined;
128
+ const from = opening ? 0 : paths.filter((path) => path <= after).length;
112
129
  const size = pageSize ?? paths.length;
113
130
  const taken = paths.slice(from, from + size);
114
131
  const next = from + taken.length;
115
- const targets = from === 0 ? (room.binaryTargets ?? []) : [];
132
+ const targets = opening ? Object.keys(room.binaryFiles ?? {}) : [];
116
133
  return {
117
134
  kind: 'bootstrap',
118
135
  roomVersion: room.roomVersion,
119
- ...(from === 0 ? { models: modelsOf(room) } : {}),
136
+ ...(opening ? { models: modelsOf(room) } : {}),
120
137
  documents: named(room.documents ?? {}, taken),
121
- revisions: named(room.revisions ?? {}, taken),
138
+ revisions: named(room.revisions ?? {}, [...taken, ...targets]),
139
+ ...(room.fileIds === undefined
140
+ ? {}
141
+ : { fileIds: named(room.fileIds, [...taken, ...targets]) }),
122
142
  changes: named(room.changes ?? {}, [...taken, ...targets]),
123
- binaryTargets: targets,
124
- ...(from === 0 && room.attachments !== undefined ? { attachments: room.attachments } : {}),
125
- ...(next < paths.length ? { pageToken: String(next) } : {}),
143
+ binaryFiles: named(room.binaryFiles ?? {}, targets),
144
+ ...(opening && room.folders !== undefined ? { folders: room.folders } : {}),
145
+ ...(next < paths.length ? { pageToken: taken.at(-1) } : {}),
126
146
  };
127
147
  }
128
- function advancement(room, from, at) {
148
+ function foldersMoved(room, from) {
149
+ const now = new Set(room.folders ?? []);
150
+ const before = new Set(from.folders ?? []);
151
+ const folders = [...now].filter((folder) => !before.has(folder)).sort();
152
+ const folderRemovals = [...before].filter((folder) => !now.has(folder)).sort();
153
+ return {
154
+ ...(folders.length === 0 ? {} : { folders }),
155
+ ...(folderRemovals.length === 0 ? {} : { folderRemovals }),
156
+ };
157
+ }
158
+ function advancement(room, from, at, whole = false) {
129
159
  const current = room.documents ?? {};
130
160
  const previous = from.documents ?? {};
131
- const currentTargets = new Set(room.binaryTargets ?? []);
132
- const previousTargets = new Set(from.binaryTargets ?? []);
161
+ const currentTargets = new Set(Object.keys(room.binaryFiles ?? {}));
162
+ const previousTargets = new Set(Object.keys(from.binaryFiles ?? {}));
133
163
  const headMoved = (path) => room.revisions?.[path] !== from.revisions?.[path] ||
164
+ room.fileIds?.[path] !== from.fileIds?.[path] ||
134
165
  !sameChange(room.changes?.[path], from.changes?.[path]);
135
166
  const changed = Object.keys(current)
136
167
  .filter((path) => current[path] !== previous[path] || headMoved(path))
137
168
  .sort();
138
- const size = pageSize ?? changed.length;
169
+ const size = whole ? changed.length : (pageSize ?? changed.length);
139
170
  const taken = changed.slice(at, at + size);
140
171
  const next = at + taken.length;
141
172
  const opening = at === 0;
@@ -145,27 +176,48 @@ export function createMemoryAdapter(options = {}) {
145
176
  const targets = opening
146
177
  ? [...currentTargets].filter((target) => !previousTargets.has(target) || headMoved(target))
147
178
  : [];
148
- const attachments = room.attachments ?? [];
149
- const declared = opening && JSON.stringify(attachments) !== JSON.stringify(from.attachments ?? []);
150
179
  const models = opening ? changedModels(room, from) : undefined;
151
180
  return {
152
181
  kind: 'net-advancement',
153
182
  roomVersion: room.roomVersion,
154
183
  ...(models === undefined ? {} : { models }),
155
184
  documents: named(current, taken),
156
- revisions: named(room.revisions ?? {}, taken),
185
+ revisions: named(room.revisions ?? {}, [...taken, ...targets]),
186
+ ...(room.fileIds === undefined
187
+ ? {}
188
+ : { fileIds: named(room.fileIds, [...taken, ...targets]) }),
157
189
  changes: named(room.changes ?? {}, [...taken, ...targets]),
158
- binaryTargets: targets,
190
+ binaryFiles: named(room.binaryFiles ?? {}, targets),
191
+ ...(opening ? foldersMoved(room, from) : {}),
159
192
  removals: opening
160
193
  ? [
161
194
  ...Object.keys(previous).filter((path) => !(path in current) && !currentTargets.has(path)),
162
195
  ...[...previousTargets].filter((target) => !currentTargets.has(target) && !(target in current)),
163
196
  ].sort()
164
197
  : [],
165
- ...(declared ? { attachments } : {}),
166
198
  ...(pageToken === undefined ? {} : { pageToken }),
167
199
  };
168
200
  }
201
+ function carry(locator) {
202
+ const at = carried.get(locator);
203
+ const current = rooms.find((entry) => entry.locator === locator);
204
+ if (at === undefined || current === undefined || current.roomVersion === at)
205
+ return;
206
+ const from = served.get(locator)?.get(at);
207
+ carried.set(locator, current.roomVersion);
208
+ if (from === undefined)
209
+ return;
210
+ const page = advancement(current, from, 0, true);
211
+ for (const observer of observers) {
212
+ observer({
213
+ kind: 'room-change',
214
+ locator,
215
+ from: at,
216
+ roomVersion: current.roomVersion,
217
+ read: async () => page,
218
+ });
219
+ }
220
+ }
169
221
  function pageOf(conversation, after) {
170
222
  const messages = conversation.messages ?? [];
171
223
  const first = typeof after === 'string' ? messages.findIndex((message) => message.position > after) : 0;
@@ -180,16 +232,13 @@ export function createMemoryAdapter(options = {}) {
180
232
  : {}),
181
233
  };
182
234
  }
183
- function historiesOf(input) {
184
- const asked = input?.conversations;
185
- if (!Array.isArray(asked))
186
- return { kind: 'rejected', code: 'invalid_request', fields: {} };
235
+ function historiesOf(request) {
187
236
  const histories = [];
188
- for (const entry of asked) {
189
- const conversation = typeof entry.conversation === 'string' ? scripted.get(entry.conversation) : undefined;
237
+ for (const entry of request.conversations) {
238
+ const conversation = scripted.get(entry.conversation);
190
239
  if (conversation === undefined)
191
240
  continue;
192
- const outcome = conversation.outcomes?.['conversation.history'];
241
+ const outcome = conversation.outcomes?.history;
193
242
  if (outcome !== undefined)
194
243
  return outcome;
195
244
  histories.push(pageOf(conversation, entry.after));
@@ -200,44 +249,47 @@ export function createMemoryAdapter(options = {}) {
200
249
  function conversationAction(request) {
201
250
  const name = request.name;
202
251
  const input = (request.input ?? {});
203
- if (name === 'conversation.history')
204
- return historiesOf(request.input);
205
- if (name === 'conversation.create') {
252
+ if (name === 'conversations/create') {
206
253
  issued += 1;
207
254
  const conversation = `conversation-${issued}`;
208
255
  scripted.set(conversation, { conversation });
209
- return { kind: 'action', result: { conversation } };
256
+ return { kind: 'action', result: { conversation_id: conversation } };
210
257
  }
211
- const named = typeof input.conversation === 'string' ? input.conversation : '';
258
+ const named = typeof input.conversation_id === 'string' ? input.conversation_id : '';
212
259
  const conversation = scripted.get(named);
213
260
  if (conversation === undefined) {
214
- return { kind: 'rejected', code: 'unknown-conversation', fields: { conversation: named } };
261
+ return {
262
+ kind: 'rejected',
263
+ code: 'unknown-conversation',
264
+ fields: { conversation: named },
265
+ retryable: false,
266
+ };
215
267
  }
216
268
  const outcome = conversation.outcomes?.[name];
217
269
  if (outcome !== undefined)
218
270
  return outcome;
219
- if (name === 'conversation.send') {
271
+ if (name === 'conversations/send') {
272
+ const operation = typeof input.operation_id === 'string' ? input.operation_id : null;
273
+ const answered = operation === null ? undefined : sentTurns.get(operation);
274
+ if (answered !== undefined)
275
+ return { kind: 'action', result: { message_id: answered } };
220
276
  issued += 1;
221
277
  const turn = `turn-${issued}`;
222
- unsealed.set(turn, named);
223
- return { kind: 'action', result: { turn } };
278
+ if (operation !== null)
279
+ sentTurns.set(operation, turn);
280
+ return { kind: 'action', result: { message_id: turn } };
224
281
  }
225
- if (name === 'conversation.cancel') {
226
- const turn = typeof input.turn === 'string' ? input.turn : '';
227
- if (sealed.has(turn) || !unsealed.has(turn)) {
228
- return { kind: 'rejected', code: 'turn-sealed', fields: { turn } };
229
- }
230
- unsealed.delete(turn);
231
- return { kind: 'action', result: { turn, cancelled: true } };
232
- }
233
- if (name === 'conversation.delete')
282
+ if (name === 'conversations/delete')
234
283
  scripted.delete(named);
235
- return { kind: 'action', result: { conversation: named } };
284
+ return { kind: 'action', result: { conversation_id: named } };
236
285
  }
237
286
  function announceList() {
238
- const entries = [...scripted.values()].flatMap(({ conversation, listed }) => listed === undefined ? [] : [{ conversation, ...listed }]);
239
- for (const listener of listeners)
240
- listener({ kind: 'list', entries });
287
+ const entries = [...scripted.values()].flatMap(({ conversation, listed, messages = [] }) => listed === undefined
288
+ ? []
289
+ : [{ conversation, ...listed, newest: messages.at(-1)?.position ?? null }]);
290
+ for (const listener of listeners) {
291
+ listener({ kind: 'list', entries, whole: true, more: false, cursor: null });
292
+ }
241
293
  }
242
294
  function discovery() {
243
295
  return {
@@ -249,7 +301,42 @@ export function createMemoryAdapter(options = {}) {
249
301
  ...(options.member === undefined ? {} : { member: options.member }),
250
302
  };
251
303
  }
304
+ function discovered() {
305
+ return resolveScript(scripts.discover, discovery, undefined).then((answer) => {
306
+ if (answer.kind === 'discovery')
307
+ for (const room of answer.rooms)
308
+ note(room.locator, room);
309
+ return answer;
310
+ });
311
+ }
312
+ function opened(notify) {
313
+ const opening = ++openings;
314
+ notify({ kind: 'opened' });
315
+ void discovered()
316
+ .catch(() => ({ kind: 'transport-uncertainty' }))
317
+ .then((answer) => {
318
+ if (opening !== openings)
319
+ return;
320
+ if (answer.kind !== 'discovery') {
321
+ openings += 1;
322
+ carried.clear();
323
+ const revoked = answer.kind === 'revoked-authorization';
324
+ notify({ kind: 'closed', revoked, lost: !revoked, authorityChanged: false });
325
+ return;
326
+ }
327
+ declaredRooms.clear();
328
+ for (const room of answer.rooms)
329
+ declaredRooms.add(room.locator);
330
+ notify({ kind: 'discovered', discovery: answer });
331
+ });
332
+ }
333
+ function emitAll(event) {
334
+ for (const observer of observers)
335
+ observer(event);
336
+ }
252
337
  return {
338
+ ...(options.roomTypes === undefined ? {} : { roomTypes: options.roomTypes }),
339
+ ...(options.typedPaths === undefined ? {} : { typedPaths: options.typedPaths }),
253
340
  setRooms(next) {
254
341
  rooms = [...next];
255
342
  retain(rooms);
@@ -258,26 +345,37 @@ export function createMemoryAdapter(options = {}) {
258
345
  scriptConversations(next);
259
346
  },
260
347
  open() {
348
+ carried.clear();
261
349
  announceList();
262
- for (const observer of observers)
263
- observer({ kind: 'opened' });
350
+ opened(emitAll);
264
351
  },
265
- close(revoked = false) {
266
- for (const observer of observers)
267
- observer({ kind: 'closed', revoked });
352
+ close({ revoked = false, lost = !revoked, authorityChanged = false } = {}) {
353
+ openings += 1;
354
+ carried.clear();
355
+ emitAll({ kind: 'closed', revoked, lost, authorityChanged });
268
356
  },
269
357
  expire(locators) {
270
358
  expiring = [...locators];
271
359
  },
272
- notifyRoomVersion({ locator, roomVersion }) {
273
- for (const observer of observers)
274
- observer({ kind: 'room-version', locator, roomVersion });
360
+ notifyRoomVersion({ locator }) {
361
+ carry(locator);
362
+ },
363
+ notifyAuthorityChanged(locators) {
364
+ const named = new Set(locators ?? [...declaredRooms, ...rooms.map((room) => room.locator)]);
365
+ const listed = rooms.filter((room) => named.has(room.locator));
366
+ const left = [...named].filter((locator) => !listed.some((room) => room.locator === locator));
367
+ for (const room of listed)
368
+ declaredRooms.add(room.locator);
369
+ for (const locator of left)
370
+ declaredRooms.delete(locator);
371
+ emitAll({
372
+ kind: 'authority-changed',
373
+ rooms: listed.map(declared),
374
+ left,
375
+ ...(options.member === undefined ? {} : { member: options.member }),
376
+ });
275
377
  },
276
378
  notifyConversation(event) {
277
- if (event.kind === 'accepted-message') {
278
- sealed.add(event.message.turn);
279
- unsealed.delete(event.message.turn);
280
- }
281
379
  for (const listener of listeners)
282
380
  listener(event);
283
381
  },
@@ -286,21 +384,31 @@ export function createMemoryAdapter(options = {}) {
286
384
  observer(mode);
287
385
  },
288
386
  heldRooms() {
289
- return held;
387
+ return [...carried.keys()].sort();
290
388
  },
291
389
  replicatedConversations() {
292
390
  return replicated;
293
391
  },
392
+ heldConversationCursor() {
393
+ return heldCursor;
394
+ },
294
395
  conversations: {
295
396
  replicate(next) {
296
397
  replicated = [...next];
297
398
  },
399
+ hold(cursor) {
400
+ heldCursor = cursor;
401
+ },
298
402
  observe(notify) {
299
403
  listeners.add(notify);
300
404
  return () => {
301
405
  listeners.delete(notify);
302
406
  };
303
407
  },
408
+ history: (request) => Promise.resolve(historiesOf(request)),
409
+ ...(options.agents === undefined
410
+ ? {}
411
+ : { agents: () => Promise.resolve(structuredClone(options.agents)) }),
304
412
  },
305
413
  serviceMode: {
306
414
  observe(notify) {
@@ -312,29 +420,29 @@ export function createMemoryAdapter(options = {}) {
312
420
  },
313
421
  roomStream: {
314
422
  hold(asked) {
315
- held = asked.map((room) => room.locator);
316
- const expired = expiring.filter((locator) => held.includes(locator));
317
- expiring = [];
318
- const moved = asked.flatMap((room) => {
423
+ const named = asked.map((room) => room.locator);
424
+ const unknown = asked.flatMap((room) => {
319
425
  const current = rooms.find((entry) => entry.locator === room.locator);
320
426
  return current === undefined ||
321
- expired.includes(room.locator) ||
322
- current.roomVersion === room.roomVersion
427
+ current.roomVersion === room.roomVersion ||
428
+ served.get(room.locator)?.has(room.roomVersion) === true
323
429
  ? []
324
- : [{ locator: room.locator, roomVersion: current.roomVersion }];
430
+ : [room.locator];
325
431
  });
432
+ const expired = [
433
+ ...new Set([...expiring.filter((locator) => named.includes(locator)), ...unknown]),
434
+ ];
435
+ expiring = expiring.filter((locator) => !named.includes(locator));
326
436
  queueMicrotask(() => {
327
- for (const observer of observers) {
328
- for (const room of moved)
329
- observer({ kind: 'room-version', ...room });
330
- observer({ kind: 'opening', expired });
437
+ for (const room of asked) {
438
+ if (expired.includes(room.locator))
439
+ carried.delete(room.locator);
440
+ else {
441
+ carried.set(room.locator, room.roomVersion);
442
+ carry(room.locator);
443
+ }
331
444
  }
332
- });
333
- },
334
- reopen() {
335
- queueMicrotask(() => {
336
- for (const observer of observers)
337
- observer({ kind: 'closed', revoked: false });
445
+ emitAll({ kind: 'caught-up', expired });
338
446
  });
339
447
  },
340
448
  observe(notify) {
@@ -343,11 +451,14 @@ export function createMemoryAdapter(options = {}) {
343
451
  if (!observers.has(notify))
344
452
  return;
345
453
  if (options.unopened === true) {
346
- notify({ kind: 'closed', revoked: false });
454
+ notify({ kind: 'closed', revoked: false, lost: true, authorityChanged: false });
347
455
  return;
348
456
  }
349
457
  announceList();
350
- notify({ kind: 'opened' });
458
+ opened((event) => {
459
+ if (observers.has(notify))
460
+ notify(event);
461
+ });
351
462
  });
352
463
  return () => {
353
464
  observers.delete(notify);
@@ -364,12 +475,7 @@ export function createMemoryAdapter(options = {}) {
364
475
  return resolveScript(scripts.authorize, () => ({}), undefined);
365
476
  },
366
477
  async discover(_authorization) {
367
- return resolveScript(scripts.discover, discovery, undefined).then((answer) => {
368
- if (answer.kind === 'discovery')
369
- for (const room of answer.rooms)
370
- note(room.locator, room);
371
- return answer;
372
- });
478
+ return discovered();
373
479
  },
374
480
  reached,
375
481
  follows(locator, roomVersion, next) {
@@ -386,7 +492,7 @@ export function createMemoryAdapter(options = {}) {
386
492
  if (room === undefined) {
387
493
  return { kind: 'room-not-readable', locator: request.locator };
388
494
  }
389
- return page(room, request.pageToken === undefined ? 0 : Number(request.pageToken));
495
+ return page(room, request.pageToken);
390
496
  }, request).then((answer) => note(request.locator, answer));
391
497
  },
392
498
  async refresh(_authorization, request) {
@@ -419,8 +525,20 @@ export function createMemoryAdapter(options = {}) {
419
525
  if (room === undefined) {
420
526
  return { kind: 'room-not-readable', locator: request.locator };
421
527
  }
422
- return { kind: 'rejected', code: 'unscripted-mutation', fields: {}, affected: {} };
423
- }, request).then((answer) => note(request.locator, answer));
528
+ return {
529
+ kind: 'rejected',
530
+ code: 'unscripted-mutation',
531
+ fields: {},
532
+ retryable: false,
533
+ affected: {},
534
+ };
535
+ }, request).then((answer) => {
536
+ const destination = request.changeBatch.between?.destination.room;
537
+ if (answer.kind === 'accepted' && answer.destination !== undefined && destination) {
538
+ note(destination, answer.destination);
539
+ }
540
+ return note(request.locator, answer);
541
+ });
424
542
  },
425
543
  async history(_authorization, request) {
426
544
  return resolveScript(scripts.history, () => {
@@ -434,11 +552,46 @@ export function createMemoryAdapter(options = {}) {
434
552
  };
435
553
  }, request);
436
554
  },
555
+ async binary(_authorization, request) {
556
+ return resolveScript(scripts.binary, () => {
557
+ const room = rooms.find((entry) => entry.locator === request.locator);
558
+ if (room === undefined)
559
+ return { kind: 'room-not-readable', locator: request.locator };
560
+ const blob = heldBlob(room, request.path);
561
+ return blob === undefined
562
+ ? { kind: 'rejected', code: 'file_not_found', fields: {}, retryable: false }
563
+ : { kind: 'binary', body: blob.stream(), mediaType: blob.type };
564
+ }, request);
565
+ },
566
+ async link(_authorization, request) {
567
+ return resolveScript(scripts.link, () => {
568
+ const room = rooms.find((entry) => entry.locator === request.locator);
569
+ if (room === undefined)
570
+ return { kind: 'room-not-readable', locator: request.locator };
571
+ const blob = heldBlob(room, request.path);
572
+ if (blob === undefined) {
573
+ return { kind: 'rejected', code: 'file_not_found', fields: {}, retryable: false };
574
+ }
575
+ const url = linked.get(blob) ?? URL.createObjectURL(blob);
576
+ linked.set(blob, url);
577
+ return { kind: 'link', url, name: lastName(request.path), mediaType: blob.type };
578
+ }, request);
579
+ },
580
+ async attach(_authorization, request) {
581
+ return resolveScript(scripts.attach, () => {
582
+ const room = rooms.find((entry) => entry.locator === request.locator);
583
+ if (room === undefined)
584
+ return { kind: 'room-not-readable', locator: request.locator };
585
+ const path = freePath(room, request.name);
586
+ attached.set(`${room.locator}\u0000${path}`, request.blob);
587
+ return { kind: 'attached', path, revision: `attached-${attached.size}` };
588
+ }, request);
589
+ },
437
590
  async action(_authorization, request) {
438
591
  return resolveScript(scripts.action, () => {
439
- if (request.name.startsWith('conversation.'))
592
+ if (request.name.startsWith('conversations/'))
440
593
  return conversationAction(request);
441
- return { kind: 'rejected', code: 'unscripted-action', fields: {} };
594
+ return { kind: 'rejected', code: 'unscripted-action', fields: {}, retryable: false };
442
595
  }, request).then((answer) => {
443
596
  if (answer.kind === 'action' && answer.at !== undefined)
444
597
  note(answer.at.locator, answer.at);
@@ -0,0 +1,28 @@
1
+ import type { AcceptedMessage } from './adapter.js';
2
+ import type { Connection } from './tables.js';
3
+ export declare const MESSAGE_TABLES: readonly string[];
4
+ export type HeldConversation = {
5
+ readonly conversation: string;
6
+ readonly read: string | null | undefined;
7
+ readonly partial: boolean;
8
+ };
9
+ export type MessageWindow = {
10
+ readonly messages: AcceptedMessage[];
11
+ readonly more: boolean;
12
+ };
13
+ export type ConversationWrite = {
14
+ readonly conversation: string;
15
+ readonly messages: readonly AcceptedMessage[];
16
+ readonly read?: {
17
+ readonly at: string | null;
18
+ readonly partial: boolean;
19
+ };
20
+ };
21
+ export declare function heldConversations(connection: Connection): HeldConversation[];
22
+ export declare function keptMessages(connection: Connection, { conversation, messages, read }: ConversationWrite): null;
23
+ export declare function messageWindow(connection: Connection, conversation: string, limit: number, before: readonly [string, string] | null, phrase: string | null): MessageWindow;
24
+ export declare function messageCount(connection: Connection, conversation: string, phrase: string | null): number;
25
+ export declare function turnArrived(connection: Connection, conversation: string, turn: string): boolean;
26
+ export declare function droppedMessages(connection: Connection, conversations: readonly string[]): void;
27
+ export declare function forgottenMessages(connection: Connection): void;
28
+ //# sourceMappingURL=messages.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../src/messages.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,KAAK,EAAE,UAAU,EAAc,MAAM,aAAa,CAAC;AAE1D,eAAO,MAAM,cAAc,EAAE,SAAS,MAAM,EAM3C,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACzC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAAE,QAAQ,CAAC,QAAQ,EAAE,eAAe,EAAE,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;CAAE,CAAC;AAE7F,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,QAAQ,EAAE,SAAS,eAAe,EAAE,CAAC;IAC9C,QAAQ,CAAC,IAAI,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC;CAC3E,CAAC;AAMF,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,UAAU,GAAG,gBAAgB,EAAE,CAmB5E;AAED,wBAAgB,YAAY,CAC1B,UAAU,EAAE,UAAU,EACtB,EAAE,YAAY,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,iBAAiB,GAClD,IAAI,CA+BN;AAkBD,wBAAgB,aAAa,CAC3B,UAAU,EAAE,UAAU,EACtB,YAAY,EAAE,MAAM,EACpB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,EACxC,MAAM,EAAE,MAAM,GAAG,IAAI,GACpB,aAAa,CAWf;AAED,wBAAgB,YAAY,CAC1B,UAAU,EAAE,UAAU,EACtB,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,MAAM,GAAG,IAAI,GACpB,MAAM,CAOR;AAED,wBAAgB,WAAW,CAAC,UAAU,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAO/F;AAED,wBAAgB,eAAe,CAAC,UAAU,EAAE,UAAU,EAAE,aAAa,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,CAK9F;AAED,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,UAAU,GAAG,IAAI,CAG9D"}