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

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