@elaraai/e3-api-server 1.0.83 → 1.0.85
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 +88 -11
- package/dist/src/errors.d.ts +15 -0
- package/dist/src/errors.d.ts.map +1 -1
- package/dist/src/errors.js +22 -1
- package/dist/src/errors.js.map +1 -1
- package/dist/src/handlers/dataflow.d.ts +89 -18
- package/dist/src/handlers/dataflow.d.ts.map +1 -1
- package/dist/src/handlers/dataflow.js +198 -123
- package/dist/src/handlers/dataflow.js.map +1 -1
- package/dist/src/handlers/dataflow.spec.js +257 -7
- package/dist/src/handlers/dataflow.spec.js.map +1 -1
- package/dist/src/handlers/datasets.d.ts +56 -15
- package/dist/src/handlers/datasets.d.ts.map +1 -1
- package/dist/src/handlers/datasets.js +200 -122
- package/dist/src/handlers/datasets.js.map +1 -1
- package/dist/src/handlers/datasets.spec.js +94 -4
- package/dist/src/handlers/datasets.spec.js.map +1 -1
- package/dist/src/handlers/functions.d.ts +11 -2
- package/dist/src/handlers/functions.d.ts.map +1 -1
- package/dist/src/handlers/functions.js +50 -20
- package/dist/src/handlers/functions.js.map +1 -1
- package/dist/src/handlers/functions.spec.js +26 -15
- package/dist/src/handlers/functions.spec.js.map +1 -1
- package/dist/src/handlers/index.d.ts +2 -2
- package/dist/src/handlers/index.d.ts.map +1 -1
- package/dist/src/handlers/index.js +2 -2
- package/dist/src/handlers/index.js.map +1 -1
- package/dist/src/handlers/packages.d.ts +1 -1
- package/dist/src/handlers/packages.d.ts.map +1 -1
- package/dist/src/handlers/packages.js +1 -1
- package/dist/src/handlers/packages.js.map +1 -1
- package/dist/src/handlers/records.d.ts +27 -7
- package/dist/src/handlers/records.d.ts.map +1 -1
- package/dist/src/handlers/records.js +36 -16
- package/dist/src/handlers/records.js.map +1 -1
- package/dist/src/handlers/records.spec.js +161 -27
- package/dist/src/handlers/records.spec.js.map +1 -1
- package/dist/src/handlers/repository.d.ts +9 -1
- package/dist/src/handlers/repository.d.ts.map +1 -1
- package/dist/src/handlers/repository.js +14 -5
- package/dist/src/handlers/repository.js.map +1 -1
- package/dist/src/handlers/tasks.d.ts +18 -6
- package/dist/src/handlers/tasks.d.ts.map +1 -1
- package/dist/src/handlers/tasks.js +31 -22
- package/dist/src/handlers/tasks.js.map +1 -1
- package/dist/src/handlers/tasks.spec.js +61 -12
- package/dist/src/handlers/tasks.spec.js.map +1 -1
- package/dist/src/handlers/workspaces.d.ts +11 -6
- package/dist/src/handlers/workspaces.d.ts.map +1 -1
- package/dist/src/handlers/workspaces.js +14 -8
- package/dist/src/handlers/workspaces.js.map +1 -1
- package/dist/src/handlers/workspaces.spec.js +25 -9
- package/dist/src/handlers/workspaces.spec.js.map +1 -1
- package/dist/src/identity.d.ts +22 -0
- package/dist/src/identity.d.ts.map +1 -0
- package/dist/src/identity.js +6 -0
- package/dist/src/identity.js.map +1 -0
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +3 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/local-dataflow.d.ts +14 -1
- package/dist/src/local-dataflow.d.ts.map +1 -1
- package/dist/src/local-dataflow.js +12 -7
- package/dist/src/local-dataflow.js.map +1 -1
- package/dist/src/middleware/auth.d.ts +1 -11
- package/dist/src/middleware/auth.d.ts.map +1 -1
- package/dist/src/middleware/auth.js.map +1 -1
- package/dist/src/middleware/repository.d.ts +1 -1
- package/dist/src/middleware/repository.d.ts.map +1 -1
- package/dist/src/middleware/repository.js +4 -10
- package/dist/src/middleware/repository.js.map +1 -1
- package/dist/src/middleware/repository.spec.js +7 -6
- package/dist/src/middleware/repository.spec.js.map +1 -1
- package/dist/src/portable.d.ts +47 -0
- package/dist/src/portable.d.ts.map +1 -0
- package/dist/src/portable.js +79 -0
- package/dist/src/portable.js.map +1 -0
- package/dist/src/portable.spec.d.ts +6 -0
- package/dist/src/portable.spec.d.ts.map +1 -0
- package/dist/src/portable.spec.js +325 -0
- package/dist/src/portable.spec.js.map +1 -0
- package/dist/src/routes/dataset-path.d.ts +28 -0
- package/dist/src/routes/dataset-path.d.ts.map +1 -0
- package/dist/src/routes/dataset-path.js +52 -0
- package/dist/src/routes/dataset-path.js.map +1 -0
- package/dist/src/routes/datasets.d.ts +7 -1
- package/dist/src/routes/datasets.d.ts.map +1 -1
- package/dist/src/routes/datasets.js +5 -11
- package/dist/src/routes/datasets.js.map +1 -1
- package/dist/src/routes/executions.d.ts +4 -2
- package/dist/src/routes/executions.d.ts.map +1 -1
- package/dist/src/routes/executions.js +31 -10
- package/dist/src/routes/executions.js.map +1 -1
- package/dist/src/routes/functions.d.ts +16 -8
- package/dist/src/routes/functions.d.ts.map +1 -1
- package/dist/src/routes/functions.js +21 -11
- package/dist/src/routes/functions.js.map +1 -1
- package/dist/src/routes/index.d.ts +4 -3
- package/dist/src/routes/index.d.ts.map +1 -1
- package/dist/src/routes/index.js +1 -0
- package/dist/src/routes/index.js.map +1 -1
- package/dist/src/routes/objects.d.ts +4 -2
- package/dist/src/routes/objects.d.ts.map +1 -1
- package/dist/src/routes/objects.js +5 -8
- package/dist/src/routes/objects.js.map +1 -1
- package/dist/src/routes/package-transfer.d.ts +1 -1
- package/dist/src/routes/package-transfer.d.ts.map +1 -1
- package/dist/src/routes/package-transfer.js +19 -22
- package/dist/src/routes/package-transfer.js.map +1 -1
- package/dist/src/routes/packages.d.ts +1 -1
- package/dist/src/routes/packages.d.ts.map +1 -1
- package/dist/src/routes/query.d.ts +53 -0
- package/dist/src/routes/query.d.ts.map +1 -0
- package/dist/src/routes/query.js +86 -0
- package/dist/src/routes/query.js.map +1 -0
- package/dist/src/routes/query.spec.d.ts +6 -0
- package/dist/src/routes/query.spec.d.ts.map +1 -0
- package/dist/src/routes/query.spec.js +49 -0
- package/dist/src/routes/query.spec.js.map +1 -0
- package/dist/src/routes/records.d.ts +32 -3
- package/dist/src/routes/records.d.ts.map +1 -1
- package/dist/src/routes/records.js +34 -7
- package/dist/src/routes/records.js.map +1 -1
- package/dist/src/routes/repositories.d.ts +1 -1
- package/dist/src/routes/repositories.d.ts.map +1 -1
- package/dist/src/routes/repository.d.ts +6 -1
- package/dist/src/routes/repository.d.ts.map +1 -1
- package/dist/src/routes/repository.js +5 -0
- package/dist/src/routes/repository.js.map +1 -1
- package/dist/src/routes/repository.spec.d.ts +6 -0
- package/dist/src/routes/repository.spec.d.ts.map +1 -0
- package/dist/src/routes/repository.spec.js +84 -0
- package/dist/src/routes/repository.spec.js.map +1 -0
- package/dist/src/routes/tasks.d.ts +1 -1
- package/dist/src/routes/tasks.d.ts.map +1 -1
- package/dist/src/routes/tasks.js +16 -4
- package/dist/src/routes/tasks.js.map +1 -1
- package/dist/src/routes/transfer.d.ts +1 -1
- package/dist/src/routes/transfer.d.ts.map +1 -1
- package/dist/src/routes/transfer.js +7 -27
- package/dist/src/routes/transfer.js.map +1 -1
- package/dist/src/routes/workspaces.d.ts +1 -1
- package/dist/src/routes/workspaces.d.ts.map +1 -1
- package/dist/src/routes/workspaces.js +34 -22
- package/dist/src/routes/workspaces.js.map +1 -1
- package/dist/src/server.js +1 -1
- package/dist/src/server.js.map +1 -1
- package/dist/src/types.d.ts +135 -5
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/types.js +7 -5
- package/dist/src/types.js.map +1 -1
- package/package.json +8 -4
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Elara AI Pty Ltd
|
|
3
|
+
* Licensed under BSL 1.1. See LICENSE for details.
|
|
4
|
+
*/
|
|
5
|
+
import { parseKeypath } from '@elaraai/e3-types';
|
|
6
|
+
/** A whole number as a query spells it: decimal digits, nothing else. */
|
|
7
|
+
const DIGITS = /^\d+$/;
|
|
8
|
+
/** What a parameter taking each least must be, as the dataset routes word it. */
|
|
9
|
+
const TAKES = {
|
|
10
|
+
0: 'a non-negative integer',
|
|
11
|
+
1: 'a positive integer',
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* The `400 bad_request` that refuses a malformed query, as the dataset routes
|
|
15
|
+
* answer one: JSON naming what was wrong, which the client reads as an
|
|
16
|
+
* `ApiError` of that type.
|
|
17
|
+
*
|
|
18
|
+
* @param message - What was wrong, naming the parameter and what it was given
|
|
19
|
+
* @returns The response
|
|
20
|
+
*/
|
|
21
|
+
export function badQuery(message) {
|
|
22
|
+
return new Response(JSON.stringify({ error: { type: 'bad_request', message } }), {
|
|
23
|
+
status: 400,
|
|
24
|
+
headers: { 'Content-Type': 'application/json' },
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Reads whole-number query parameters, each at or above the least it takes.
|
|
29
|
+
*
|
|
30
|
+
* @remarks
|
|
31
|
+
* A parameter absent, or given empty, is left out. One given that is not a
|
|
32
|
+
* whole number at or above its least — `abc`, `-1`, `1.5`, `NaN` — or that is
|
|
33
|
+
* past the integers a number holds exactly, is refused before any store is
|
|
34
|
+
* asked: `400 bad_request`, naming it and what it was given.
|
|
35
|
+
*
|
|
36
|
+
* @param c - The request's context
|
|
37
|
+
* @param least - Each parameter by name, and the least value it takes
|
|
38
|
+
* @returns The parameters given, by name; or the 400 that refuses one
|
|
39
|
+
*/
|
|
40
|
+
export function wholeQuery(c, least) {
|
|
41
|
+
const read = {};
|
|
42
|
+
for (const name of Object.keys(least)) {
|
|
43
|
+
const given = c.req.query(name);
|
|
44
|
+
if (given === undefined || given === '')
|
|
45
|
+
continue;
|
|
46
|
+
const value = DIGITS.test(given) ? Number(given) : NaN;
|
|
47
|
+
if (!(value >= least[name])) {
|
|
48
|
+
return badQuery(`${name} must be ${TAKES[least[name]]}, got ${JSON.stringify(given)}`);
|
|
49
|
+
}
|
|
50
|
+
if (!Number.isSafeInteger(value)) {
|
|
51
|
+
return badQuery(`${name} must be at most ${Number.MAX_SAFE_INTEGER}, got ${JSON.stringify(given)}`);
|
|
52
|
+
}
|
|
53
|
+
read[name] = value;
|
|
54
|
+
}
|
|
55
|
+
return read;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Reads the dataset paths a query names: each `path` parameter one keypath, as
|
|
59
|
+
* a status names a dataset (`.inputs.x`, `` .inputs.`my field` ``).
|
|
60
|
+
*
|
|
61
|
+
* @remarks
|
|
62
|
+
* Absent, or given only empty, there are none. One that is not a keypath —
|
|
63
|
+
* `inputs.x`, `.inputs.` — is refused before any store is asked:
|
|
64
|
+
* `400 bad_request`, naming it. Whether a path names anything is the route's
|
|
65
|
+
* to judge.
|
|
66
|
+
*
|
|
67
|
+
* @param c - The request's context
|
|
68
|
+
* @returns The paths given, or `undefined` when none is; or the 400 that
|
|
69
|
+
* refuses one
|
|
70
|
+
*/
|
|
71
|
+
export function pathsQuery(c) {
|
|
72
|
+
const given = (c.req.queries('path') ?? []).filter((path) => path !== '');
|
|
73
|
+
if (given.length === 0)
|
|
74
|
+
return undefined;
|
|
75
|
+
const paths = [];
|
|
76
|
+
for (const path of given) {
|
|
77
|
+
try {
|
|
78
|
+
paths.push(parseKeypath(path));
|
|
79
|
+
}
|
|
80
|
+
catch (err) {
|
|
81
|
+
return badQuery(`path must be a dataset's path, as a status names it (.inputs.x), got ${JSON.stringify(path)}: ${err instanceof Error ? err.message : String(err)}`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return paths;
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=query.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query.js","sourceRoot":"","sources":["../../../src/routes/query.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAQH,OAAO,EAAE,YAAY,EAAiB,MAAM,mBAAmB,CAAC;AAEhE,yEAAyE;AACzE,MAAM,MAAM,GAAG,OAAO,CAAC;AAOvB,iFAAiF;AACjF,MAAM,KAAK,GAA+B;IACxC,CAAC,EAAE,wBAAwB;IAC3B,CAAC,EAAE,oBAAoB;CACxB,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,UAAU,QAAQ,CAAC,OAAe;IACtC,OAAO,IAAI,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,EAAE,CAAC,EAAE;QAC/E,MAAM,EAAE,GAAG;QACX,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;KAChD,CAAC,CAAC;AACL,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,UAAU,CAAmB,CAAU,EAAE,KAAsC;IAC7F,MAAM,IAAI,GAA+B,EAAE,CAAC;IAC5C,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAQ,EAAE,CAAC;QAC7C,MAAM,KAAK,GAAG,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAChC,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,EAAE;YAAE,SAAS;QAClD,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;QACvD,IAAI,CAAC,CAAC,KAAK,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;YAC5B,OAAO,QAAQ,CAAC,GAAG,IAAI,YAAY,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,SAAS,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACzF,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC;YACjC,OAAO,QAAQ,CAAC,GAAG,IAAI,oBAAoB,MAAM,CAAC,gBAAgB,SAAS,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACtG,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC;IACrB,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,UAAU,CAAC,CAAU;IACnC,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,KAAK,EAAE,CAAC,CAAC;IAC1E,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IACzC,MAAM,KAAK,GAAe,EAAE,CAAC;IAC7B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,CAAC;YACH,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC;QACjC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,QAAQ,CAAC,wEAAwE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACvK,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query.spec.d.ts","sourceRoot":"","sources":["../../../src/routes/query.spec.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Elara AI Pty Ltd
|
|
3
|
+
* Licensed under BSL 1.1. See LICENSE for details.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* What a route reads from a query, as a host's own route reads it through the
|
|
7
|
+
* helpers both entries export: a window's offset and limit, refused as the
|
|
8
|
+
* dataset routes refuse them.
|
|
9
|
+
*/
|
|
10
|
+
import { describe, it } from 'node:test';
|
|
11
|
+
import assert from 'node:assert/strict';
|
|
12
|
+
import { Hono } from 'hono';
|
|
13
|
+
import * as root from '../index.js';
|
|
14
|
+
import * as portable from '../portable.js';
|
|
15
|
+
import { badQuery, pathsQuery, wholeQuery } from './query.js';
|
|
16
|
+
/** A host's own route over a window, reading its offset and limit as upstream's do. */
|
|
17
|
+
function windowRoute() {
|
|
18
|
+
const app = new Hono();
|
|
19
|
+
app.get('/window', (c) => {
|
|
20
|
+
const read = wholeQuery(c, { offset: 0, limit: 1 });
|
|
21
|
+
return read instanceof Response ? read : c.json(read);
|
|
22
|
+
});
|
|
23
|
+
return app;
|
|
24
|
+
}
|
|
25
|
+
describe('the query a route reads', () => {
|
|
26
|
+
it('reads a window\'s offset and limit, and refuses one that is no whole number at or above its least', async () => {
|
|
27
|
+
const app = windowRoute();
|
|
28
|
+
assert.deepEqual(await (await app.request('/window?offset=0&limit=5')).json(), { offset: 0, limit: 5 });
|
|
29
|
+
assert.deepEqual(await (await app.request('/window?limit=')).json(), {}, 'a parameter given empty is left out');
|
|
30
|
+
for (const [query, message] of [
|
|
31
|
+
['limit=0', 'limit must be a positive integer, got "0"'],
|
|
32
|
+
['offset=-1', 'offset must be a non-negative integer, got "-1"'],
|
|
33
|
+
['offset=1.5', 'offset must be a non-negative integer, got "1.5"'],
|
|
34
|
+
['limit=9007199254740993', 'limit must be at most 9007199254740991, got "9007199254740993"'],
|
|
35
|
+
]) {
|
|
36
|
+
const refused = await app.request(`/window?${query}`);
|
|
37
|
+
assert.equal(refused.status, 400, query);
|
|
38
|
+
assert.deepEqual(await refused.json(), { error: { type: 'bad_request', message } }, query);
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
it('is read through the helpers both entries export', () => {
|
|
42
|
+
for (const entry of [root, portable]) {
|
|
43
|
+
assert.equal(entry.wholeQuery, wholeQuery);
|
|
44
|
+
assert.equal(entry.badQuery, badQuery);
|
|
45
|
+
assert.equal(entry.pathsQuery, pathsQuery);
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
//# sourceMappingURL=query.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query.spec.js","sourceRoot":"","sources":["../../../src/routes/query.spec.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;GAIG;AAEH,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,KAAK,IAAI,MAAM,aAAa,CAAC;AACpC,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE9D,uFAAuF;AACvF,SAAS,WAAW;IAClB,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;IACvB,GAAG,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC,CAAC,EAAE,EAAE;QACvB,MAAM,IAAI,GAAG,UAAU,CAAC,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;QACpD,OAAO,IAAI,YAAY,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;IACH,OAAO,GAAG,CAAC;AACb,CAAC;AAED,QAAQ,CAAC,yBAAyB,EAAE,GAAG,EAAE;IACvC,EAAE,CAAC,mGAAmG,EAAE,KAAK,IAAI,EAAE;QACjH,MAAM,GAAG,GAAG,WAAW,EAAE,CAAC;QAC1B,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,OAAO,CAAC,0BAA0B,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;QACxG,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,EAAE,EAAE,qCAAqC,CAAC,CAAC;QAChH,KAAK,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,IAAI;YAC7B,CAAC,SAAS,EAAE,2CAA2C,CAAC;YACxD,CAAC,WAAW,EAAE,iDAAiD,CAAC;YAChE,CAAC,YAAY,EAAE,kDAAkD,CAAC;YAClE,CAAC,wBAAwB,EAAE,gEAAgE,CAAC;SACpF,EAAE,CAAC;YACX,MAAM,OAAO,GAAG,MAAM,GAAG,CAAC,OAAO,CAAC,WAAW,KAAK,EAAE,CAAC,CAAC;YACtD,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;YACzC,MAAM,CAAC,SAAS,CAAC,MAAM,OAAO,CAAC,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,EAAE,EAAE,KAAK,CAAC,CAAC;QAC7F,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iDAAiD,EAAE,GAAG,EAAE;QACzD,KAAK,MAAM,KAAK,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,EAAE,CAAC;YACrC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;YAC3C,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;YACvC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;QAC7C,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -12,12 +12,41 @@
|
|
|
12
12
|
* When auth is configured the committed `actor` is derived from the verified
|
|
13
13
|
* identity (the client-supplied actor is ignored), so the audit trail cannot
|
|
14
14
|
* be forged. Compaction is gated to an elevated role.
|
|
15
|
+
*
|
|
16
|
+
* A host whose requests have a time limit gives the routes its deadline, which
|
|
17
|
+
* a mutation and a compaction answer under, and the page a history request
|
|
18
|
+
* that names no limit is answered with.
|
|
15
19
|
*/
|
|
16
20
|
import { Hono } from 'hono';
|
|
17
|
-
import type { StorageBackend } from '@elaraai/e3-core';
|
|
18
|
-
import type { Identity } from '../
|
|
21
|
+
import type { StorageBackend } from '@elaraai/e3-core/portable';
|
|
22
|
+
import type { Identity } from '../identity.js';
|
|
19
23
|
import type { GetRunner } from './functions.js';
|
|
20
|
-
|
|
24
|
+
/** Options for {@link createWorkspaceRecordRoutes}: the limits a host's
|
|
25
|
+
* requests have, which the local server, with none, leaves unset. */
|
|
26
|
+
export interface RecordRoutesOptions {
|
|
27
|
+
/** A mutation's and a compaction's deadline, under the host's request
|
|
28
|
+
* timeout: each answers its typed outcome, `timed_out` or `conflict`, 2 s
|
|
29
|
+
* under it rather than outlasting it. Unset, neither has one but its own
|
|
30
|
+
* limits. */
|
|
31
|
+
syncDeadlineMs?: number;
|
|
32
|
+
/** How many commits a history request that names no `limit` is answered
|
|
33
|
+
* with, the newest first; the client pages on from the last one's parent.
|
|
34
|
+
* Unset, the whole chain. */
|
|
35
|
+
historyLimit?: number;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Record routes, mounted at `/api/repos/:repo/workspaces/:ws/records`.
|
|
39
|
+
*
|
|
40
|
+
* @param storage - Storage backend
|
|
41
|
+
* @param getRepoPath - A repository's identifier from its name
|
|
42
|
+
* @param getRunner - Each repository's task runner, which a mutation's
|
|
43
|
+
* program runs on
|
|
44
|
+
* @param options - The host's sync deadline, and its history page
|
|
45
|
+
* @returns The routes
|
|
46
|
+
* @throws {RangeError} When `syncDeadlineMs` is not a positive number of
|
|
47
|
+
* milliseconds, or `historyLimit` not a positive whole number of commits.
|
|
48
|
+
*/
|
|
49
|
+
export declare function createWorkspaceRecordRoutes(storage: StorageBackend, getRepoPath: (repo: string) => string, getRunner: GetRunner, options?: RecordRoutesOptions): Hono<{
|
|
21
50
|
Variables: {
|
|
22
51
|
identity?: Identity;
|
|
23
52
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"records.d.ts","sourceRoot":"","sources":["../../../src/routes/records.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH
|
|
1
|
+
{"version":3,"file":"records.d.ts","sourceRoot":"","sources":["../../../src/routes/records.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAGhE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAG/C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAahD;sEACsE;AACtE,MAAM,WAAW,mBAAmB;IAClC;;;kBAGc;IACd,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;kCAE8B;IAC9B,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,cAAc,EACvB,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,EACrC,SAAS,EAAE,SAAS,EACpB,OAAO,GAAE,mBAAwB;eAUC;QAAE,QAAQ,CAAC,EAAE,QAAQ,CAAA;KAAE;0CAyD1D"}
|
|
@@ -12,6 +12,10 @@
|
|
|
12
12
|
* When auth is configured the committed `actor` is derived from the verified
|
|
13
13
|
* identity (the client-supplied actor is ignored), so the audit trail cannot
|
|
14
14
|
* be forged. Compaction is gated to an elevated role.
|
|
15
|
+
*
|
|
16
|
+
* A host whose requests have a time limit gives the routes its deadline, which
|
|
17
|
+
* a mutation and a compaction answer under, and the page a history request
|
|
18
|
+
* that names no limit is answered with.
|
|
15
19
|
*/
|
|
16
20
|
import { Hono } from 'hono';
|
|
17
21
|
import { variant } from '@elaraai/east';
|
|
@@ -19,6 +23,7 @@ import { decodeBody, sendError } from '../beast2.js';
|
|
|
19
23
|
import { errorToVariant } from '../errors.js';
|
|
20
24
|
import { MutationCallRequestType, MutationResultType, RecordSignatureType } from '../types.js';
|
|
21
25
|
import { callMutationSync, getRecordHistory, describeRecord, compactRecord } from '../handlers/records.js';
|
|
26
|
+
import { wholeQuery } from './query.js';
|
|
22
27
|
/** Roles allowed to compact a record's history (drops the prior chain). */
|
|
23
28
|
const COMPACT_ROLES = ['admin', 'owner'];
|
|
24
29
|
/** The audited actor + whether it is authoritative (auth-verified, so the
|
|
@@ -28,7 +33,27 @@ function resolveActor(identity) {
|
|
|
28
33
|
return { actor: `auth:${identity.email ?? identity.sub}`, authoritative: true };
|
|
29
34
|
return { actor: 'api', authoritative: false };
|
|
30
35
|
}
|
|
31
|
-
|
|
36
|
+
/**
|
|
37
|
+
* Record routes, mounted at `/api/repos/:repo/workspaces/:ws/records`.
|
|
38
|
+
*
|
|
39
|
+
* @param storage - Storage backend
|
|
40
|
+
* @param getRepoPath - A repository's identifier from its name
|
|
41
|
+
* @param getRunner - Each repository's task runner, which a mutation's
|
|
42
|
+
* program runs on
|
|
43
|
+
* @param options - The host's sync deadline, and its history page
|
|
44
|
+
* @returns The routes
|
|
45
|
+
* @throws {RangeError} When `syncDeadlineMs` is not a positive number of
|
|
46
|
+
* milliseconds, or `historyLimit` not a positive whole number of commits.
|
|
47
|
+
*/
|
|
48
|
+
export function createWorkspaceRecordRoutes(storage, getRepoPath, getRunner, options = {}) {
|
|
49
|
+
// A limit no request could meet would refuse every one of them, or answer
|
|
50
|
+
// each with nothing, so a host is told when it mounts the routes.
|
|
51
|
+
if (options.syncDeadlineMs !== undefined && !(options.syncDeadlineMs > 0)) {
|
|
52
|
+
throw new RangeError(`syncDeadlineMs must be a positive number of milliseconds, got ${options.syncDeadlineMs}`);
|
|
53
|
+
}
|
|
54
|
+
if (options.historyLimit !== undefined && !(Number.isSafeInteger(options.historyLimit) && options.historyLimit >= 1)) {
|
|
55
|
+
throw new RangeError(`historyLimit must be a positive whole number of commits, got ${options.historyLimit}`);
|
|
56
|
+
}
|
|
32
57
|
const app = new Hono();
|
|
33
58
|
// GET /:rec — describe the record's mutations (for encoding arguments)
|
|
34
59
|
app.get('/:rec', async (c) => {
|
|
@@ -43,11 +68,13 @@ export function createWorkspaceRecordRoutes(storage, getRepoPath, getRunner) {
|
|
|
43
68
|
// GET /:rec/history?from=<hash>&limit=N — commit chain, newest first
|
|
44
69
|
app.get('/:rec/history', async (c) => {
|
|
45
70
|
const repoPath = getRepoPath(c.req.param('repo'));
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
const
|
|
71
|
+
// A limit of 0, or a malformed one, is refused rather than read as none:
|
|
72
|
+
// an absent limit asks for the host's page, or the whole chain.
|
|
73
|
+
const window = wholeQuery(c, { limit: 1 });
|
|
74
|
+
if (window instanceof Response)
|
|
75
|
+
return window;
|
|
49
76
|
const from = c.req.query('from') || undefined;
|
|
50
|
-
return getRecordHistory(storage, repoPath, c.req.param('ws'), c.req.param('rec'), limit, from);
|
|
77
|
+
return getRecordHistory(storage, repoPath, c.req.param('ws'), c.req.param('rec'), window.limit ?? options.historyLimit, from);
|
|
51
78
|
});
|
|
52
79
|
// POST /:rec/mutations/:mut — apply a mutation synchronously (200 MutationResult)
|
|
53
80
|
app.post('/:rec/mutations/:mut', async (c) => {
|
|
@@ -58,7 +85,7 @@ export function createWorkspaceRecordRoutes(storage, getRepoPath, getRunner) {
|
|
|
58
85
|
// Idempotency key travels out-of-band as a header (not the Beast2 body), so
|
|
59
86
|
// adding it changes no wire type; the request signal aborts on disconnect.
|
|
60
87
|
const idempotencyKey = c.req.header('Idempotency-Key') || undefined;
|
|
61
|
-
return await callMutationSync(storage, repoPath, getRunner(repoPath), c.req.param('ws'), c.req.param('rec'), c.req.param('mut'), req, actor, authoritative, { signal: c.req.raw.signal, idempotencyKey, verbose: c.req.query('verbose') === '1' });
|
|
88
|
+
return await callMutationSync(storage, repoPath, getRunner(repoPath), c.req.param('ws'), c.req.param('rec'), c.req.param('mut'), req, actor, authoritative, { signal: c.req.raw.signal, idempotencyKey, verbose: c.req.query('verbose') === '1', budgetMs: options.syncDeadlineMs });
|
|
62
89
|
}
|
|
63
90
|
catch (err) {
|
|
64
91
|
return sendError(MutationResultType, errorToVariant(err));
|
|
@@ -72,7 +99,7 @@ export function createWorkspaceRecordRoutes(storage, getRepoPath, getRunner) {
|
|
|
72
99
|
return sendError(MutationResultType, variant('permission_denied', { path: 'compact' }));
|
|
73
100
|
}
|
|
74
101
|
try {
|
|
75
|
-
return await compactRecord(storage, repoPath, c.req.param('ws'), c.req.param('rec'), resolveActor(identity).actor);
|
|
102
|
+
return await compactRecord(storage, repoPath, c.req.param('ws'), c.req.param('rec'), resolveActor(identity).actor, options.syncDeadlineMs);
|
|
76
103
|
}
|
|
77
104
|
catch (err) {
|
|
78
105
|
return sendError(MutationResultType, errorToVariant(err));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"records.js","sourceRoot":"","sources":["../../../src/routes/records.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH
|
|
1
|
+
{"version":3,"file":"records.js","sourceRoot":"","sources":["../../../src/routes/records.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Licensed under BSL 1.1. See LICENSE for details.
|
|
4
4
|
*/
|
|
5
5
|
import { Hono } from 'hono';
|
|
6
|
-
import type { StorageBackend } from '@elaraai/e3-core';
|
|
6
|
+
import type { StorageBackend } from '@elaraai/e3-core/portable';
|
|
7
7
|
/**
|
|
8
8
|
* The routes of the repositories a host keeps, mounted at `/api/repos`: list
|
|
9
9
|
* them, create one and remove one. They go through the storage backend's
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"repositories.d.ts","sourceRoot":"","sources":["../../../src/routes/repositories.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"repositories.d.ts","sourceRoot":"","sources":["../../../src/routes/repositories.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAIhE;;;;;;;;;GASG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,cAAc,8EAa/D;AAED;;;;;;GAMG;AACH,wBAAgB,4BAA4B,+EAyB3C"}
|
|
@@ -3,11 +3,16 @@
|
|
|
3
3
|
* Licensed under BSL 1.1. See LICENSE for details.
|
|
4
4
|
*/
|
|
5
5
|
import { Hono } from 'hono';
|
|
6
|
-
import type { StorageBackend, TransferBackend } from '@elaraai/e3-core';
|
|
6
|
+
import type { StorageBackend, TransferBackend } from '@elaraai/e3-core/portable';
|
|
7
7
|
/**
|
|
8
8
|
* Repository routes, mounted at `/api/repos/:repo`: its status, its record,
|
|
9
9
|
* and gc.
|
|
10
10
|
*
|
|
11
|
+
* @remarks
|
|
12
|
+
* The record is read, never upgraded: mounted without the repository gate, the
|
|
13
|
+
* route answers a repository that owes upgrades as the gate does, 503
|
|
14
|
+
* `repository_upgrade_pending` with a `Retry-After` ({@link getRecord}).
|
|
15
|
+
*
|
|
11
16
|
* @param storage - Storage backend
|
|
12
17
|
* @param getRepoPath - The repository identifier for a repo name
|
|
13
18
|
* @param transferBackend - Files and dispatches the job gc runs as, and holds
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"repository.d.ts","sourceRoot":"","sources":["../../../src/routes/repository.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"repository.d.ts","sourceRoot":"","sources":["../../../src/routes/repository.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAKjF;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,cAAc,EACvB,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,EACrC,eAAe,EAAE,eAAe,8EAiCjC"}
|
|
@@ -10,6 +10,11 @@ import { GcRequestType } from '../types.js';
|
|
|
10
10
|
* Repository routes, mounted at `/api/repos/:repo`: its status, its record,
|
|
11
11
|
* and gc.
|
|
12
12
|
*
|
|
13
|
+
* @remarks
|
|
14
|
+
* The record is read, never upgraded: mounted without the repository gate, the
|
|
15
|
+
* route answers a repository that owes upgrades as the gate does, 503
|
|
16
|
+
* `repository_upgrade_pending` with a `Retry-After` ({@link getRecord}).
|
|
17
|
+
*
|
|
13
18
|
* @param storage - Storage backend
|
|
14
19
|
* @param getRepoPath - The repository identifier for a repo name
|
|
15
20
|
* @param transferBackend - Files and dispatches the job gc runs as, and holds
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"repository.js","sourceRoot":"","sources":["../../../src/routes/repository.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AACvF,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C
|
|
1
|
+
{"version":3,"file":"repository.js","sourceRoot":"","sources":["../../../src/routes/repository.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AACvF,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,sBAAsB,CACpC,OAAuB,EACvB,WAAqC,EACrC,eAAgC;IAEhC,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;IAEvB,sDAAsD;IACtD,GAAG,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;QAC7B,MAAM,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAE,CAAC;QAClC,MAAM,QAAQ,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;QACnC,OAAO,SAAS,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;IAEH,kFAAkF;IAClF,GAAG,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;QAC7B,MAAM,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAE,CAAC;QAClC,MAAM,QAAQ,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;QACnC,OAAO,SAAS,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;IAEH,4CAA4C;IAC5C,GAAG,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;QAC1B,MAAM,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAE,CAAC;QAClC,MAAM,OAAO,GAAG,MAAM,UAAU,CAAC,CAAC,EAAE,aAAa,CAAC,CAAC;QACnD,OAAO,OAAO,CAAC,IAAI,EAAE,OAAO,EAAE,eAAe,CAAC,MAAM,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;IAEH,uDAAuD;IACvD,GAAG,CAAC,GAAG,CAAC,kBAAkB,EAAE,CAAC,CAAC,EAAE,EAAE;QAChC,MAAM,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAE,CAAC;QAClC,MAAM,WAAW,GAAG,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,aAAa,CAAE,CAAC;QAChD,OAAO,WAAW,CAAC,eAAe,CAAC,MAAM,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC;IAChE,CAAC,CAAC,CAAC;IAEH,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repository.spec.d.ts","sourceRoot":"","sources":["../../../src/routes/repository.spec.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Elara AI Pty Ltd
|
|
3
|
+
* Licensed under BSL 1.1. See LICENSE for details.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* The repository routes' record: a read, which applies no upgrade the
|
|
7
|
+
* repository owes and waits on no work running in it, whether the repository
|
|
8
|
+
* gate is mounted ahead of it or not. Without the gate, a repository that owes
|
|
9
|
+
* upgrades is refused as the gate refuses it.
|
|
10
|
+
*/
|
|
11
|
+
import { describe, it } from 'node:test';
|
|
12
|
+
import assert from 'node:assert/strict';
|
|
13
|
+
import { Hono } from 'hono';
|
|
14
|
+
import { decodeBeast2For, variant } from '@elaraai/east';
|
|
15
|
+
import { InMemoryTransferBackend, TASKS_LOCK, repositoryUpgradeStep } from '@elaraai/e3-core';
|
|
16
|
+
import { InMemoryStorage } from '@elaraai/e3-core/test';
|
|
17
|
+
import { RepositoryRecordType } from '@elaraai/e3-types';
|
|
18
|
+
import { getRecord } from '../handlers/repository.js';
|
|
19
|
+
import { createRepositoryGate } from '../middleware/repository.js';
|
|
20
|
+
import { ResponseType } from '../types.js';
|
|
21
|
+
import { createRepositoryRoutes } from './repository.js';
|
|
22
|
+
const decodeRecord = decodeBeast2For(ResponseType(RepositoryRecordType));
|
|
23
|
+
/** A repository written before its host's backend shipped a layout step,
|
|
24
|
+
* which it now owes, and the names of the steps applied. */
|
|
25
|
+
async function owing() {
|
|
26
|
+
const storage = new InMemoryStorage();
|
|
27
|
+
await storage.repos.create('owing');
|
|
28
|
+
const applied = [];
|
|
29
|
+
storage.upgrades.push({ name: 'host-layout-2', apply: async () => { applied.push('host-layout-2'); return null; } });
|
|
30
|
+
return { storage, applied };
|
|
31
|
+
}
|
|
32
|
+
/** The refusal the gate answers too, with when to ask again. */
|
|
33
|
+
async function assertRefused(response, what) {
|
|
34
|
+
assert.equal(response.status, 503, what);
|
|
35
|
+
assert.equal(response.headers.get('Retry-After'), '5', what);
|
|
36
|
+
assert.deepEqual(await response.json(), {
|
|
37
|
+
error: {
|
|
38
|
+
type: 'repository_upgrade_pending',
|
|
39
|
+
message: 'the repository owing owes the upgrade "host-layout-2", which a job applies before the repository is read — retry once it has',
|
|
40
|
+
},
|
|
41
|
+
}, what);
|
|
42
|
+
}
|
|
43
|
+
describe('the repository record route', () => {
|
|
44
|
+
it('reads a repository that owes an upgrade without applying it, refused at once as the gate refuses it, while a task runs or not', async () => {
|
|
45
|
+
const { storage, applied } = await owing();
|
|
46
|
+
const app = new Hono();
|
|
47
|
+
app.route('/api/repos/:repo', createRepositoryRoutes(storage, (repo) => repo, new InMemoryTransferBackend({ storage, getRepoPath: (repo) => repo })));
|
|
48
|
+
await assertRefused(await app.request('/api/repos/owing/record'), 'the route, with no gate ahead of it');
|
|
49
|
+
await assertRefused(await getRecord(storage, 'owing'), 'the handler, as a host calls it');
|
|
50
|
+
// A task running in the repository holds no read: the refusal is at once.
|
|
51
|
+
const task = await storage.locks.acquire('owing', TASKS_LOCK, variant('dataflow', null), { mode: 'shared' });
|
|
52
|
+
assert.ok(task !== null);
|
|
53
|
+
try {
|
|
54
|
+
const started = Date.now();
|
|
55
|
+
await assertRefused(await app.request('/api/repos/owing/record'), 'the route, while a task runs');
|
|
56
|
+
assert.ok(Date.now() - started < 5_000, 'the read waited on the running task');
|
|
57
|
+
}
|
|
58
|
+
finally {
|
|
59
|
+
await task.release();
|
|
60
|
+
}
|
|
61
|
+
assert.deepEqual(applied, [], 'no read applies the step');
|
|
62
|
+
// Once the host's job has applied it, the record is read.
|
|
63
|
+
assert.deepEqual(await repositoryUpgradeStep(storage, 'owing', { budgetMs: 10_000 }), { owed: [] });
|
|
64
|
+
const read = decodeRecord(new Uint8Array(await (await app.request('/api/repos/owing/record')).arrayBuffer()));
|
|
65
|
+
assert.equal(read.type, 'success');
|
|
66
|
+
if (read.type !== 'success')
|
|
67
|
+
return;
|
|
68
|
+
assert.ok(read.value.upgrades.some(({ name }) => name === 'host-layout-2'), 'the record names the step the job applied');
|
|
69
|
+
assert.deepEqual(applied, ['host-layout-2']);
|
|
70
|
+
});
|
|
71
|
+
it('answers the record behind the gate as it always has: the gate applies the step, and the route reads it', async () => {
|
|
72
|
+
const { storage, applied } = await owing();
|
|
73
|
+
const app = new Hono();
|
|
74
|
+
app.use('/api/repos/:repo/*', createRepositoryGate(storage, (repo) => repo));
|
|
75
|
+
app.route('/api/repos/:repo', createRepositoryRoutes(storage, (repo) => repo, new InMemoryTransferBackend({ storage, getRepoPath: (repo) => repo })));
|
|
76
|
+
const read = decodeRecord(new Uint8Array(await (await app.request('/api/repos/owing/record')).arrayBuffer()));
|
|
77
|
+
assert.equal(read.type, 'success');
|
|
78
|
+
if (read.type !== 'success')
|
|
79
|
+
return;
|
|
80
|
+
assert.ok(read.value.upgrades.some(({ name }) => name === 'host-layout-2'), 'the record names the step the gate applied');
|
|
81
|
+
assert.deepEqual(applied, ['host-layout-2']);
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
//# sourceMappingURL=repository.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repository.spec.js","sourceRoot":"","sources":["../../../src/routes/repository.spec.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
* Licensed under BSL 1.1. See LICENSE for details.
|
|
4
4
|
*/
|
|
5
5
|
import { Hono } from 'hono';
|
|
6
|
-
import type { StorageBackend } from '@elaraai/e3-core';
|
|
6
|
+
import type { StorageBackend } from '@elaraai/e3-core/portable';
|
|
7
7
|
export declare function createTaskRoutes(storage: StorageBackend, getRepoPath: (repo: string) => string): Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
|
|
8
8
|
//# sourceMappingURL=tasks.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tasks.d.ts","sourceRoot":"","sources":["../../../src/routes/tasks.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"tasks.d.ts","sourceRoot":"","sources":["../../../src/routes/tasks.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAKhE,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,cAAc,EACvB,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,8EA0CtC"}
|
package/dist/src/routes/tasks.js
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { Hono } from 'hono';
|
|
6
6
|
import { listTasks, getTask, listExecutions } from '../handlers/tasks.js';
|
|
7
|
+
import { TASK_EXECUTIONS_PAGE_DEFAULT, TASK_EXECUTIONS_PAGE_MAX } from '../types.js';
|
|
8
|
+
import { wholeQuery } from './query.js';
|
|
7
9
|
export function createTaskRoutes(storage, getRepoPath) {
|
|
8
10
|
const app = new Hono();
|
|
9
11
|
// GET /api/repos/:repo/workspaces/:ws/tasks - List tasks
|
|
@@ -21,15 +23,25 @@ export function createTaskRoutes(storage, getRepoPath) {
|
|
|
21
23
|
const taskName = c.req.param('task');
|
|
22
24
|
return getTask(storage, repoPath, ws, taskName);
|
|
23
25
|
});
|
|
24
|
-
// GET /api/repos/:repo/workspaces/:ws/tasks/:task/executions[?
|
|
25
|
-
//
|
|
26
|
+
// GET /api/repos/:repo/workspaces/:ws/tasks/:task/executions[?limit=<n>&before=<executionId>] - A page
|
|
27
|
+
// of a task's history: its runs, the latest first, at most `limit` of them —
|
|
28
|
+
// TASK_EXECUTIONS_PAGE_DEFAULT unless given, and never more than
|
|
29
|
+
// TASK_EXECUTIONS_PAGE_MAX — those before the run `before` names. A limit
|
|
30
|
+
// that is not a positive integer is refused before any store is asked, and
|
|
31
|
+
// an id that is no UUIDv7 by the store.
|
|
26
32
|
app.get('/:task/executions', async (c) => {
|
|
27
33
|
const repo = c.req.param('repo');
|
|
28
34
|
const repoPath = getRepoPath(repo);
|
|
29
35
|
const ws = c.req.param('ws');
|
|
30
36
|
const taskName = c.req.param('task');
|
|
31
|
-
const
|
|
32
|
-
|
|
37
|
+
const window = wholeQuery(c, { limit: 1 });
|
|
38
|
+
if (window instanceof Response)
|
|
39
|
+
return window;
|
|
40
|
+
const before = c.req.query('before');
|
|
41
|
+
return listExecutions(storage, repoPath, ws, taskName, {
|
|
42
|
+
limit: Math.min(window.limit ?? TASK_EXECUTIONS_PAGE_DEFAULT, TASK_EXECUTIONS_PAGE_MAX),
|
|
43
|
+
...(before !== undefined && before !== '' && { before }),
|
|
44
|
+
});
|
|
33
45
|
});
|
|
34
46
|
return app;
|
|
35
47
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tasks.js","sourceRoot":"","sources":["../../../src/routes/tasks.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"tasks.js","sourceRoot":"","sources":["../../../src/routes/tasks.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC1E,OAAO,EAAE,4BAA4B,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AACrF,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExC,MAAM,UAAU,gBAAgB,CAC9B,OAAuB,EACvB,WAAqC;IAErC,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;IAEvB,yDAAyD;IACzD,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;QACvB,MAAM,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAE,CAAC;QAClC,MAAM,QAAQ,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;QACnC,MAAM,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAE,CAAC;QAC9B,OAAO,SAAS,CAAC,OAAO,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;IAEH,qEAAqE;IACrE,GAAG,CAAC,GAAG,CAAC,QAAQ,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;QAC5B,MAAM,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAE,CAAC;QAClC,MAAM,QAAQ,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;QACnC,MAAM,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAE,CAAC;QAC9B,MAAM,QAAQ,GAAG,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAE,CAAC;QACtC,OAAO,OAAO,CAAC,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,QAAQ,CAAC,CAAC;IAClD,CAAC,CAAC,CAAC;IAEH,uGAAuG;IACvG,6EAA6E;IAC7E,iEAAiE;IACjE,0EAA0E;IAC1E,2EAA2E;IAC3E,wCAAwC;IACxC,GAAG,CAAC,GAAG,CAAC,mBAAmB,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;QACvC,MAAM,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAE,CAAC;QAClC,MAAM,QAAQ,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;QACnC,MAAM,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAE,CAAC;QAC9B,MAAM,QAAQ,GAAG,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAE,CAAC;QACtC,MAAM,MAAM,GAAG,UAAU,CAAC,CAAC,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;QAC3C,IAAI,MAAM,YAAY,QAAQ;YAAE,OAAO,MAAM,CAAC;QAC9C,MAAM,MAAM,GAAG,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QACrC,OAAO,cAAc,CAAC,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,QAAQ,EAAE;YACrD,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,IAAI,4BAA4B,EAAE,wBAAwB,CAAC;YACvF,GAAG,CAAC,MAAM,KAAK,SAAS,IAAI,MAAM,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;SACzD,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Licensed under BSL 1.1. See LICENSE for details.
|
|
4
4
|
*/
|
|
5
5
|
import { Hono } from 'hono';
|
|
6
|
-
import { type StorageBackend, type TransferBackend } from '@elaraai/e3-core';
|
|
6
|
+
import { type StorageBackend, type TransferBackend } from '@elaraai/e3-core/portable';
|
|
7
7
|
/** Options for {@link createTransferRoutes}. */
|
|
8
8
|
export interface TransferRouteOptions {
|
|
9
9
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transfer.d.ts","sourceRoot":"","sources":["../../../src/routes/transfer.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"transfer.d.ts","sourceRoot":"","sources":["../../../src/routes/transfer.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAI5B,OAAO,EAIL,KAAK,cAAc,EACnB,KAAK,eAAe,EACrB,MAAM,2BAA2B,CAAC;AAWnC,gDAAgD;AAChD,MAAM,WAAW,oBAAoB;IACnC;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AA2DD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,cAAc,EACvB,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,EACrC,eAAe,EAAE,eAAe,EAChC,OAAO,GAAE,oBAAyB;;EAoKnC"}
|