@lovrozagar/oat 0.5.0 → 0.6.1
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 +65 -37
- 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 +20 -5
- package/dist/runtime/async.d.ts.map +1 -1
- package/dist/runtime/async.js +65 -9
- 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 +67 -20
- 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 +86 -58
- package/dist/runtime/client.js.map +1 -1
- package/dist/runtime/fixture.d.ts.map +1 -1
- package/dist/runtime/fixture.js +86 -21
- package/dist/runtime/fixture.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/sse.d.ts +17 -0
- package/dist/runtime/sse.d.ts.map +1 -0
- package/dist/runtime/sse.js +62 -0
- package/dist/runtime/sse.js.map +1 -0
- 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/dist/spec/collection.d.ts +2 -0
- package/dist/spec/collection.d.ts.map +1 -1
- package/dist/spec/collection.js +16 -0
- package/dist/spec/collection.js.map +1 -1
- package/dist/spec/graph.d.ts +2 -0
- package/dist/spec/graph.d.ts.map +1 -1
- package/dist/spec/graph.js +2 -1
- package/dist/spec/graph.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" },
|
|
@@ -783,7 +807,7 @@ Numbers use the ladder `1, 2, 5, 10, 20, 50, 100` so **lexical order ≠ numeric
|
|
|
783
807
|
|
|
784
808
|
Empty schemas (`{}`, `true`, `additionalProperties: {}`) and cyclic `$ref`s after inlining stop the walk — they become a scalar or `{}`, never another object descent. A `RangeError` during generation is a `COVERAGE_GAP` on that entity naming the operationId and JSON pointer (`fixture generation overflow on table.create (/)`), not `blocked by unknown`.
|
|
785
809
|
|
|
786
|
-
String fields honour `format` and `
|
|
810
|
+
String fields honour `format`, `pattern`, and `minLength` together: `email` → `oat-{variant}-{index}@example.test`, `uri` / `url` → `https://example.test/...`, `uuid` → a fixed-shape UUID, `pattern` → a string that matches (or the field is omitted / the entity is a gap). A generated string is padded to `minLength` (repeat the last character) without breaking `pattern`. `"Quarterly Report N"` is only used when the document does not constrain the string. `minLength` greater than `maxLength` omits an optional field and records `missingRequired` on a required one.
|
|
787
811
|
|
|
788
812
|
An operation with `x-invite` is not `entity.create`. oat does not POST a generated invitee. The invite check sends `granteeField` = the peer's `inviteAs`. Missing `inviteAs` is a coverage gap naming the tag.
|
|
789
813
|
|
|
@@ -870,7 +894,7 @@ Collection shape is derived from the success JSON schema, not from hardcoded wra
|
|
|
870
894
|
| page | `page`, `pageNumber`, `page_number`, `offset` |
|
|
871
895
|
| limit | `limit`, `perPage`, `per_page`, `pageSize`, `page_size` |
|
|
872
896
|
|
|
873
|
-
Success schema is the first JSON media type on responses `200`, `201`, `202`, `2XX`, or `default`. Request schema is taken from `requestBody` preferring `multipart/form-data`, then `application/x-www-form-urlencoded`, then JSON. A
|
|
897
|
+
Success schema is the first JSON media type on responses `200`, `201`, `202`, `2XX`, or `default`. Request schema is taken from `requestBody` preferring `multipart/form-data`, then `application/x-www-form-urlencoded`, then JSON. A success response that lists `text/event-stream` is a stream: oat consumes it to the end and does not treat the raw body as a JSON schema defect. Media type is the stream tag — there is no `x-stream`.
|
|
874
898
|
|
|
875
899
|
## OpenAPI meta tags
|
|
876
900
|
|
|
@@ -949,6 +973,8 @@ An invite operation is **not** the entity's fixture create, even when it is `POS
|
|
|
949
973
|
|
|
950
974
|
Timeline asserted: cannot read → invite → still cannot → accept → can → revoke → cannot.
|
|
951
975
|
|
|
976
|
+
Accept and revoke send the documented JSON request body when the operation declares one, filled from the invite token / grant id (and other known scope values). A path-only accept (`POST /invites/{token}` with no body) stays path-only — oat does not invent a body.
|
|
977
|
+
|
|
952
978
|
**Fallback:** the check does not run.
|
|
953
979
|
|
|
954
980
|
### `x-query`
|
|
@@ -983,11 +1009,17 @@ x-async:
|
|
|
983
1009
|
pollIntervalMs: 2000
|
|
984
1010
|
```
|
|
985
1011
|
|
|
986
|
-
|
|
1012
|
+
`x-async` means **POST is a receipt, then poll**. It is not how you mark a stream. A stream is a success response that lists `text/event-stream`.
|
|
1013
|
+
|
|
1014
|
+
When the start response is JSON, oat polls `poll` until `until` matches, then treats that payload as the result. `poll` may be an operationId or `"GET /path/{id}"`. Defaults: `timeoutMs: 120000`, `pollIntervalMs: 2000`.
|
|
1015
|
+
|
|
1016
|
+
When the start response is `text/event-stream`, oat parses `event:` / `data:` frames. `data` is JSON when it starts with `{` / `[`. `idFrom` is resolved against **each event's JSON `data`**, first hit wins — `$.batch_id` on `event: batch` data `{ "batch_id": "…" }` works. A frame named `complete` / `error`, or one whose `data` matches `until`, is the terminal record; `successWhen` is applied to it and oat does **not** GET the poll route. Poll only if the stream ended without a terminal frame **and** `idFrom` resolved.
|
|
1017
|
+
|
|
1018
|
+
A spec may leave both `x-async` and `text/event-stream` on the same operation. The stream is still the result; `x-async` only supplies `idFrom` / `until` / the poll fallback.
|
|
987
1019
|
|
|
988
1020
|
`until` / `successWhen` use the same `field.op.value` predicates as filters (`eq`, `in`, …).
|
|
989
1021
|
|
|
990
|
-
**Fallback:** treated as synchronous; async checks are `COVERAGE_GAP`.
|
|
1022
|
+
**Fallback:** treated as synchronous; async checks are `COVERAGE_GAP`. A 2xx stream without `x-async` is consumed and recorded; async checks do not run.
|
|
991
1023
|
|
|
992
1024
|
### `x-effects`
|
|
993
1025
|
|
|
@@ -1211,8 +1243,8 @@ Order is fixed (foundations first) so cascade suppression has a cause to point a
|
|
|
1211
1243
|
| `softdelete.absent-from-default-list` | a soft-deleted row is gone from the default list | `x-soft-delete` | `list.read-after-write` |
|
|
1212
1244
|
| `invalidation.declared-route-changes` | after a write, the other entity's listed route actually changes | `x-invalidate` naming another entity | list, persist |
|
|
1213
1245
|
| `effects.declared-effect-occurs` | `x-effects` cardinality delta is observed on the named list | `x-effects` | `list.read-after-write` |
|
|
1214
|
-
| `async.reaches-terminal-state` |
|
|
1215
|
-
| `async.receipt-identifies-the-job` | `idFrom` on the receipt resolves to a pollable job
|
|
1246
|
+
| `async.reaches-terminal-state` | `x-async` reaches `until` (poll, or a terminal SSE frame) before `timeoutMs` | `x-async` | — |
|
|
1247
|
+
| `async.receipt-identifies-the-job` | `idFrom` on the receipt (JSON object or SSE event JSON) resolves to a pollable job | `x-async` + `idFrom` | — |
|
|
1216
1248
|
| `patch.minimality` | PATCH `{ name }` does not clear other writable fields | update + item route | — |
|
|
1217
1249
|
| `idempotency.replay-does-not-duplicate` | same Idempotency-Key + same body does not create a second row | create + documented Idempotency-Key header | list, persist |
|
|
1218
1250
|
| `delete.absent-record-returns-404` | DELETE of a missing id is 404, not 200 | delete | — |
|
|
@@ -1471,7 +1503,6 @@ const result = await run({
|
|
|
1471
1503
|
baseUrl: config.baseUrl,
|
|
1472
1504
|
principals: config.principals,
|
|
1473
1505
|
seed: 1,
|
|
1474
|
-
concurrency: 1,
|
|
1475
1506
|
maxInFlight: 4,
|
|
1476
1507
|
onProgress: (snap) => {
|
|
1477
1508
|
// snap.phase, snap.entity, snap.check, snap.message, …
|
|
@@ -1494,7 +1525,7 @@ const { doc: resolved, externalRefs } = dereference(doc)
|
|
|
1494
1525
|
const model = buildModel(resolved)
|
|
1495
1526
|
```
|
|
1496
1527
|
|
|
1497
|
-
Types exported: `OatConfig`, `Principal`, `AuthFlow`, `AuthStep`, `Hooks`, `Uploads`, `UploadRequest`, `UploadFile`, `RunOptions`, `RunResult`, `Finding`, `Verdict`, `Actor`, `SpecModel`, `EntityModel`, `OperationModel`, `OpenApiDocument`, matrix types.
|
|
1528
|
+
Types exported: `OatConfig`, `Principal`, `AuthFlow`, `AuthRefresh`, `AuthStep`, `Hooks`, `Uploads`, `UploadRequest`, `UploadFile`, `RunOptions`, `RunResult`, `Finding`, `Verdict`, `Actor`, `SpecModel`, `EntityModel`, `OperationModel`, `OpenApiDocument`, `AuthRefreshRequiredError`, matrix types.
|
|
1498
1529
|
|
|
1499
1530
|
## CI
|
|
1500
1531
|
|
|
@@ -1508,7 +1539,7 @@ Against **your** API:
|
|
|
1508
1539
|
# GitHub Actions sketch
|
|
1509
1540
|
- run: npm i -D @lovrozagar/oat
|
|
1510
1541
|
- run: oat doctor --spec "$SPEC_URL"
|
|
1511
|
-
- run: oat run --config oat.config.ts
|
|
1542
|
+
- run: oat run --config oat.config.ts
|
|
1512
1543
|
env:
|
|
1513
1544
|
API_TOKEN: ${{ secrets.API_TOKEN }}
|
|
1514
1545
|
API_TOKEN_B: ${{ secrets.API_TOKEN_B }}
|
|
@@ -1516,8 +1547,6 @@ Against **your** API:
|
|
|
1516
1547
|
|
|
1517
1548
|
Gate on exit code 1 (defects). Read `oat-out/oat-report.json` if you need to classify verdicts.
|
|
1518
1549
|
|
|
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
1550
|
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
1551
|
|
|
1523
1552
|
## Reference defects (`oat serve --defects`)
|
|
@@ -1599,7 +1628,7 @@ oat run --config labs/local.config.ts --base-url <url>
|
|
|
1599
1628
|
These are deliberate. An agent should not invent a flag for them.
|
|
1600
1629
|
|
|
1601
1630
|
- **No request timeout.** `fetch` waits until the server answers. Watch `idle_ms`.
|
|
1602
|
-
- **No retry on 5xx / 429
|
|
1631
|
+
- **No retry on 5xx / 429.** One 401 → force refresh + single retry. A second 401 is evidence.
|
|
1603
1632
|
- **No OpenAPI `security`.** Put credentials in `principals`. Cookie auth is a `headers: { cookie: "…" }` (or a flow that sets that header).
|
|
1604
1633
|
- **No `servers[]`.** Always set `baseUrl`.
|
|
1605
1634
|
- **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 +1638,6 @@ These are deliberate. An agent should not invent a flag for them.
|
|
|
1609
1638
|
- **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
1639
|
- **Equality filter grammar** cannot express `neq` / `gt` / `like` / `and` / `or`. Those checks did-not-apply, they do not fail.
|
|
1611
1640
|
- **`or()` is postgrest-only.**
|
|
1612
|
-
- **Concurrency > 1 on nested graphs invents pagination bugs.** Use `1`.
|
|
1613
1641
|
- **Leftover rows on a shared DB** poison numeric and filter checks. Wipe between runs.
|
|
1614
1642
|
- **`--only` uses plan names** (`store`, not `stores` or `/v1/stores`).
|
|
1615
1643
|
- **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
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Async operation support.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* most of the surface.
|
|
4
|
+
* `x-async` means the HTTP response is a receipt, then oat polls. A `text/event-stream` body is
|
|
5
|
+
* the result itself — parse events, do not `JSON.parse` the raw string. Poll only when the
|
|
6
|
+
* stream ends without a terminal frame and `idFrom` resolved.
|
|
8
7
|
*/
|
|
9
8
|
import type { AsyncSpec } from "../spec/extensions.ts";
|
|
10
9
|
import type { Client, Exchange } from "./client.ts";
|
|
10
|
+
import { type SseEvent } from "./sse.ts";
|
|
11
11
|
export interface AsyncOutcome {
|
|
12
12
|
/** Terminal state reached, or null on timeout. */
|
|
13
13
|
terminal: Record<string, unknown> | null;
|
|
@@ -23,6 +23,21 @@ export interface AsyncOutcome {
|
|
|
23
23
|
* language as the filter grammar rather than inventing a second one.
|
|
24
24
|
*/
|
|
25
25
|
export declare function matchesPredicate(record: Record<string, unknown>, expression: string): boolean;
|
|
26
|
+
export declare function readPath(body: unknown, path: string): unknown;
|
|
27
|
+
/**
|
|
28
|
+
* Resolve `idFrom` against a JSON receipt, or against each SSE event's JSON `data` (first hit).
|
|
29
|
+
*/
|
|
30
|
+
export declare function resolveAsyncId(receipt: unknown, idFrom: string): unknown;
|
|
31
|
+
export interface StreamAsyncView {
|
|
32
|
+
id: unknown;
|
|
33
|
+
/** Event data that carried `idFrom`, for binding the poll path. */
|
|
34
|
+
idRecord: Record<string, unknown> | null;
|
|
35
|
+
/** `complete` / `error` frame, or a frame whose `data` matches `until`. */
|
|
36
|
+
terminal: Record<string, unknown> | null;
|
|
37
|
+
events: SseEvent[];
|
|
38
|
+
}
|
|
39
|
+
/** Read job id and terminal state from SSE frames. `null` when the body is not a stream. */
|
|
40
|
+
export declare function inspectStreamAsync(receipt: unknown, spec: AsyncSpec): StreamAsyncView | null;
|
|
26
41
|
/**
|
|
27
42
|
* Polls until `until` matches, the timeout expires, or the poll route stops responding.
|
|
28
43
|
*
|
|
@@ -30,5 +45,5 @@ export declare function matchesPredicate(record: Record<string, unknown>, expres
|
|
|
30
45
|
* behave the same way, and the interval is fixed rather than exponential — the point is to
|
|
31
46
|
* observe when the state changes, not to be gentle with an API oat is already hammering.
|
|
32
47
|
*/
|
|
33
|
-
export declare function driveAsync(client: Client, spec: AsyncSpec, receipt: unknown, scope: Record<string, string>, headers: Record<string, string>): Promise<AsyncOutcome>;
|
|
48
|
+
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
49
|
//# sourceMappingURL=async.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"async.d.ts","sourceRoot":"","sources":["../../src/runtime/async.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"async.d.ts","sourceRoot":"","sources":["../../src/runtime/async.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAA;AACtD,OAAO,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AACnD,OAAO,EAAE,KAAK,QAAQ,EAAa,MAAM,UAAU,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;AAED,wBAAgB,QAAQ,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAU7D;AAOD;;GAEG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAYxE;AAED,MAAM,WAAW,eAAe;IAC/B,EAAE,EAAE,OAAO,CAAA;IACX,mEAAmE;IACnE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAA;IACxC,2EAA2E;IAC3E,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAA;IACxC,MAAM,EAAE,QAAQ,EAAE,CAAA;CAClB;AAED,4FAA4F;AAC5F,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,GAAG,eAAe,GAAG,IAAI,CAsB5F;AAED;;;;;;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"}
|