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