@agentshouse/mdruntime 0.1.0-cfbf80d8d4644 → 0.1.0-cfcf93be6310b

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