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