@agentshouse/mdruntime-adapter 0.1.0-cb89e0425af50 → 0.1.0-cb8fc491b1a92
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/README.md +2 -2
- package/dist/actions.d.ts +162 -3
- package/dist/actions.d.ts.map +1 -1
- package/dist/actions.js +52 -172
- package/dist/adapter.d.ts +1 -0
- package/dist/adapter.d.ts.map +1 -1
- package/dist/adapter.js +265 -216
- package/dist/app-reference.d.ts +2 -0
- package/dist/app-reference.d.ts.map +1 -0
- package/dist/app-reference.js +2 -0
- package/dist/crockford.d.ts +4 -0
- package/dist/crockford.d.ts.map +1 -0
- package/dist/crockford.js +3 -0
- package/dist/discovery.d.ts +7 -10
- package/dist/discovery.d.ts.map +1 -1
- package/dist/discovery.js +40 -40
- package/dist/house-model.d.ts +2983 -0
- package/dist/house-model.d.ts.map +1 -0
- package/dist/house-model.js +1643 -0
- package/dist/index.d.ts +9 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -1
- package/dist/model.d.ts +5 -3
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +10 -32
- package/dist/part.d.ts +14 -0
- package/dist/part.d.ts.map +1 -0
- package/dist/part.js +90 -0
- package/dist/read.d.ts +50 -45
- package/dist/read.d.ts.map +1 -1
- package/dist/read.js +243 -242
- package/dist/refusal.d.ts +0 -3
- package/dist/refusal.d.ts.map +1 -1
- package/dist/refusal.js +2 -6
- package/dist/room-stream.d.ts +19 -0
- package/dist/room-stream.d.ts.map +1 -0
- package/dist/room-stream.js +57 -0
- package/dist/room-types.d.ts +7 -0
- package/dist/room-types.d.ts.map +1 -0
- package/dist/room-types.js +126 -0
- package/dist/site.d.ts +1 -1
- package/dist/site.d.ts.map +1 -1
- package/dist/site.js +101 -90
- package/dist/stream.d.ts +2 -11
- package/dist/stream.d.ts.map +1 -1
- package/dist/stream.js +139 -140
- package/dist/transport.d.ts +70 -22
- package/dist/transport.d.ts.map +1 -1
- package/dist/transport.js +50 -9
- package/dist/upload.d.ts +12 -0
- package/dist/upload.d.ts.map +1 -0
- package/dist/upload.js +63 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +1 -1
- package/dist/write.d.ts +17 -9
- package/dist/write.d.ts.map +1 -1
- package/dist/write.js +138 -43
- package/package.json +8 -5
- package/AGENTS.md +0 -269
- package/dist/gather.d.ts +0 -4
- package/dist/gather.d.ts.map +0 -1
- package/dist/gather.js +0 -45
- package/dist/links.d.ts +0 -20
- package/dist/links.d.ts.map +0 -1
- package/dist/links.js +0 -104
package/dist/adapter.js
CHANGED
|
@@ -1,108 +1,99 @@
|
|
|
1
|
+
import { stepped } from '@agentshouse/mdruntime';
|
|
1
2
|
import { DISCOVER_PREFIXES, capabilities, discoveredRoom, mapDiscovery, parseDiscovery, trackedFrom, } from './discovery.js';
|
|
2
|
-
import { partModels, streamedModels, viewModels } from './model.js';
|
|
3
|
-
import { gathered, gatheredEach, selections } from './gather.js';
|
|
4
3
|
import { createSiteAdapter } from './site.js';
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
4
|
+
import { authorityRoot, bootstrapping, parseFileAddress, parseGrant, } from './read.js';
|
|
5
|
+
import { roomStreams } from './room-stream.js';
|
|
6
|
+
import { sha256Hex } from './digest.js';
|
|
7
|
+
import { finishedUpload, houseUpload } from './upload.js';
|
|
7
8
|
import { houseAction } from './actions.js';
|
|
8
|
-
import { classifyDoor,
|
|
9
|
+
import { classifyDoor, discoveryFailure, doorRefusal } from './refusal.js';
|
|
9
10
|
import { houseNotifications } from './stream.js';
|
|
10
|
-
import {
|
|
11
|
-
import { batchPaths, deltaBody, deltaBytes, deltaDigest, historyOutcome, houseChanges, mutationOutcome, operationHead, rejectedMapping, revisionBody, uploadable, } from './write.js';
|
|
12
|
-
import {
|
|
11
|
+
import { houseDoorRoute, } from './transport.js';
|
|
12
|
+
import { batchPaths, deltaBody, deltaBytes, deltaDigest, historyOutcome, houseChanges, houseMove, mutationOutcome, operationHead, rejectedMapping, revisionBody, uploadable, } from './write.js';
|
|
13
|
+
import { HOUSE_NAMEABLE_ROOM_TYPES, HOUSE_ROOM_TYPES } from './room-types.js';
|
|
14
|
+
import { followsVersion, logPosition, presented, reachedVersion, roomVersion } from './version.js';
|
|
13
15
|
const UNCERTAIN = { kind: 'transport-uncertainty' };
|
|
14
16
|
const UNCERTAIN_READ = { kind: 'transport-uncertainty' };
|
|
17
|
+
const OCTET_STREAM = 'application/octet-stream';
|
|
18
|
+
const EXPIRED_ADDRESS = 403;
|
|
15
19
|
function lapsedGrant(outcome) {
|
|
16
20
|
return outcome.kind === 'rejected' && outcome.code === 'upload_unavailable';
|
|
17
21
|
}
|
|
22
|
+
async function heldFiles(authority, held, bound) {
|
|
23
|
+
const listed = await held.files(null, bound + 1);
|
|
24
|
+
const files = listed.slice(0, bound);
|
|
25
|
+
const through = listed.length > bound ? (files.at(-1)?.path ?? null) : null;
|
|
26
|
+
return files.length === 0 ? null : { room: authority, through, files };
|
|
27
|
+
}
|
|
28
|
+
async function reused(scope, held) {
|
|
29
|
+
const sent = new Set(scope.contents.map((entry) => entry.path));
|
|
30
|
+
const contents = [...scope.contents];
|
|
31
|
+
const asked = scope.files.filter((file) => file.binary === undefined && !sent.has(file.path));
|
|
32
|
+
if (asked.length === 0)
|
|
33
|
+
return scope;
|
|
34
|
+
if (held === undefined)
|
|
35
|
+
return null;
|
|
36
|
+
const kept = await held.contents(asked.map(({ path }) => path));
|
|
37
|
+
for (const file of asked) {
|
|
38
|
+
const found = kept.get(file.path);
|
|
39
|
+
if (found === undefined ||
|
|
40
|
+
found.revision !== file.revision ||
|
|
41
|
+
(await sha256Hex(found.content)) !== file.sha256) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
contents.push({ path: file.path, content: found.content });
|
|
45
|
+
}
|
|
46
|
+
return { ...scope, contents };
|
|
47
|
+
}
|
|
18
48
|
export function createAgentsHouseAdapter(options) {
|
|
19
49
|
if ('site' in options)
|
|
20
|
-
return createSiteAdapter(options.site);
|
|
50
|
+
return createSiteAdapter(options.site, options.snapshot === true);
|
|
21
51
|
const transport = options.transport;
|
|
22
52
|
const notifications = houseNotifications(transport, 'credential');
|
|
23
53
|
const tracked = new Map();
|
|
24
|
-
const
|
|
54
|
+
const rooms = roomStreams('credential');
|
|
55
|
+
const unreused = new Set();
|
|
25
56
|
let bounds;
|
|
26
57
|
function declared() {
|
|
27
58
|
if (bounds === undefined)
|
|
28
59
|
throw new Error('no Discovery declared the bounds');
|
|
29
60
|
return bounds;
|
|
30
61
|
}
|
|
31
|
-
async function
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
const seen = [
|
|
41
|
-
...(continued?.seen ?? []),
|
|
42
|
-
...entry.schemas.filter((schema) => scope.files.some((file) => file.path === schema)),
|
|
43
|
-
];
|
|
44
|
-
const models = await partModels(scope, entry, new Set(seen), next === null);
|
|
45
|
-
const continuation = next === null ? null : { after: next, seen };
|
|
62
|
+
async function streamedPage(entry, token, held) {
|
|
63
|
+
const part = await rooms.part(entry, token);
|
|
64
|
+
if ('kind' in part)
|
|
65
|
+
return part;
|
|
66
|
+
const scope = await reused(part.scope, held);
|
|
67
|
+
if (scope === null) {
|
|
68
|
+
unreused.add(entry.locator);
|
|
69
|
+
return UNCERTAIN_READ;
|
|
70
|
+
}
|
|
46
71
|
return {
|
|
47
|
-
...
|
|
48
|
-
...(models === undefined ? {} : { models }),
|
|
49
|
-
...(
|
|
72
|
+
...(await stepped(bootstrapping(scope))),
|
|
73
|
+
...(part.models === undefined ? {} : { models: part.models }),
|
|
74
|
+
...(part.pageToken === undefined ? {} : { pageToken: part.pageToken }),
|
|
75
|
+
bytes: part.bytes,
|
|
50
76
|
};
|
|
51
77
|
}
|
|
52
|
-
async function
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
async function streamed(entry, request, from) {
|
|
65
|
-
const links = handed.get(entry.locator);
|
|
66
|
-
if (links?.roomVersion !== request.fromRoomVersion)
|
|
67
|
-
return null;
|
|
68
|
-
const advanced = notifications.advance(entry, from, (path, revision) => links.revisions.get(path) === revision);
|
|
69
|
-
if (advanced === null)
|
|
70
|
-
return null;
|
|
71
|
-
const relinked = streamedLinks(links, advanced);
|
|
72
|
-
if (relinked === null)
|
|
73
|
-
return null;
|
|
74
|
-
handed.set(entry.locator, relinked);
|
|
75
|
-
const models = await streamedModels(advanced.schemas, entry, 'credential');
|
|
76
|
-
return models === undefined ? advanced.page : { ...advanced.page, models };
|
|
78
|
+
async function openedRoom(entry, request) {
|
|
79
|
+
const reusing = unreused.delete(entry.locator) ? undefined : request.held;
|
|
80
|
+
const files = reusing === undefined
|
|
81
|
+
? null
|
|
82
|
+
: await heldFiles(entry.authority, reusing, declared().max_held_files);
|
|
83
|
+
const answered = await transport.streamed(houseDoorRoute('bootstrap'), {
|
|
84
|
+
prefixes: [authorityRoot(entry.handle)],
|
|
85
|
+
...(files === null ? {} : { held: [files] }),
|
|
86
|
+
}, request.signal ?? new AbortController().signal);
|
|
87
|
+
if (answered.room === null)
|
|
88
|
+
return classifyDoor(answered.status, answered.body, entry.locator) ?? UNCERTAIN_READ;
|
|
89
|
+
return rooms.head(entry, answered.room, request.arriving, request.signal);
|
|
77
90
|
}
|
|
78
91
|
function held(rooms) {
|
|
79
|
-
|
|
92
|
+
notifications.send(rooms.flatMap((room) => {
|
|
80
93
|
const entry = tracked.get(room.locator);
|
|
81
94
|
const at = logPosition(room.roomVersion);
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
handed.set(room.locator, heldLinks(room));
|
|
85
|
-
notifications.reach(entry.authority, at);
|
|
86
|
-
return [presented(entry.authority, at)];
|
|
87
|
-
});
|
|
88
|
-
notifications.send(kept);
|
|
89
|
-
}
|
|
90
|
-
function adopted(entry, outcome) {
|
|
91
|
-
if (outcome.kind !== 'accepted')
|
|
92
|
-
return outcome;
|
|
93
|
-
const { originals, ...accepted } = outcome;
|
|
94
|
-
const links = handed.get(entry.locator);
|
|
95
|
-
if (links === undefined)
|
|
96
|
-
return accepted;
|
|
97
|
-
const follows = followsVersion(links.roomVersion, accepted.roomVersion);
|
|
98
|
-
const relinked = acceptedLinks(links, accepted, follows ? accepted.roomVersion : links.roomVersion, originals);
|
|
99
|
-
handed.set(entry.locator, relinked.links);
|
|
100
|
-
const at = logPosition(accepted.roomVersion);
|
|
101
|
-
if (follows && at !== null)
|
|
102
|
-
notifications.reach(entry.authority, at);
|
|
103
|
-
return relinked.attachments === undefined
|
|
104
|
-
? accepted
|
|
105
|
-
: { ...accepted, attachments: relinked.attachments };
|
|
95
|
+
return entry === undefined || at === null ? [] : [presented(entry.authority, at)];
|
|
96
|
+
}));
|
|
106
97
|
}
|
|
107
98
|
function authorityFor(locator) {
|
|
108
99
|
return tracked.get(locator);
|
|
@@ -121,147 +112,154 @@ export function createAgentsHouseAdapter(options) {
|
|
|
121
112
|
return result;
|
|
122
113
|
return { ...result, at: { locator, roomVersion: roomVersion({ position, logEpoch }) } };
|
|
123
114
|
}
|
|
124
|
-
function weight(entry) {
|
|
125
|
-
return contentPrefixes(entry).length;
|
|
126
|
-
}
|
|
127
|
-
async function bootstrapped(entries, answer) {
|
|
128
|
-
let open = entries.map((entry, ask) => ({ entry, ask }));
|
|
129
|
-
while (open.length > 0) {
|
|
130
|
-
const unreached = [];
|
|
131
|
-
for (const part of selections(open, ({ entry }) => weight(entry), declared().max_prefixes)) {
|
|
132
|
-
const answered = await transport.door(DOOR_BOOTSTRAP, {
|
|
133
|
-
prefixes: part.flatMap(({ entry }) => contentPrefixes(entry)),
|
|
134
|
-
});
|
|
135
|
-
const classified = part.map(({ entry }) => classifyDoor(answered.status, answered.body, entry.locator));
|
|
136
|
-
const envelope = classified[0] === null ? parseEnvelope(answered.body) : null;
|
|
137
|
-
for (const [index, asked] of part.entries()) {
|
|
138
|
-
const failure = classified[index];
|
|
139
|
-
if (failure !== null && failure !== undefined) {
|
|
140
|
-
answer(asked.ask, failure);
|
|
141
|
-
continue;
|
|
142
|
-
}
|
|
143
|
-
if (envelope === null) {
|
|
144
|
-
answer(asked.ask, UNCERTAIN_READ);
|
|
145
|
-
continue;
|
|
146
|
-
}
|
|
147
|
-
const scope = envelope.scopes.find((candidate) => matchesScope(candidate, asked.entry));
|
|
148
|
-
if (scope === undefined && envelope.next !== undefined) {
|
|
149
|
-
unreached.push(asked);
|
|
150
|
-
continue;
|
|
151
|
-
}
|
|
152
|
-
answer(asked.ask, await bootstrappedRoom(envelope, scope, asked.entry, null));
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
open = unreached;
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
async function continuedBootstrap(entry, continued) {
|
|
159
|
-
const after = { room: entry.authority, path: continued.after };
|
|
160
|
-
const answered = await transport.door(DOOR_BOOTSTRAP, {
|
|
161
|
-
prefixes: contentPrefixes(entry),
|
|
162
|
-
after,
|
|
163
|
-
});
|
|
164
|
-
const failure = classifyDoor(answered.status, answered.body, entry.locator);
|
|
165
|
-
if (failure !== null)
|
|
166
|
-
return failure;
|
|
167
|
-
const envelope = parseEnvelope(answered.body);
|
|
168
|
-
if (envelope === null)
|
|
169
|
-
return UNCERTAIN_READ;
|
|
170
|
-
const scope = envelope.scopes.find((candidate) => matchesScope(candidate, entry));
|
|
171
|
-
return bootstrappedRoom(envelope, scope, entry, continued);
|
|
172
|
-
}
|
|
173
|
-
function layered(asks) {
|
|
174
|
-
const depths = new Map();
|
|
175
|
-
const layers = [];
|
|
176
|
-
for (const ask of asks) {
|
|
177
|
-
const depth = depths.get(ask.entry.authority) ?? 0;
|
|
178
|
-
depths.set(ask.entry.authority, depth + 1);
|
|
179
|
-
(layers[depth] ??= []).push(ask);
|
|
180
|
-
}
|
|
181
|
-
return layers;
|
|
182
|
-
}
|
|
183
|
-
async function enumerated(asks) {
|
|
184
|
-
const answers = new Map();
|
|
185
|
-
const parts = layered(asks).flatMap((layer) => selections(layer, (ask) => weight(ask.entry), declared().max_prefixes));
|
|
186
|
-
for (const part of parts) {
|
|
187
|
-
let open = [...part];
|
|
188
|
-
while (open.length > 0) {
|
|
189
|
-
const answer = await transport.door(DOOR_ENUMERATE, {
|
|
190
|
-
prefixes: open.flatMap((ask) => contentPrefixes(ask.entry)),
|
|
191
|
-
positions: open.map((ask) => presented(ask.entry.authority, ask.from)),
|
|
192
|
-
});
|
|
193
|
-
const classified = open.map((ask) => classifyRead(answer.status, answer.body, ask.entry.locator));
|
|
194
|
-
if (classified[0]?.kind === 'beyond-horizon') {
|
|
195
|
-
const named = doorRefusal(answer.body)?.fields.authority;
|
|
196
|
-
const expired = open.filter((ask) => ask.entry.authority === named);
|
|
197
|
-
const answered = expired.length === 0 ? UNCERTAIN_READ : { kind: 'beyond-horizon' };
|
|
198
|
-
const gone = expired.length === 0 ? open : expired;
|
|
199
|
-
for (const ask of gone)
|
|
200
|
-
answers.set(ask, answered);
|
|
201
|
-
open = open.filter((ask) => !gone.includes(ask));
|
|
202
|
-
continue;
|
|
203
|
-
}
|
|
204
|
-
const enumeration = classified[0] === null ? parseEnumeration(answer.body, 'credential') : null;
|
|
205
|
-
for (const [index, ask] of open.entries()) {
|
|
206
|
-
const failure = classified[index];
|
|
207
|
-
if (failure !== null && failure !== undefined) {
|
|
208
|
-
answers.set(ask, failure);
|
|
209
|
-
continue;
|
|
210
|
-
}
|
|
211
|
-
answers.set(ask, enumeration === null ? UNCERTAIN_READ : await enumeratedRoom(enumeration, ask));
|
|
212
|
-
}
|
|
213
|
-
open = [];
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
return asks.map((ask) => answers.get(ask) ?? UNCERTAIN_READ);
|
|
217
|
-
}
|
|
218
|
-
const bootstrapTogether = gatheredEach(bootstrapped);
|
|
219
|
-
const enumerateTogether = gathered(enumerated);
|
|
220
115
|
async function uploaded(head, changes, delta, bytes, entry, handed, keep) {
|
|
221
116
|
const paths = batchPaths(changes);
|
|
222
117
|
const sha256 = await deltaDigest(delta);
|
|
223
118
|
if (handed !== undefined && handed.sha256 === sha256) {
|
|
224
119
|
return { body: { ...head, paths, upload: handed, runtime: notifications.runtime } };
|
|
225
120
|
}
|
|
226
|
-
const minted = await transport.door(
|
|
121
|
+
const minted = await transport.door(houseDoorRoute('batch-upload'), {
|
|
122
|
+
...head,
|
|
123
|
+
paths,
|
|
124
|
+
bytes,
|
|
125
|
+
sha256,
|
|
126
|
+
});
|
|
227
127
|
if (minted.status !== 200) {
|
|
228
|
-
return {
|
|
128
|
+
return {
|
|
129
|
+
outcome: await stepped(mutationOutcome(minted.status, minted.body, entry.locator, entry.handle)),
|
|
130
|
+
};
|
|
229
131
|
}
|
|
230
132
|
const grant = parseGrant(minted.body);
|
|
231
|
-
if (grant === null
|
|
133
|
+
if (grant === null)
|
|
232
134
|
return { outcome: UNCERTAIN };
|
|
233
135
|
const delivered = await transport.transfer({
|
|
234
136
|
act: 'authored_batch_upload',
|
|
235
|
-
method: 'POST',
|
|
236
137
|
url: grant.url,
|
|
237
|
-
answer: 'json',
|
|
238
138
|
body: `${grant.operation}\n${delta}`,
|
|
239
139
|
});
|
|
240
140
|
if (delivered.status !== 200)
|
|
241
141
|
return { outcome: UNCERTAIN };
|
|
242
142
|
const holding = parseGrant(delivered.body);
|
|
243
|
-
if (holding === null
|
|
143
|
+
if (holding === null)
|
|
244
144
|
return { outcome: UNCERTAIN };
|
|
245
145
|
const upload = { url: holding.url, operation: holding.operation, bytes, sha256 };
|
|
246
146
|
await keep(upload);
|
|
247
147
|
return { body: { ...head, paths, upload, runtime: notifications.runtime } };
|
|
248
148
|
}
|
|
249
|
-
async function
|
|
149
|
+
async function staged(answer, { changeBatch: batch, file }) {
|
|
150
|
+
const refusal = doorRefusal(answer.body);
|
|
151
|
+
if (refusal?.code !== 'files_unstaged')
|
|
152
|
+
return null;
|
|
153
|
+
const uploads = refusal.fields.upload;
|
|
154
|
+
if (!Array.isArray(uploads))
|
|
155
|
+
return false;
|
|
156
|
+
for (const named of uploads) {
|
|
157
|
+
const upload = houseUpload(named);
|
|
158
|
+
const change = batch.changes.find((held) => held.kind === 'binary' && held.sha256 === named.sha256);
|
|
159
|
+
if (upload === null || change === undefined)
|
|
160
|
+
return false;
|
|
161
|
+
const finished = await finishedUpload(transport, upload, await file(change.localId));
|
|
162
|
+
if (finished?.body?.staged !== true)
|
|
163
|
+
return false;
|
|
164
|
+
}
|
|
165
|
+
return true;
|
|
166
|
+
}
|
|
167
|
+
async function submit(request, head, changes, delta, bytes, entry, handed, keep, into) {
|
|
250
168
|
const inline = { ...head, changes, runtime: notifications.runtime };
|
|
251
169
|
const prepared = uploadable(inline, bytes, declared())
|
|
252
170
|
? await uploaded(head, changes, delta, bytes, entry, handed, keep)
|
|
253
171
|
: { body: inline };
|
|
254
172
|
if ('outcome' in prepared)
|
|
255
173
|
return prepared.outcome;
|
|
256
|
-
const answer = await transport.door(
|
|
257
|
-
|
|
174
|
+
const answer = await transport.door(houseDoorRoute('batch'), prepared.body);
|
|
175
|
+
const stagedFiles = await staged(answer, request);
|
|
176
|
+
if (stagedFiles === false)
|
|
177
|
+
return UNCERTAIN;
|
|
178
|
+
const settled = stagedFiles === null ? answer : await transport.door(houseDoorRoute('batch'), prepared.body);
|
|
179
|
+
if (stagedFiles === true && (await staged(settled, request)) !== null)
|
|
180
|
+
return UNCERTAIN;
|
|
181
|
+
return stepped(mutationOutcome(settled.status, settled.body, entry.locator, entry.handle, into === undefined
|
|
182
|
+
? undefined
|
|
183
|
+
: {
|
|
184
|
+
source: { authority: entry.authority, handle: entry.handle },
|
|
185
|
+
destination: { authority: into.authority, handle: into.handle },
|
|
186
|
+
}));
|
|
187
|
+
}
|
|
188
|
+
function addressed(answer, locator) {
|
|
189
|
+
const failure = classifyDoor(answer.status, answer.body, locator);
|
|
190
|
+
if (failure !== null) {
|
|
191
|
+
return failure.kind === 'scoped-refusal'
|
|
192
|
+
? {
|
|
193
|
+
kind: 'rejected',
|
|
194
|
+
code: failure.code,
|
|
195
|
+
fields: failure.fields,
|
|
196
|
+
retryable: failure.retryable,
|
|
197
|
+
}
|
|
198
|
+
: failure;
|
|
199
|
+
}
|
|
200
|
+
const address = parseFileAddress(answer.body);
|
|
201
|
+
return address === null ? UNCERTAIN_READ : { kind: 'address', ...address };
|
|
202
|
+
}
|
|
203
|
+
function linked(answer, locator) {
|
|
204
|
+
const address = addressed(answer, locator);
|
|
205
|
+
return address.kind === 'address'
|
|
206
|
+
? { kind: 'link', url: address.url, name: address.name, mediaType: address.mediaType }
|
|
207
|
+
: address;
|
|
208
|
+
}
|
|
209
|
+
function refusedAttach(answer, locator) {
|
|
210
|
+
const failure = classifyDoor(answer.status, answer.body, locator);
|
|
211
|
+
if (failure === null)
|
|
212
|
+
return null;
|
|
213
|
+
return failure.kind === 'scoped-refusal'
|
|
214
|
+
? {
|
|
215
|
+
kind: 'rejected',
|
|
216
|
+
code: failure.code,
|
|
217
|
+
fields: failure.fields,
|
|
218
|
+
retryable: failure.retryable,
|
|
219
|
+
}
|
|
220
|
+
: failure;
|
|
221
|
+
}
|
|
222
|
+
async function downloaded(ask, locator) {
|
|
223
|
+
let address = addressed(await ask(), locator);
|
|
224
|
+
if (address.kind !== 'address')
|
|
225
|
+
return address;
|
|
226
|
+
let read = await transport.download(address.url);
|
|
227
|
+
if (read.status === EXPIRED_ADDRESS) {
|
|
228
|
+
address = addressed(await ask(), locator);
|
|
229
|
+
if (address.kind !== 'address')
|
|
230
|
+
return address;
|
|
231
|
+
read = await transport.download(address.url);
|
|
232
|
+
}
|
|
233
|
+
if (read.status !== 200 || read.body === null)
|
|
234
|
+
return UNCERTAIN_READ;
|
|
235
|
+
return {
|
|
236
|
+
kind: 'binary',
|
|
237
|
+
body: read.body,
|
|
238
|
+
mediaType: address.mediaType,
|
|
239
|
+
sha256: address.sha256,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
function pathAddress(request) {
|
|
243
|
+
const entry = authorityFor(request.locator);
|
|
244
|
+
if (entry === undefined)
|
|
245
|
+
return null;
|
|
246
|
+
return () => transport.door(houseDoorRoute('files/get'), {
|
|
247
|
+
room_ref: entry.authority,
|
|
248
|
+
path: request.path,
|
|
249
|
+
revision: request.revision,
|
|
250
|
+
});
|
|
258
251
|
}
|
|
259
252
|
return {
|
|
253
|
+
roomTypes: HOUSE_ROOM_TYPES,
|
|
254
|
+
nameableRoomTypes: HOUSE_NAMEABLE_ROOM_TYPES,
|
|
260
255
|
async authorize() {
|
|
261
256
|
return options.authorize();
|
|
262
257
|
},
|
|
263
|
-
async discover(_authorization) {
|
|
264
|
-
const answer = await transport.door(
|
|
258
|
+
async discover(_authorization, scope) {
|
|
259
|
+
const answer = await transport.door(houseDoorRoute('discover'), {
|
|
260
|
+
prefixes: [...DISCOVER_PREFIXES],
|
|
261
|
+
...(scope === undefined ? {} : { rooms: scope.authorities }),
|
|
262
|
+
});
|
|
265
263
|
const failed = discoveryFailure(answer.status, answer.body);
|
|
266
264
|
if (failed !== null)
|
|
267
265
|
return failed;
|
|
@@ -270,62 +268,68 @@ export function createAgentsHouseAdapter(options) {
|
|
|
270
268
|
return { kind: 'transport-uncertainty' };
|
|
271
269
|
bounds = document.bounds;
|
|
272
270
|
const entries = trackedFrom(document);
|
|
273
|
-
|
|
271
|
+
const covered = scope === undefined
|
|
272
|
+
? null
|
|
273
|
+
: [
|
|
274
|
+
...new Set([
|
|
275
|
+
...[...tracked.values()]
|
|
276
|
+
.filter((entry) => scope.authorities.includes(entry.authority))
|
|
277
|
+
.map((entry) => entry.locator),
|
|
278
|
+
...entries.map((entry) => entry.locator),
|
|
279
|
+
]),
|
|
280
|
+
];
|
|
281
|
+
if (covered === null)
|
|
282
|
+
tracked.clear();
|
|
283
|
+
else
|
|
284
|
+
for (const locator of covered)
|
|
285
|
+
tracked.delete(locator);
|
|
274
286
|
for (const entry of entries)
|
|
275
287
|
tracked.set(entry.locator, entry);
|
|
276
|
-
|
|
277
|
-
if (!tracked.has(locator))
|
|
278
|
-
handed.delete(locator);
|
|
279
|
-
notifications.track(entries);
|
|
288
|
+
notifications.track([...tracked.values()]);
|
|
280
289
|
return {
|
|
281
|
-
...mapDiscovery(document, document.authorities.map((authority) => discoveredRoom(authority)), capabilities(document)),
|
|
290
|
+
...mapDiscovery(document, document.authorities.map((authority) => discoveredRoom(authority)), capabilities(document.actions)),
|
|
282
291
|
member: document.member,
|
|
292
|
+
...(covered === null ? {} : { covered }),
|
|
283
293
|
};
|
|
284
294
|
},
|
|
285
295
|
async bootstrap(_authorization, request) {
|
|
286
296
|
const entry = authorityFor(request.locator);
|
|
287
297
|
if (entry === undefined)
|
|
288
298
|
return { kind: 'room-not-readable', locator: request.locator };
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
return bootstrapTogether(entry);
|
|
293
|
-
},
|
|
294
|
-
async refresh(_authorization, request) {
|
|
295
|
-
const entry = authorityFor(request.locator);
|
|
296
|
-
if (entry === undefined)
|
|
297
|
-
return { kind: 'room-not-readable', locator: request.locator };
|
|
298
|
-
if (request.pageToken !== undefined)
|
|
299
|
-
return { kind: 'transport-uncertainty' };
|
|
300
|
-
const from = logPosition(request.fromRoomVersion);
|
|
301
|
-
if (from === null)
|
|
302
|
-
return { kind: 'beyond-horizon' };
|
|
303
|
-
const answered = await streamed(entry, request, from);
|
|
304
|
-
if (answered !== null)
|
|
305
|
-
return answered;
|
|
306
|
-
return enumerateTogether({ entry, from, fromRoomVersion: request.fromRoomVersion });
|
|
299
|
+
return request.pageToken === undefined
|
|
300
|
+
? openedRoom(entry, request)
|
|
301
|
+
: streamedPage(entry, request.pageToken, request.held);
|
|
307
302
|
},
|
|
308
303
|
async mutate(_authorization, request, keep) {
|
|
309
304
|
const entry = authorityFor(request.locator);
|
|
310
305
|
if (entry === undefined)
|
|
311
306
|
return { kind: 'room-not-readable', locator: request.locator };
|
|
312
|
-
const
|
|
307
|
+
const batch = request.changeBatch;
|
|
308
|
+
const destination = batch.between?.destination.room;
|
|
309
|
+
const into = destination === undefined ? undefined : authorityFor(destination);
|
|
310
|
+
if (destination !== undefined && into === undefined) {
|
|
311
|
+
return { kind: 'room-not-readable', locator: destination };
|
|
312
|
+
}
|
|
313
|
+
const mapped = into === undefined
|
|
314
|
+
? houseChanges(batch, entry.handle)
|
|
315
|
+
: houseMove(batch, entry.handle, into.handle);
|
|
313
316
|
if (mapped.kind === 'unmapped')
|
|
314
317
|
return rejectedMapping(mapped);
|
|
315
|
-
const head = operationHead(
|
|
318
|
+
const head = operationHead(batch);
|
|
316
319
|
const delta = deltaBody(mapped.changes);
|
|
317
320
|
const bytes = deltaBytes(delta);
|
|
318
321
|
const handed = request.upload;
|
|
319
|
-
const
|
|
322
|
+
const answer = (upload) => submit(request, head, mapped.changes, delta, bytes, entry, upload, keep, into);
|
|
323
|
+
const outcome = await answer(handed);
|
|
320
324
|
if (handed === undefined || !lapsedGrant(outcome))
|
|
321
|
-
return
|
|
322
|
-
return
|
|
325
|
+
return outcome;
|
|
326
|
+
return answer(undefined);
|
|
323
327
|
},
|
|
324
328
|
async history(_authorization, request) {
|
|
325
329
|
const entry = authorityFor(request.locator);
|
|
326
330
|
if (entry === undefined)
|
|
327
331
|
return { kind: 'room-not-readable', locator: request.locator };
|
|
328
|
-
const answer = await transport.door(
|
|
332
|
+
const answer = await transport.door(houseDoorRoute('revisions'), revisionBody({
|
|
329
333
|
path: request.path,
|
|
330
334
|
handle: entry.handle,
|
|
331
335
|
...(request.revision === undefined ? {} : { revision: request.revision }),
|
|
@@ -336,6 +340,47 @@ export function createAgentsHouseAdapter(options) {
|
|
|
336
340
|
...(request.revision === undefined ? {} : { revision: request.revision }),
|
|
337
341
|
});
|
|
338
342
|
},
|
|
343
|
+
async binary(_authorization, request) {
|
|
344
|
+
const ask = pathAddress(request);
|
|
345
|
+
if (ask === null)
|
|
346
|
+
return { kind: 'room-not-readable', locator: request.locator };
|
|
347
|
+
return downloaded(ask, request.locator);
|
|
348
|
+
},
|
|
349
|
+
async link(_authorization, request) {
|
|
350
|
+
const ask = pathAddress(request);
|
|
351
|
+
if (ask === null)
|
|
352
|
+
return { kind: 'room-not-readable', locator: request.locator };
|
|
353
|
+
return linked(await ask(), request.locator);
|
|
354
|
+
},
|
|
355
|
+
async attach(_authorization, request) {
|
|
356
|
+
const entry = authorityFor(request.locator);
|
|
357
|
+
if (entry === undefined)
|
|
358
|
+
return { kind: 'room-not-readable', locator: request.locator };
|
|
359
|
+
const declared = await transport.door(houseDoorRoute('files/attach'), {
|
|
360
|
+
room_ref: entry.authority,
|
|
361
|
+
name: request.name,
|
|
362
|
+
bytes: request.byteLength,
|
|
363
|
+
sha256: request.sha256,
|
|
364
|
+
media_type: request.mediaType.length > 0 ? request.mediaType : OCTET_STREAM,
|
|
365
|
+
});
|
|
366
|
+
const refused = refusedAttach(declared, request.locator);
|
|
367
|
+
if (refused !== null)
|
|
368
|
+
return refused;
|
|
369
|
+
const upload = houseUpload(declared.body?.upload);
|
|
370
|
+
if (upload === null)
|
|
371
|
+
return UNCERTAIN_READ;
|
|
372
|
+
const finished = await finishedUpload(transport, upload, request.blob);
|
|
373
|
+
if (finished === null)
|
|
374
|
+
return UNCERTAIN_READ;
|
|
375
|
+
const unfinished = refusedAttach(finished, request.locator);
|
|
376
|
+
if (unfinished !== null)
|
|
377
|
+
return unfinished;
|
|
378
|
+
const { path, revision } = (finished.body ?? {});
|
|
379
|
+
const root = `/rooms/${entry.handle}/`;
|
|
380
|
+
return typeof path === 'string' && path.startsWith(root) && typeof revision === 'string'
|
|
381
|
+
? { kind: 'attached', path: path.slice(root.length), revision }
|
|
382
|
+
: UNCERTAIN_READ;
|
|
383
|
+
},
|
|
339
384
|
reached(locator, version, target) {
|
|
340
385
|
return reachedVersion(version, target, authorityFor(locator)?.logEpoch);
|
|
341
386
|
},
|
|
@@ -345,7 +390,11 @@ export function createAgentsHouseAdapter(options) {
|
|
|
345
390
|
async action(_authorization, request) {
|
|
346
391
|
return withAt(await houseAction(transport, request));
|
|
347
392
|
},
|
|
348
|
-
roomStream: {
|
|
393
|
+
roomStream: {
|
|
394
|
+
hold: held,
|
|
395
|
+
reopen: notifications.reopen,
|
|
396
|
+
observe: notifications.observe,
|
|
397
|
+
},
|
|
349
398
|
conversations: notifications.conversations,
|
|
350
399
|
serviceMode: notifications.serviceMode,
|
|
351
400
|
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app-reference.d.ts","sourceRoot":"","sources":["../src/app-reference.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,qBAAqB,QAEjC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crockford.d.ts","sourceRoot":"","sources":["../src/crockford.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,kBAAkB,qCAAqC,CAAC;AAErE,eAAO,MAAM,mBAAmB,uCAA4B,CAAC;AAE7D,eAAO,MAAM,2BAA2B,QAAqC,CAAC"}
|