@agentshouse/mdruntime-adapter 0.1.0-cf6a7cf003c4b → 0.1.0-cf75412975d5b
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 -43
- package/dist/actions.d.ts.map +1 -1
- package/dist/actions.js +8 -295
- package/dist/adapter.d.ts +1 -0
- package/dist/adapter.d.ts.map +1 -1
- package/dist/adapter.js +291 -230
- 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 +23 -12
- package/dist/discovery.d.ts.map +1 -1
- package/dist/discovery.js +94 -50
- 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 +911 -0
- package/dist/house-model.d.ts.map +1 -0
- package/dist/house-model.js +496 -0
- package/dist/index.d.ts +11 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -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 +54 -44
- package/dist/read.d.ts.map +1 -1
- package/dist/read.js +271 -214
- 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 +20 -0
- package/dist/room-stream.d.ts.map +1 -0
- package/dist/room-stream.js +78 -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 -104
- package/dist/stream.d.ts +12 -12
- package/dist/stream.d.ts.map +1 -1
- package/dist/stream.js +207 -151
- package/dist/transport.d.ts +91 -20
- 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 +19 -10
- package/dist/write.d.ts.map +1 -1
- package/dist/write.js +142 -54
- 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,141 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { gathered, gatheredEach, 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 && file.outline === 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
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
notifications.
|
|
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 };
|
|
67
|
+
function opened(frame) {
|
|
68
|
+
const document = parseDiscovery(frame);
|
|
69
|
+
if (document === null)
|
|
70
|
+
return null;
|
|
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()]);
|
|
46
76
|
return {
|
|
47
|
-
...
|
|
48
|
-
|
|
49
|
-
...(continuation === null ? {} : { pageToken: JSON.stringify(continuation) }),
|
|
77
|
+
...mapDiscovery(document, document.authorities.map((authority) => discoveredRoom(authority)), capabilities(document.actions)),
|
|
78
|
+
member: document.member,
|
|
50
79
|
};
|
|
51
80
|
}
|
|
52
|
-
|
|
53
|
-
const
|
|
54
|
-
if (
|
|
55
|
-
return { kind: 'room-not-readable', locator: ask.entry.locator };
|
|
56
|
-
notifications.reach(ask.entry.authority, view);
|
|
57
|
-
const answer = refreshFrom(view, ask.fromRoomVersion);
|
|
58
|
-
if (answer.kind !== 'net-advancement')
|
|
59
|
-
return answer;
|
|
60
|
-
handed.set(ask.entry.locator, enumeratedLinks(view, handed.get(ask.entry.locator)));
|
|
61
|
-
const models = await viewModels(view, ask.entry);
|
|
62
|
-
return models === undefined ? answer : { ...answer, models };
|
|
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)
|
|
81
|
+
function changed(frame) {
|
|
82
|
+
const change = parseAuthorityChange(frame);
|
|
83
|
+
if (change === null)
|
|
70
84
|
return null;
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
return
|
|
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 namedDocument(entry, path, signal) {
|
|
114
|
+
const answered = await transport.streamed(houseDoorRoute('bootstrap'), { room_ref: entry.authority, prefixes: [`${authorityRoot(entry.handle)}/${path}`] }, signal ?? new AbortController().signal);
|
|
115
|
+
if (answered.room === null)
|
|
116
|
+
return classifyDoor(answered.status, answered.body, entry.locator) ?? UNCERTAIN_READ;
|
|
117
|
+
return rooms.named(entry, answered.room, signal);
|
|
118
|
+
}
|
|
119
|
+
async function openedRoom(entry, request) {
|
|
120
|
+
const reusing = unreused.delete(entry.locator) ? undefined : request.held;
|
|
121
|
+
const files = reusing === undefined
|
|
122
|
+
? null
|
|
123
|
+
: await heldFiles(entry.authority, reusing, declared().max_held_files);
|
|
124
|
+
const answered = await transport.streamed(houseDoorRoute('bootstrap'), {
|
|
125
|
+
room_ref: entry.authority,
|
|
126
|
+
prefixes: [authorityRoot(entry.handle)],
|
|
127
|
+
...(files === null ? {} : { held: [files] }),
|
|
128
|
+
}, request.signal ?? new AbortController().signal);
|
|
129
|
+
if (answered.room === null)
|
|
130
|
+
return classifyDoor(answered.status, answered.body, entry.locator) ?? UNCERTAIN_READ;
|
|
131
|
+
return rooms.head(entry, answered.room, request.arriving, request.signal);
|
|
77
132
|
}
|
|
78
133
|
function held(rooms) {
|
|
79
|
-
|
|
134
|
+
notifications.send(rooms.flatMap((room) => {
|
|
80
135
|
const entry = tracked.get(room.locator);
|
|
81
136
|
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 };
|
|
137
|
+
return entry === undefined || at === null ? [] : [presented(entry.authority, at)];
|
|
138
|
+
}));
|
|
106
139
|
}
|
|
107
140
|
function authorityFor(locator) {
|
|
108
141
|
return tracked.get(locator);
|
|
@@ -121,114 +154,25 @@ export function createAgentsHouseAdapter(options) {
|
|
|
121
154
|
return result;
|
|
122
155
|
return { ...result, at: { locator, roomVersion: roomVersion({ position, logEpoch }) } };
|
|
123
156
|
}
|
|
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
157
|
async function uploaded(head, changes, delta, bytes, entry, handed, keep) {
|
|
221
158
|
const paths = batchPaths(changes);
|
|
222
159
|
const sha256 = await deltaDigest(delta);
|
|
223
160
|
if (handed !== undefined && handed.sha256 === sha256) {
|
|
224
161
|
return { body: { ...head, paths, upload: handed, runtime: notifications.runtime } };
|
|
225
162
|
}
|
|
226
|
-
const minted = await transport.door(
|
|
163
|
+
const minted = await transport.door(houseDoorRoute('batch-upload'), {
|
|
164
|
+
...head,
|
|
165
|
+
paths,
|
|
166
|
+
bytes,
|
|
167
|
+
sha256,
|
|
168
|
+
});
|
|
227
169
|
if (minted.status !== 200) {
|
|
228
|
-
return {
|
|
170
|
+
return {
|
|
171
|
+
outcome: await stepped(mutationOutcome(minted.status, minted.body, entry.locator, entry.handle)),
|
|
172
|
+
};
|
|
229
173
|
}
|
|
230
174
|
const grant = parseGrant(minted.body);
|
|
231
|
-
if (grant === null
|
|
175
|
+
if (grant === null)
|
|
232
176
|
return { outcome: UNCERTAIN };
|
|
233
177
|
const delivered = await transport.transfer({
|
|
234
178
|
act: 'authored_batch_upload',
|
|
@@ -238,92 +182,162 @@ export function createAgentsHouseAdapter(options) {
|
|
|
238
182
|
if (delivered.status !== 200)
|
|
239
183
|
return { outcome: UNCERTAIN };
|
|
240
184
|
const holding = parseGrant(delivered.body);
|
|
241
|
-
if (holding === null
|
|
185
|
+
if (holding === null)
|
|
242
186
|
return { outcome: UNCERTAIN };
|
|
243
187
|
const upload = { url: holding.url, operation: holding.operation, bytes, sha256 };
|
|
244
188
|
await keep(upload);
|
|
245
189
|
return { body: { ...head, paths, upload, runtime: notifications.runtime } };
|
|
246
190
|
}
|
|
247
|
-
async function
|
|
191
|
+
async function staged(answer, { changeBatch: batch, file }) {
|
|
192
|
+
const refusal = doorRefusal(answer.body);
|
|
193
|
+
if (refusal?.code !== 'files_unstaged')
|
|
194
|
+
return null;
|
|
195
|
+
const uploads = refusal.fields.upload;
|
|
196
|
+
if (!Array.isArray(uploads))
|
|
197
|
+
return false;
|
|
198
|
+
for (const named of uploads) {
|
|
199
|
+
const upload = houseUpload(named);
|
|
200
|
+
const change = batch.changes.find((held) => held.kind === 'binary' && held.sha256 === named.sha256);
|
|
201
|
+
if (upload === null || change === undefined)
|
|
202
|
+
return false;
|
|
203
|
+
const finished = await finishedUpload(transport, upload, await file(change.localId));
|
|
204
|
+
if (finished?.body?.staged !== true)
|
|
205
|
+
return false;
|
|
206
|
+
}
|
|
207
|
+
return true;
|
|
208
|
+
}
|
|
209
|
+
async function submit(request, head, changes, delta, bytes, entry, handed, keep, into) {
|
|
248
210
|
const inline = { ...head, changes, runtime: notifications.runtime };
|
|
249
211
|
const prepared = uploadable(inline, bytes, declared())
|
|
250
212
|
? await uploaded(head, changes, delta, bytes, entry, handed, keep)
|
|
251
213
|
: { body: inline };
|
|
252
214
|
if ('outcome' in prepared)
|
|
253
215
|
return prepared.outcome;
|
|
254
|
-
const answer = await transport.door(
|
|
255
|
-
|
|
216
|
+
const answer = await transport.door(houseDoorRoute('batch'), prepared.body);
|
|
217
|
+
const stagedFiles = await staged(answer, request);
|
|
218
|
+
if (stagedFiles === false)
|
|
219
|
+
return UNCERTAIN;
|
|
220
|
+
const settled = stagedFiles === null ? answer : await transport.door(houseDoorRoute('batch'), prepared.body);
|
|
221
|
+
if (stagedFiles === true && (await staged(settled, request)) !== null)
|
|
222
|
+
return UNCERTAIN;
|
|
223
|
+
return stepped(mutationOutcome(settled.status, settled.body, entry.locator, entry.handle, into === undefined
|
|
224
|
+
? undefined
|
|
225
|
+
: {
|
|
226
|
+
source: { authority: entry.authority, handle: entry.handle },
|
|
227
|
+
destination: { authority: into.authority, handle: into.handle },
|
|
228
|
+
}));
|
|
229
|
+
}
|
|
230
|
+
function addressed(answer, locator) {
|
|
231
|
+
const failure = classifyDoor(answer.status, answer.body, locator);
|
|
232
|
+
if (failure !== null) {
|
|
233
|
+
return failure.kind === 'scoped-refusal'
|
|
234
|
+
? {
|
|
235
|
+
kind: 'rejected',
|
|
236
|
+
code: failure.code,
|
|
237
|
+
fields: failure.fields,
|
|
238
|
+
retryable: failure.retryable,
|
|
239
|
+
}
|
|
240
|
+
: failure;
|
|
241
|
+
}
|
|
242
|
+
const address = parseFileAddress(answer.body);
|
|
243
|
+
return address === null ? UNCERTAIN_READ : { kind: 'address', ...address };
|
|
244
|
+
}
|
|
245
|
+
function linked(answer, locator) {
|
|
246
|
+
const address = addressed(answer, locator);
|
|
247
|
+
return address.kind === 'address'
|
|
248
|
+
? { kind: 'link', url: address.url, name: address.name, mediaType: address.mediaType }
|
|
249
|
+
: address;
|
|
250
|
+
}
|
|
251
|
+
function refusedAttach(answer, locator) {
|
|
252
|
+
const failure = classifyDoor(answer.status, answer.body, locator);
|
|
253
|
+
if (failure === null)
|
|
254
|
+
return null;
|
|
255
|
+
return failure.kind === 'scoped-refusal'
|
|
256
|
+
? {
|
|
257
|
+
kind: 'rejected',
|
|
258
|
+
code: failure.code,
|
|
259
|
+
fields: failure.fields,
|
|
260
|
+
retryable: failure.retryable,
|
|
261
|
+
}
|
|
262
|
+
: failure;
|
|
263
|
+
}
|
|
264
|
+
async function downloaded(ask, locator) {
|
|
265
|
+
let address = addressed(await ask(), locator);
|
|
266
|
+
if (address.kind !== 'address')
|
|
267
|
+
return address;
|
|
268
|
+
let read = await transport.download(address.url);
|
|
269
|
+
if (read.status === EXPIRED_ADDRESS) {
|
|
270
|
+
address = addressed(await ask(), locator);
|
|
271
|
+
if (address.kind !== 'address')
|
|
272
|
+
return address;
|
|
273
|
+
read = await transport.download(address.url);
|
|
274
|
+
}
|
|
275
|
+
if (read.status !== 200 || read.body === null)
|
|
276
|
+
return UNCERTAIN_READ;
|
|
277
|
+
return {
|
|
278
|
+
kind: 'binary',
|
|
279
|
+
body: read.body,
|
|
280
|
+
mediaType: address.mediaType,
|
|
281
|
+
sha256: address.sha256,
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
function pathAddress(request) {
|
|
285
|
+
const entry = authorityFor(request.locator);
|
|
286
|
+
if (entry === undefined)
|
|
287
|
+
return null;
|
|
288
|
+
return () => transport.door(houseDoorRoute('files/get'), {
|
|
289
|
+
room_ref: entry.authority,
|
|
290
|
+
path: request.path,
|
|
291
|
+
revision: request.revision,
|
|
292
|
+
});
|
|
256
293
|
}
|
|
257
294
|
return {
|
|
295
|
+
roomTypes: HOUSE_ROOM_TYPES,
|
|
296
|
+
nameableRoomTypes: HOUSE_NAMEABLE_ROOM_TYPES,
|
|
297
|
+
typedPaths: HOUSE_TYPED_PATHS,
|
|
258
298
|
async authorize() {
|
|
259
299
|
return options.authorize();
|
|
260
300
|
},
|
|
261
|
-
async discover(_authorization) {
|
|
262
|
-
const answer = await transport.door(DOOR_DISCOVER, { prefixes: [...DISCOVER_PREFIXES] });
|
|
263
|
-
const failed = discoveryFailure(answer.status, answer.body);
|
|
264
|
-
if (failed !== null)
|
|
265
|
-
return failed;
|
|
266
|
-
const document = parseDiscovery(answer.body);
|
|
267
|
-
if (document === null)
|
|
268
|
-
return { kind: 'transport-uncertainty' };
|
|
269
|
-
bounds = document.bounds;
|
|
270
|
-
const entries = trackedFrom(document);
|
|
271
|
-
tracked.clear();
|
|
272
|
-
for (const entry of entries)
|
|
273
|
-
tracked.set(entry.locator, entry);
|
|
274
|
-
for (const locator of handed.keys())
|
|
275
|
-
if (!tracked.has(locator))
|
|
276
|
-
handed.delete(locator);
|
|
277
|
-
notifications.track(entries);
|
|
278
|
-
return {
|
|
279
|
-
...mapDiscovery(document, document.authorities.map((authority) => discoveredRoom(authority)), capabilities(document)),
|
|
280
|
-
member: document.member,
|
|
281
|
-
};
|
|
282
|
-
},
|
|
283
301
|
async bootstrap(_authorization, request) {
|
|
284
302
|
const entry = authorityFor(request.locator);
|
|
285
303
|
if (entry === undefined)
|
|
286
304
|
return { kind: 'room-not-readable', locator: request.locator };
|
|
287
|
-
if (request.
|
|
288
|
-
return
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
async refresh(_authorization, request) {
|
|
293
|
-
const entry = authorityFor(request.locator);
|
|
294
|
-
if (entry === undefined)
|
|
295
|
-
return { kind: 'room-not-readable', locator: request.locator };
|
|
296
|
-
if (request.pageToken !== undefined)
|
|
297
|
-
return { kind: 'transport-uncertainty' };
|
|
298
|
-
const from = logPosition(request.fromRoomVersion);
|
|
299
|
-
if (from === null)
|
|
300
|
-
return { kind: 'beyond-horizon' };
|
|
301
|
-
const answered = await streamed(entry, request, from);
|
|
302
|
-
if (answered !== null)
|
|
303
|
-
return answered;
|
|
304
|
-
return enumerateTogether({ entry, from, fromRoomVersion: request.fromRoomVersion });
|
|
305
|
+
if (request.path !== undefined)
|
|
306
|
+
return namedDocument(entry, request.path, request.signal);
|
|
307
|
+
return request.pageToken === undefined
|
|
308
|
+
? openedRoom(entry, request)
|
|
309
|
+
: streamedPage(entry, request.pageToken, request.held);
|
|
305
310
|
},
|
|
306
311
|
async mutate(_authorization, request, keep) {
|
|
307
312
|
const entry = authorityFor(request.locator);
|
|
308
313
|
if (entry === undefined)
|
|
309
314
|
return { kind: 'room-not-readable', locator: request.locator };
|
|
310
|
-
const
|
|
315
|
+
const batch = request.changeBatch;
|
|
316
|
+
const destination = batch.between?.destination.room;
|
|
317
|
+
const into = destination === undefined ? undefined : authorityFor(destination);
|
|
318
|
+
if (destination !== undefined && into === undefined) {
|
|
319
|
+
return { kind: 'room-not-readable', locator: destination };
|
|
320
|
+
}
|
|
321
|
+
const mapped = into === undefined
|
|
322
|
+
? houseChanges(batch, entry.handle)
|
|
323
|
+
: houseMove(batch, entry.handle, into.handle);
|
|
311
324
|
if (mapped.kind === 'unmapped')
|
|
312
325
|
return rejectedMapping(mapped);
|
|
313
|
-
const head = operationHead(
|
|
326
|
+
const head = operationHead(batch, entry.authority);
|
|
314
327
|
const delta = deltaBody(mapped.changes);
|
|
315
328
|
const bytes = deltaBytes(delta);
|
|
316
329
|
const handed = request.upload;
|
|
317
|
-
const
|
|
330
|
+
const answer = (upload) => submit(request, head, mapped.changes, delta, bytes, entry, upload, keep, into);
|
|
331
|
+
const outcome = await answer(handed);
|
|
318
332
|
if (handed === undefined || !lapsedGrant(outcome))
|
|
319
|
-
return
|
|
320
|
-
return
|
|
333
|
+
return outcome;
|
|
334
|
+
return answer(undefined);
|
|
321
335
|
},
|
|
322
336
|
async history(_authorization, request) {
|
|
323
337
|
const entry = authorityFor(request.locator);
|
|
324
338
|
if (entry === undefined)
|
|
325
339
|
return { kind: 'room-not-readable', locator: request.locator };
|
|
326
|
-
const answer = await transport.door(
|
|
340
|
+
const answer = await transport.door(houseDoorRoute('revisions'), revisionBody({
|
|
327
341
|
path: request.path,
|
|
328
342
|
handle: entry.handle,
|
|
329
343
|
...(request.revision === undefined ? {} : { revision: request.revision }),
|
|
@@ -334,6 +348,47 @@ export function createAgentsHouseAdapter(options) {
|
|
|
334
348
|
...(request.revision === undefined ? {} : { revision: request.revision }),
|
|
335
349
|
});
|
|
336
350
|
},
|
|
351
|
+
async binary(_authorization, request) {
|
|
352
|
+
const ask = pathAddress(request);
|
|
353
|
+
if (ask === null)
|
|
354
|
+
return { kind: 'room-not-readable', locator: request.locator };
|
|
355
|
+
return downloaded(ask, request.locator);
|
|
356
|
+
},
|
|
357
|
+
async link(_authorization, request) {
|
|
358
|
+
const ask = pathAddress(request);
|
|
359
|
+
if (ask === null)
|
|
360
|
+
return { kind: 'room-not-readable', locator: request.locator };
|
|
361
|
+
return linked(await ask(), request.locator);
|
|
362
|
+
},
|
|
363
|
+
async attach(_authorization, request) {
|
|
364
|
+
const entry = authorityFor(request.locator);
|
|
365
|
+
if (entry === undefined)
|
|
366
|
+
return { kind: 'room-not-readable', locator: request.locator };
|
|
367
|
+
const declared = await transport.door(houseDoorRoute('files/attach'), {
|
|
368
|
+
room_ref: entry.authority,
|
|
369
|
+
name: request.name,
|
|
370
|
+
bytes: request.byteLength,
|
|
371
|
+
sha256: request.sha256,
|
|
372
|
+
media_type: request.mediaType.length > 0 ? request.mediaType : OCTET_STREAM,
|
|
373
|
+
});
|
|
374
|
+
const refused = refusedAttach(declared, request.locator);
|
|
375
|
+
if (refused !== null)
|
|
376
|
+
return refused;
|
|
377
|
+
const upload = houseUpload(declared.body?.upload);
|
|
378
|
+
if (upload === null)
|
|
379
|
+
return UNCERTAIN_READ;
|
|
380
|
+
const finished = await finishedUpload(transport, upload, request.blob);
|
|
381
|
+
if (finished === null)
|
|
382
|
+
return UNCERTAIN_READ;
|
|
383
|
+
const unfinished = refusedAttach(finished, request.locator);
|
|
384
|
+
if (unfinished !== null)
|
|
385
|
+
return unfinished;
|
|
386
|
+
const { path, revision } = (finished.body ?? {});
|
|
387
|
+
const root = `/rooms/${entry.handle}/`;
|
|
388
|
+
return typeof path === 'string' && path.startsWith(root) && typeof revision === 'string'
|
|
389
|
+
? { kind: 'attached', path: path.slice(root.length), revision }
|
|
390
|
+
: UNCERTAIN_READ;
|
|
391
|
+
},
|
|
337
392
|
reached(locator, version, target) {
|
|
338
393
|
return reachedVersion(version, target, authorityFor(locator)?.logEpoch);
|
|
339
394
|
},
|
|
@@ -343,7 +398,13 @@ export function createAgentsHouseAdapter(options) {
|
|
|
343
398
|
async action(_authorization, request) {
|
|
344
399
|
return withAt(await houseAction(transport, request));
|
|
345
400
|
},
|
|
346
|
-
|
|
401
|
+
search(_authorization, request) {
|
|
402
|
+
return houseSearch(transport, [...tracked.values()], request);
|
|
403
|
+
},
|
|
404
|
+
roomStream: {
|
|
405
|
+
hold: held,
|
|
406
|
+
observe: notifications.observe,
|
|
407
|
+
},
|
|
347
408
|
conversations: notifications.conversations,
|
|
348
409
|
serviceMode: notifications.serviceMode,
|
|
349
410
|
};
|
|
@@ -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"}
|