@agentshouse/mdruntime 0.1.0-alpha.9 → 0.1.0-c30ed2b8e04d5

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 (130) hide show
  1. package/AGENTS.md +217 -0
  2. package/README.md +43 -0
  3. package/dist/adapter.d.ts +184 -34
  4. package/dist/adapter.d.ts.map +1 -1
  5. package/dist/adapter.js +1 -10
  6. package/dist/attached-model.d.ts +11 -0
  7. package/dist/attached-model.d.ts.map +1 -0
  8. package/dist/attached-model.js +34 -0
  9. package/dist/attachment.d.ts +3 -0
  10. package/dist/attachment.d.ts.map +1 -0
  11. package/dist/attachment.js +8 -0
  12. package/dist/bootstrap.d.ts +7 -9
  13. package/dist/bootstrap.d.ts.map +1 -1
  14. package/dist/bootstrap.js +29 -28
  15. package/dist/change-set.d.ts +33 -0
  16. package/dist/change-set.d.ts.map +1 -0
  17. package/dist/change-set.js +83 -0
  18. package/dist/change.d.ts +9 -0
  19. package/dist/change.d.ts.map +1 -1
  20. package/dist/change.js +6 -5
  21. package/dist/client.d.ts +38 -32
  22. package/dist/client.d.ts.map +1 -1
  23. package/dist/client.js +114 -183
  24. package/dist/collection.d.ts +13 -16
  25. package/dist/collection.d.ts.map +1 -1
  26. package/dist/collection.js +45 -88
  27. package/dist/connection.d.ts +47 -44
  28. package/dist/connection.d.ts.map +1 -1
  29. package/dist/connection.js +1542 -1158
  30. package/dist/conversation.d.ts +44 -22
  31. package/dist/conversation.d.ts.map +1 -1
  32. package/dist/conversation.js +540 -328
  33. package/dist/durability.js +6 -6
  34. package/dist/file.d.ts +2 -2
  35. package/dist/file.d.ts.map +1 -1
  36. package/dist/file.js +48 -71
  37. package/dist/host.d.ts +12 -2
  38. package/dist/host.d.ts.map +1 -1
  39. package/dist/host.js +97 -119
  40. package/dist/identity.d.ts +1 -0
  41. package/dist/identity.d.ts.map +1 -1
  42. package/dist/identity.js +10 -9
  43. package/dist/index.d.ts +12 -11
  44. package/dist/index.d.ts.map +1 -1
  45. package/dist/index.js +1 -3
  46. package/dist/log.js +5 -6
  47. package/dist/memory.d.ts +15 -7
  48. package/dist/memory.d.ts.map +1 -1
  49. package/dist/memory.js +255 -220
  50. package/dist/model.d.ts +10 -29
  51. package/dist/model.d.ts.map +1 -1
  52. package/dist/model.js +18 -21
  53. package/dist/observation.d.ts +27 -0
  54. package/dist/observation.d.ts.map +1 -0
  55. package/dist/observation.js +95 -0
  56. package/dist/outcome.d.ts +10 -29
  57. package/dist/outcome.d.ts.map +1 -1
  58. package/dist/outcome.js +1 -23
  59. package/dist/overlay.d.ts +9 -2
  60. package/dist/overlay.d.ts.map +1 -1
  61. package/dist/overlay.js +187 -43
  62. package/dist/paths.d.ts +0 -1
  63. package/dist/paths.d.ts.map +1 -1
  64. package/dist/paths.js +12 -19
  65. package/dist/query.d.ts +215 -10
  66. package/dist/query.d.ts.map +1 -1
  67. package/dist/query.js +421 -15
  68. package/dist/reach.d.ts +40 -0
  69. package/dist/reach.d.ts.map +1 -0
  70. package/dist/reach.js +277 -0
  71. package/dist/read-side.d.ts +18 -0
  72. package/dist/read-side.d.ts.map +1 -0
  73. package/dist/read-side.js +377 -0
  74. package/dist/refresh.d.ts +5 -5
  75. package/dist/refresh.d.ts.map +1 -1
  76. package/dist/refresh.js +49 -30
  77. package/dist/replica.d.ts +25 -5
  78. package/dist/replica.d.ts.map +1 -1
  79. package/dist/replica.js +227 -38
  80. package/dist/room-draft.d.ts +127 -0
  81. package/dist/room-draft.d.ts.map +1 -0
  82. package/dist/room-draft.js +329 -0
  83. package/dist/rows.d.ts +41 -0
  84. package/dist/rows.d.ts.map +1 -0
  85. package/dist/rows.js +100 -0
  86. package/dist/save.d.ts +6 -4
  87. package/dist/save.d.ts.map +1 -1
  88. package/dist/save.js +262 -350
  89. package/dist/search.d.ts +61 -0
  90. package/dist/search.d.ts.map +1 -0
  91. package/dist/search.js +125 -0
  92. package/dist/status.d.ts +4 -6
  93. package/dist/status.d.ts.map +1 -1
  94. package/dist/status.js +9 -13
  95. package/dist/store.d.ts +59 -32
  96. package/dist/store.d.ts.map +1 -1
  97. package/dist/store.js +338 -134
  98. package/dist/transfer.d.ts +5 -0
  99. package/dist/transfer.d.ts.map +1 -1
  100. package/dist/transfer.js +1 -5
  101. package/dist/wire.d.ts +51 -16
  102. package/dist/wire.d.ts.map +1 -1
  103. package/dist/wire.js +77 -108
  104. package/package.json +6 -12
  105. package/dist/adapter.js.map +0 -1
  106. package/dist/bootstrap.js.map +0 -1
  107. package/dist/change.js.map +0 -1
  108. package/dist/client.js.map +0 -1
  109. package/dist/collection.js.map +0 -1
  110. package/dist/connection.js.map +0 -1
  111. package/dist/conversation.js.map +0 -1
  112. package/dist/durability.js.map +0 -1
  113. package/dist/file.js.map +0 -1
  114. package/dist/host.js.map +0 -1
  115. package/dist/identity.js.map +0 -1
  116. package/dist/index.js.map +0 -1
  117. package/dist/log.js.map +0 -1
  118. package/dist/memory.js.map +0 -1
  119. package/dist/model.js.map +0 -1
  120. package/dist/outcome.js.map +0 -1
  121. package/dist/overlay.js.map +0 -1
  122. package/dist/paths.js.map +0 -1
  123. package/dist/query.js.map +0 -1
  124. package/dist/refresh.js.map +0 -1
  125. package/dist/replica.js.map +0 -1
  126. package/dist/save.js.map +0 -1
  127. package/dist/status.js.map +0 -1
  128. package/dist/store.js.map +0 -1
  129. package/dist/transfer.js.map +0 -1
  130. package/dist/wire.js.map +0 -1
package/dist/memory.js CHANGED
@@ -1,41 +1,47 @@
1
- import { ROOM_CONTRACT_VERSION } from './adapter.js';
2
- import { MD_MODEL } from './collection.js';
1
+ import { sameChange } from './overlay.js';
3
2
  const FIXTURE_RETENTION = {
4
- history: {
5
- kind: 'duration',
6
- seconds: 1
7
- },
3
+ history: { kind: 'duration', seconds: 1 },
8
4
  outcome: 1,
9
- refreshHorizon: 1
5
+ refreshHorizon: 1,
10
6
  };
11
7
  function declaredRetention(value) {
12
8
  const retention = value ?? FIXTURE_RETENTION;
13
- if (retention.history.kind === 'no-declared-time-bound') return retention;
9
+ if (retention.history.kind === 'no-declared-time-bound')
10
+ return retention;
14
11
  if (Number.isFinite(retention.history.seconds) && retention.history.seconds > 0) {
15
12
  return retention;
16
13
  }
17
14
  throw new Error('history retention duration must be a finite positive number of seconds');
18
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
+ }
19
29
  function named(source, paths) {
20
30
  const taken = {};
21
- for (const path of paths){
31
+ for (const path of paths) {
22
32
  const value = source[path];
23
- if (value !== undefined) taken[path] = value;
33
+ if (value !== undefined)
34
+ taken[path] = value;
24
35
  }
25
36
  return taken;
26
37
  }
27
38
  async function resolveScript(answer, fallback, request) {
28
- if (answer === undefined) return fallback();
39
+ if (answer === undefined)
40
+ return fallback();
29
41
  return typeof answer === 'function' ? answer(request) : answer;
30
42
  }
31
43
  export function createMemoryAdapter(options = {}) {
32
- let rooms = options.rooms === undefined ? [] : [
33
- ...options.rooms
34
- ];
35
- const mdModel = options.mdModel ?? MD_MODEL;
36
- const supportedVersions = options.supportedVersions ?? [
37
- ROOM_CONTRACT_VERSION
38
- ];
44
+ let rooms = options.rooms === undefined ? [] : [...options.rooms];
39
45
  const capabilities = options.capabilities ?? [];
40
46
  const providerLimits = options.providerLimits ?? [];
41
47
  const retention = declaredRetention(options.retention);
@@ -45,20 +51,44 @@ export function createMemoryAdapter(options = {}) {
45
51
  const refreshPages = new Map();
46
52
  const observers = new Set();
47
53
  const listeners = new Set();
54
+ const modeObservers = new Set();
48
55
  const scripted = new Map();
49
56
  const unsealed = new Map();
50
57
  const sealed = new Set();
51
58
  const historyPageSize = options.historyPageSize;
52
- let selected = [];
53
- let viewed = [];
59
+ let held = [];
60
+ let expiring = [];
61
+ let replicated = [];
54
62
  let issued = 0;
55
63
  function scriptConversations(entries) {
56
64
  scripted.clear();
57
- for (const entry of entries)scripted.set(entry.conversation, entry);
65
+ for (const entry of entries)
66
+ scripted.set(entry.conversation, entry);
58
67
  }
59
68
  scriptConversations(options.conversations ?? []);
69
+ const ordinals = new Map();
70
+ function note(locator, answer) {
71
+ const roomVersion = answer.roomVersion;
72
+ const key = `${locator}\u0000${String(roomVersion)}`;
73
+ if (typeof roomVersion === 'string' && !ordinals.has(key))
74
+ ordinals.set(key, ordinals.size);
75
+ return answer;
76
+ }
77
+ function reached(locator, roomVersion, target) {
78
+ const scripted = scripts.reached;
79
+ if (typeof scripted === 'boolean')
80
+ return scripted;
81
+ if (scripted !== undefined)
82
+ return scripted(locator, roomVersion, target);
83
+ if (roomVersion === target)
84
+ return true;
85
+ const held = ordinals.get(`${locator}\u0000${roomVersion}`);
86
+ const wanted = ordinals.get(`${locator}\u0000${target}`);
87
+ return held !== undefined && wanted !== undefined && held >= wanted;
88
+ }
60
89
  function retain(entries) {
61
- for (const room of entries){
90
+ for (const room of entries) {
91
+ note(room.locator, room);
62
92
  const versions = served.get(room.locator) ?? new Map();
63
93
  versions.set(room.roomVersion, room);
64
94
  served.set(room.locator, versions);
@@ -69,15 +99,11 @@ export function createMemoryAdapter(options = {}) {
69
99
  return {
70
100
  name: room.name,
71
101
  locator: room.locator,
72
- collectionRoot: room.collectionRoot,
73
102
  roomVersion: room.roomVersion,
74
103
  operations: room.operations,
75
104
  replica: room.replica,
76
- attachedModel: room.attachedModel,
77
- ...room.typedSelection === undefined ? {} : {
78
- typedSelection: room.typedSelection
79
- },
80
- providerOwnedPaths: room.providerOwnedPaths
105
+ ...(room.typedSelection === undefined ? {} : { typedSelection: room.typedSelection }),
106
+ providerOwnedPaths: room.providerOwnedPaths,
81
107
  };
82
108
  }
83
109
  function page(room, from) {
@@ -85,15 +111,17 @@ export function createMemoryAdapter(options = {}) {
85
111
  const size = pageSize ?? paths.length;
86
112
  const taken = paths.slice(from, from + size);
87
113
  const next = from + taken.length;
114
+ const targets = from === 0 ? (room.binaryTargets ?? []) : [];
88
115
  return {
89
116
  kind: 'bootstrap',
90
117
  roomVersion: room.roomVersion,
118
+ ...(from === 0 ? { models: modelsOf(room) } : {}),
91
119
  documents: named(room.documents ?? {}, taken),
92
120
  revisions: named(room.revisions ?? {}, taken),
93
- binaryTargets: from === 0 ? room.binaryTargets ?? [] : [],
94
- ...next < paths.length ? {
95
- pageToken: String(next)
96
- } : {}
121
+ changes: named(room.changes ?? {}, [...taken, ...targets]),
122
+ binaryTargets: targets,
123
+ ...(from === 0 && room.attachments !== undefined ? { attachments: room.attachments } : {}),
124
+ ...(next < paths.length ? { pageToken: String(next) } : {}),
97
125
  };
98
126
  }
99
127
  function advancement(room, from, at) {
@@ -101,295 +129,302 @@ export function createMemoryAdapter(options = {}) {
101
129
  const previous = from.documents ?? {};
102
130
  const currentTargets = new Set(room.binaryTargets ?? []);
103
131
  const previousTargets = new Set(from.binaryTargets ?? []);
104
- const changed = Object.keys(current).filter((path)=>current[path] !== previous[path]).sort();
132
+ const headMoved = (path) => room.revisions?.[path] !== from.revisions?.[path] ||
133
+ !sameChange(room.changes?.[path], from.changes?.[path]);
134
+ const changed = Object.keys(current)
135
+ .filter((path) => current[path] !== previous[path] || headMoved(path))
136
+ .sort();
105
137
  const size = pageSize ?? changed.length;
106
138
  const taken = changed.slice(at, at + size);
107
139
  const next = at + taken.length;
108
140
  const opening = at === 0;
109
141
  const pageToken = next < changed.length ? `refresh-${refreshPages.size}` : undefined;
110
- if (pageToken !== undefined) refreshPages.set(pageToken, {
111
- room,
112
- from,
113
- at: next
114
- });
142
+ if (pageToken !== undefined)
143
+ refreshPages.set(pageToken, { room, from, at: next });
144
+ const targets = opening
145
+ ? [...currentTargets].filter((target) => !previousTargets.has(target) || headMoved(target))
146
+ : [];
147
+ const attachments = room.attachments ?? [];
148
+ const declared = opening && JSON.stringify(attachments) !== JSON.stringify(from.attachments ?? []);
149
+ const models = opening ? changedModels(room, from) : undefined;
115
150
  return {
116
151
  kind: 'net-advancement',
117
152
  roomVersion: room.roomVersion,
153
+ ...(models === undefined ? {} : { models }),
118
154
  documents: named(current, taken),
119
155
  revisions: named(room.revisions ?? {}, taken),
120
- binaryTargets: opening ? [
121
- ...currentTargets
122
- ].filter((target)=>!previousTargets.has(target)) : [],
123
- removals: opening ? [
124
- ...Object.keys(previous).filter((path)=>!(path in current) && !currentTargets.has(path)),
125
- ...[
126
- ...previousTargets
127
- ].filter((target)=>!currentTargets.has(target) && !(target in current))
128
- ].sort() : [],
129
- ...pageToken === undefined ? {} : {
130
- pageToken
131
- }
156
+ changes: named(room.changes ?? {}, [...taken, ...targets]),
157
+ binaryTargets: targets,
158
+ removals: opening
159
+ ? [
160
+ ...Object.keys(previous).filter((path) => !(path in current) && !currentTargets.has(path)),
161
+ ...[...previousTargets].filter((target) => !currentTargets.has(target) && !(target in current)),
162
+ ].sort()
163
+ : [],
164
+ ...(declared ? { attachments } : {}),
165
+ ...(pageToken === undefined ? {} : { pageToken }),
132
166
  };
133
167
  }
134
- function historyOf(conversation, cursor) {
168
+ function pageOf(conversation, after) {
135
169
  const messages = conversation.messages ?? [];
136
- const until = typeof cursor === 'string' ? Number(cursor) : messages.length;
137
- if (!Number.isInteger(until) || until < 0 || until > messages.length) {
138
- return {
139
- kind: 'rejected',
140
- code: 'unknown-cursor',
141
- fields: {
142
- cursor
143
- }
144
- };
145
- }
146
- const size = historyPageSize ?? messages.length;
147
- const from = Math.max(0, until - size);
170
+ const first = typeof after === 'string' ? messages.findIndex((message) => message.position > after) : 0;
171
+ const from = first === -1 ? messages.length : first;
172
+ const page = messages.slice(from, from + (historyPageSize ?? messages.length));
173
+ const last = page.at(-1);
148
174
  return {
149
- kind: 'action',
150
- result: {
151
- messages: messages.slice(from, until),
152
- ...from === 0 ? {} : {
153
- cursor: String(from)
154
- }
155
- }
175
+ conversation: conversation.conversation,
176
+ messages: page,
177
+ ...(last !== undefined && from + page.length < messages.length
178
+ ? { next: last.position }
179
+ : {}),
156
180
  };
157
181
  }
182
+ function historiesOf(input) {
183
+ const asked = input?.conversations;
184
+ if (!Array.isArray(asked))
185
+ return { kind: 'rejected', code: 'invalid_request', fields: {} };
186
+ const histories = [];
187
+ for (const entry of asked) {
188
+ const conversation = typeof entry.conversation === 'string' ? scripted.get(entry.conversation) : undefined;
189
+ if (conversation === undefined)
190
+ continue;
191
+ const outcome = conversation.outcomes?.['conversation.history'];
192
+ if (outcome !== undefined)
193
+ return outcome;
194
+ histories.push(pageOf(conversation, entry.after));
195
+ }
196
+ const result = { histories };
197
+ return { kind: 'action', result };
198
+ }
158
199
  function conversationAction(request) {
159
200
  const name = request.name;
160
- const input = request.input ?? {};
201
+ const input = (request.input ?? {});
202
+ if (name === 'conversation.history')
203
+ return historiesOf(request.input);
161
204
  if (name === 'conversation.create') {
162
205
  issued += 1;
163
206
  const conversation = `conversation-${issued}`;
164
- scripted.set(conversation, {
165
- conversation
166
- });
167
- return {
168
- kind: 'action',
169
- result: {
170
- conversation
171
- }
172
- };
207
+ scripted.set(conversation, { conversation });
208
+ return { kind: 'action', result: { conversation } };
173
209
  }
174
210
  const named = typeof input.conversation === 'string' ? input.conversation : '';
175
211
  const conversation = scripted.get(named);
176
212
  if (conversation === undefined) {
177
- return {
178
- kind: 'rejected',
179
- code: 'unknown-conversation',
180
- fields: {
181
- conversation: named
182
- }
183
- };
213
+ return { kind: 'rejected', code: 'unknown-conversation', fields: { conversation: named } };
184
214
  }
185
215
  const outcome = conversation.outcomes?.[name];
186
- if (outcome !== undefined) return outcome;
187
- if (name === 'conversation.history') return historyOf(conversation, input.cursor);
216
+ if (outcome !== undefined)
217
+ return outcome;
188
218
  if (name === 'conversation.send') {
189
219
  issued += 1;
190
220
  const turn = `turn-${issued}`;
191
221
  unsealed.set(turn, named);
192
- return {
193
- kind: 'action',
194
- result: {
195
- turn
196
- }
197
- };
222
+ return { kind: 'action', result: { turn } };
198
223
  }
199
224
  if (name === 'conversation.cancel') {
200
225
  const turn = typeof input.turn === 'string' ? input.turn : '';
201
226
  if (sealed.has(turn) || !unsealed.has(turn)) {
202
- return {
203
- kind: 'rejected',
204
- code: 'turn-sealed',
205
- fields: {
206
- turn
207
- }
208
- };
227
+ return { kind: 'rejected', code: 'turn-sealed', fields: { turn } };
209
228
  }
210
229
  unsealed.delete(turn);
211
- return {
212
- kind: 'action',
213
- result: {
214
- turn,
215
- cancelled: true
216
- }
217
- };
230
+ return { kind: 'action', result: { turn, cancelled: true } };
218
231
  }
219
- if (name === 'conversation.delete') scripted.delete(named);
220
- return {
221
- kind: 'action',
222
- result: {
223
- conversation: named
224
- }
225
- };
232
+ if (name === 'conversation.delete')
233
+ scripted.delete(named);
234
+ return { kind: 'action', result: { conversation: named } };
226
235
  }
227
236
  function discovery() {
228
237
  return {
229
238
  kind: 'discovery',
230
- supportedVersions,
231
- mdModel,
232
239
  rooms: rooms.map(declared),
233
240
  capabilities,
234
241
  providerLimits,
235
- retention
242
+ retention,
236
243
  };
237
244
  }
238
245
  return {
239
- setRooms (next) {
240
- rooms = [
241
- ...next
242
- ];
246
+ setRooms(next) {
247
+ rooms = [...next];
243
248
  retain(rooms);
244
249
  },
245
- setConversations (next) {
250
+ setConversations(next) {
246
251
  scriptConversations(next);
247
252
  },
248
- notifyRoomVersion (notification) {
249
- for (const observer of observers)observer(notification);
253
+ open() {
254
+ for (const observer of observers)
255
+ observer({ kind: 'opened' });
256
+ },
257
+ close(revoked = false) {
258
+ for (const observer of observers)
259
+ observer({ kind: 'closed', revoked });
250
260
  },
251
- notifyConversation (event) {
261
+ expire(locators) {
262
+ expiring = [...locators];
263
+ },
264
+ notifyRoomVersion({ locator, roomVersion }) {
265
+ for (const observer of observers)
266
+ observer({ kind: 'room-version', locator, roomVersion });
267
+ },
268
+ notifyConversation(event) {
252
269
  if (event.kind === 'accepted-message') {
253
270
  sealed.add(event.message.turn);
254
271
  unsealed.delete(event.message.turn);
255
272
  }
256
- for (const listener of listeners)listener(event);
273
+ for (const listener of listeners)
274
+ listener(event);
257
275
  },
258
- selectedRooms () {
259
- return selected;
276
+ notifyServiceMode(mode) {
277
+ for (const observer of modeObservers)
278
+ observer(mode);
260
279
  },
261
- viewedConversations () {
262
- return viewed;
280
+ heldRooms() {
281
+ return held;
282
+ },
283
+ replicatedConversations() {
284
+ return replicated;
263
285
  },
264
286
  conversations: {
265
- view (next) {
266
- viewed = [
267
- ...next
268
- ];
287
+ replicate(next) {
288
+ replicated = [...next];
269
289
  },
270
- observe (notify) {
290
+ observe(notify) {
271
291
  listeners.add(notify);
272
- return ()=>{
292
+ return () => {
273
293
  listeners.delete(notify);
274
294
  };
275
- }
295
+ },
276
296
  },
277
- roomVersions: {
278
- select (locators) {
279
- selected = [
280
- ...locators
281
- ];
297
+ serviceMode: {
298
+ observe(notify) {
299
+ modeObservers.add(notify);
300
+ return () => {
301
+ modeObservers.delete(notify);
302
+ };
282
303
  },
283
- observe (notify) {
304
+ },
305
+ roomStream: {
306
+ hold(asked) {
307
+ held = asked.map((room) => room.locator);
308
+ const expired = expiring.filter((locator) => held.includes(locator));
309
+ expiring = [];
310
+ const moved = asked.flatMap((room) => {
311
+ const current = rooms.find((entry) => entry.locator === room.locator);
312
+ return current === undefined ||
313
+ expired.includes(room.locator) ||
314
+ current.roomVersion === room.roomVersion
315
+ ? []
316
+ : [{ locator: room.locator, roomVersion: current.roomVersion }];
317
+ });
318
+ queueMicrotask(() => {
319
+ for (const observer of observers) {
320
+ for (const room of moved)
321
+ observer({ kind: 'room-version', ...room });
322
+ observer({ kind: 'opening', expired });
323
+ }
324
+ });
325
+ },
326
+ observe(notify) {
284
327
  observers.add(notify);
285
- return ()=>{
328
+ queueMicrotask(() => {
329
+ if (!observers.has(notify))
330
+ return;
331
+ notify(options.unopened === true ? { kind: 'closed', revoked: false } : { kind: 'opened' });
332
+ });
333
+ return () => {
286
334
  observers.delete(notify);
287
335
  };
288
- }
336
+ },
289
337
  },
290
- script (operation, answer) {
291
- if (answer === undefined) delete scripts[operation];
292
- else scripts[operation] = answer;
338
+ script(operation, answer) {
339
+ if (answer === undefined)
340
+ delete scripts[operation];
341
+ else
342
+ scripts[operation] = answer;
293
343
  },
294
- async model (_authorization, request) {
295
- return resolveScript(scripts.model, ()=>{
296
- const room = rooms.find((entry)=>entry.locator === request.locator);
297
- const answer = request.attached === 'collection' ? room?.model : room?.selectionModel;
298
- return answer ?? {
299
- kind: 'model-unavailable',
300
- reason: 'not-readable'
301
- };
302
- }, request);
344
+ async authorize() {
345
+ return resolveScript(scripts.authorize, () => ({}), undefined);
303
346
  },
304
- async authorize () {
305
- return resolveScript(scripts.authorize, ()=>({}), undefined);
347
+ async discover(_authorization) {
348
+ return resolveScript(scripts.discover, discovery, undefined).then((answer) => {
349
+ if (answer.kind === 'discovery')
350
+ for (const room of answer.rooms)
351
+ note(room.locator, room);
352
+ return answer;
353
+ });
306
354
  },
307
- async discover (_authorization) {
308
- return resolveScript(scripts.discover, discovery, undefined);
355
+ reached,
356
+ follows(locator, roomVersion, next) {
357
+ const scripted = scripts.follows;
358
+ if (typeof scripted === 'boolean')
359
+ return scripted;
360
+ if (scripted !== undefined)
361
+ return scripted(locator, roomVersion, next);
362
+ return true;
309
363
  },
310
- async bootstrap (_authorization, request) {
311
- return resolveScript(scripts.bootstrap, ()=>{
312
- const room = rooms.find((entry)=>entry.locator === request.locator);
364
+ async bootstrap(_authorization, request) {
365
+ return resolveScript(scripts.bootstrap, () => {
366
+ const room = rooms.find((entry) => entry.locator === request.locator);
313
367
  if (room === undefined) {
314
- return {
315
- kind: 'room-not-readable',
316
- locator: request.locator
317
- };
368
+ return { kind: 'room-not-readable', locator: request.locator };
318
369
  }
319
370
  return page(room, request.pageToken === undefined ? 0 : Number(request.pageToken));
320
- }, request);
371
+ }, request).then((answer) => note(request.locator, answer));
321
372
  },
322
- async refresh (_authorization, request) {
323
- return resolveScript(scripts.refresh, ()=>{
324
- const room = rooms.find((entry)=>entry.locator === request.locator);
373
+ async refresh(_authorization, request) {
374
+ return resolveScript(scripts.refresh, () => {
375
+ const room = rooms.find((entry) => entry.locator === request.locator);
325
376
  if (room === undefined) {
326
- return {
327
- kind: 'room-not-readable',
328
- locator: request.locator
329
- };
377
+ return { kind: 'room-not-readable', locator: request.locator };
330
378
  }
331
379
  if (request.pageToken !== undefined) {
332
380
  const continuation = refreshPages.get(request.pageToken);
333
- if (continuation === undefined || continuation.room.locator !== request.locator || continuation.from.roomVersion !== request.fromRoomVersion) {
381
+ if (continuation === undefined ||
382
+ continuation.room.locator !== request.locator ||
383
+ continuation.from.roomVersion !== request.fromRoomVersion) {
334
384
  throw new Error('Invalid Refresh Page Token');
335
385
  }
336
386
  return advancement(continuation.room, continuation.from, continuation.at);
337
387
  }
338
388
  if (request.fromRoomVersion === room.roomVersion) {
339
- return {
340
- kind: 'still-current'
341
- };
389
+ return { kind: 'still-current' };
342
390
  }
343
391
  const from = served.get(room.locator)?.get(request.fromRoomVersion);
344
- if (from === undefined) return {
345
- kind: 'beyond-horizon'
346
- };
392
+ if (from === undefined)
393
+ return { kind: 'beyond-horizon' };
347
394
  return advancement(room, from, 0);
348
- }, request);
395
+ }, request).then((answer) => note(request.locator, answer));
349
396
  },
350
- async mutate (_authorization, request) {
351
- return resolveScript(scripts.mutate, ()=>{
352
- const room = rooms.find((entry)=>entry.locator === request.locator);
397
+ async mutate(_authorization, request) {
398
+ return resolveScript(scripts.mutate, () => {
399
+ const room = rooms.find((entry) => entry.locator === request.locator);
353
400
  if (room === undefined) {
354
- return {
355
- kind: 'room-not-readable',
356
- locator: request.locator
357
- };
401
+ return { kind: 'room-not-readable', locator: request.locator };
358
402
  }
359
- return {
360
- kind: 'rejected',
361
- code: 'unscripted-mutation',
362
- fields: {},
363
- affected: {}
364
- };
365
- }, request);
403
+ return { kind: 'rejected', code: 'unscripted-mutation', fields: {}, affected: {} };
404
+ }, request).then((answer) => note(request.locator, answer));
366
405
  },
367
- async history (_authorization, request) {
368
- return resolveScript(scripts.history, ()=>{
369
- const room = rooms.find((entry)=>entry.locator === request.locator);
406
+ async history(_authorization, request) {
407
+ return resolveScript(scripts.history, () => {
408
+ const room = rooms.find((entry) => entry.locator === request.locator);
370
409
  if (room === undefined) {
371
- return {
372
- kind: 'room-not-readable',
373
- locator: request.locator
374
- };
410
+ return { kind: 'room-not-readable', locator: request.locator };
375
411
  }
376
412
  return {
377
413
  kind: 'history',
378
- revisions: []
414
+ revisions: [],
379
415
  };
380
416
  }, request);
381
417
  },
382
- async action (_authorization, request) {
383
- return resolveScript(scripts.action, ()=>{
384
- if (request.name.startsWith('conversation.')) return conversationAction(request);
385
- return {
386
- kind: 'rejected',
387
- code: 'unscripted-action',
388
- fields: {}
389
- };
390
- }, request);
391
- }
418
+ async action(_authorization, request) {
419
+ return resolveScript(scripts.action, () => {
420
+ if (request.name.startsWith('conversation.'))
421
+ return conversationAction(request);
422
+ return { kind: 'rejected', code: 'unscripted-action', fields: {} };
423
+ }, request).then((answer) => {
424
+ if (answer.kind === 'action' && answer.at !== undefined)
425
+ note(answer.at.locator, answer.at);
426
+ return answer;
427
+ });
428
+ },
392
429
  };
393
430
  }
394
-
395
- //# sourceMappingURL=memory.js.map