@agentshouse/mdruntime-adapter 0.1.0-cd23c45caa798 → 0.1.0-cd36fe2bc9d48

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.
package/dist/read.js CHANGED
@@ -13,6 +13,8 @@ export function answering() {
13
13
  changes: new Map(),
14
14
  binaryFiles: new Map(),
15
15
  removals: new Set(),
16
+ folders: new Set(),
17
+ folderRemovals: new Set(),
16
18
  };
17
19
  }
18
20
  export function revised(answer, path, revision) {
@@ -29,12 +31,26 @@ export function forgotten(answer, path) {
29
31
  answer.binaryFiles.delete(path);
30
32
  answer.removals.delete(path);
31
33
  }
32
- export function* answered(answer) {
33
- const removals = [];
34
- for (const path of answer.removals) {
35
- removals.push(path);
34
+ export function folded(answer, path, removed) {
35
+ if (removed) {
36
+ answer.folders.delete(path);
37
+ answer.folderRemovals.add(path);
38
+ }
39
+ else {
40
+ answer.folderRemovals.delete(path);
41
+ answer.folders.add(path);
42
+ }
43
+ }
44
+ function* pathsOf(paths) {
45
+ const list = [];
46
+ for (const path of paths) {
47
+ list.push(path);
36
48
  yield ROW_WEIGHT;
37
49
  }
50
+ return list;
51
+ }
52
+ export function* answered(answer) {
53
+ const removals = yield* pathsOf(answer.removals);
38
54
  return {
39
55
  documents: yield* patching({}, answer.documents),
40
56
  revisions: yield* patching({}, answer.revisions),
@@ -42,6 +58,10 @@ export function* answered(answer) {
42
58
  changes: yield* patching({}, answer.changes),
43
59
  binaryFiles: yield* patching({}, answer.binaryFiles),
44
60
  removals,
61
+ ...(answer.folders.size === 0 ? {} : { folders: yield* pathsOf(answer.folders) }),
62
+ ...(answer.folderRemovals.size === 0
63
+ ? {}
64
+ : { folderRemovals: yield* pathsOf(answer.folderRemovals) }),
45
65
  };
46
66
  }
47
67
  export function authorityRoot(handle) {
@@ -59,22 +79,37 @@ export function parseGrant(body) {
59
79
  const transfer = body.transfer;
60
80
  if (typeof transfer.url !== 'string')
61
81
  return null;
62
- if (transfer.method === 'GET')
63
- return { method: 'GET', url: transfer.url };
64
82
  if (transfer.method !== 'POST' || typeof transfer.operation !== 'string')
65
83
  return null;
66
- return { method: 'POST', operation: transfer.operation, url: transfer.url };
84
+ return { operation: transfer.operation, url: transfer.url };
85
+ }
86
+ export function parseFileAddress(body) {
87
+ if (typeof body !== 'object' || body === null)
88
+ return null;
89
+ const { download, name, sha256, media_type: mediaType } = body;
90
+ if (typeof download !== 'string' ||
91
+ typeof name !== 'string' ||
92
+ typeof sha256 !== 'string' ||
93
+ typeof mediaType !== 'string') {
94
+ return null;
95
+ }
96
+ return { url: download, name, sha256, mediaType };
67
97
  }
68
98
  function changeAuthor(value) {
69
99
  if (typeof value !== 'object' || value === null)
70
100
  return null;
71
- const { user, site, name } = value;
101
+ const { user, site, inbox, name } = value;
72
102
  if (typeof name !== 'string')
73
103
  return null;
74
- if (typeof user === 'string' && site === undefined)
104
+ const named = [user, site, inbox].filter((id) => id !== undefined);
105
+ if (named.length !== 1)
106
+ return null;
107
+ if (typeof user === 'string')
75
108
  return { kind: 'user', id: user, name };
76
- if (typeof site === 'string' && user === undefined)
109
+ if (typeof site === 'string')
77
110
  return { kind: 'site', id: site, name };
111
+ if (typeof inbox === 'string')
112
+ return { kind: 'inbox', id: inbox, name };
78
113
  return null;
79
114
  }
80
115
  export function headChange(held) {
@@ -91,15 +126,12 @@ function headed(head) {
91
126
  return head === undefined ? {} : { head };
92
127
  }
93
128
  export function binaryFileOf(held) {
94
- if (held.file === undefined)
129
+ if (held.media_type === undefined)
95
130
  return undefined;
96
- const { file, bytes, media_type: mediaType, sha256 } = held;
97
- if (typeof file !== 'string' ||
98
- typeof bytes !== 'number' ||
99
- typeof mediaType !== 'string' ||
100
- typeof sha256 !== 'string')
131
+ const { bytes, media_type: mediaType, sha256 } = held;
132
+ if (typeof bytes !== 'number' || typeof mediaType !== 'string' || typeof sha256 !== 'string')
101
133
  return null;
102
- return { fileRef: file, byteLength: bytes, mediaType, sha256 };
134
+ return { byteLength: bytes, mediaType, sha256 };
103
135
  }
104
136
  function binaried(binary) {
105
137
  return binary === undefined ? {} : { binary };
@@ -183,6 +215,24 @@ export function parseParted(body) {
183
215
  export function parseSiteParted(body) {
184
216
  return parseRoomParts(body);
185
217
  }
218
+ export function partInventory(room) {
219
+ return {
220
+ parts: room.parts.length,
221
+ bytes: room.parts.reduce((sum, part) => sum + part.bytes, 0),
222
+ };
223
+ }
224
+ export function manifested(room, inventory, pageToken) {
225
+ return {
226
+ kind: 'bootstrap',
227
+ roomVersion: roomVersion(room),
228
+ documents: {},
229
+ revisions: {},
230
+ changes: {},
231
+ binaryFiles: {},
232
+ inventory,
233
+ pageToken,
234
+ };
235
+ }
186
236
  export function emptyScope(room) {
187
237
  return {
188
238
  anchor: room.anchor,
@@ -195,6 +245,7 @@ export function emptyScope(room) {
195
245
  function* partLines(text, mode) {
196
246
  const files = [];
197
247
  const contents = [];
248
+ const folders = [];
198
249
  let start = 0;
199
250
  while (start < text.length) {
200
251
  const end = text.indexOf('\n', start);
@@ -208,8 +259,11 @@ function* partLines(text, mode) {
208
259
  return null;
209
260
  }
210
261
  start = end + 1;
211
- if (folderLine(entry))
262
+ if (folderLine(entry)) {
263
+ folders.push(entry.folder);
264
+ yield ROW_WEIGHT;
212
265
  continue;
266
+ }
213
267
  const file = parseFile(entry, mode);
214
268
  if (file === null)
215
269
  return null;
@@ -221,21 +275,32 @@ function* partLines(text, mode) {
221
275
  return null;
222
276
  yield ROW_WEIGHT;
223
277
  }
224
- return { files, contents };
278
+ return { files, contents, folders };
225
279
  }
226
280
  function folderLine(entry) {
227
281
  if (typeof entry !== 'object' || entry === null || !('folder' in entry))
228
282
  return false;
229
283
  return typeof entry.folder === 'string' && Object.keys(entry).length === 1;
230
284
  }
231
- export async function housePart(room, body, listed, mode) {
232
- const payload = await checkedPayload(body, listed);
285
+ export async function housePart(room, frame, listed, mode) {
286
+ const payload = await checkedPayload(frame, listed);
233
287
  if (payload === null)
234
288
  return null;
235
289
  const text = await gunzipped(payload);
236
290
  const read = text === null ? null : await stepped(partLines(text, mode));
237
291
  return read === null ? { kind: 'transport-uncertainty' } : { ...emptyScope(room), ...read };
238
292
  }
293
+ function folderChange(value) {
294
+ if (typeof value !== 'object' || value === null)
295
+ return null;
296
+ const { path, folder, removed, ...rest } = value;
297
+ return typeof path === 'string' &&
298
+ folder === true &&
299
+ (removed === undefined || removed === true) &&
300
+ Object.keys(rest).length === 0
301
+ ? { path, removed: removed === true }
302
+ : null;
303
+ }
239
304
  function parseChange(value, mode) {
240
305
  if (typeof value !== 'object' || value === null)
241
306
  return null;
@@ -292,7 +357,13 @@ export function parseEnumeration(body, mode) {
292
357
  if (!Array.isArray(held.changes))
293
358
  return null;
294
359
  const changes = [];
360
+ const folders = [];
295
361
  for (const change of held.changes) {
362
+ const folder = folderChange(change);
363
+ if (folder !== null) {
364
+ folders.push(folder);
365
+ continue;
366
+ }
296
367
  const mapped = parseChange(change, mode);
297
368
  if (mapped === null)
298
369
  return null;
@@ -305,6 +376,7 @@ export function parseEnumeration(body, mode) {
305
376
  authority: held.authority,
306
377
  ...at,
307
378
  changes,
379
+ ...(folders.length === 0 ? {} : { folders }),
308
380
  ...(next === undefined ? {} : { next }),
309
381
  });
310
382
  }
@@ -328,6 +400,7 @@ function advances(asked, next) {
328
400
  }
329
401
  export async function wholeView(first, more) {
330
402
  const changes = new Map(first.changes.map((change) => [change.path, change]));
403
+ const folders = new Map((first.folders ?? []).map((folder) => [folder.path, folder]));
331
404
  let view = first;
332
405
  while (view.next !== undefined) {
333
406
  const asked = view.next;
@@ -343,6 +416,10 @@ export async function wholeView(first, more) {
343
416
  changes.delete(change.path);
344
417
  changes.set(change.path, change);
345
418
  }
419
+ for (const folder of page.folders ?? []) {
420
+ folders.delete(folder.path);
421
+ folders.set(folder.path, folder);
422
+ }
346
423
  view = page;
347
424
  }
348
425
  return {
@@ -350,46 +427,7 @@ export async function wholeView(first, more) {
350
427
  position: view.position,
351
428
  logEpoch: first.logEpoch,
352
429
  changes: [...changes.values()],
353
- };
354
- }
355
- export function* advancedScope(scope, view) {
356
- const files = new Map();
357
- for (const file of scope.files) {
358
- files.set(file.path, file);
359
- yield ROW_WEIGHT;
360
- }
361
- const contents = new Map();
362
- for (const entry of scope.contents) {
363
- contents.set(entry.path, entry.content);
364
- yield ROW_WEIGHT;
365
- }
366
- for (const change of view.changes) {
367
- files.delete(change.path);
368
- contents.delete(change.path);
369
- yield ROW_WEIGHT;
370
- if ('removed' in change)
371
- continue;
372
- files.set(change.path, {
373
- path: change.path,
374
- revision: change.revision,
375
- version: change.version,
376
- ...headed(change.head),
377
- ...('binary' in change ? { binary: change.binary } : {}),
378
- });
379
- if ('content' in change)
380
- contents.set(change.path, change.content);
381
- }
382
- const listed = [];
383
- for (const [path, content] of contents) {
384
- listed.push({ path, content });
385
- yield ROW_WEIGHT;
386
- }
387
- return {
388
- ...scope,
389
- position: view.position,
390
- logEpoch: view.logEpoch,
391
- files: [...files.values()],
392
- contents: listed,
430
+ ...(folders.size === 0 ? {} : { folders: [...folders.values()] }),
393
431
  };
394
432
  }
395
433
  export function* bootstrapping(scope) {
@@ -411,7 +449,9 @@ export function* bootstrapping(scope) {
411
449
  if (file.revision !== undefined)
412
450
  revised(answer, file.path, file.revision);
413
451
  }
414
- const { removals: _removals, ...page } = yield* answered(answer);
452
+ for (const folder of scope.folders ?? [])
453
+ folded(answer, folder, false);
454
+ const { removals: _removals, folderRemovals: _folderRemovals, ...page } = yield* answered(answer);
415
455
  return { kind: 'bootstrap', roomVersion: roomVersion(scope), ...page };
416
456
  }
417
457
  export function schemaOf(scope, schema) {
@@ -445,6 +485,10 @@ function* refreshing(view, fromRoomVersion) {
445
485
  if (change.revision !== undefined)
446
486
  revised(answer, change.path, change.revision);
447
487
  }
488
+ for (const folder of view.folders ?? []) {
489
+ yield ROW_WEIGHT;
490
+ folded(answer, folder.path, folder.removed);
491
+ }
448
492
  return { kind: 'net-advancement', roomVersion: roomVersion(view), ...(yield* answered(answer)) };
449
493
  }
450
494
  export async function refreshFrom(view, fromRoomVersion) {
@@ -0,0 +1,6 @@
1
+ export declare const INBOX_CHANNEL_KINDS: readonly ['telegram-bot', 'whatsapp', 'messenger', 'instagram'];
2
+ export type InboxChannelKind = (typeof INBOX_CHANNEL_KINDS)[number];
3
+ export declare const INBOX_CHAT_TYPE = "inbox-chat";
4
+ export declare const INBOX_MESSAGE_TYPE = "inbox-message";
5
+ export declare const HOUSE_ROOM_TYPES: string;
6
+ //# sourceMappingURL=room-types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"room-types.d.ts","sourceRoot":"","sources":["../src/room-types.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,mBAAmB,YAAI,cAAc,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,CAAU,CAAC;AAEnG,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,mBAAmB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEpE,eAAO,MAAM,eAAe,eAAe,CAAC;AAE5C,eAAO,MAAM,kBAAkB,kBAAkB,CAAC;AAalD,eAAO,MAAM,gBAAgB,QAsDjB,CAAC"}
@@ -0,0 +1,66 @@
1
+ export const INBOX_CHANNEL_KINDS = ['telegram-bot', 'whatsapp', 'messenger', 'instagram'];
2
+ export const INBOX_CHAT_TYPE = 'inbox-chat';
3
+ export const INBOX_MESSAGE_TYPE = 'inbox-message';
4
+ const IDENTITY = "{ type: string, pattern: '^\\S+$' }";
5
+ const AGENT = [' agent: { type: string, format: uuid }', ' name: { type: string }'];
6
+ const ASSIGNEE = [
7
+ ' type: object',
8
+ ' properties:',
9
+ ' user: { type: string, format: uuid }',
10
+ ...AGENT,
11
+ ];
12
+ export const HOUSE_ROOM_TYPES = [
13
+ `${INBOX_CHAT_TYPE}:`,
14
+ ' properties:',
15
+ ' inbox: { type: string, format: uuid }',
16
+ ` channel: { enum: [${INBOX_CHANNEL_KINDS.join(', ')}] }`,
17
+ ` chat: ${IDENTITY}`,
18
+ ' customer:',
19
+ ' type: object',
20
+ ' properties:',
21
+ ' name: { type: string }',
22
+ ` username: ${IDENTITY}`,
23
+ ` phone: ${IDENTITY}`,
24
+ ' assignee:',
25
+ ...ASSIGNEE,
26
+ ' return_to:',
27
+ ' type: object',
28
+ ' properties:',
29
+ ...AGENT,
30
+ ' conversation: { type: string, format: uuid }',
31
+ ' required: [inbox, channel, chat, customer]',
32
+ `${INBOX_MESSAGE_TYPE}:`,
33
+ ' properties:',
34
+ ' direction: { enum: [in, out] }',
35
+ ' sender:',
36
+ ' type: object',
37
+ ' properties:',
38
+ ' name: { type: string }',
39
+ ` username: ${IDENTITY}`,
40
+ ' sent_at: { type: string, format: date-time }',
41
+ ` channel_message: ${IDENTITY}`,
42
+ ' state: { enum: [sending, sent, failed] }',
43
+ ' reason: { type: string }',
44
+ ' reactions:',
45
+ ' type: array',
46
+ ' items:',
47
+ ' type: object',
48
+ ' properties:',
49
+ ' reaction: { type: string, minLength: 1 }',
50
+ ' sender:',
51
+ ' type: object',
52
+ ' properties:',
53
+ ` id: ${IDENTITY}`,
54
+ ' name: { type: string }',
55
+ ` username: ${IDENTITY}`,
56
+ ' count: { type: integer, minimum: 1 }',
57
+ ' required: [reaction]',
58
+ ` post: ${IDENTITY}`,
59
+ ' assignee:',
60
+ ...ASSIGNEE,
61
+ ' sent_outside: { const: true }',
62
+ ' required: [direction]',
63
+ ' if: { properties: { direction: { const: in } } }',
64
+ ' then: { required: [sent_at] }',
65
+ '',
66
+ ].join('\n');
@@ -1 +1 @@
1
- {"version":3,"file":"site.d.ts","sourceRoot":"","sources":["../src/site.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,OAAO,EAUR,MAAM,wBAAwB,CAAC;AA6BhC,OAAO,EAOL,KAAK,kBAAkB,EACxB,MAAM,gBAAgB,CAAC;AAuCxB,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,kBAAkB,EAAE,QAAQ,UAAQ,GAAG,OAAO,CAkQ1F"}
1
+ {"version":3,"file":"site.d.ts","sourceRoot":"","sources":["../src/site.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,OAAO,EAWR,MAAM,wBAAwB,CAAC;AA+BhC,OAAO,EAML,KAAK,kBAAkB,EACxB,MAAM,gBAAgB,CAAC;AAoCxB,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,kBAAkB,EAAE,QAAQ,UAAQ,GAAG,OAAO,CAoP1F"}
package/dist/site.js CHANGED
@@ -2,12 +2,12 @@ import { stepped } from '@agentshouse/mdruntime';
2
2
  import { actionOutcome, SITE_SUBMIT } from './actions.js';
3
3
  import { mapDiscovery, parseSiteDiscovery, siteCapabilities, siteTracked, } from './discovery.js';
4
4
  import { partModels, streamedModels, viewModels } from './model.js';
5
- import { advancedScope, bootstrapping, emptyScope, housePart, parseEnumeration, parseSiteParted, refreshFrom, viewOf, wholeView, } from './read.js';
6
- import { PART_ATTEMPTS, unverifiedPart } from './part.js';
5
+ import { bootstrapping, emptyScope, housePart, manifested, parseEnumeration, parseSiteParted, partInventory, refreshFrom, viewOf, wholeView, } from './read.js';
6
+ import { PART_ATTEMPTS, partArrival, partFrame, unverifiedPart } from './part.js';
7
7
  import { classifyDoor, classifyRead, discoveryFailure } from './refusal.js';
8
+ import { HOUSE_ROOM_TYPES } from './room-types.js';
8
9
  import { houseNotifications } from './stream.js';
9
- import { SITE_DOOR_BOOTSTRAP, SITE_DOOR_DISCOVER, SITE_DOOR_ENUMERATE, SITE_DOOR_SUBMIT, SITE_FILE_RECORD, SITE_FILES, } from './transport.js';
10
- import { houseReferrer } from './referrer.js';
10
+ import { SITE_DOOR_BOOTSTRAP, SITE_DOOR_DISCOVER, SITE_DOOR_ENUMERATE, SITE_DOOR_SUBMIT, SITE_FILES, } from './transport.js';
11
11
  import { followsVersion, logPosition, presented, reachedVersion, roomVersion, } from './version.js';
12
12
  const UNCERTAIN = { kind: 'transport-uncertainty' };
13
13
  function unfound() {
@@ -24,28 +24,15 @@ function siteRoom(tracked) {
24
24
  providerOwnedPaths: [],
25
25
  };
26
26
  }
27
- function settled(refused) {
28
- return refused.kind === 'beyond-horizon' ? UNCERTAIN : refused;
29
- }
30
27
  export function createSiteAdapter(transport, snapshot = false) {
31
28
  const notifications = houseNotifications(transport, 'site');
32
29
  const tracked = new Map();
33
30
  const expired = new Map();
34
- function published(referrer) {
35
- const named = houseReferrer(referrer, tracked);
36
- return named === null || !tracked.has(named.locator) ? null : named;
37
- }
38
31
  function fileRoute(request) {
39
- const named = published(request.kind === 'path' ? { room: request.locator, path: request.path } : request.referrer);
40
- if (named === null) {
41
- return { kind: 'rejected', code: 'referrer_unknown', fields: {}, retryable: false };
42
- }
43
- if (request.fileRef === undefined)
44
- return unfound();
45
- const { room_ref, path, record } = named.referrer;
46
- return `${SITE_FILES}${room_ref}/${encodeURIComponent(request.fileRef)}/${path === undefined
47
- ? `${SITE_FILE_RECORD}/${encodeURIComponent(record)}`
48
- : encodeURIComponent(path)}`;
32
+ const entry = tracked.get(request.locator);
33
+ if (entry === undefined)
34
+ return { kind: 'room-not-readable', locator: request.locator };
35
+ return `${SITE_FILES}${entry.authority}/${encodeURIComponent(request.revision)}/${encodeURIComponent(request.path)}`;
49
36
  }
50
37
  async function enumerated(entry, from, page) {
51
38
  const answer = await transport.door(SITE_DOOR_ENUMERATE, {
@@ -79,34 +66,29 @@ export function createSiteAdapter(transport, snapshot = false) {
79
66
  const room = parseSiteParted(answer.body);
80
67
  return room === null || room.anchor !== entry.authority ? UNCERTAIN : room;
81
68
  }
82
- async function fetchedPart(entry, room, listed) {
69
+ async function fetchedPart(entry, room, listed, received, arriving) {
70
+ if (received !== undefined) {
71
+ const kept = new Uint8Array(received);
72
+ const held = await housePart(room, kept, listed, 'site');
73
+ if (held !== null)
74
+ return 'kind' in held ? held : { scope: held, frame: kept };
75
+ }
83
76
  for (let attempt = 0; attempt < PART_ATTEMPTS; attempt += 1) {
84
- const answer = await transport.read(`${SITE_DOOR_BOOTSTRAP}${entry.authority}?part=${encodeURIComponent(listed.part)}`);
77
+ const answer = await transport.read(`${SITE_DOOR_BOOTSTRAP}${entry.authority}?part=${encodeURIComponent(listed.part)}`, partArrival(listed, arriving));
85
78
  const failure = classifyRead(answer.status, answer.body, entry.locator);
86
79
  if (failure?.kind === 'beyond-horizon')
87
80
  expired.set(entry.locator, room.position);
88
81
  if (failure !== null)
89
82
  return failure;
90
- const scope = await housePart(room, answer.body, listed, 'site');
83
+ const frame = partFrame(answer.body);
84
+ if (frame === null)
85
+ continue;
86
+ const scope = await housePart(room, frame, listed, 'site');
91
87
  if (scope !== null)
92
- return scope;
88
+ return 'kind' in scope ? scope : { scope, frame };
93
89
  }
94
90
  return unverifiedPart(entry.locator);
95
91
  }
96
- async function caughtUp(entry, continued) {
97
- const listed = continued.room.parts[continued.next];
98
- const scope = listed === undefined
99
- ? emptyScope(continued.room)
100
- : await fetchedPart(entry, continued.room, listed);
101
- if ('kind' in scope)
102
- return scope;
103
- if (continued.room.parts.length > 1 || roomVersion(scope) === roomVersion(entry))
104
- return scope;
105
- const view = await enumeration(entry, scope);
106
- if ('kind' in view)
107
- return settled(view);
108
- return stepped(advancedScope(scope, view));
109
- }
110
92
  async function refreshed(entry, fromRoomVersion) {
111
93
  const from = logPosition(fromRoomVersion);
112
94
  if (from === null)
@@ -129,6 +111,7 @@ export function createSiteAdapter(transport, snapshot = false) {
129
111
  return {
130
112
  localSaves: true,
131
113
  exhaustiveDiscovery: true,
114
+ roomTypes: HOUSE_ROOM_TYPES,
132
115
  async authorize() {
133
116
  return {};
134
117
  },
@@ -152,17 +135,25 @@ export function createSiteAdapter(transport, snapshot = false) {
152
135
  if (entry === undefined)
153
136
  return { kind: 'room-not-readable', locator: request.locator };
154
137
  let continued;
138
+ let inventory;
155
139
  if (request.pageToken === undefined) {
156
140
  const room = await partedRoom(entry);
157
141
  if ('kind' in room)
158
142
  return room;
159
143
  continued = { room, next: 0, seen: [] };
144
+ inventory = partInventory(room);
145
+ if (inventory.parts > 0)
146
+ return manifested(room, inventory, JSON.stringify(continued));
160
147
  }
161
148
  else
162
149
  continued = JSON.parse(request.pageToken);
163
- const scope = await caughtUp(entry, continued);
164
- if ('kind' in scope)
165
- return scope;
150
+ const listed = continued.room.parts[continued.next];
151
+ const sent = listed === undefined
152
+ ? { scope: emptyScope(continued.room), frame: null }
153
+ : await fetchedPart(entry, continued.room, listed, request.received, request.arriving);
154
+ if ('kind' in sent)
155
+ return sent;
156
+ const { scope } = sent;
166
157
  notifications.reach(entry.authority, scope);
167
158
  const next = continued.next + 1;
168
159
  const more = next < continued.room.parts.length;
@@ -171,10 +162,13 @@ export function createSiteAdapter(transport, snapshot = false) {
171
162
  ...entry.schemas.filter((schema) => scope.files.some((file) => file.path === schema)),
172
163
  ];
173
164
  const models = await partModels(scope, entry, new Set(seen), !more);
165
+ const { folders: _folders, ...page } = await stepped(bootstrapping(scope));
174
166
  return {
175
- ...(await stepped(bootstrapping(scope))),
167
+ ...page,
176
168
  ...(models === undefined ? {} : { models }),
177
169
  ...(more ? { pageToken: JSON.stringify({ ...continued, next, seen }) } : {}),
170
+ ...(inventory === undefined ? {} : { inventory }),
171
+ ...(sent.frame === null ? {} : { received: sent.frame }),
178
172
  };
179
173
  },
180
174
  async refresh(_authorization, request) {
@@ -203,8 +197,8 @@ export function createSiteAdapter(transport, snapshot = false) {
203
197
  const read = await transport.download(route);
204
198
  if (read.status === 404)
205
199
  return unfound();
206
- return read.status === 200 && read.blob !== null
207
- ? { kind: 'binary', blob: read.blob }
200
+ return read.status === 200 && read.body !== null
201
+ ? { kind: 'binary', body: read.body, mediaType: read.mediaType }
208
202
  : UNCERTAIN;
209
203
  },
210
204
  async link(_authorization, request) {
@@ -217,13 +211,6 @@ export function createSiteAdapter(transport, snapshot = false) {
217
211
  name: request.path.slice(request.path.lastIndexOf('/') + 1),
218
212
  };
219
213
  },
220
- referrerScope(referrer) {
221
- const named = published(referrer);
222
- if (named === null)
223
- return null;
224
- const { path } = named.referrer;
225
- return { locator: named.locator, ...(path === undefined ? {} : { path }) };
226
- },
227
214
  reached(locator, version, target) {
228
215
  return reachedVersion(version, target, tracked.get(locator)?.logEpoch);
229
216
  },
package/dist/stream.d.ts CHANGED
@@ -15,6 +15,7 @@ export type HouseNotifications = {
15
15
  reopen(): void;
16
16
  track(authorities: readonly TrackedAuthority[]): void;
17
17
  reach(authority: string, at: LogPosition): void;
18
+ edited(tracked: TrackedAuthority, from: LogPosition): readonly string[];
18
19
  advance(tracked: TrackedAuthority, from: LogPosition, holds?: HeldRevision): Generator<number, StreamedAdvancement | null, void>;
19
20
  conversations: ConversationStream;
20
21
  serviceMode: ServiceModeSignal;
@@ -1 +1 @@
1
- {"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../src/stream.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,kBAAkB,EAEvB,KAAK,WAAW,EAChB,KAAK,eAAe,EAEpB,KAAK,iBAAiB,EACtB,KAAK,cAAc,EAEpB,MAAM,wBAAwB,CAAC;AAGhC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAEvD,OAAO,KAAK,EACV,SAAS,EACT,aAAa,EAEb,SAAS,EAET,gBAAgB,EAEjB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAe,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AA0C7D,MAAM,MAAM,YAAY,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC;AAEvE,MAAM,MAAM,mBAAmB,GAAG;IAChC,IAAI,EAAE,WAAW,CAAC;IAClB,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;CAChD,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,EAAE,QAAQ,EAAE,cAAc,GAAG,MAAM,IAAI,CAAC;IACxF,IAAI,CAAC,KAAK,EAAE,SAAS,aAAa,EAAE,GAAG,IAAI,CAAC;IAC5C,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,WAAW,GAAG,IAAI,CAAC;IAClD,MAAM,IAAI,IAAI,CAAC;IACf,KAAK,CAAC,WAAW,EAAE,SAAS,gBAAgB,EAAE,GAAG,IAAI,CAAC;IACtD,KAAK,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,WAAW,GAAG,IAAI,CAAC;IAChD,OAAO,CACL,OAAO,EAAE,gBAAgB,EACzB,IAAI,EAAE,WAAW,EACjB,KAAK,CAAC,EAAE,YAAY,GACnB,SAAS,CAAC,MAAM,EAAE,mBAAmB,GAAG,IAAI,EAAE,IAAI,CAAC,CAAC;IACvD,aAAa,EAAE,kBAAkB,CAAC;IAClC,WAAW,EAAE,iBAAiB,CAAC;CAChC,CAAC;AAmFF,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,GAAG,kBAAkB,CA0iB5F"}
1
+ {"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../src/stream.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,kBAAkB,EAEvB,KAAK,WAAW,EAChB,KAAK,eAAe,EAEpB,KAAK,iBAAiB,EACtB,KAAK,cAAc,EAEpB,MAAM,wBAAwB,CAAC;AAGhC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAUvD,OAAO,KAAK,EACV,SAAS,EACT,aAAa,EAEb,SAAS,EAET,gBAAgB,EAEjB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAe,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AA0C7D,MAAM,MAAM,YAAY,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC;AAEvE,MAAM,MAAM,mBAAmB,GAAG;IAChC,IAAI,EAAE,WAAW,CAAC;IAClB,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;CAChD,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,EAAE,QAAQ,EAAE,cAAc,GAAG,MAAM,IAAI,CAAC;IACxF,IAAI,CAAC,KAAK,EAAE,SAAS,aAAa,EAAE,GAAG,IAAI,CAAC;IAC5C,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,WAAW,GAAG,IAAI,CAAC;IAClD,MAAM,IAAI,IAAI,CAAC;IACf,KAAK,CAAC,WAAW,EAAE,SAAS,gBAAgB,EAAE,GAAG,IAAI,CAAC;IACtD,KAAK,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,WAAW,GAAG,IAAI,CAAC;IAChD,MAAM,CAAC,OAAO,EAAE,gBAAgB,EAAE,IAAI,EAAE,WAAW,GAAG,SAAS,MAAM,EAAE,CAAC;IACxE,OAAO,CACL,OAAO,EAAE,gBAAgB,EACzB,IAAI,EAAE,WAAW,EACjB,KAAK,CAAC,EAAE,YAAY,GACnB,SAAS,CAAC,MAAM,EAAE,mBAAmB,GAAG,IAAI,EAAE,IAAI,CAAC,CAAC;IACvD,aAAa,EAAE,kBAAkB,CAAC;IAClC,WAAW,EAAE,iBAAiB,CAAC;CAChC,CAAC;AAmFF,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,GAAG,kBAAkB,CA8jB5F"}
package/dist/stream.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { ROW_WEIGHT, } from '@agentshouse/mdruntime';
2
2
  import { acceptedMessage } from './actions.js';
3
3
  import { sha256Hex } from './digest.js';
4
- import { answered, answering, binaryFileOf, forgotten, headChange, revised } from './read.js';
4
+ import { answered, answering, binaryFileOf, folded, forgotten, headChange, revised, } from './read.js';
5
5
  import { roomVersion } from './version.js';
6
6
  const HELD_FRAMES_MAX = 256;
7
7
  function turnOperation(frame) {
@@ -556,6 +556,17 @@ export function houseNotifications(transport, mode) {
556
556
  held,
557
557
  });
558
558
  },
559
+ edited(tracked, from) {
560
+ const line = lines.get(tracked.authority);
561
+ const start = position(from.position);
562
+ if (line === undefined || start === null || line.logEpoch !== from.logEpoch)
563
+ return [];
564
+ return [
565
+ ...new Set(line.frames
566
+ .filter((frame) => frame.position > start)
567
+ .flatMap((frame) => frame.entries.flatMap((entry) => entry.kind === 'edited' && entry.revision !== undefined ? [entry.path] : []))),
568
+ ];
569
+ },
559
570
  *advance(tracked, from, holds) {
560
571
  const line = lines.get(tracked.authority);
561
572
  const start = position(from.position);
@@ -570,6 +581,10 @@ export function houseNotifications(transport, mode) {
570
581
  for (const frame of pending) {
571
582
  for (const entry of frame.entries) {
572
583
  yield ROW_WEIGHT + (entry.text?.length ?? 0);
584
+ if (entry.kind === 'folder') {
585
+ folded(answer, entry.path, entry.removed === true);
586
+ continue;
587
+ }
573
588
  if (entry.kind === 'edited' &&
574
589
  entry.revision !== undefined &&
575
590
  holds?.(entry.path, entry.revision) === true) {
@@ -15,27 +15,26 @@ export type HouseDoorAnswer = {
15
15
  body: unknown;
16
16
  };
17
17
  export declare const DOOR_FILES_GET = "/door/files/get";
18
+ export declare const DOOR_FILES_FINISH = "/door/files/finish";
18
19
  export declare const SITE_FILES = "/_house/files/";
19
- export declare const SITE_FILE_RECORD = "record";
20
- export type HouseByteAct = 'authored_batch_upload' | 'file_upload' | 'binary_stage';
21
- export type HouseByteBody = string | ArrayBuffer | Uint8Array | Blob;
20
+ export type HouseByteAct = 'authored_batch_upload';
22
21
  export type HouseBytesAnswer = {
23
22
  status: number;
24
- blob: Blob | null;
23
+ body: ReadableStream<Uint8Array> | null;
24
+ mediaType: string;
25
25
  };
26
26
  export type HouseByteTransfer = {
27
27
  act: HouseByteAct;
28
28
  url: string;
29
- body: HouseByteBody;
30
- operation?: string;
29
+ body: string;
31
30
  };
32
31
  export type HouseStreamEntry = {
33
32
  path: string;
34
- kind: 'edited' | 'removed' | 'piece';
33
+ kind: 'edited' | 'removed' | 'piece' | 'folder';
34
+ removed?: true;
35
35
  revision?: string;
36
36
  sha256?: string;
37
37
  text?: string;
38
- file?: string;
39
38
  bytes?: number;
40
39
  media_type?: string;
41
40
  author?: {
@@ -165,16 +164,17 @@ export type HouseStream = {
165
164
  closed(notify: (closing: HouseClosing) => void): () => void;
166
165
  };
167
166
  export type HouseDoor = {
168
- door(route: string, body: unknown): Promise<HouseDoorAnswer>;
167
+ door(route: string, body: unknown, arriving?: (bytes: number) => void): Promise<HouseDoorAnswer>;
169
168
  stream: HouseStream;
170
169
  };
171
170
  export type HouseTransport = HouseDoor & {
172
171
  transfer(request: HouseByteTransfer): Promise<HouseDoorAnswer>;
172
+ put(url: string, body: Blob): Promise<number>;
173
173
  download(url: string): Promise<HouseBytesAnswer>;
174
174
  };
175
175
  export type HouseSiteTransport = HouseDoor & {
176
176
  online(): boolean;
177
- read(route: string): Promise<HouseDoorAnswer>;
177
+ read(route: string, arriving?: (bytes: number) => void): Promise<HouseDoorAnswer>;
178
178
  download(route: string): Promise<HouseBytesAnswer>;
179
179
  };
180
180
  export {};