@lovrozagar/oat 0.5.0 → 0.6.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/README.md +51 -31
- package/bin/oat.js +3 -1
- package/dist/cli.js +50 -11
- package/dist/config/define-config.d.ts +17 -3
- package/dist/config/define-config.d.ts.map +1 -1
- package/dist/config/define-config.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -0
- package/dist/runtime/async.d.ts +1 -1
- package/dist/runtime/async.d.ts.map +1 -1
- package/dist/runtime/async.js +10 -3
- package/dist/runtime/async.js.map +1 -1
- package/dist/runtime/auth.d.ts +23 -3
- package/dist/runtime/auth.d.ts.map +1 -1
- package/dist/runtime/auth.js +111 -27
- package/dist/runtime/auth.js.map +1 -1
- package/dist/runtime/checks.d.ts +2 -0
- package/dist/runtime/checks.d.ts.map +1 -1
- package/dist/runtime/checks.js +1 -1
- package/dist/runtime/checks.js.map +1 -1
- package/dist/runtime/client.d.ts +17 -1
- package/dist/runtime/client.d.ts.map +1 -1
- package/dist/runtime/client.js +83 -58
- package/dist/runtime/client.js.map +1 -1
- package/dist/runtime/run.d.ts +0 -6
- package/dist/runtime/run.d.ts.map +1 -1
- package/dist/runtime/run.js +17 -19
- package/dist/runtime/run.js.map +1 -1
- package/dist/runtime/teardown.js +1 -1
- package/dist/runtime/teardown.js.map +1 -1
- package/dist/runtime/world.js +2 -2
- package/dist/runtime/world.js.map +1 -1
- package/labs/oob-auth.config.ts +11 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -86,15 +86,15 @@ oat help # same as oat --help
|
|
|
86
86
|
oat --help
|
|
87
87
|
```
|
|
88
88
|
|
|
89
|
-
Unknown commands and missing required flags exit `2`.
|
|
89
|
+
Unknown commands, unknown flags, and missing required flags exit `2`.
|
|
90
90
|
|
|
91
91
|
## How a run works
|
|
92
92
|
|
|
93
93
|
1. **Load** the OpenAPI document (URL or path). Internal `$ref`s are inlined. External `$ref`s are reported, never fetched.
|
|
94
94
|
2. **Model** entities by inverting `x-invalidate` (or path heuristics) into a read surface per entity.
|
|
95
|
-
3. **Authenticate** every configured principal (static headers and/or an auth flow). Credentials refresh
|
|
95
|
+
3. **Authenticate** every configured principal (static headers and/or an auth flow). Credentials refresh on a countdown from `exp` (default 30s buffer) before every dispatch and each async poll.
|
|
96
96
|
4. **Seed** a cohort of records per entity, in parent-before-child order, using each entity's create operation.
|
|
97
|
-
5. **Test** the matrix, one entity at a time: foundations first, then composition, writes, isolation, declared effects. Checks inside an entity stay ordered.
|
|
97
|
+
5. **Test** the matrix, one entity at a time: foundations first, then composition, writes, isolation, declared effects. Entities run in series. Checks inside an entity stay ordered.
|
|
98
98
|
6. **Teardown** everything the run created, unless `--keep-fixtures` / `keepFixtures: true`.
|
|
99
99
|
|
|
100
100
|
The first principal is the writer. Isolation needs a second principal with different `roots`. A rank lattice needs two or more principals that share `roots` and differ in `rank`. Invite checks need `x-invite` plus a peer with `inviteAs`.
|
|
@@ -188,7 +188,6 @@ Same object. `${NAME}` is interpolated after load. There is no `defineConfig` wr
|
|
|
188
188
|
],
|
|
189
189
|
"seed": 42,
|
|
190
190
|
"cohortSize": 7,
|
|
191
|
-
"concurrency": 1,
|
|
192
191
|
"outDir": "./oat-out"
|
|
193
192
|
}
|
|
194
193
|
```
|
|
@@ -254,24 +253,23 @@ oat help
|
|
|
254
253
|
|
|
255
254
|
Requires `--config`. CLI flags override the same field in the config when both are set.
|
|
256
255
|
|
|
257
|
-
| flag | default | meaning
|
|
258
|
-
| ----------------- | ------------------------------ |
|
|
259
|
-
| `--config` | required | module or JSON file, default export
|
|
260
|
-
| `--base-url` | `config.baseUrl` | backend origin
|
|
261
|
-
| `--only` | `config.only` or all entities | comma-separated entity names as `oat plan` prints them (singularised)
|
|
262
|
-
| `--seed` | `config.seed` or `1` | fixture generation seed (reproducible)
|
|
263
|
-
| `--out` | `config.outDir` or `./oat-out` | report directory
|
|
264
|
-
| `--
|
|
265
|
-
| `--
|
|
266
|
-
| `--
|
|
267
|
-
| `--quiet` | false | no stderr progress; files under `--out` still update |
|
|
256
|
+
| flag | default | meaning |
|
|
257
|
+
| ----------------- | ------------------------------ | --------------------------------------------------------------------- |
|
|
258
|
+
| `--config` | required | module or JSON file, default export |
|
|
259
|
+
| `--base-url` | `config.baseUrl` | backend origin |
|
|
260
|
+
| `--only` | `config.only` or all entities | comma-separated entity names as `oat plan` prints them (singularised) |
|
|
261
|
+
| `--seed` | `config.seed` or `1` | fixture generation seed (reproducible) |
|
|
262
|
+
| `--out` | `config.outDir` or `./oat-out` | report directory |
|
|
263
|
+
| `--max-in-flight` | `config.maxInFlight` or `4` | HTTP requests allowed at once |
|
|
264
|
+
| `--keep-fixtures` | `config.keepFixtures` or false | do not DELETE what the run created |
|
|
265
|
+
| `--quiet` | false | no stderr progress; files under `--out` still update |
|
|
268
266
|
|
|
269
|
-
**Exit codes:** `0` no defects, `1` at least one root-cause finding (`BACKEND_BUG`, `SPEC_BUG`, `SECURITY`, `AMBIGUITY`), `2` usage error (missing `--config`, no principals). `COVERAGE_GAP` and `BLOCKED` do not fail the process.
|
|
267
|
+
**Exit codes:** `0` no defects, `1` at least one root-cause finding (`BACKEND_BUG`, `SPEC_BUG`, `SECURITY`, `AMBIGUITY`), `2` usage error (missing `--config`, no principals, unknown flag). `COVERAGE_GAP` and `BLOCKED` do not fail the process.
|
|
270
268
|
|
|
271
269
|
Example:
|
|
272
270
|
|
|
273
271
|
```bash
|
|
274
|
-
oat run --config oat.config.ts --only store,product --
|
|
272
|
+
oat run --config oat.config.ts --only store,product --out oat-out/prod
|
|
275
273
|
```
|
|
276
274
|
|
|
277
275
|
`--only store,product` matches the **entity names** from `oat plan`, not path segments. `/v1/stores` is usually the entity `store`. If a name is unknown, that entity is simply not tested (the others still run).
|
|
@@ -431,7 +429,6 @@ export default defineConfig({
|
|
|
431
429
|
roots: { org_id: "org_shared" }, // path params oat cannot create; also declarable via x-root
|
|
432
430
|
seed: 42, // fixture generation; a failing run with the same seed is identical
|
|
433
431
|
cohortSize: 12, // records created per entity (default 7)
|
|
434
|
-
concurrency: 1, // entities in parallel
|
|
435
432
|
maxInFlight: 4, // HTTP in flight
|
|
436
433
|
only: ["store", "product"], // restrict entities
|
|
437
434
|
keepFixtures: false,
|
|
@@ -450,7 +447,6 @@ export default defineConfig({
|
|
|
450
447
|
| `roots` | no | `{}` | Shared path params (merged with each principal's `roots`) |
|
|
451
448
|
| `seed` | no | `1` | Integer. Same seed → same fixture bodies |
|
|
452
449
|
| `cohortSize` | no | `7` | Sliced from the 7 built-in variants. Larger repeats the pattern |
|
|
453
|
-
| `concurrency` | no | `1` | Entity-level only. Checks inside one entity stay ordered |
|
|
454
450
|
| `maxInFlight` | no | `4` | Across the whole run |
|
|
455
451
|
| `only` | no | all | Entity names from `oat plan` |
|
|
456
452
|
| `keepFixtures` | no | `false` | Skip DELETE at the end |
|
|
@@ -458,7 +454,7 @@ export default defineConfig({
|
|
|
458
454
|
|
|
459
455
|
`spec` may be a path relative to `baseUrl` (`/v1/openapi/spec`) or an absolute URL or a file.
|
|
460
456
|
|
|
461
|
-
CLI `--base-url`, `--only`, `--seed`, `--out`, `--
|
|
457
|
+
CLI `--base-url`, `--only`, `--seed`, `--out`, `--max-in-flight`, `--keep-fixtures` override these when passed.
|
|
462
458
|
|
|
463
459
|
### Spec loading
|
|
464
460
|
|
|
@@ -540,16 +536,33 @@ principals: [
|
|
|
540
536
|
|
|
541
537
|
```ts
|
|
542
538
|
auth: {
|
|
543
|
-
steps: [ /*
|
|
539
|
+
steps: [ /* register / verify — first acquire only */ ],
|
|
544
540
|
credentialFrom: "$.access_token", // JSON path in the last (or saved) response
|
|
545
541
|
expiresInFrom: "$.expires_in", // lifetime in seconds
|
|
546
542
|
header: "authorization", // default
|
|
547
543
|
template: "Bearer {credential}", // default
|
|
548
544
|
assumeTtlMs: 3600000, // used only if neither expiresInFrom nor JWT exp is present
|
|
545
|
+
refreshBufferMs: 30_000, // optional; default 30s. Proactive when expiresAt - now <= this
|
|
546
|
+
refresh: { // signup flows must set this — re-running steps is not a refresh
|
|
547
|
+
steps: [
|
|
548
|
+
{
|
|
549
|
+
operationId: "auth.refreshToken",
|
|
550
|
+
body: { refresh_token: "{refreshToken}" },
|
|
551
|
+
saveAs: {
|
|
552
|
+
credential: "$.access_token",
|
|
553
|
+
refreshToken: "$.refresh_token",
|
|
554
|
+
},
|
|
555
|
+
},
|
|
556
|
+
],
|
|
557
|
+
},
|
|
549
558
|
}
|
|
550
559
|
```
|
|
551
560
|
|
|
552
|
-
Expiry is `expiresInFrom` (seconds) → JWT `exp` claim → `assumeTtlMs`.
|
|
561
|
+
Expiry is `expiresInFrom` (seconds) → JWT `exp` claim → `assumeTtlMs`. `assumeTtlMs` is only the fallback lifetime when nothing else revealed expiry — it is not used for the refresh threshold when `expiresAt` is known.
|
|
562
|
+
|
|
563
|
+
Refresh is countdown-based (`expiresAt - refreshBufferMs`, default 30s) before every dispatch and before each async poll. Signup flows declare `auth.refresh` (refresh-token operation). Re-running `steps` is only the fallback when `refresh` is omitted (API-key / token-exchange principals). A register-like first hop without `refresh` fails closed (`AUTH_REFRESH_REQUIRED`) rather than signing up again.
|
|
564
|
+
|
|
565
|
+
One 401 → force refresh + single retry with live headers. A second 401 is evidence. 5xx / 429 are never a refresh trigger. `expiresAt === null` (static-header principal) never proactive-refreshes.
|
|
553
566
|
|
|
554
567
|
Each step is one of:
|
|
555
568
|
|
|
@@ -561,7 +574,7 @@ Each step is one of:
|
|
|
561
574
|
body: { key: "${API_KEY}" },
|
|
562
575
|
headers: { "x-extra": "1" },
|
|
563
576
|
query: { realm: "test" },
|
|
564
|
-
saveAs: {
|
|
577
|
+
saveAs: { credential: "$.access_token", refreshToken: "$.refresh_token" },
|
|
565
578
|
saveClaimsFrom: { token: "$.access_token", bind: { orgId: "orgs.0.oid" } },
|
|
566
579
|
bind: { address: "user@example.test" }, // literals, with {name} interpolation
|
|
567
580
|
expect: [200], // default: any 2xx
|
|
@@ -585,7 +598,7 @@ Each step is one of:
|
|
|
585
598
|
{ outOfBand: { address: "{address}", kind: "email-verify", as: "verifyToken" } }
|
|
586
599
|
```
|
|
587
600
|
|
|
588
|
-
Later steps interpolate `{name}` from the flow scope. `saveAs` paths are `$.foo.bar` / `$.orgs.0.id` (dot + numeric index only; no JSON Pointer, no filters). `saveClaimsFrom` reads a JWT's _claims_ (signature is not verified — oat is reading its own credential). `rootsFromFlow` maps path parameter names to those bound keys.
|
|
601
|
+
Later steps interpolate `{name}` from the flow scope. `saveAs` paths are `$.foo.bar` / `$.orgs.0.id` (dot + numeric index only; no JSON Pointer, no filters). Bind `saveAs.refreshToken` from `$.refresh_token` so `{refreshToken}` interpolates in `auth.refresh`. `saveClaimsFrom` reads a JWT's _claims_ (signature is not verified — oat is reading its own credential). `rootsFromFlow` maps path parameter names to those bound keys.
|
|
589
602
|
|
|
590
603
|
`bind` on a step runs **before** the request. `saveAs` / `saveClaimsFrom` run **after**. `credentialFrom` is read from the last HTTP response unless a step already saved `credential`.
|
|
591
604
|
|
|
@@ -598,6 +611,16 @@ function signUp(email: string): AuthFlow {
|
|
|
598
611
|
return {
|
|
599
612
|
credentialFrom: "$.access_token",
|
|
600
613
|
expiresInFrom: "$.access_token_expires_in",
|
|
614
|
+
refresh: {
|
|
615
|
+
steps: [
|
|
616
|
+
{
|
|
617
|
+
body: { refresh_token: "{refreshToken}" },
|
|
618
|
+
method: "POST",
|
|
619
|
+
path: "/v1/auth/refresh",
|
|
620
|
+
saveAs: { credential: "$.access_token", refreshToken: "$.refresh_token" },
|
|
621
|
+
},
|
|
622
|
+
],
|
|
623
|
+
},
|
|
601
624
|
steps: [
|
|
602
625
|
{
|
|
603
626
|
bind: { address: email },
|
|
@@ -610,6 +633,7 @@ function signUp(email: string): AuthFlow {
|
|
|
610
633
|
body: { token: "{verifyToken}" },
|
|
611
634
|
method: "POST",
|
|
612
635
|
path: "/v1/auth/email/verify",
|
|
636
|
+
saveAs: { credential: "$.access_token", refreshToken: "$.refresh_token" },
|
|
613
637
|
saveClaimsFrom: {
|
|
614
638
|
token: "$.access_token",
|
|
615
639
|
bind: { orgId: "orgs.0.oid", projectId: "orgs.0.pids.0" },
|
|
@@ -1471,7 +1495,6 @@ const result = await run({
|
|
|
1471
1495
|
baseUrl: config.baseUrl,
|
|
1472
1496
|
principals: config.principals,
|
|
1473
1497
|
seed: 1,
|
|
1474
|
-
concurrency: 1,
|
|
1475
1498
|
maxInFlight: 4,
|
|
1476
1499
|
onProgress: (snap) => {
|
|
1477
1500
|
// snap.phase, snap.entity, snap.check, snap.message, …
|
|
@@ -1494,7 +1517,7 @@ const { doc: resolved, externalRefs } = dereference(doc)
|
|
|
1494
1517
|
const model = buildModel(resolved)
|
|
1495
1518
|
```
|
|
1496
1519
|
|
|
1497
|
-
Types exported: `OatConfig`, `Principal`, `AuthFlow`, `AuthStep`, `Hooks`, `Uploads`, `UploadRequest`, `UploadFile`, `RunOptions`, `RunResult`, `Finding`, `Verdict`, `Actor`, `SpecModel`, `EntityModel`, `OperationModel`, `OpenApiDocument`, matrix types.
|
|
1520
|
+
Types exported: `OatConfig`, `Principal`, `AuthFlow`, `AuthRefresh`, `AuthStep`, `Hooks`, `Uploads`, `UploadRequest`, `UploadFile`, `RunOptions`, `RunResult`, `Finding`, `Verdict`, `Actor`, `SpecModel`, `EntityModel`, `OperationModel`, `OpenApiDocument`, `AuthRefreshRequiredError`, matrix types.
|
|
1498
1521
|
|
|
1499
1522
|
## CI
|
|
1500
1523
|
|
|
@@ -1508,7 +1531,7 @@ Against **your** API:
|
|
|
1508
1531
|
# GitHub Actions sketch
|
|
1509
1532
|
- run: npm i -D @lovrozagar/oat
|
|
1510
1533
|
- run: oat doctor --spec "$SPEC_URL"
|
|
1511
|
-
- run: oat run --config oat.config.ts
|
|
1534
|
+
- run: oat run --config oat.config.ts
|
|
1512
1535
|
env:
|
|
1513
1536
|
API_TOKEN: ${{ secrets.API_TOKEN }}
|
|
1514
1537
|
API_TOKEN_B: ${{ secrets.API_TOKEN_B }}
|
|
@@ -1516,8 +1539,6 @@ Against **your** API:
|
|
|
1516
1539
|
|
|
1517
1540
|
Gate on exit code 1 (defects). Read `oat-out/oat-report.json` if you need to classify verdicts.
|
|
1518
1541
|
|
|
1519
|
-
Nested APIs: keep `--concurrency 1`. Higher values create children while a parent page-walk is in flight and produce false `pagination.page-walk-covers-set` findings.
|
|
1520
|
-
|
|
1521
1542
|
Wipe leftover rows on a shared database between runs if a previous `--keep-fixtures` or a crashed teardown left data. Leftover rows make numeric/filter checks look like type bugs (`1, 10, 2` from old TEXT-sorted leftovers mixed with a fresh numeric cohort).
|
|
1522
1543
|
|
|
1523
1544
|
## Reference defects (`oat serve --defects`)
|
|
@@ -1599,7 +1620,7 @@ oat run --config labs/local.config.ts --base-url <url>
|
|
|
1599
1620
|
These are deliberate. An agent should not invent a flag for them.
|
|
1600
1621
|
|
|
1601
1622
|
- **No request timeout.** `fetch` waits until the server answers. Watch `idle_ms`.
|
|
1602
|
-
- **No retry on 5xx / 429
|
|
1623
|
+
- **No retry on 5xx / 429.** One 401 → force refresh + single retry. A second 401 is evidence.
|
|
1603
1624
|
- **No OpenAPI `security`.** Put credentials in `principals`. Cookie auth is a `headers: { cookie: "…" }` (or a flow that sets that header).
|
|
1604
1625
|
- **No `servers[]`.** Always set `baseUrl`.
|
|
1605
1626
|
- **No OCR.** Multipart and file parts are sent as dummy / pool / `resolveUpload` bytes. oat checks HTTP status and JSON responses, not whether a PDF is a real invoice.
|
|
@@ -1609,7 +1630,6 @@ These are deliberate. An agent should not invent a flag for them.
|
|
|
1609
1630
|
- **Rate-limit pacing only covers requests oat itself sends.** It cannot see traffic from anything else hitting the backend at the same time, so a shared budget can still trip even when oat's own share was within the declared rate.
|
|
1610
1631
|
- **Equality filter grammar** cannot express `neq` / `gt` / `like` / `and` / `or`. Those checks did-not-apply, they do not fail.
|
|
1611
1632
|
- **`or()` is postgrest-only.**
|
|
1612
|
-
- **Concurrency > 1 on nested graphs invents pagination bugs.** Use `1`.
|
|
1613
1633
|
- **Leftover rows on a shared DB** poison numeric and filter checks. Wipe between runs.
|
|
1614
1634
|
- **`--only` uses plan names** (`store`, not `stores` or `/v1/stores`).
|
|
1615
1635
|
- **Default cohort is 7.** `pagination.limit-respects-documented-max` needs `cohortSize > maxLimit`.
|
package/bin/oat.js
CHANGED
package/dist/cli.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { createWriteStream, writeFileSync } from "node:fs";
|
|
3
3
|
import { mkdir, rm, writeFile } from "node:fs/promises";
|
|
4
4
|
import { dirname, resolve } from "node:path";
|
|
5
|
+
import { pathToFileURL } from "node:url";
|
|
5
6
|
import { interpolate, loadConfig } from "./config/load.js";
|
|
6
7
|
import { report } from "./report/console.js";
|
|
7
8
|
import { renderMatrixGraph, renderMatrixHtml } from "./report/matrix.js";
|
|
@@ -10,7 +11,28 @@ import { createStderrProgress, formatProgressJsonl, formatProgressLine, formatPr
|
|
|
10
11
|
import { renderTeardown } from "./runtime/teardown.js";
|
|
11
12
|
import { buildModel } from "./spec/graph.js";
|
|
12
13
|
import { dereference, loadSpec } from "./spec/load.js";
|
|
13
|
-
|
|
14
|
+
export const KNOWN_FLAGS = new Set([
|
|
15
|
+
"help",
|
|
16
|
+
"config",
|
|
17
|
+
"spec",
|
|
18
|
+
"base-url",
|
|
19
|
+
"only",
|
|
20
|
+
"profile",
|
|
21
|
+
"seed",
|
|
22
|
+
"out",
|
|
23
|
+
"keep-fixtures",
|
|
24
|
+
"max-in-flight",
|
|
25
|
+
"quiet",
|
|
26
|
+
"untagged",
|
|
27
|
+
"backend",
|
|
28
|
+
"dialect",
|
|
29
|
+
"fuzz",
|
|
30
|
+
"precision",
|
|
31
|
+
"max-defects",
|
|
32
|
+
"json",
|
|
33
|
+
"parser",
|
|
34
|
+
]);
|
|
35
|
+
export function parseArgs(argv) {
|
|
14
36
|
const [command = "help", ...rest] = argv;
|
|
15
37
|
const flags = {};
|
|
16
38
|
for (let i = 0; i < rest.length; i++) {
|
|
@@ -29,7 +51,14 @@ function parseArgs(argv) {
|
|
|
29
51
|
}
|
|
30
52
|
return { command, flags };
|
|
31
53
|
}
|
|
32
|
-
|
|
54
|
+
export function unknownFlag(flags) {
|
|
55
|
+
for (const key of Object.keys(flags)) {
|
|
56
|
+
if (!KNOWN_FLAGS.has(key))
|
|
57
|
+
return key;
|
|
58
|
+
}
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
61
|
+
export const USAGE = `oat — OpenAPI Tester
|
|
33
62
|
|
|
34
63
|
oat run --config <file> test a live backend and write a report
|
|
35
64
|
oat plan --spec <url|file> derive and print the test model (offline)
|
|
@@ -46,7 +75,6 @@ Flags
|
|
|
46
75
|
--seed integer seed for fixture generation (default 1)
|
|
47
76
|
--out output directory for the report (default ./oat-out)
|
|
48
77
|
--keep-fixtures leave created records in place instead of tearing them down
|
|
49
|
-
--concurrency entities tested in parallel (default 1)
|
|
50
78
|
--max-in-flight requests allowed in flight at once (default 4)
|
|
51
79
|
--quiet no live progress on stderr (progress.log still written)
|
|
52
80
|
--untagged serve (or test) a document with every x-* tag stripped
|
|
@@ -139,7 +167,6 @@ async function commandRun(flags) {
|
|
|
139
167
|
...(config.profiles === undefined ? {} : { profiles: config.profiles }),
|
|
140
168
|
...(config.rateLimits === undefined ? {} : { rateLimits: config.rateLimits }),
|
|
141
169
|
keepFixtures: flags["keep-fixtures"] === true || config.keepFixtures === true,
|
|
142
|
-
concurrency: Number.parseInt(str(flags, "concurrency") ?? "", 10) || config.concurrency || 1,
|
|
143
170
|
maxInFlight: Number.parseInt(str(flags, "max-in-flight") ?? "", 10) || config.maxInFlight || 4,
|
|
144
171
|
seed: seedFlag === undefined ? (config.seed ?? 1) : Number.parseInt(seedFlag, 10),
|
|
145
172
|
onProgress,
|
|
@@ -195,8 +222,13 @@ async function commandRun(flags) {
|
|
|
195
222
|
* not failures, and a CI gate should react to defects only. */
|
|
196
223
|
return result.findings.filter((f) => f.verdict !== "COVERAGE_GAP" && f.verdict !== "BLOCKED").length > 0 ? 1 : 0;
|
|
197
224
|
}
|
|
198
|
-
async function main() {
|
|
225
|
+
export async function main() {
|
|
199
226
|
const { command, flags } = parseArgs(process.argv.slice(2));
|
|
227
|
+
const unknown = unknownFlag(flags);
|
|
228
|
+
if (unknown !== undefined) {
|
|
229
|
+
process.stderr.write(`oat: unknown flag "--${unknown}"\n\n${USAGE}`);
|
|
230
|
+
return 2;
|
|
231
|
+
}
|
|
200
232
|
if (command === "help" || flags.help === true) {
|
|
201
233
|
process.stdout.write(USAGE);
|
|
202
234
|
return 0;
|
|
@@ -403,10 +435,17 @@ async function main() {
|
|
|
403
435
|
return 2;
|
|
404
436
|
}
|
|
405
437
|
}
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
438
|
+
export function start() {
|
|
439
|
+
main().then((code) => {
|
|
440
|
+
process.exitCode = code;
|
|
441
|
+
}, (error) => {
|
|
442
|
+
process.stderr.write(`oat: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
443
|
+
process.exitCode = 1;
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
/* `node dist/cli.js …` is the test/CI entry. `bin/oat.js` calls start() itself because
|
|
447
|
+
* argv[1] is the bin wrapper, not this module. */
|
|
448
|
+
if (process.argv[1] !== undefined && import.meta.url === pathToFileURL(resolve(process.argv[1])).href) {
|
|
449
|
+
start();
|
|
450
|
+
}
|
|
412
451
|
//# sourceMappingURL=cli.js.map
|
|
@@ -56,8 +56,13 @@ export interface OutOfBandStep {
|
|
|
56
56
|
};
|
|
57
57
|
}
|
|
58
58
|
export type AuthStep = OperationStep | RequestStep | OutOfBandStep;
|
|
59
|
+
/** How to renew a credential without re-running the acquire chain. */
|
|
60
|
+
export interface AuthRefresh {
|
|
61
|
+
/** Run against the existing scope so `{refreshToken}` interpolates. */
|
|
62
|
+
steps: [AuthStep, ...AuthStep[]];
|
|
63
|
+
}
|
|
59
64
|
export interface AuthFlow {
|
|
60
|
-
/** Run in order; values flow forward through the scope. */
|
|
65
|
+
/** Run in order; values flow forward through the scope. First acquire only. */
|
|
61
66
|
steps: [AuthStep, ...AuthStep[]];
|
|
62
67
|
/** Where the credential is in the final response, e.g. `$.access_token`. */
|
|
63
68
|
credentialFrom: string;
|
|
@@ -69,6 +74,17 @@ export interface AuthFlow {
|
|
|
69
74
|
template?: string;
|
|
70
75
|
/** Used only when neither the response nor the credential reveals an expiry. */
|
|
71
76
|
assumeTtlMs?: number;
|
|
77
|
+
/**
|
|
78
|
+
* Proactive refresh fires when `expiresAt - now <= refreshBufferMs`.
|
|
79
|
+
* Defaults to 30_000. Ignored when `expiresAt` is null (static-header principal).
|
|
80
|
+
*/
|
|
81
|
+
refreshBufferMs?: number;
|
|
82
|
+
/**
|
|
83
|
+
* Renew via these steps (typically `auth.refreshToken` + `{refreshToken}`).
|
|
84
|
+
* Signup flows must set this — re-running `steps` would register again.
|
|
85
|
+
* Omitted: re-run `steps` (API-key / token-exchange). Register-like first hops fail closed.
|
|
86
|
+
*/
|
|
87
|
+
refresh?: AuthRefresh;
|
|
72
88
|
}
|
|
73
89
|
export interface Principal {
|
|
74
90
|
id: string;
|
|
@@ -213,8 +229,6 @@ export interface OatConfig {
|
|
|
213
229
|
seed?: number;
|
|
214
230
|
/** Instances seeded per entity. Larger cohorts discriminate more, and cost more. */
|
|
215
231
|
cohortSize?: number;
|
|
216
|
-
/** Entities tested in parallel. Checks within one entity always stay ordered. */
|
|
217
|
-
concurrency?: number;
|
|
218
232
|
/** Requests allowed in flight at once. Past a server's comfort this makes runs slower. */
|
|
219
233
|
maxInFlight?: number;
|
|
220
234
|
/** Restrict the run to these entity names. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"define-config.d.ts","sourceRoot":"","sources":["../../src/config/define-config.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,UAAU,QAAQ;IACjB;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC/B;;;;OAIG;IACH,cAAc,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,CAAA;IAChE,8EAA8E;IAC9E,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC7B,8CAA8C;IAC9C,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;CACjB;AAED,2FAA2F;AAC3F,MAAM,WAAW,aAAc,SAAQ,QAAQ;IAC9C,WAAW,EAAE,MAAM,CAAA;IACnB,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAC9B;AAED,sEAAsE;AACtE,MAAM,WAAW,WAAY,SAAQ,QAAQ;IAC5C,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAC9B;AAED;;;;;;GAMG;AACH,MAAM,WAAW,aAAa;IAC7B,SAAS,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAA;CACxD;AAED,MAAM,MAAM,QAAQ,GAAG,aAAa,GAAG,WAAW,GAAG,aAAa,CAAA;AAIlE,MAAM,WAAW,QAAQ;
|
|
1
|
+
{"version":3,"file":"define-config.d.ts","sourceRoot":"","sources":["../../src/config/define-config.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"define-config.js","sourceRoot":"","sources":["../../src/config/define-config.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;
|
|
1
|
+
{"version":3,"file":"define-config.js","sourceRoot":"","sources":["../../src/config/define-config.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AA4QH;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,YAAY,CAAC,MAAiB;IAC7C,OAAO,MAAM,CAAA;AACd,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { defineConfig } from "./config/define-config.ts";
|
|
2
|
-
export type { AuthFlow, AuthStep, Hooks, OatConfig, OperationStep, OutOfBandRequest, OutOfBandStep, Principal, ProfileSpec, RateLimitSpec, RequestStep, UploadFile, UploadRequest, UploadResolution, Uploads, } from "./config/define-config.ts";
|
|
2
|
+
export type { AuthFlow, AuthRefresh, AuthStep, Hooks, OatConfig, OperationStep, OutOfBandRequest, OutOfBandStep, Principal, ProfileSpec, RateLimitSpec, RequestStep, UploadFile, UploadRequest, UploadResolution, Uploads, } from "./config/define-config.ts";
|
|
3
3
|
export { loadConfig } from "./config/load.ts";
|
|
4
4
|
export { deriveCollectionShape, deriveIdentity } from "./spec/collection.ts";
|
|
5
5
|
export type { CollectionShape } from "./spec/collection.ts";
|
|
@@ -7,6 +7,7 @@ export { buildModel } from "./spec/graph.ts";
|
|
|
7
7
|
export type { EntityModel, OperationModel, SpecModel } from "./spec/graph.ts";
|
|
8
8
|
export { dereference, loadSpec, normalisePath, parseRouteRef } from "./spec/load.ts";
|
|
9
9
|
export type { OpenApiDocument } from "./spec/types.ts";
|
|
10
|
+
export { AuthRefreshRequiredError } from "./runtime/auth.ts";
|
|
10
11
|
export { run } from "./runtime/run.ts";
|
|
11
12
|
export type { RunOptions, RunResult } from "./runtime/run.ts";
|
|
12
13
|
export type { Actor } from "./runtime/checks.ts";
|
package/dist/index.js
CHANGED
|
@@ -3,6 +3,7 @@ export { loadConfig } from "./config/load.js";
|
|
|
3
3
|
export { deriveCollectionShape, deriveIdentity } from "./spec/collection.js";
|
|
4
4
|
export { buildModel } from "./spec/graph.js";
|
|
5
5
|
export { dereference, loadSpec, normalisePath, parseRouteRef } from "./spec/load.js";
|
|
6
|
+
export { AuthRefreshRequiredError } from "./runtime/auth.js";
|
|
6
7
|
export { run } from "./runtime/run.js";
|
|
7
8
|
export { buildMatrixGraph, matrixViewFromReport, renderMatrixGraph, renderMatrixHtml } from "./report/matrix.js";
|
|
8
9
|
export { coverageByCheck, renderConsole, renderJson, renderMarkdown, renderRepros } from "./report/render.js";
|
package/dist/runtime/async.d.ts
CHANGED
|
@@ -30,5 +30,5 @@ export declare function matchesPredicate(record: Record<string, unknown>, expres
|
|
|
30
30
|
* behave the same way, and the interval is fixed rather than exponential — the point is to
|
|
31
31
|
* observe when the state changes, not to be gentle with an API oat is already hammering.
|
|
32
32
|
*/
|
|
33
|
-
export declare function driveAsync(client: Client, spec: AsyncSpec, receipt: unknown, scope: Record<string, string>, headers: Record<string, string>): Promise<AsyncOutcome>;
|
|
33
|
+
export declare function driveAsync(client: Client, spec: AsyncSpec, receipt: unknown, scope: Record<string, string>, headers: () => Record<string, string>, refreshIfStale?: (force?: boolean) => Promise<void>): Promise<AsyncOutcome>;
|
|
34
34
|
//# sourceMappingURL=async.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"async.d.ts","sourceRoot":"","sources":["../../src/runtime/async.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAA;AACtD,OAAO,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAGnD,MAAM,WAAW,YAAY;IAC5B,kDAAkD;IAClD,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAA;IACxC,SAAS,EAAE,QAAQ,EAAE,CAAA;IACrB,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,OAAO,CAAA;IACjB,2FAA2F;IAC3F,SAAS,EAAE,OAAO,CAAA;CAClB;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CA0B7F;AAcD;;;;;;GAMG;AACH,wBAAsB,UAAU,CAC/B,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,SAAS,EACf,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC7B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,
|
|
1
|
+
{"version":3,"file":"async.d.ts","sourceRoot":"","sources":["../../src/runtime/async.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAA;AACtD,OAAO,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAGnD,MAAM,WAAW,YAAY;IAC5B,kDAAkD;IAClD,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAA;IACxC,SAAS,EAAE,QAAQ,EAAE,CAAA;IACrB,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,OAAO,CAAA;IACjB,2FAA2F;IAC3F,SAAS,EAAE,OAAO,CAAA;CAClB;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CA0B7F;AAcD;;;;;;GAMG;AACH,wBAAsB,UAAU,CAC/B,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,SAAS,EACf,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC7B,OAAO,EAAE,MAAM,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACrC,cAAc,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,GACjD,OAAO,CAAC,YAAY,CAAC,CA4EvB"}
|
package/dist/runtime/async.js
CHANGED
|
@@ -60,7 +60,7 @@ function readPath(body, path) {
|
|
|
60
60
|
* behave the same way, and the interval is fixed rather than exponential — the point is to
|
|
61
61
|
* observe when the state changes, not to be gentle with an API oat is already hammering.
|
|
62
62
|
*/
|
|
63
|
-
export async function driveAsync(client, spec, receipt, scope, headers) {
|
|
63
|
+
export async function driveAsync(client, spec, receipt, scope, headers, refreshIfStale) {
|
|
64
64
|
const exchanges = [];
|
|
65
65
|
const began = performance.now();
|
|
66
66
|
const pollScope = { ...scope };
|
|
@@ -79,7 +79,7 @@ export async function driveAsync(client, spec, receipt, scope, headers) {
|
|
|
79
79
|
try {
|
|
80
80
|
path = fillPath(template, pollScope);
|
|
81
81
|
}
|
|
82
|
-
catch
|
|
82
|
+
catch {
|
|
83
83
|
return {
|
|
84
84
|
elapsedMs: performance.now() - began,
|
|
85
85
|
exchanges,
|
|
@@ -91,7 +91,14 @@ export async function driveAsync(client, spec, receipt, scope, headers) {
|
|
|
91
91
|
}
|
|
92
92
|
let polls = 0;
|
|
93
93
|
while (performance.now() - began < spec.timeoutMs) {
|
|
94
|
-
|
|
94
|
+
/* Live headers + countdown refresh per poll: a long POST can outlive remaining TTL,
|
|
95
|
+
* and a snapshot taken before the loop would keep sending the dead Bearer. */
|
|
96
|
+
if (refreshIfStale !== undefined)
|
|
97
|
+
await refreshIfStale();
|
|
98
|
+
const exchange = await client.request(method, path, {
|
|
99
|
+
headers,
|
|
100
|
+
...(refreshIfStale === undefined ? {} : { refreshIfStale }),
|
|
101
|
+
});
|
|
95
102
|
exchanges.push(exchange);
|
|
96
103
|
polls += 1;
|
|
97
104
|
if (exchange.status < 300) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"async.js","sourceRoot":"","sources":["../../src/runtime/async.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAarC;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAA+B,EAAE,UAAkB;IACnF,MAAM,QAAQ,GAAG,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IACtC,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,KAAK,CAAA;IACrC,MAAM,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,QAAyC,CAAA;IAC7D,MAAM,GAAG,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IACvC,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAA;IAE5B,QAAQ,EAAE,EAAE,CAAC;QACZ,KAAK,IAAI;YACR,OAAO,MAAM,CAAC,MAAM,CAAC,KAAK,GAAG,CAAA;QAC9B,KAAK,KAAK;YACT,OAAO,MAAM,CAAC,MAAM,CAAC,KAAK,GAAG,CAAA;QAC9B,KAAK,IAAI;YACR,OAAO,GAAG;iBACR,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;iBACvB,KAAK,CAAC,GAAG,CAAC;iBACV,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;iBACpB,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAA;QAC3B,KAAK,IAAI;YACR,IAAI,GAAG,KAAK,MAAM;gBAAE,OAAO,MAAM,KAAK,IAAI,IAAI,MAAM,KAAK,SAAS,CAAA;YAClE,IAAI,GAAG,KAAK,MAAM;gBAAE,OAAO,MAAM,KAAK,IAAI,CAAA;YAC1C,IAAI,GAAG,KAAK,OAAO;gBAAE,OAAO,MAAM,KAAK,KAAK,CAAA;YAC5C,OAAO,KAAK,CAAA;QACb;YACC,OAAO,KAAK,CAAA;IACd,CAAC;AACF,CAAC;AAED,SAAS,QAAQ,CAAC,IAAa,EAAE,IAAY;IAC5C,IAAI,IAAI,GAAY,IAAI,CAAA;IACxB,KAAK,MAAM,OAAO,IAAI,IAAI;SACxB,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC;SACrB,KAAK,CAAC,GAAG,CAAC;SACV,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;QACnB,IAAI,IAAI,KAAK,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ;YAAE,OAAO,SAAS,CAAA;QAC/D,IAAI,GAAI,IAAgC,CAAC,OAAO,CAAC,CAAA;IAClD,CAAC;IACD,OAAO,IAAI,CAAA;AACZ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAC/B,MAAc,EACd,IAAe,EACf,OAAgB,EAChB,KAA6B,EAC7B,
|
|
1
|
+
{"version":3,"file":"async.js","sourceRoot":"","sources":["../../src/runtime/async.ts"],"names":[],"mappings":"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"}
|
package/dist/runtime/auth.d.ts
CHANGED
|
@@ -6,10 +6,16 @@
|
|
|
6
6
|
* delivered outside HTTP — verification tokens, OTPs, magic links — comes from a hook the caller
|
|
7
7
|
* writes. That hook is the entire backend-coupling surface.
|
|
8
8
|
*/
|
|
9
|
-
import type { AuthFlow, Hooks } from "../config/define-config.ts";
|
|
9
|
+
import type { AuthFlow, AuthStep, Hooks } from "../config/define-config.ts";
|
|
10
10
|
import type { SpecModel } from "../spec/graph.ts";
|
|
11
11
|
import type { Client } from "./client.ts";
|
|
12
12
|
export type AcquireSpec = AuthFlow;
|
|
13
|
+
export declare const DEFAULT_REFRESH_BUFFER_MS = 30000;
|
|
14
|
+
/** Signup acquire without `auth.refresh` — re-running `steps` would register again. */
|
|
15
|
+
export declare class AuthRefreshRequiredError extends Error {
|
|
16
|
+
readonly code: "AUTH_REFRESH_REQUIRED";
|
|
17
|
+
constructor(principalId: string);
|
|
18
|
+
}
|
|
13
19
|
export interface PrincipalRuntime {
|
|
14
20
|
id: string;
|
|
15
21
|
headers: () => Record<string, string>;
|
|
@@ -17,10 +23,16 @@ export interface PrincipalRuntime {
|
|
|
17
23
|
address: string | null;
|
|
18
24
|
/** Values bound during acquisition — roots discovered from the credential live here too. */
|
|
19
25
|
scope: Record<string, string>;
|
|
20
|
-
|
|
26
|
+
/**
|
|
27
|
+
* Renew when `expiresAt - now <= refreshBufferMs`, or immediately when `force` is set (401).
|
|
28
|
+
* `expiresAt === null` never proactive-refreshes. Single-flight per principal.
|
|
29
|
+
*/
|
|
30
|
+
refreshIfStale: (force?: boolean) => Promise<void>;
|
|
21
31
|
/** Forces reacquisition, used when a control probe proves the credential died early. */
|
|
22
32
|
reacquire: () => Promise<void>;
|
|
23
33
|
expiresAt: number | null;
|
|
34
|
+
/** True when `headers` carry a credential this principal issued (current or previous). */
|
|
35
|
+
matches: (headers: Record<string, string>) => boolean;
|
|
24
36
|
}
|
|
25
37
|
export declare function readPath(body: unknown, path: string): unknown;
|
|
26
38
|
/**
|
|
@@ -35,12 +47,20 @@ export interface AcquireContext {
|
|
|
35
47
|
hooks: Hooks;
|
|
36
48
|
principalId: string;
|
|
37
49
|
}
|
|
38
|
-
/** Runs
|
|
50
|
+
/** Runs an auth step list against an existing scope, returning the credential and bindings. */
|
|
51
|
+
export declare function runAuthSteps(steps: readonly AuthStep[], spec: AcquireSpec, context: AcquireContext, initialScope?: Record<string, string>): Promise<{
|
|
52
|
+
credential: string;
|
|
53
|
+
scope: Record<string, string>;
|
|
54
|
+
expiresAt: number | null;
|
|
55
|
+
}>;
|
|
56
|
+
/** Runs the acquire chain once, returning the credential and everything bound along the way. */
|
|
39
57
|
export declare function runAcquireChain(spec: AcquireSpec, context: AcquireContext): Promise<{
|
|
40
58
|
credential: string;
|
|
41
59
|
scope: Record<string, string>;
|
|
42
60
|
expiresAt: number | null;
|
|
43
61
|
}>;
|
|
62
|
+
/** First HTTP hop looks like signup — re-running `steps` would 409, not refresh. */
|
|
63
|
+
export declare function acquireLooksLikeRegister(spec: AcquireSpec, model: SpecModel): boolean;
|
|
44
64
|
/** Wraps a chain in a credential that refreshes itself before it expires. */
|
|
45
65
|
export declare function createPrincipal(id: string, spec: AcquireSpec, context: AcquireContext): Promise<PrincipalRuntime>;
|
|
46
66
|
//# sourceMappingURL=auth.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/runtime/auth.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,QAAQ,
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/runtime/auth.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAA8B,MAAM,4BAA4B,CAAA;AAEvG,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAEjD,OAAO,KAAK,EAAE,MAAM,EAAY,MAAM,aAAa,CAAA;AAInD,MAAM,MAAM,WAAW,GAAG,QAAQ,CAAA;AAElC,eAAO,MAAM,yBAAyB,QAAS,CAAA;AAE/C,uFAAuF;AACvF,qBAAa,wBAAyB,SAAQ,KAAK;IAClD,QAAQ,CAAC,IAAI,EAAG,uBAAuB,CAAS;gBAEpC,WAAW,EAAE,MAAM;CAO/B;AAED,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;;;OAGG;IACH,cAAc,EAAE,CAAC,KAAK,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAClD,wFAAwF;IACxF,SAAS,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IAC9B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,0FAA0F;IAC1F,OAAO,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,OAAO,CAAA;CACrD;AAED,wBAAgB,QAAQ,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAe7D;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,+FAA+F;AAC/F,wBAAsB,YAAY,CACjC,KAAK,EAAE,SAAS,QAAQ,EAAE,EAC1B,IAAI,EAAE,WAAW,EACjB,OAAO,EAAE,cAAc,EACvB,YAAY,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GACvC,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,CAkF1F;AAED,gGAAgG;AAChG,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,CAE1F;AAgBD,oFAAoF;AACpF,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,SAAS,GAAG,OAAO,CAGrF;AAkID,6EAA6E;AAC7E,wBAAsB,eAAe,CACpC,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,WAAW,EACjB,OAAO,EAAE,cAAc,GACrB,OAAO,CAAC,gBAAgB,CAAC,CA4E3B"}
|