@ifc-lite/collab-server 0.2.5 → 0.3.0
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/dist/auth.d.ts +2 -0
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +3 -1
- package/dist/auth.js.map +1 -1
- package/dist/bin.js +102 -2
- package/dist/bin.js.map +1 -1
- package/dist/blob-route.d.ts +24 -0
- package/dist/blob-route.d.ts.map +1 -1
- package/dist/blob-route.js +82 -0
- package/dist/blob-route.js.map +1 -1
- package/dist/index.d.ts +5 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/layer-registry-route.d.ts +46 -0
- package/dist/layer-registry-route.d.ts.map +1 -0
- package/dist/layer-registry-route.js +503 -0
- package/dist/layer-registry-route.js.map +1 -0
- package/dist/layer-registry.d.ts +69 -0
- package/dist/layer-registry.d.ts.map +1 -0
- package/dist/layer-registry.js +119 -0
- package/dist/layer-registry.js.map +1 -0
- package/dist/room-manager.d.ts +16 -0
- package/dist/room-manager.d.ts.map +1 -1
- package/dist/room-manager.js +29 -0
- package/dist/room-manager.js.map +1 -1
- package/dist/room-token.d.ts +160 -0
- package/dist/room-token.d.ts.map +1 -0
- package/dist/room-token.js +387 -0
- package/dist/room-token.js.map +1 -0
- package/dist/server.d.ts +60 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +130 -0
- package/dist/server.js.map +1 -1
- package/package.json +4 -2
|
@@ -0,0 +1,503 @@
|
|
|
1
|
+
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
4
|
+
import * as crypto from 'node:crypto';
|
|
5
|
+
import { getProvenance } from '@ifc-lite/ifcx';
|
|
6
|
+
import { mergeIntoRef } from '@ifc-lite/merge';
|
|
7
|
+
import { ANONYMOUS_USER_ID } from './auth.js';
|
|
8
|
+
import { LayerPushError, } from './layer-registry.js';
|
|
9
|
+
const DEFAULT_MAX_BYTES = 50 * 1024 * 1024;
|
|
10
|
+
const BASE = '/api/v1/';
|
|
11
|
+
/**
|
|
12
|
+
* Registry credentials are `Authorization: Bearer` ONLY — no `?token=`
|
|
13
|
+
* fallback. A query-string secret leaks via access logs, reverse proxies,
|
|
14
|
+
* traces, and copied URLs (same reasoning as the /metrics endpoint; the
|
|
15
|
+
* websocket path keeps `?token=` only because browsers cannot set
|
|
16
|
+
* handshake headers, which does not apply to registry API clients).
|
|
17
|
+
*/
|
|
18
|
+
function extractToken(req) {
|
|
19
|
+
const header = req.headers['authorization'];
|
|
20
|
+
if (typeof header === 'string') {
|
|
21
|
+
const m = /^Bearer\s+(.+)$/i.exec(header.trim());
|
|
22
|
+
if (m)
|
|
23
|
+
return m[1];
|
|
24
|
+
}
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
function json(res, status, body) {
|
|
28
|
+
res.writeHead(status, { 'content-type': 'application/json' });
|
|
29
|
+
res.end(JSON.stringify(body));
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
async function readBody(req, maxBytes) {
|
|
33
|
+
const chunks = [];
|
|
34
|
+
let total = 0;
|
|
35
|
+
for await (const chunk of req) {
|
|
36
|
+
const buf = chunk;
|
|
37
|
+
total += buf.byteLength;
|
|
38
|
+
if (total > maxBytes)
|
|
39
|
+
return null;
|
|
40
|
+
chunks.push(buf);
|
|
41
|
+
}
|
|
42
|
+
return Buffer.concat(chunks).toString('utf-8');
|
|
43
|
+
}
|
|
44
|
+
/** Parse JSON, surfacing the failure reason instead of swallowing it. */
|
|
45
|
+
function parseJson(text) {
|
|
46
|
+
try {
|
|
47
|
+
return { value: JSON.parse(text) };
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
return { error: err instanceof Error ? err.message : String(err) };
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Map a store-thrown `LayerPushError` onto the HTTP surface: capacity
|
|
55
|
+
* exhaustion is 507, integrity/conflict gates are 409. Every route that
|
|
56
|
+
* writes through the store (push, ref PUT, merge, reviews) must route
|
|
57
|
+
* through this — an unwrapped throw becomes a bare 500.
|
|
58
|
+
*/
|
|
59
|
+
function handlePushError(res, err) {
|
|
60
|
+
if (err instanceof LayerPushError) {
|
|
61
|
+
return json(res, err.code === 'registry-full' ? 507 : 409, { error: err.message, code: err.code });
|
|
62
|
+
}
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
/** Runtime shape validation for ref policies; undefined = invalid. */
|
|
66
|
+
function parseRefPolicy(value) {
|
|
67
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value))
|
|
68
|
+
return undefined;
|
|
69
|
+
const raw = value;
|
|
70
|
+
const policy = {};
|
|
71
|
+
if (raw.requireHumanApproval !== undefined) {
|
|
72
|
+
if (typeof raw.requireHumanApproval !== 'boolean')
|
|
73
|
+
return undefined;
|
|
74
|
+
policy.requireHumanApproval = raw.requireHumanApproval;
|
|
75
|
+
}
|
|
76
|
+
if (raw.requiredChecks !== undefined) {
|
|
77
|
+
if (!Array.isArray(raw.requiredChecks) || !raw.requiredChecks.every((c) => typeof c === 'string')) {
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
80
|
+
policy.requiredChecks = raw.requiredChecks;
|
|
81
|
+
}
|
|
82
|
+
return policy;
|
|
83
|
+
}
|
|
84
|
+
/** Runtime shape validation for review decisions; undefined = invalid. */
|
|
85
|
+
function parseReviewDecision(value) {
|
|
86
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value))
|
|
87
|
+
return undefined;
|
|
88
|
+
const raw = value;
|
|
89
|
+
if (typeof raw.entity !== 'string' || (raw.decision !== 'accept' && raw.decision !== 'reject')) {
|
|
90
|
+
return undefined;
|
|
91
|
+
}
|
|
92
|
+
const decision = { entity: raw.entity, decision: raw.decision };
|
|
93
|
+
if (raw.componentKey !== undefined) {
|
|
94
|
+
if (typeof raw.componentKey !== 'string')
|
|
95
|
+
return undefined;
|
|
96
|
+
decision.componentKey = raw.componentKey;
|
|
97
|
+
}
|
|
98
|
+
if (raw.comment !== undefined) {
|
|
99
|
+
if (typeof raw.comment !== 'string')
|
|
100
|
+
return undefined;
|
|
101
|
+
decision.comment = raw.comment;
|
|
102
|
+
}
|
|
103
|
+
return decision;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Handle a registry request. Returns false (untouched response) when the
|
|
107
|
+
* path is not a registry path, true when a response was written.
|
|
108
|
+
*/
|
|
109
|
+
export async function handleLayerRegistryRequest(req, res, opts) {
|
|
110
|
+
const url = new URL(req.url ?? '/', 'http://localhost');
|
|
111
|
+
if (!url.pathname.startsWith(BASE))
|
|
112
|
+
return false;
|
|
113
|
+
const rawSegments = url.pathname.slice(BASE.length).split('/').filter(Boolean);
|
|
114
|
+
const [head] = rawSegments;
|
|
115
|
+
if (head !== 'layers' && head !== 'refs' && head !== 'reviews')
|
|
116
|
+
return false;
|
|
117
|
+
let segments;
|
|
118
|
+
try {
|
|
119
|
+
segments = rawSegments.map(decodeURIComponent);
|
|
120
|
+
}
|
|
121
|
+
catch (err) {
|
|
122
|
+
// Malformed percent-escapes are a client error, not a server fault.
|
|
123
|
+
return json(res, 400, {
|
|
124
|
+
error: `malformed percent-encoding in path: ${err instanceof Error ? err.message : String(err)}`,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
const method = req.method ?? 'GET';
|
|
128
|
+
let principal = null;
|
|
129
|
+
if (opts.authorize) {
|
|
130
|
+
principal = await opts.authorize(extractToken(req), method);
|
|
131
|
+
if (!principal)
|
|
132
|
+
return json(res, 401, { error: 'unauthorized' });
|
|
133
|
+
}
|
|
134
|
+
const registry = opts.registry;
|
|
135
|
+
const maxBytes = opts.maxBytes ?? DEFAULT_MAX_BYTES;
|
|
136
|
+
// ----- layers ------------------------------------------------------------
|
|
137
|
+
if (head === 'layers') {
|
|
138
|
+
if (method === 'GET' && segments.length === 1) {
|
|
139
|
+
return json(res, 200, { layers: registry.listLayers() });
|
|
140
|
+
}
|
|
141
|
+
if (method === 'GET' && segments.length === 2) {
|
|
142
|
+
const id = segments[1].startsWith('blake3:') ? segments[1] : `blake3:${segments[1]}`;
|
|
143
|
+
if (!registry.hasLayer(id))
|
|
144
|
+
return json(res, 404, { error: `no layer ${id}` });
|
|
145
|
+
return json(res, 200, registry.loadLayer(id));
|
|
146
|
+
}
|
|
147
|
+
if (method === 'POST' && segments.length === 1) {
|
|
148
|
+
const text = await readBody(req, maxBytes);
|
|
149
|
+
if (text === null)
|
|
150
|
+
return json(res, 413, { error: `body exceeds ${maxBytes} bytes` });
|
|
151
|
+
const parsed = parseJson(text);
|
|
152
|
+
if (parsed.error !== undefined)
|
|
153
|
+
return json(res, 400, { error: `invalid JSON body: ${parsed.error}` });
|
|
154
|
+
const file = parsed.value;
|
|
155
|
+
if (!file || typeof file.header !== 'object' || !Array.isArray(file.data)) {
|
|
156
|
+
return json(res, 400, { error: 'body must be an IFCX layer document' });
|
|
157
|
+
}
|
|
158
|
+
// Bind the manifest author to the push credential: provenance is
|
|
159
|
+
// otherwise self-asserted, and a spoofed author.principal defeats
|
|
160
|
+
// both the no-self-approval guard and requireHumanApproval (the
|
|
161
|
+
// approver "differs" from a name the real author invented). A
|
|
162
|
+
// manifest-less push would dodge the binding entirely — and later
|
|
163
|
+
// dodge the self-approval separation (no author to compare) — so
|
|
164
|
+
// authenticated pushes must carry provenance. Only enforceable when
|
|
165
|
+
// the deployment authenticates real identities; the anonymous dev
|
|
166
|
+
// sentinel is not a credential to bind against.
|
|
167
|
+
if (principal && principal.userId !== ANONYMOUS_USER_ID) {
|
|
168
|
+
const manifest = getProvenance(file);
|
|
169
|
+
if (!manifest) {
|
|
170
|
+
return json(res, 400, {
|
|
171
|
+
error: 'authenticated pushes must carry a provenance manifest (author identity is bound to the credential)',
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
if (manifest.author.principal !== principal.userId) {
|
|
175
|
+
return json(res, 403, {
|
|
176
|
+
error: `manifest author.principal "${manifest.author.principal}" must match the authenticated principal "${principal.userId}"`,
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
try {
|
|
181
|
+
return json(res, 201, { id: registry.push(file) });
|
|
182
|
+
}
|
|
183
|
+
catch (err) {
|
|
184
|
+
const handled = handlePushError(res, err);
|
|
185
|
+
if (handled)
|
|
186
|
+
return handled;
|
|
187
|
+
// Content that cannot be canonicalized (non-finite numbers, exotic
|
|
188
|
+
// value types) is a client error, not a server fault.
|
|
189
|
+
if (err instanceof Error && err.message.includes('canonicalizable')) {
|
|
190
|
+
return json(res, 400, { error: err.message });
|
|
191
|
+
}
|
|
192
|
+
throw err;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return json(res, 405, { error: `unsupported ${method} on layers` });
|
|
196
|
+
}
|
|
197
|
+
// ----- refs --------------------------------------------------------------
|
|
198
|
+
if (head === 'refs') {
|
|
199
|
+
if (method === 'GET' && segments.length === 1) {
|
|
200
|
+
return json(res, 200, { refs: registry.listRefs() });
|
|
201
|
+
}
|
|
202
|
+
if (method === 'PUT' && segments.length === 2) {
|
|
203
|
+
const text = await readBody(req, maxBytes);
|
|
204
|
+
if (text === null)
|
|
205
|
+
return json(res, 413, { error: `body exceeds ${maxBytes} bytes` });
|
|
206
|
+
const parsed = parseJson(text);
|
|
207
|
+
if (parsed.error !== undefined)
|
|
208
|
+
return json(res, 400, { error: `invalid JSON body: ${parsed.error}` });
|
|
209
|
+
const body = parsed.value;
|
|
210
|
+
if (!body ||
|
|
211
|
+
(body.layers !== undefined &&
|
|
212
|
+
!(Array.isArray(body.layers) && body.layers.every((id) => typeof id === 'string')))) {
|
|
213
|
+
return json(res, 400, { error: 'body must be { layers?: string[], policy?: RefPolicy }' });
|
|
214
|
+
}
|
|
215
|
+
const policy = body.policy === undefined ? undefined : parseRefPolicy(body.policy);
|
|
216
|
+
if (body.policy !== undefined && policy === undefined) {
|
|
217
|
+
return json(res, 400, {
|
|
218
|
+
error: 'policy must be { requireHumanApproval?: boolean, requiredChecks?: string[] }',
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
const name = segments[1];
|
|
222
|
+
const existing = registry.getRef(name);
|
|
223
|
+
const layers = body.layers ?? existing?.layers ?? [];
|
|
224
|
+
const missing = layers.filter((id) => !registry.hasLayer(id));
|
|
225
|
+
if (missing.length > 0)
|
|
226
|
+
return json(res, 422, { error: `unknown layer(s): ${missing.join(', ')}` });
|
|
227
|
+
// Policy-protected refs only move through the merge endpoint — that
|
|
228
|
+
// is where required checks and approval rules are enforced.
|
|
229
|
+
const layersChanged = existing !== undefined && JSON.stringify(existing.layers) !== JSON.stringify(layers);
|
|
230
|
+
if (existing?.policy && layersChanged) {
|
|
231
|
+
return json(res, 409, {
|
|
232
|
+
error: `ref ${name} is policy-protected; move it via POST ${BASE}refs/${name}/merge`,
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
// A protected ref's policy is immutable through this API in v1:
|
|
236
|
+
// letting any write principal PUT `{ "policy": {} }` would strip
|
|
237
|
+
// requireHumanApproval/requiredChecks and neuter the merge gate.
|
|
238
|
+
// (Idempotent re-PUT of the identical policy is allowed.)
|
|
239
|
+
if (existing?.policy &&
|
|
240
|
+
policy !== undefined &&
|
|
241
|
+
JSON.stringify(existing.policy) !== JSON.stringify(policy)) {
|
|
242
|
+
return json(res, 409, {
|
|
243
|
+
error: `ref ${name} is policy-protected; its policy cannot be changed via PUT`,
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
const entry = {
|
|
247
|
+
layers,
|
|
248
|
+
...(policy ? { policy } : existing?.policy ? { policy: existing.policy } : {}),
|
|
249
|
+
};
|
|
250
|
+
try {
|
|
251
|
+
registry.setRef(name, entry);
|
|
252
|
+
}
|
|
253
|
+
catch (err) {
|
|
254
|
+
const handled = handlePushError(res, err);
|
|
255
|
+
if (handled)
|
|
256
|
+
return handled;
|
|
257
|
+
throw err;
|
|
258
|
+
}
|
|
259
|
+
return json(res, existing ? 200 : 201, { ref: name, ...entry });
|
|
260
|
+
}
|
|
261
|
+
if (method === 'GET' && segments.length === 2) {
|
|
262
|
+
const entry = registry.getRef(segments[1]);
|
|
263
|
+
if (!entry)
|
|
264
|
+
return json(res, 404, { error: `no ref ${segments[1]}` });
|
|
265
|
+
return json(res, 200, { ref: segments[1], ...entry });
|
|
266
|
+
}
|
|
267
|
+
if (method === 'POST' && segments.length === 3 && segments[2] === 'merge') {
|
|
268
|
+
const text = await readBody(req, maxBytes);
|
|
269
|
+
if (text === null)
|
|
270
|
+
return json(res, 413, { error: `body exceeds ${maxBytes} bytes` });
|
|
271
|
+
const parsed = parseJson(text);
|
|
272
|
+
if (parsed.error !== undefined)
|
|
273
|
+
return json(res, 400, { error: `invalid JSON body: ${parsed.error}` });
|
|
274
|
+
const body = parsed.value;
|
|
275
|
+
if (!body?.candidate)
|
|
276
|
+
return json(res, 400, { error: 'body must include { candidate: <layer id> }' });
|
|
277
|
+
if (!registry.getRef(segments[1]))
|
|
278
|
+
return json(res, 404, { error: `no ref ${segments[1]}` });
|
|
279
|
+
if (!registry.hasLayer(body.candidate))
|
|
280
|
+
return json(res, 404, { error: `no layer ${body.candidate}` });
|
|
281
|
+
const init = { candidateId: body.candidate, into: segments[1] };
|
|
282
|
+
if (body.preview)
|
|
283
|
+
init.preview = true;
|
|
284
|
+
if (body.resolve === 'ours' || body.resolve === 'theirs')
|
|
285
|
+
init.resolve = body.resolve;
|
|
286
|
+
// Per-conflict resolutions from the review UI: strictly-shaped
|
|
287
|
+
// ours/theirs decisions ('edited' stays a local-flow feature).
|
|
288
|
+
if (body.resolutions !== undefined) {
|
|
289
|
+
if (!Array.isArray(body.resolutions)) {
|
|
290
|
+
return json(res, 400, { error: 'resolutions must be an array of { path, component_key?, choice }' });
|
|
291
|
+
}
|
|
292
|
+
const parsedResolutions = [];
|
|
293
|
+
for (const item of body.resolutions) {
|
|
294
|
+
if (typeof item !== 'object' || item === null) {
|
|
295
|
+
return json(res, 400, { error: 'each resolution must be { path, component_key?, choice: "ours" | "theirs" }' });
|
|
296
|
+
}
|
|
297
|
+
const raw = item;
|
|
298
|
+
const choice = raw.choice;
|
|
299
|
+
if (typeof raw.path !== 'string' || (choice !== 'ours' && choice !== 'theirs')) {
|
|
300
|
+
return json(res, 400, { error: 'each resolution must be { path, component_key?, choice: "ours" | "theirs" }' });
|
|
301
|
+
}
|
|
302
|
+
if (raw.component_key !== undefined && typeof raw.component_key !== 'string') {
|
|
303
|
+
return json(res, 400, { error: 'component_key must be a string when present' });
|
|
304
|
+
}
|
|
305
|
+
parsedResolutions.push({
|
|
306
|
+
path: raw.path,
|
|
307
|
+
choice,
|
|
308
|
+
...(raw.component_key !== undefined ? { componentKey: raw.component_key } : {}),
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
init.resolutions = parsedResolutions;
|
|
312
|
+
}
|
|
313
|
+
if (Array.isArray(body.waivers))
|
|
314
|
+
init.waivers = body.waivers;
|
|
315
|
+
if (body.allow_unrelated)
|
|
316
|
+
init.allowUnrelated = true;
|
|
317
|
+
if (principal)
|
|
318
|
+
init.principal = principal.userId;
|
|
319
|
+
// `requireHumanApproval` derives from server-verified state — an
|
|
320
|
+
// approved review object for this (candidate, ref), recorded by the
|
|
321
|
+
// feedback endpoint with the approver's authenticated identity. A
|
|
322
|
+
// caller-asserted approved_by body field would let any write-capable
|
|
323
|
+
// agent bypass the branch protection (unlike the CLI, where the
|
|
324
|
+
// local store's operator IS the approver). Only the LATEST review
|
|
325
|
+
// for the pair is authoritative: a stale approval must not outlive
|
|
326
|
+
// a newer review that was reopened or marked changes-requested.
|
|
327
|
+
const latestReview = registry
|
|
328
|
+
.listReviews()
|
|
329
|
+
.filter((r) => r.layerId === body.candidate && r.into === segments[1])
|
|
330
|
+
.reduce((acc, r) => (acc === undefined || r.openedAt >= acc.openedAt ? r : acc), undefined);
|
|
331
|
+
if (latestReview?.status === 'approved' && latestReview.approvedBy !== undefined) {
|
|
332
|
+
init.approvedBy = latestReview.approvedBy;
|
|
333
|
+
}
|
|
334
|
+
// The shared flow's requireHumanApproval only fires for
|
|
335
|
+
// `author.kind === 'agent'`, but the kind is self-asserted — an
|
|
336
|
+
// agent that claims to be human would skip the gate. The registry
|
|
337
|
+
// cannot attest species, so for protected refs it requires an
|
|
338
|
+
// approval for EVERY candidate, from a principal other than the
|
|
339
|
+
// (push-credential-bound) author. Previews stay read-only.
|
|
340
|
+
const targetRef = registry.getRef(segments[1]);
|
|
341
|
+
if (targetRef?.policy?.requireHumanApproval && !body.preview) {
|
|
342
|
+
if (init.approvedBy === undefined) {
|
|
343
|
+
return json(res, 403, {
|
|
344
|
+
status: 'policy-failure',
|
|
345
|
+
reason: `ref ${segments[1]} requires an approved review for every merge candidate`,
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
const candidateAuthor = getProvenance(registry.loadLayer(body.candidate))?.author.principal;
|
|
349
|
+
if (candidateAuthor === undefined) {
|
|
350
|
+
// Unknown authorship means approver-vs-author separation cannot
|
|
351
|
+
// be verified — fail closed rather than let a de-facto author
|
|
352
|
+
// approve their own manifest-stripped layer.
|
|
353
|
+
return json(res, 403, {
|
|
354
|
+
status: 'policy-failure',
|
|
355
|
+
reason: `candidate ${body.candidate} carries no provenance author; requireHumanApproval refs need attributable candidates`,
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
if (init.approvedBy === candidateAuthor) {
|
|
359
|
+
return json(res, 403, {
|
|
360
|
+
status: 'policy-failure',
|
|
361
|
+
reason: `approval by the layer author ${candidateAuthor} does not satisfy requireHumanApproval`,
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
let outcome;
|
|
366
|
+
try {
|
|
367
|
+
outcome = mergeIntoRef(registry, init);
|
|
368
|
+
}
|
|
369
|
+
catch (err) {
|
|
370
|
+
const handled = handlePushError(res, err);
|
|
371
|
+
if (handled)
|
|
372
|
+
return handled;
|
|
373
|
+
throw err;
|
|
374
|
+
}
|
|
375
|
+
switch (outcome.status) {
|
|
376
|
+
case 'fast-forward':
|
|
377
|
+
return json(res, 200, { status: outcome.status, layers: outcome.refLayers });
|
|
378
|
+
case 'merged':
|
|
379
|
+
return json(res, 200, {
|
|
380
|
+
status: outcome.status,
|
|
381
|
+
merge_layer: outcome.mergeLayerId,
|
|
382
|
+
layers: outcome.refLayers,
|
|
383
|
+
ancestor_matched: outcome.ancestorMatched,
|
|
384
|
+
});
|
|
385
|
+
case 'preview':
|
|
386
|
+
return json(res, 200, { status: outcome.status, plan: outcome.plan });
|
|
387
|
+
case 'conflicts':
|
|
388
|
+
return json(res, 409, { status: outcome.status, conflicts: outcome.conflicts });
|
|
389
|
+
case 'policy-failure':
|
|
390
|
+
return json(res, 403, { status: outcome.status, reason: outcome.reason });
|
|
391
|
+
case 'unrelated-base':
|
|
392
|
+
return json(res, 422, { status: outcome.status, declared_base: outcome.declaredBase });
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
return json(res, 405, { error: `unsupported ${method} on refs` });
|
|
396
|
+
}
|
|
397
|
+
// ----- reviews -----------------------------------------------------------
|
|
398
|
+
if (method === 'GET' && segments.length === 1) {
|
|
399
|
+
return json(res, 200, { reviews: registry.listReviews() });
|
|
400
|
+
}
|
|
401
|
+
if (method === 'GET' && segments.length === 2) {
|
|
402
|
+
const review = registry.getReview(segments[1]);
|
|
403
|
+
if (!review)
|
|
404
|
+
return json(res, 404, { error: `no review ${segments[1]}` });
|
|
405
|
+
return json(res, 200, review);
|
|
406
|
+
}
|
|
407
|
+
if (method === 'POST' && segments.length === 1) {
|
|
408
|
+
const text = await readBody(req, maxBytes);
|
|
409
|
+
if (text === null)
|
|
410
|
+
return json(res, 413, { error: `body exceeds ${maxBytes} bytes` });
|
|
411
|
+
const parsed = parseJson(text);
|
|
412
|
+
if (parsed.error !== undefined)
|
|
413
|
+
return json(res, 400, { error: `invalid JSON body: ${parsed.error}` });
|
|
414
|
+
const body = parsed.value;
|
|
415
|
+
if (!body?.layer_id || !body.into) {
|
|
416
|
+
return json(res, 400, { error: 'body must include { layer_id, into }' });
|
|
417
|
+
}
|
|
418
|
+
if (!registry.hasLayer(body.layer_id))
|
|
419
|
+
return json(res, 404, { error: `no layer ${body.layer_id}` });
|
|
420
|
+
if (!registry.getRef(body.into))
|
|
421
|
+
return json(res, 404, { error: `no ref ${body.into}` });
|
|
422
|
+
const review = {
|
|
423
|
+
id: crypto.randomUUID(),
|
|
424
|
+
layerId: body.layer_id,
|
|
425
|
+
into: body.into,
|
|
426
|
+
reviewers: Array.isArray(body.reviewers) ? body.reviewers : [],
|
|
427
|
+
status: 'open',
|
|
428
|
+
feedback: [],
|
|
429
|
+
...(principal ? { openedBy: principal.userId } : {}),
|
|
430
|
+
openedAt: new Date().toISOString(),
|
|
431
|
+
};
|
|
432
|
+
try {
|
|
433
|
+
registry.putReview(review);
|
|
434
|
+
}
|
|
435
|
+
catch (err) {
|
|
436
|
+
const handled = handlePushError(res, err);
|
|
437
|
+
if (handled)
|
|
438
|
+
return handled;
|
|
439
|
+
throw err;
|
|
440
|
+
}
|
|
441
|
+
return json(res, 201, { id: review.id });
|
|
442
|
+
}
|
|
443
|
+
if (method === 'POST' && segments.length === 3 && segments[2] === 'feedback') {
|
|
444
|
+
const review = registry.getReview(segments[1]);
|
|
445
|
+
if (!review)
|
|
446
|
+
return json(res, 404, { error: `no review ${segments[1]}` });
|
|
447
|
+
// When the review names reviewers, only they may act on it.
|
|
448
|
+
const actor = principal?.userId ?? 'anonymous';
|
|
449
|
+
if (review.reviewers.length > 0 && !review.reviewers.includes(actor)) {
|
|
450
|
+
return json(res, 403, { error: `only the named reviewers may act on review ${review.id}` });
|
|
451
|
+
}
|
|
452
|
+
const text = await readBody(req, maxBytes);
|
|
453
|
+
if (text === null)
|
|
454
|
+
return json(res, 413, { error: `body exceeds ${maxBytes} bytes` });
|
|
455
|
+
const parsed = parseJson(text);
|
|
456
|
+
if (parsed.error !== undefined)
|
|
457
|
+
return json(res, 400, { error: `invalid JSON body: ${parsed.error}` });
|
|
458
|
+
const body = parsed.value;
|
|
459
|
+
if (!body || !Array.isArray(body.decisions)) {
|
|
460
|
+
return json(res, 400, { error: 'body must include { decisions: [...] }' });
|
|
461
|
+
}
|
|
462
|
+
// Stored reviews are a contract for downstream tooling: reject unknown
|
|
463
|
+
// decision shapes and status values instead of persisting them verbatim.
|
|
464
|
+
const decisions = [];
|
|
465
|
+
for (const item of body.decisions) {
|
|
466
|
+
const decision = parseReviewDecision(item);
|
|
467
|
+
if (!decision) {
|
|
468
|
+
return json(res, 400, {
|
|
469
|
+
error: 'each decision must be { entity: string, decision: "accept" | "reject", componentKey?: string, comment?: string }',
|
|
470
|
+
});
|
|
471
|
+
}
|
|
472
|
+
decisions.push(decision);
|
|
473
|
+
}
|
|
474
|
+
if (body.status !== undefined && body.status !== 'approved' && body.status !== 'changes-requested') {
|
|
475
|
+
return json(res, 400, { error: 'status must be "approved" or "changes-requested"' });
|
|
476
|
+
}
|
|
477
|
+
if (body.status === 'approved') {
|
|
478
|
+
// No self-approval: the layer's manifest author cannot satisfy the
|
|
479
|
+
// approval its own merge needs. (Human-vs-agent identity of the
|
|
480
|
+
// approver is the auth provider's responsibility — the registry
|
|
481
|
+
// enforces attributability and separation, not species.)
|
|
482
|
+
const layer = registry.hasLayer(review.layerId) ? registry.loadLayer(review.layerId) : undefined;
|
|
483
|
+
const author = layer ? getProvenance(layer)?.author.principal : undefined;
|
|
484
|
+
if (author !== undefined && author === actor) {
|
|
485
|
+
return json(res, 403, { error: `layer author ${author} cannot approve their own review` });
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
review.feedback.push(...decisions);
|
|
489
|
+
if (body.status === 'approved' || body.status === 'changes-requested') {
|
|
490
|
+
review.status = body.status;
|
|
491
|
+
// Approval identity is server-recorded, never caller-asserted: the
|
|
492
|
+
// merge endpoint reads it back for requireHumanApproval policies.
|
|
493
|
+
if (body.status === 'approved')
|
|
494
|
+
review.approvedBy = actor;
|
|
495
|
+
else
|
|
496
|
+
delete review.approvedBy;
|
|
497
|
+
}
|
|
498
|
+
registry.putReview(review);
|
|
499
|
+
return json(res, 200, { id: review.id, status: review.status, decision_count: review.feedback.length });
|
|
500
|
+
}
|
|
501
|
+
return json(res, 405, { error: `unsupported ${method} on reviews` });
|
|
502
|
+
}
|
|
503
|
+
//# sourceMappingURL=layer-registry-route.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layer-registry-route.js","sourceRoot":"","sources":["../src/layer-registry-route.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { IfcxFile } from '@ifc-lite/ifcx';
|
|
2
|
+
import type { LayerRefStore, RefEntry } from '@ifc-lite/merge';
|
|
3
|
+
export interface RegistryReviewDecision {
|
|
4
|
+
entity: string;
|
|
5
|
+
componentKey?: string;
|
|
6
|
+
decision: 'accept' | 'reject';
|
|
7
|
+
comment?: string;
|
|
8
|
+
}
|
|
9
|
+
export type RegistryReviewStatus = 'open' | 'changes-requested' | 'approved';
|
|
10
|
+
export interface RegistryReview {
|
|
11
|
+
id: string;
|
|
12
|
+
layerId: string;
|
|
13
|
+
into: string;
|
|
14
|
+
reviewers: string[];
|
|
15
|
+
status: RegistryReviewStatus;
|
|
16
|
+
feedback: RegistryReviewDecision[];
|
|
17
|
+
openedBy?: string;
|
|
18
|
+
openedAt: string;
|
|
19
|
+
/**
|
|
20
|
+
* Authenticated principal that set status to `approved` — recorded by
|
|
21
|
+
* the feedback endpoint, never caller-asserted. The merge endpoint
|
|
22
|
+
* derives `requireHumanApproval` satisfaction from this field.
|
|
23
|
+
*/
|
|
24
|
+
approvedBy?: string;
|
|
25
|
+
}
|
|
26
|
+
/** Thrown by `push` when content fails the integrity or provenance gate. */
|
|
27
|
+
export declare class LayerPushError extends Error {
|
|
28
|
+
readonly code: 'id-mismatch' | 'invalid-provenance' | 'content-conflict' | 'registry-full';
|
|
29
|
+
constructor(code: 'id-mismatch' | 'invalid-provenance' | 'content-conflict' | 'registry-full', message: string);
|
|
30
|
+
}
|
|
31
|
+
export interface LayerRegistryStore extends LayerRefStore {
|
|
32
|
+
/** Verify the content address (and manifest when present), then store. */
|
|
33
|
+
push(file: IfcxFile): string;
|
|
34
|
+
hasLayer(layerId: string): boolean;
|
|
35
|
+
listLayers(): string[];
|
|
36
|
+
listRefs(): Record<string, RefEntry>;
|
|
37
|
+
getReview(id: string): RegistryReview | undefined;
|
|
38
|
+
listReviews(): RegistryReview[];
|
|
39
|
+
putReview(review: RegistryReview): void;
|
|
40
|
+
}
|
|
41
|
+
export interface MemoryLayerRegistryLimits {
|
|
42
|
+
/** Max stored layers (default 10 000). Layers are immutable and never evicted. */
|
|
43
|
+
maxLayers?: number;
|
|
44
|
+
/** Max named refs (default 1 000). */
|
|
45
|
+
maxRefs?: number;
|
|
46
|
+
/** Max review objects (default 10 000). */
|
|
47
|
+
maxReviews?: number;
|
|
48
|
+
}
|
|
49
|
+
export declare class MemoryLayerRegistry implements LayerRegistryStore {
|
|
50
|
+
private readonly layers;
|
|
51
|
+
private readonly refs;
|
|
52
|
+
private readonly reviews;
|
|
53
|
+
private readonly maxLayers;
|
|
54
|
+
private readonly maxRefs;
|
|
55
|
+
private readonly maxReviews;
|
|
56
|
+
constructor(limits?: MemoryLayerRegistryLimits);
|
|
57
|
+
push(file: IfcxFile): string;
|
|
58
|
+
hasLayer(layerId: string): boolean;
|
|
59
|
+
listLayers(): string[];
|
|
60
|
+
loadLayer(layerId: string): IfcxFile;
|
|
61
|
+
storeLayer(file: IfcxFile): string;
|
|
62
|
+
getRef(name: string): RefEntry | undefined;
|
|
63
|
+
setRef(name: string, entry: RefEntry): void;
|
|
64
|
+
listRefs(): Record<string, RefEntry>;
|
|
65
|
+
getReview(id: string): RegistryReview | undefined;
|
|
66
|
+
listReviews(): RegistryReview[];
|
|
67
|
+
putReview(review: RegistryReview): void;
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=layer-registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layer-registry.d.ts","sourceRoot":"","sources":["../src/layer-registry.ts"],"names":[],"mappings":"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"}
|