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