@lovrozagar/oat 0.1.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/LICENSE +21 -0
- package/README.md +401 -0
- package/bin/oat.js +2 -0
- package/dist/cli.js +386 -0
- package/dist/config/define-config.d.ts +165 -0
- package/dist/config/define-config.d.ts.map +1 -0
- package/dist/config/define-config.js +24 -0
- package/dist/config/define-config.js.map +1 -0
- package/dist/config/load.d.ts +10 -0
- package/dist/config/load.d.ts.map +1 -0
- package/dist/config/load.js +61 -0
- package/dist/config/load.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +9 -0
- package/dist/reference/defects.d.ts +254 -0
- package/dist/reference/defects.d.ts.map +1 -0
- package/dist/reference/defects.js +265 -0
- package/dist/reference/defects.js.map +1 -0
- package/dist/reference/dialect.d.ts +104 -0
- package/dist/reference/dialect.d.ts.map +1 -0
- package/dist/reference/dialect.js +174 -0
- package/dist/reference/dialect.js.map +1 -0
- package/dist/reference/http.d.ts +79 -0
- package/dist/reference/http.d.ts.map +1 -0
- package/dist/reference/http.js +889 -0
- package/dist/reference/http.js.map +1 -0
- package/dist/reference/model.d.ts +56 -0
- package/dist/reference/model.d.ts.map +1 -0
- package/dist/reference/model.js +156 -0
- package/dist/reference/model.js.map +1 -0
- package/dist/reference/query.d.ts +45 -0
- package/dist/reference/query.d.ts.map +1 -0
- package/dist/reference/query.js +353 -0
- package/dist/reference/query.js.map +1 -0
- package/dist/reference/spec.d.ts +13 -0
- package/dist/reference/spec.d.ts.map +1 -0
- package/dist/reference/spec.js +547 -0
- package/dist/reference/spec.js.map +1 -0
- package/dist/reference/store-api.d.ts +59 -0
- package/dist/reference/store-api.d.ts.map +1 -0
- package/dist/reference/store-api.js +76 -0
- package/dist/reference/store-api.js.map +1 -0
- package/dist/reference/stores/memory.d.ts +27 -0
- package/dist/reference/stores/memory.d.ts.map +1 -0
- package/dist/reference/stores/memory.js +97 -0
- package/dist/reference/stores/memory.js.map +1 -0
- package/dist/reference/stores/postgres.d.ts +61 -0
- package/dist/reference/stores/postgres.d.ts.map +1 -0
- package/dist/reference/stores/postgres.js +406 -0
- package/dist/reference/stores/postgres.js.map +1 -0
- package/dist/reference/stores/sqlite-driver.d.ts +54 -0
- package/dist/reference/stores/sqlite-driver.d.ts.map +1 -0
- package/dist/reference/stores/sqlite-driver.js +99 -0
- package/dist/reference/stores/sqlite-driver.js.map +1 -0
- package/dist/reference/stores/sqlite.d.ts +68 -0
- package/dist/reference/stores/sqlite.d.ts.map +1 -0
- package/dist/reference/stores/sqlite.js +464 -0
- package/dist/reference/stores/sqlite.js.map +1 -0
- package/dist/report/console.d.ts +33 -0
- package/dist/report/console.d.ts.map +1 -0
- package/dist/report/console.js +237 -0
- package/dist/report/console.js.map +1 -0
- package/dist/report/matrix.d.ts +131 -0
- package/dist/report/matrix.d.ts.map +1 -0
- package/dist/report/matrix.js +865 -0
- package/dist/report/matrix.js.map +1 -0
- package/dist/report/render.d.ts +66 -0
- package/dist/report/render.d.ts.map +1 -0
- package/dist/report/render.js +423 -0
- package/dist/report/render.js.map +1 -0
- package/dist/runtime/async.d.ts +34 -0
- package/dist/runtime/async.d.ts.map +1 -0
- package/dist/runtime/async.js +130 -0
- package/dist/runtime/async.js.map +1 -0
- package/dist/runtime/auth.d.ts +46 -0
- package/dist/runtime/auth.d.ts.map +1 -0
- package/dist/runtime/auth.js +236 -0
- package/dist/runtime/auth.js.map +1 -0
- package/dist/runtime/checks.d.ts +89 -0
- package/dist/runtime/checks.d.ts.map +1 -0
- package/dist/runtime/checks.js +3400 -0
- package/dist/runtime/checks.js.map +1 -0
- package/dist/runtime/client.d.ts +65 -0
- package/dist/runtime/client.d.ts.map +1 -0
- package/dist/runtime/client.js +122 -0
- package/dist/runtime/client.js.map +1 -0
- package/dist/runtime/finding.d.ts +50 -0
- package/dist/runtime/finding.d.ts.map +1 -0
- package/dist/runtime/finding.js +50 -0
- package/dist/runtime/finding.js.map +1 -0
- package/dist/runtime/fixture.d.ts +20 -0
- package/dist/runtime/fixture.d.ts.map +1 -0
- package/dist/runtime/fixture.js +175 -0
- package/dist/runtime/fixture.js.map +1 -0
- package/dist/runtime/progress.d.ts +41 -0
- package/dist/runtime/progress.d.ts.map +1 -0
- package/dist/runtime/progress.js +186 -0
- package/dist/runtime/progress.js.map +1 -0
- package/dist/runtime/run.d.ts +69 -0
- package/dist/runtime/run.d.ts.map +1 -0
- package/dist/runtime/run.js +575 -0
- package/dist/runtime/run.js.map +1 -0
- package/dist/runtime/teardown.d.ts +38 -0
- package/dist/runtime/teardown.d.ts.map +1 -0
- package/dist/runtime/teardown.js +129 -0
- package/dist/runtime/teardown.js.map +1 -0
- package/dist/runtime/validate.d.ts +31 -0
- package/dist/runtime/validate.d.ts.map +1 -0
- package/dist/runtime/validate.js +111 -0
- package/dist/runtime/validate.js.map +1 -0
- package/dist/runtime/world.d.ts +49 -0
- package/dist/runtime/world.d.ts.map +1 -0
- package/dist/runtime/world.js +119 -0
- package/dist/runtime/world.js.map +1 -0
- package/dist/spec/collection.d.ts +31 -0
- package/dist/spec/collection.d.ts.map +1 -0
- package/dist/spec/collection.js +121 -0
- package/dist/spec/collection.js.map +1 -0
- package/dist/spec/conventions.d.ts +103 -0
- package/dist/spec/conventions.d.ts.map +1 -0
- package/dist/spec/conventions.js +275 -0
- package/dist/spec/conventions.js.map +1 -0
- package/dist/spec/extensions.d.ts +74 -0
- package/dist/spec/extensions.d.ts.map +1 -0
- package/dist/spec/extensions.js +335 -0
- package/dist/spec/extensions.js.map +1 -0
- package/dist/spec/graph.d.ts +89 -0
- package/dist/spec/graph.d.ts.map +1 -0
- package/dist/spec/graph.js +318 -0
- package/dist/spec/graph.js.map +1 -0
- package/dist/spec/load.d.ts +36 -0
- package/dist/spec/load.d.ts.map +1 -0
- package/dist/spec/load.js +166 -0
- package/dist/spec/load.js.map +1 -0
- package/dist/spec/types.d.ts +57 -0
- package/dist/spec/types.d.ts.map +1 -0
- package/dist/spec/types.js +24 -0
- package/dist/spec/types.js.map +1 -0
- package/labs/README.md +130 -0
- package/labs/annotated-openapi.yaml +311 -0
- package/labs/anyrow.config.ts +107 -0
- package/labs/demo.mjs +124 -0
- package/labs/local.config.ts +41 -0
- package/labs/minimal.config.ts +32 -0
- package/package.json +80 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Async operation support.
|
|
3
|
+
*
|
|
4
|
+
* For an operation declared with `x-async`, the HTTP response is a receipt, not an outcome. oat
|
|
5
|
+
* drives the poll route to a terminal state and treats that payload as the real result — without
|
|
6
|
+
* this, an extraction or batch API is testable only as "returned 202", which for such an API is
|
|
7
|
+
* most of the surface.
|
|
8
|
+
*/
|
|
9
|
+
import { fillPath } from "./world.js";
|
|
10
|
+
/**
|
|
11
|
+
* A single PostgREST-style predicate, reused here so `until`/`successWhen` speak the same
|
|
12
|
+
* language as the filter grammar rather than inventing a second one.
|
|
13
|
+
*/
|
|
14
|
+
export function matchesPredicate(record, expression) {
|
|
15
|
+
const segments = expression.split(".");
|
|
16
|
+
if (segments.length < 3)
|
|
17
|
+
return false;
|
|
18
|
+
const [field, op] = segments;
|
|
19
|
+
const raw = segments.slice(2).join(".");
|
|
20
|
+
const actual = record[field];
|
|
21
|
+
switch (op) {
|
|
22
|
+
case "eq":
|
|
23
|
+
return String(actual) === raw;
|
|
24
|
+
case "neq":
|
|
25
|
+
return String(actual) !== raw;
|
|
26
|
+
case "in":
|
|
27
|
+
return raw
|
|
28
|
+
.replace(/^\(|\)$/g, "")
|
|
29
|
+
.split(",")
|
|
30
|
+
.map((s) => s.trim())
|
|
31
|
+
.includes(String(actual));
|
|
32
|
+
case "is":
|
|
33
|
+
if (raw === "null")
|
|
34
|
+
return actual === null || actual === undefined;
|
|
35
|
+
if (raw === "true")
|
|
36
|
+
return actual === true;
|
|
37
|
+
if (raw === "false")
|
|
38
|
+
return actual === false;
|
|
39
|
+
return false;
|
|
40
|
+
default:
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function readPath(body, path) {
|
|
45
|
+
let node = body;
|
|
46
|
+
for (const segment of path.replace(/^\$\.?/, "").split(".").filter(Boolean)) {
|
|
47
|
+
if (node === null || typeof node !== "object")
|
|
48
|
+
return undefined;
|
|
49
|
+
node = node[segment];
|
|
50
|
+
}
|
|
51
|
+
return node;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Polls until `until` matches, the timeout expires, or the poll route stops responding.
|
|
55
|
+
*
|
|
56
|
+
* Polling is capped by wall-clock rather than attempt count so a slow backend and a fast one
|
|
57
|
+
* behave the same way, and the interval is fixed rather than exponential — the point is to
|
|
58
|
+
* observe when the state changes, not to be gentle with an API oat is already hammering.
|
|
59
|
+
*/
|
|
60
|
+
export async function driveAsync(client, spec, receipt, scope, headers) {
|
|
61
|
+
const exchanges = [];
|
|
62
|
+
const began = performance.now();
|
|
63
|
+
const pollScope = { ...scope };
|
|
64
|
+
if (spec.idFrom !== undefined) {
|
|
65
|
+
const id = readPath(receipt, spec.idFrom);
|
|
66
|
+
if (id !== undefined && id !== null) {
|
|
67
|
+
/* The receipt names the job; bind it to whichever poll-path parameter is unresolved. */
|
|
68
|
+
const parameters = [...spec.poll.matchAll(/\{([^}]+)\}/g)].map((m) => m[1] ?? "");
|
|
69
|
+
const unbound = parameters.find((name) => pollScope[name] === undefined);
|
|
70
|
+
const target = unbound ?? spec.idFrom.replace(/^\$\./, "");
|
|
71
|
+
pollScope[target] = String(id);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const [method = "GET", template = spec.poll] = spec.poll.split(" ");
|
|
75
|
+
let path;
|
|
76
|
+
try {
|
|
77
|
+
path = fillPath(template, pollScope);
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
return {
|
|
81
|
+
elapsedMs: performance.now() - began,
|
|
82
|
+
exchanges,
|
|
83
|
+
polls: 0,
|
|
84
|
+
succeeded: false,
|
|
85
|
+
terminal: null,
|
|
86
|
+
timedOut: false,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
let polls = 0;
|
|
90
|
+
while (performance.now() - began < spec.timeoutMs) {
|
|
91
|
+
const exchange = await client.request(method, path, { headers });
|
|
92
|
+
exchanges.push(exchange);
|
|
93
|
+
polls += 1;
|
|
94
|
+
if (exchange.status < 300) {
|
|
95
|
+
const body = exchange.responseBody;
|
|
96
|
+
if (body !== null && typeof body === "object") {
|
|
97
|
+
const record = body;
|
|
98
|
+
const done = spec.until === undefined || matchesPredicate(record, spec.until);
|
|
99
|
+
if (done) {
|
|
100
|
+
return {
|
|
101
|
+
elapsedMs: performance.now() - began,
|
|
102
|
+
exchanges,
|
|
103
|
+
polls,
|
|
104
|
+
succeeded: spec.successWhen === undefined || matchesPredicate(record, spec.successWhen),
|
|
105
|
+
terminal: record,
|
|
106
|
+
timedOut: false,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
else if (exchange.status === 404 && polls > 1) {
|
|
112
|
+
/* The job existed and then did not — a vanished job is terminal, and reporting it as a
|
|
113
|
+
* timeout would name the wrong problem. */
|
|
114
|
+
break;
|
|
115
|
+
}
|
|
116
|
+
await sleep(spec.pollIntervalMs);
|
|
117
|
+
}
|
|
118
|
+
return {
|
|
119
|
+
elapsedMs: performance.now() - began,
|
|
120
|
+
exchanges,
|
|
121
|
+
polls,
|
|
122
|
+
succeeded: false,
|
|
123
|
+
terminal: null,
|
|
124
|
+
timedOut: true,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
function sleep(ms) {
|
|
128
|
+
return new Promise((done) => setTimeout(done, ms));
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=async.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"async.js","sourceRoot":"","sources":["../../src/runtime/async.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Credential acquisition and lifecycle.
|
|
3
|
+
*
|
|
4
|
+
* oat knows nothing about any login route. A principal declares a chain of steps; each step is
|
|
5
|
+
* an operation from the spec (or a raw request), values flow forward by JSON path, and anything
|
|
6
|
+
* delivered outside HTTP — verification tokens, OTPs, magic links — comes from a hook the caller
|
|
7
|
+
* writes. That hook is the entire backend-coupling surface.
|
|
8
|
+
*/
|
|
9
|
+
import type { AuthFlow, Hooks } from "../config/define-config.ts";
|
|
10
|
+
import type { SpecModel } from "../spec/graph.ts";
|
|
11
|
+
import type { Client } from "./client.ts";
|
|
12
|
+
export type AcquireSpec = AuthFlow;
|
|
13
|
+
export interface PrincipalRuntime {
|
|
14
|
+
id: string;
|
|
15
|
+
headers: () => Record<string, string>;
|
|
16
|
+
/** Address this principal was provisioned under, for cascade teardown. */
|
|
17
|
+
address: string | null;
|
|
18
|
+
/** Values bound during acquisition — roots discovered from the credential live here too. */
|
|
19
|
+
scope: Record<string, string>;
|
|
20
|
+
refreshIfStale: () => Promise<void>;
|
|
21
|
+
/** Forces reacquisition, used when a control probe proves the credential died early. */
|
|
22
|
+
reacquire: () => Promise<void>;
|
|
23
|
+
expiresAt: number | null;
|
|
24
|
+
}
|
|
25
|
+
export declare function readPath(body: unknown, path: string): unknown;
|
|
26
|
+
/**
|
|
27
|
+
* Decodes a JWT expiry without verifying the signature — oat is reading its own credential to
|
|
28
|
+
* schedule a refresh, not making a trust decision about it.
|
|
29
|
+
*/
|
|
30
|
+
export declare function jwtClaims(credential: string): Record<string, unknown> | null;
|
|
31
|
+
export declare function jwtExpiryMs(credential: string): number | null;
|
|
32
|
+
export interface AcquireContext {
|
|
33
|
+
client: Client;
|
|
34
|
+
model: SpecModel;
|
|
35
|
+
hooks: Hooks;
|
|
36
|
+
principalId: string;
|
|
37
|
+
}
|
|
38
|
+
/** Runs the chain once, returning the credential and everything bound along the way. */
|
|
39
|
+
export declare function runAcquireChain(spec: AcquireSpec, context: AcquireContext): Promise<{
|
|
40
|
+
credential: string;
|
|
41
|
+
scope: Record<string, string>;
|
|
42
|
+
expiresAt: number | null;
|
|
43
|
+
}>;
|
|
44
|
+
/** Wraps a chain in a credential that refreshes itself before it expires. */
|
|
45
|
+
export declare function createPrincipal(id: string, spec: AcquireSpec, context: AcquireContext): Promise<PrincipalRuntime>;
|
|
46
|
+
//# sourceMappingURL=auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/runtime/auth.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAY,KAAK,EAA8B,MAAM,4BAA4B,CAAA;AACvG,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AACjD,OAAO,KAAK,EAAE,MAAM,EAAY,MAAM,aAAa,CAAA;AAInD,MAAM,MAAM,WAAW,GAAG,QAAQ,CAAA;AAElC,MAAM,WAAW,gBAAgB;IAChC,EAAE,EAAE,MAAM,CAAA;IACV,OAAO,EAAE,MAAM,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACrC,0EAA0E;IAC1E,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,4FAA4F;IAC5F,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC7B,cAAc,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IACnC,wFAAwF;IACxF,SAAS,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IAC9B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;CACxB;AAED,wBAAgB,QAAQ,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAU7D;AAkBD;;;GAGG;AACH,wBAAgB,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAQ5E;AAED,wBAAgB,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAG7D;AAED,MAAM,WAAW,cAAc;IAC9B,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,SAAS,CAAA;IAChB,KAAK,EAAE,KAAK,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;CACnB;AAED,wFAAwF;AACxF,wBAAsB,eAAe,CACpC,IAAI,EAAE,WAAW,EACjB,OAAO,EAAE,cAAc,GACrB,OAAO,CAAC;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CA+E1F;AAgHD,6EAA6E;AAC7E,wBAAsB,eAAe,CACpC,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,WAAW,EACjB,OAAO,EAAE,cAAc,GACrB,OAAO,CAAC,gBAAgB,CAAC,CAmC3B"}
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Credential acquisition and lifecycle.
|
|
3
|
+
*
|
|
4
|
+
* oat knows nothing about any login route. A principal declares a chain of steps; each step is
|
|
5
|
+
* an operation from the spec (or a raw request), values flow forward by JSON path, and anything
|
|
6
|
+
* delivered outside HTTP — verification tokens, OTPs, magic links — comes from a hook the caller
|
|
7
|
+
* writes. That hook is the entire backend-coupling surface.
|
|
8
|
+
*/
|
|
9
|
+
export function readPath(body, path) {
|
|
10
|
+
let node = body;
|
|
11
|
+
for (const segment of path.replace(/^\$\.?/, "").split(".").filter(Boolean)) {
|
|
12
|
+
if (node === null || typeof node !== "object")
|
|
13
|
+
return undefined;
|
|
14
|
+
const index = Number.parseInt(segment, 10);
|
|
15
|
+
node = Array.isArray(node)
|
|
16
|
+
? (Number.isNaN(index) ? undefined : node[index])
|
|
17
|
+
: node[segment];
|
|
18
|
+
}
|
|
19
|
+
return node;
|
|
20
|
+
}
|
|
21
|
+
/** `{name}` placeholders resolved from the accumulated scope. */
|
|
22
|
+
function interpolate(value, scope) {
|
|
23
|
+
if (typeof value === "string") {
|
|
24
|
+
return value.replace(/\{([A-Za-z0-9_]+)\}/g, (match, key) => scope[key] ?? match);
|
|
25
|
+
}
|
|
26
|
+
if (Array.isArray(value))
|
|
27
|
+
return value.map((item) => interpolate(item, scope));
|
|
28
|
+
if (value !== null && typeof value === "object") {
|
|
29
|
+
const out = {};
|
|
30
|
+
for (const [key, item] of Object.entries(value)) {
|
|
31
|
+
out[key] = interpolate(item, scope);
|
|
32
|
+
}
|
|
33
|
+
return out;
|
|
34
|
+
}
|
|
35
|
+
return value;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Decodes a JWT expiry without verifying the signature — oat is reading its own credential to
|
|
39
|
+
* schedule a refresh, not making a trust decision about it.
|
|
40
|
+
*/
|
|
41
|
+
export function jwtClaims(credential) {
|
|
42
|
+
const parts = credential.split(".");
|
|
43
|
+
if (parts.length !== 3 || parts[1] === undefined)
|
|
44
|
+
return null;
|
|
45
|
+
try {
|
|
46
|
+
return JSON.parse(Buffer.from(parts[1], "base64url").toString("utf8"));
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
export function jwtExpiryMs(credential) {
|
|
53
|
+
const claims = jwtClaims(credential);
|
|
54
|
+
return typeof claims?.exp === "number" ? claims.exp * 1000 : null;
|
|
55
|
+
}
|
|
56
|
+
/** Runs the chain once, returning the credential and everything bound along the way. */
|
|
57
|
+
export async function runAcquireChain(spec, context) {
|
|
58
|
+
const scope = {};
|
|
59
|
+
let last;
|
|
60
|
+
for (const [index, step] of spec.steps.entries()) {
|
|
61
|
+
const stepBind = "bind" in step ? step.bind : undefined;
|
|
62
|
+
for (const [name, literal] of Object.entries(stepBind ?? {})) {
|
|
63
|
+
scope[name] = String(interpolate(literal, scope));
|
|
64
|
+
}
|
|
65
|
+
if ("outOfBand" in step) {
|
|
66
|
+
const address = String(interpolate(step.outOfBand.address, scope));
|
|
67
|
+
const value = await resolveOutOfBand(context, address, step.outOfBand.kind);
|
|
68
|
+
scope[step.outOfBand.as] = value;
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
const request = resolveRequest(step, context, scope, index);
|
|
72
|
+
last = await context.client.request(request.method, request.path, {
|
|
73
|
+
...(request.body === undefined ? {} : { body: request.body }),
|
|
74
|
+
...(request.headers === undefined ? {} : { headers: request.headers }),
|
|
75
|
+
...(request.query === undefined ? {} : { query: request.query }),
|
|
76
|
+
});
|
|
77
|
+
const acceptable = ("expect" in step ? step.expect : undefined) ?? [];
|
|
78
|
+
const ok = acceptable.length > 0 ? acceptable.includes(last.status) : last.status < 300;
|
|
79
|
+
if (!ok) {
|
|
80
|
+
throw new Error(`oat: principal "${context.principalId}" failed at auth step ${index + 1} ` +
|
|
81
|
+
`(${request.method} ${request.path}): expected ` +
|
|
82
|
+
`${acceptable.length > 0 ? acceptable.join("/") : "2xx"}, got ${last.status} — ` +
|
|
83
|
+
`${JSON.stringify(last.responseBody).slice(0, 200)}`);
|
|
84
|
+
}
|
|
85
|
+
const saveClaimsFrom = "saveClaimsFrom" in step ? step.saveClaimsFrom : undefined;
|
|
86
|
+
if (saveClaimsFrom !== undefined) {
|
|
87
|
+
const token = readPath(last.responseBody, saveClaimsFrom.token);
|
|
88
|
+
const claims = typeof token === "string" ? jwtClaims(token) : null;
|
|
89
|
+
if (claims === null) {
|
|
90
|
+
throw new Error(`oat: auth step ${index + 1} for "${context.principalId}" expected a JWT at ` +
|
|
91
|
+
`${saveClaimsFrom.token}, but it is absent or not decodable`);
|
|
92
|
+
}
|
|
93
|
+
for (const [name, claimPath] of Object.entries(saveClaimsFrom.bind)) {
|
|
94
|
+
const value = readPath(claims, claimPath);
|
|
95
|
+
if (value === undefined || value === null) {
|
|
96
|
+
throw new Error(`oat: auth step ${index + 1} binds ${name} to claim "${claimPath}", which the token ` +
|
|
97
|
+
`does not carry. Available claims: ${Object.keys(claims).join(", ")}`);
|
|
98
|
+
}
|
|
99
|
+
scope[name] = String(value);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
const saveAs = "saveAs" in step ? step.saveAs : undefined;
|
|
103
|
+
for (const [name, path] of Object.entries(saveAs ?? {})) {
|
|
104
|
+
const value = readPath(last.responseBody, path);
|
|
105
|
+
if (value === undefined || value === null) {
|
|
106
|
+
throw new Error(`oat: auth step ${index + 1} for "${context.principalId}" declares saveAs.${name} = ` +
|
|
107
|
+
`"${path}", which is not present in the response`);
|
|
108
|
+
}
|
|
109
|
+
scope[name] = String(value);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
const credential = scope.credential ?? String(readPath(last?.responseBody, spec.credentialFrom) ?? "");
|
|
113
|
+
if (credential === "" || credential === "undefined") {
|
|
114
|
+
throw new Error(`oat: principal "${context.principalId}" completed its chain but ${spec.credentialFrom} ` +
|
|
115
|
+
"did not yield a credential");
|
|
116
|
+
}
|
|
117
|
+
return { credential, expiresAt: computeExpiry(spec, last, credential), scope };
|
|
118
|
+
}
|
|
119
|
+
function computeExpiry(spec, last, credential) {
|
|
120
|
+
/* Prefer what the API states, then what the credential itself carries, then the configured
|
|
121
|
+
* fallback. Guessing an expiry that is too long means requests start failing mid-run for
|
|
122
|
+
* reasons that look like backend defects. */
|
|
123
|
+
if (spec.expiresInFrom !== undefined) {
|
|
124
|
+
const seconds = readPath(last?.responseBody, spec.expiresInFrom);
|
|
125
|
+
if (typeof seconds === "number")
|
|
126
|
+
return Date.now() + seconds * 1000;
|
|
127
|
+
}
|
|
128
|
+
const fromJwt = jwtExpiryMs(credential);
|
|
129
|
+
if (fromJwt !== null)
|
|
130
|
+
return fromJwt;
|
|
131
|
+
if (spec.assumeTtlMs !== undefined)
|
|
132
|
+
return Date.now() + spec.assumeTtlMs;
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
function isOperationStep(step) {
|
|
136
|
+
return "operationId" in step;
|
|
137
|
+
}
|
|
138
|
+
function isRequestStep(step) {
|
|
139
|
+
return "method" in step && "path" in step;
|
|
140
|
+
}
|
|
141
|
+
function resolveRequest(step, context, scope, index) {
|
|
142
|
+
/* Two shapes, distinguished structurally: name an operation from the document, or give a raw
|
|
143
|
+
* method and path for an endpoint the document does not describe. */
|
|
144
|
+
if (isOperationStep(step)) {
|
|
145
|
+
const op = context.model.byOperationId.get(step.operationId);
|
|
146
|
+
if (op === undefined) {
|
|
147
|
+
throw new Error(`oat: auth step ${index + 1} names operation "${step.operationId}", which is not in the ` +
|
|
148
|
+
"document");
|
|
149
|
+
}
|
|
150
|
+
return {
|
|
151
|
+
method: op.method,
|
|
152
|
+
path: String(interpolate(op.path, scope)),
|
|
153
|
+
...(step.body === undefined ? {} : { body: interpolate(step.body, scope) }),
|
|
154
|
+
...(step.headers === undefined
|
|
155
|
+
? {}
|
|
156
|
+
: { headers: interpolate(step.headers, scope) }),
|
|
157
|
+
...(step.query === undefined
|
|
158
|
+
? {}
|
|
159
|
+
: { query: interpolate(step.query, scope) }),
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
if (!isRequestStep(step)) {
|
|
163
|
+
throw new Error(`oat: auth step ${index + 1} declares neither an operationId nor a method and path`);
|
|
164
|
+
}
|
|
165
|
+
return {
|
|
166
|
+
method: step.method,
|
|
167
|
+
path: String(interpolate(step.path, scope)),
|
|
168
|
+
...(step.body === undefined ? {} : { body: interpolate(step.body, scope) }),
|
|
169
|
+
...(step.headers === undefined
|
|
170
|
+
? {}
|
|
171
|
+
: { headers: interpolate(step.headers, scope) }),
|
|
172
|
+
...(step.query === undefined
|
|
173
|
+
? {}
|
|
174
|
+
: { query: interpolate(step.query, scope) }),
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Out-of-band values usually land in an eventually-consistent store, so the hook is polled with
|
|
179
|
+
* backoff rather than called once. A single read that happens to race the write looks exactly
|
|
180
|
+
* like a broken delivery mechanism.
|
|
181
|
+
*/
|
|
182
|
+
async function resolveOutOfBand(context, address, kind) {
|
|
183
|
+
const hook = context.hooks.resolveOutOfBand;
|
|
184
|
+
if (hook === undefined) {
|
|
185
|
+
throw new Error(`oat: principal "${context.principalId}" needs a "${kind}" value delivered outside HTTP, ` +
|
|
186
|
+
"but the config declares no resolveOutOfBand hook. oat cannot read your mail catcher, " +
|
|
187
|
+
"KV store or webhook sink — supply a function that can.");
|
|
188
|
+
}
|
|
189
|
+
let delay = 200;
|
|
190
|
+
for (let attempt = 1; attempt <= 6; attempt++) {
|
|
191
|
+
const value = await hook({ address, attempt, kind });
|
|
192
|
+
if (value !== null && value !== "")
|
|
193
|
+
return value;
|
|
194
|
+
await new Promise((done) => setTimeout(done, delay));
|
|
195
|
+
delay = Math.min(delay * 2, 3000);
|
|
196
|
+
}
|
|
197
|
+
throw new Error(`oat: no "${kind}" value arrived for ${address} after 6 attempts. The delivery mechanism is ` +
|
|
198
|
+
"either slow beyond the backoff window or not firing at all.");
|
|
199
|
+
}
|
|
200
|
+
/** Wraps a chain in a credential that refreshes itself before it expires. */
|
|
201
|
+
export async function createPrincipal(id, spec, context) {
|
|
202
|
+
let credential = "";
|
|
203
|
+
let expiresAt = null;
|
|
204
|
+
let scope = {};
|
|
205
|
+
const acquire = async () => {
|
|
206
|
+
const result = await runAcquireChain(spec, { ...context, principalId: id });
|
|
207
|
+
credential = result.credential;
|
|
208
|
+
expiresAt = result.expiresAt;
|
|
209
|
+
scope = result.scope;
|
|
210
|
+
};
|
|
211
|
+
await acquire();
|
|
212
|
+
const header = spec.header ?? "authorization";
|
|
213
|
+
const template = spec.template ?? "Bearer {credential}";
|
|
214
|
+
const runtime = {
|
|
215
|
+
/* Whatever the flow bound as an address is what teardown will cascade on. */
|
|
216
|
+
address: scope.address ?? scope.email ?? null,
|
|
217
|
+
expiresAt,
|
|
218
|
+
headers: () => ({ [header]: template.replace("{credential}", credential) }),
|
|
219
|
+
id,
|
|
220
|
+
reacquire: acquire,
|
|
221
|
+
/* Refresh at three quarters of the lifetime, before dispatch rather than mid-flight, so a
|
|
222
|
+
* run stays reproducible and no request is ever retried for timing reasons alone. */
|
|
223
|
+
refreshIfStale: async () => {
|
|
224
|
+
if (expiresAt === null)
|
|
225
|
+
return;
|
|
226
|
+
const issuedWindow = expiresAt - Date.now();
|
|
227
|
+
if (issuedWindow > 0 && issuedWindow < 0.25 * (spec.assumeTtlMs ?? 300_000))
|
|
228
|
+
await acquire();
|
|
229
|
+
else if (issuedWindow <= 0)
|
|
230
|
+
await acquire();
|
|
231
|
+
},
|
|
232
|
+
scope,
|
|
233
|
+
};
|
|
234
|
+
return runtime;
|
|
235
|
+
}
|
|
236
|
+
//# sourceMappingURL=auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/runtime/auth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The check registry.
|
|
3
|
+
*
|
|
4
|
+
* Each check asserts one property that must hold for any correct implementation. They need no
|
|
5
|
+
* ground truth — they compare the API against itself, through independent projections, or against
|
|
6
|
+
* the oracle of what oat just wrote. Check ids are stable: the conformance suite asserts on them.
|
|
7
|
+
*/
|
|
8
|
+
import type { InviteSpec } from "../spec/extensions.ts";
|
|
9
|
+
import type { QueryCapability } from "../spec/extensions.ts";
|
|
10
|
+
import type { OperationModel, SpecModel } from "../spec/graph.ts";
|
|
11
|
+
import type { Client } from "./client.ts";
|
|
12
|
+
import type { FindingCollector } from "./finding.ts";
|
|
13
|
+
import type { SchemaValidator } from "./validate.ts";
|
|
14
|
+
import { type Record_ } from "./world.ts";
|
|
15
|
+
/** A resolved principal as checks see it — identity, lattice position, and how to speak as it. */
|
|
16
|
+
export interface Actor {
|
|
17
|
+
id: string;
|
|
18
|
+
role: string | undefined;
|
|
19
|
+
/** Higher can do everything a lower rank can. Same rank = peers. */
|
|
20
|
+
rank: number;
|
|
21
|
+
headers: () => Record<string, string>;
|
|
22
|
+
/** Tenant identity from config / the auth flow — not the full path scope. */
|
|
23
|
+
roots: Record<string, string>;
|
|
24
|
+
scope: Record<string, string>;
|
|
25
|
+
/** Value the owner puts in an invite body for this principal. */
|
|
26
|
+
inviteAs: string | undefined;
|
|
27
|
+
}
|
|
28
|
+
export interface CheckContext {
|
|
29
|
+
entityName: string;
|
|
30
|
+
identity: string;
|
|
31
|
+
model: SpecModel;
|
|
32
|
+
client: Client;
|
|
33
|
+
findings: FindingCollector;
|
|
34
|
+
scope: Record<string, string>;
|
|
35
|
+
listOp: OperationModel;
|
|
36
|
+
readOp: OperationModel | undefined;
|
|
37
|
+
createOp: OperationModel | undefined;
|
|
38
|
+
updateOp: OperationModel | undefined;
|
|
39
|
+
deleteOp: OperationModel | undefined;
|
|
40
|
+
collectionKey: string | null;
|
|
41
|
+
records: Record_[];
|
|
42
|
+
query: QueryCapability | null;
|
|
43
|
+
softDelete: string | null;
|
|
44
|
+
invite: InviteSpec | null;
|
|
45
|
+
auth: () => Record<string, string>;
|
|
46
|
+
/**
|
|
47
|
+
* Every configured principal, primary first. Isolation checks still use `altAuth` (the first
|
|
48
|
+
* actor whose scope is a different tenant). Lattice checks walk this list by `rank`.
|
|
49
|
+
*/
|
|
50
|
+
actors: Actor[];
|
|
51
|
+
/** Principal in a different tenant, when one is configured. Derived, not `principals[1]`. */
|
|
52
|
+
altAuth: (() => Record<string, string>) | undefined;
|
|
53
|
+
altScope: Record<string, string> | undefined;
|
|
54
|
+
validator: SchemaValidator | undefined;
|
|
55
|
+
seed: number;
|
|
56
|
+
/** Operations on this entity declared async via `x-async`. */
|
|
57
|
+
asyncOps: OperationModel[];
|
|
58
|
+
/** Operations on this entity that declare `x-effects`. */
|
|
59
|
+
effectOps: OperationModel[];
|
|
60
|
+
}
|
|
61
|
+
export interface Check {
|
|
62
|
+
id: string;
|
|
63
|
+
/** Skipped silently when the entity lacks the operations this check needs. */
|
|
64
|
+
applicable: (ctx: CheckContext) => boolean;
|
|
65
|
+
/**
|
|
66
|
+
* Check ids whose failure makes this one's result meaningless. When any has already fired for
|
|
67
|
+
* this entity, the check is skipped rather than reporting a consequence as a separate defect —
|
|
68
|
+
* one root cause should produce one finding, not a page of them.
|
|
69
|
+
*/
|
|
70
|
+
dependsOn?: string[];
|
|
71
|
+
/**
|
|
72
|
+
* What this check needs in order to run at all, in the reader's terms.
|
|
73
|
+
*
|
|
74
|
+
* Reported when `applicable` returns false. Skipping silently would leave someone on an API
|
|
75
|
+
* shaped differently from the fixture believing a quiet run meant a clean one, when in truth
|
|
76
|
+
* half the suite never fired.
|
|
77
|
+
*/
|
|
78
|
+
needs?: string;
|
|
79
|
+
/**
|
|
80
|
+
* Whether the check changes server state. Mutating checks run alone and in order; read-only
|
|
81
|
+
* checks batch together. Two writers racing on the same cohort would make each other's
|
|
82
|
+
* observations wrong, and the resulting finding would describe the interference rather than
|
|
83
|
+
* the backend.
|
|
84
|
+
*/
|
|
85
|
+
mutates?: boolean;
|
|
86
|
+
run: (ctx: CheckContext) => Promise<void>;
|
|
87
|
+
}
|
|
88
|
+
export declare const CHECKS: readonly Check[];
|
|
89
|
+
//# sourceMappingURL=checks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"checks.d.ts","sourceRoot":"","sources":["../../src/runtime/checks.ts"],"names":[],"mappings":"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"}
|