@lovrozagar/oat 0.7.7 → 0.8.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 +90 -27
- package/dist/cli.js +4 -0
- package/dist/config/define-config.d.ts +39 -7
- package/dist/config/define-config.d.ts.map +1 -1
- package/dist/config/define-config.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/reference/defects.d.ts +7 -0
- package/dist/reference/defects.d.ts.map +1 -1
- package/dist/reference/defects.js +7 -0
- package/dist/reference/defects.js.map +1 -1
- package/dist/reference/http.d.ts.map +1 -1
- package/dist/reference/http.js +37 -0
- package/dist/reference/http.js.map +1 -1
- package/dist/reference/model.d.ts +2 -0
- package/dist/reference/model.d.ts.map +1 -1
- package/dist/reference/model.js +1 -0
- package/dist/reference/model.js.map +1 -1
- package/dist/reference/spec.d.ts.map +1 -1
- package/dist/reference/spec.js +4 -2
- package/dist/reference/spec.js.map +1 -1
- package/dist/report/console.d.ts.map +1 -1
- package/dist/report/console.js +58 -0
- package/dist/report/console.js.map +1 -1
- package/dist/runtime/auth.d.ts +4 -0
- package/dist/runtime/auth.d.ts.map +1 -1
- package/dist/runtime/auth.js +43 -12
- package/dist/runtime/auth.js.map +1 -1
- package/dist/runtime/checks.d.ts +8 -0
- package/dist/runtime/checks.d.ts.map +1 -1
- package/dist/runtime/checks.js +220 -13
- package/dist/runtime/checks.js.map +1 -1
- package/dist/runtime/client.d.ts +15 -0
- package/dist/runtime/client.d.ts.map +1 -1
- package/dist/runtime/client.js +55 -6
- package/dist/runtime/client.js.map +1 -1
- package/dist/runtime/cookies.d.ts +58 -0
- package/dist/runtime/cookies.d.ts.map +1 -0
- package/dist/runtime/cookies.js +191 -0
- package/dist/runtime/cookies.js.map +1 -0
- package/dist/runtime/exchanges.d.ts.map +1 -1
- package/dist/runtime/exchanges.js +14 -0
- package/dist/runtime/exchanges.js.map +1 -1
- package/dist/runtime/fixture.d.ts +10 -0
- package/dist/runtime/fixture.d.ts.map +1 -1
- package/dist/runtime/fixture.js +107 -0
- package/dist/runtime/fixture.js.map +1 -1
- package/dist/runtime/run.d.ts.map +1 -1
- package/dist/runtime/run.js +49 -2
- package/dist/runtime/run.js.map +1 -1
- package/dist/runtime/unique.d.ts +47 -0
- package/dist/runtime/unique.d.ts.map +1 -0
- package/dist/runtime/unique.js +185 -0
- package/dist/runtime/unique.js.map +1 -0
- package/dist/runtime/world.d.ts +8 -0
- package/dist/runtime/world.d.ts.map +1 -1
- package/dist/runtime/world.js +53 -8
- package/dist/runtime/world.js.map +1 -1
- package/dist/spec/extensions.d.ts +21 -0
- package/dist/spec/extensions.d.ts.map +1 -1
- package/dist/spec/extensions.js +64 -0
- package/dist/spec/extensions.js.map +1 -1
- package/dist/spec/graph.d.ts +8 -0
- package/dist/spec/graph.d.ts.map +1 -1
- package/dist/spec/graph.js +7 -1
- package/dist/spec/graph.js.map +1 -1
- package/labs/annotated-openapi.yaml +25 -0
- package/labs/oob-auth.config.ts +56 -20
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -103,7 +103,7 @@ The first principal is the writer. Isolation needs a second principal with diffe
|
|
|
103
103
|
|
|
104
104
|
oat never needs ground truth about your data. A filter and its negation must partition the set; a page walk must cover the collection; a record read four ways must read the same.
|
|
105
105
|
|
|
106
|
-
oat does **not** use OpenAPI `security` / `securitySchemes`, `servers[]`,
|
|
106
|
+
oat does **not** use OpenAPI `security` / `securitySchemes`, `servers[]`, webhooks, callbacks, or `links`. Auth is the config. The primary origin is `baseUrl`. Extra hosts go in `origins[]`, each with its own spec — do not merge them into the primary document. A RequestStep whose `path` is an absolute `http(s)` URL is a recorded hop on that URL (a mailed consume page) — that is not an `origins[]` entry and does not need a spec. Request bodies follow the document: JSON, `multipart/form-data` (scalars + dummy / pool / `each` / `resolveUpload` files), or `application/x-www-form-urlencoded`. `hooks.resolveInput` can replace a generated JSON field (a Stripe test `pm_…`); `hooks.resolveHeaders` can attach a one-shot header (Turnstile) per request.
|
|
107
107
|
|
|
108
108
|
## Quick start
|
|
109
109
|
|
|
@@ -611,7 +611,7 @@ Each step is one of:
|
|
|
611
611
|
}
|
|
612
612
|
```
|
|
613
613
|
|
|
614
|
-
**Request step** (when the document has no auth operations):
|
|
614
|
+
**Request step** (when the document has no auth operations, or the hop is not in the spec):
|
|
615
615
|
|
|
616
616
|
```ts
|
|
617
617
|
{
|
|
@@ -622,25 +622,37 @@ Each step is one of:
|
|
|
622
622
|
}
|
|
623
623
|
```
|
|
624
624
|
|
|
625
|
+
`path` is joined to this origin's `baseUrl`, unless it is an absolute `http://` or `https://` URL — then oat dispatches to that URL as a recorded exchange (method, URL, status, redirects, response headers). That is how a consume page on the app origin is an auth step when `baseUrl` is the API. Unknown host is allowed. `redirect` defaults to `"follow"` with a per-request cookie jar, so a 303 that sets `Set-Cookie` still leaves that cookie visible to `saveAs`. `redirect: "manual"` stops at the first 3xx (`expect: [303]` then bind from that response).
|
|
626
|
+
|
|
625
627
|
**Out-of-band step** (email link, OTP — oat cannot collect this itself):
|
|
626
628
|
|
|
627
629
|
```ts
|
|
628
|
-
{ outOfBand: { address: "{address}", kind: "email-verify", as: "
|
|
630
|
+
{ outOfBand: { address: "{address}", kind: "email-verify", as: "verifyLink" } }
|
|
629
631
|
```
|
|
630
632
|
|
|
631
|
-
|
|
633
|
+
The hook returns a string. If that string is a URL, a later RequestStep must GET it — a hook-side `fetch` is not a recorded exchange.
|
|
634
|
+
|
|
635
|
+
Later steps interpolate `{name}` from the flow scope. `saveAs` addresses:
|
|
636
|
+
|
|
637
|
+
- `$.foo.bar` / `$.orgs.0.id` — JSON body (dot + numeric index only; no JSON Pointer, no filters)
|
|
638
|
+
- `cookie:<name>` — that cookie on `Set-Cookie` for this hop, including followed hops
|
|
639
|
+
- `header:<name>` — a non-set-cookie response header, case-insensitive
|
|
640
|
+
|
|
641
|
+
Missing cookie / header / JSON path fails the auth step closed. Bind `saveAs.refreshToken` from `$.refresh_token` or `cookie:refresh` so `{refreshToken}` interpolates in `auth.refresh`. `saveClaimsFrom.token` is a JSON path into the response body (`$.access_token`) or a scope key already bound by `saveAs` (`credential`). Signature is not verified — oat is reading its own credential. `rootsFromFlow` maps path parameter names to those bound keys.
|
|
632
642
|
|
|
633
|
-
`bind` on a step runs **before** the request. `saveAs`
|
|
643
|
+
`bind` on a step runs **before** the request. `saveAs` then `saveClaimsFrom` run **after**. `credentialFrom` is read from the last HTTP response unless a step already saved `credential`. `outOfBand.as = "credential"` still wins over `credentialFrom`.
|
|
634
644
|
|
|
635
645
|
If a step's status is not acceptable, auth fails the run (not a finding): `oat: principal "alpha" failed at auth step 2 (POST /v1/…)`.
|
|
636
646
|
|
|
637
|
-
A complete register →
|
|
647
|
+
A complete register → mailed GET → cookie session example:
|
|
638
648
|
|
|
639
649
|
```ts
|
|
640
650
|
function signUp(email: string): AuthFlow {
|
|
641
651
|
return {
|
|
642
652
|
credentialFrom: "$.access_token",
|
|
643
653
|
expiresInFrom: "$.access_token_expires_in",
|
|
654
|
+
header: "cookie",
|
|
655
|
+
template: "session={credential}",
|
|
644
656
|
refresh: {
|
|
645
657
|
steps: [
|
|
646
658
|
{
|
|
@@ -657,22 +669,41 @@ function signUp(email: string): AuthFlow {
|
|
|
657
669
|
body: { email, password: "…" },
|
|
658
670
|
method: "POST",
|
|
659
671
|
path: "/v1/auth/register/email",
|
|
672
|
+
saveAs: { credential: "$.access_token", refreshToken: "$.refresh_token" },
|
|
660
673
|
},
|
|
661
|
-
{ outOfBand: { address: email, as: "
|
|
674
|
+
{ outOfBand: { address: email, as: "verifyLink", kind: "email-verify" } },
|
|
662
675
|
{
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
saveAs: {
|
|
676
|
+
method: "GET",
|
|
677
|
+
path: "{verifyLink}", // absolute URL; not joined to baseUrl
|
|
678
|
+
expect: [200, 303],
|
|
679
|
+
saveAs: {
|
|
680
|
+
credential: "cookie:session",
|
|
681
|
+
refreshToken: "cookie:refresh",
|
|
682
|
+
},
|
|
667
683
|
saveClaimsFrom: {
|
|
668
|
-
token: "
|
|
684
|
+
token: "credential",
|
|
669
685
|
bind: { orgId: "orgs.0.oid", projectId: "orgs.0.pids.0" },
|
|
670
686
|
},
|
|
671
687
|
},
|
|
672
688
|
],
|
|
673
689
|
}
|
|
674
690
|
}
|
|
691
|
+
```
|
|
692
|
+
|
|
693
|
+
When the user path **is** POST-token, keep that chain — it is still valid:
|
|
675
694
|
|
|
695
|
+
```ts
|
|
696
|
+
{ outOfBand: { address: email, as: "verifyToken", kind: "email-verify" } },
|
|
697
|
+
{
|
|
698
|
+
body: { token: "{verifyToken}" },
|
|
699
|
+
method: "POST",
|
|
700
|
+
path: "/v1/auth/email/verify",
|
|
701
|
+
saveAs: { credential: "$.access_token", refreshToken: "$.refresh_token" },
|
|
702
|
+
saveClaimsFrom: { token: "$.access_token", bind: { orgId: "orgs.0.oid" } },
|
|
703
|
+
}
|
|
704
|
+
```
|
|
705
|
+
|
|
706
|
+
```ts
|
|
676
707
|
principals: [
|
|
677
708
|
{
|
|
678
709
|
id: "alpha",
|
|
@@ -733,13 +764,14 @@ The CLI writes `principals.json` into each run folder (and `.oat/runs/latest/pri
|
|
|
733
764
|
```ts
|
|
734
765
|
hooks: {
|
|
735
766
|
// Return null to retry (attempt is 1-based). oat backs off until a value arrives.
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
767
|
+
// `scope` is the flow at this step; `headers` are the live principal credential headers.
|
|
768
|
+
resolveOutOfBand: async ({ address, kind, attempt, scope, headers }) => {
|
|
769
|
+
const link = await readMailCatcher(address, kind)
|
|
770
|
+
return link // URL or token string, or null
|
|
739
771
|
},
|
|
740
|
-
// Remove a principal this run provisioned
|
|
741
|
-
teardownPrincipal: async (address) => {
|
|
742
|
-
await fetch(`https://api.example.com/
|
|
772
|
+
// Remove a principal this run provisioned. `ctx` is the last live credential.
|
|
773
|
+
teardownPrincipal: async (address, { credential, headers }) => {
|
|
774
|
+
await fetch(`https://api.example.com/v1/auth/account`, { method: "DELETE", headers })
|
|
743
775
|
},
|
|
744
776
|
// Return a file to send, `{ fields }` to replace the whole request, or null to fall through.
|
|
745
777
|
resolveUpload: async ({ operationId, field, contentMediaType }) => {
|
|
@@ -785,7 +817,7 @@ hooks: {
|
|
|
785
817
|
}
|
|
786
818
|
```
|
|
787
819
|
|
|
788
|
-
Without `resolveOutOfBand`, an `outOfBand` step cannot complete. oat polls the hook; the hook must not sleep. Returning `""` is treated like `null`.
|
|
820
|
+
Without `resolveOutOfBand`, an `outOfBand` step cannot complete. oat polls the hook; the hook must not sleep. Returning `""` is treated like `null`. The hook is not a recorded hop: if the human GETs a mailed URL, a later RequestStep must GET that URL so it appears in `exchanges.jsonl` and can `expect` / `saveAs`.
|
|
789
821
|
|
|
790
822
|
Default schedule (0.6.2, unchanged unless `outOfBand` is set): **6** attempts, first sleep **200** ms, doubling, cap **3000** ms. oat sleeps after every miss, including the last, so the worst-case wait is
|
|
791
823
|
|
|
@@ -800,7 +832,7 @@ outOfBand: { attempts: 20, initialMs: 1000, maxMs: 8000 }
|
|
|
800
832
|
|
|
801
833
|
Worst-case wait is `sum_{i=0}^{attempts-1} min(initialMs × 2^i, maxMs)`. `worstCaseWaitMs()` from the package computes it. Existing configs that omit `outOfBand` do not slow down.
|
|
802
834
|
|
|
803
|
-
Without `teardownPrincipal`, provisioned accounts are reported as leftover rather than cascade-deleted. Per-record DELETE still runs for seeded rows when a delete (or `x-cleanup`) exists.
|
|
835
|
+
Without `teardownPrincipal`, provisioned accounts are reported as leftover rather than cascade-deleted. Per-record DELETE still runs for seeded rows when a delete (or `x-cleanup`) exists. The hook is called with the last live credential (`ctx.credential` / `ctx.headers`), not an already-cleared one, so authenticated delete-account is expressible without a tester-key god route. One-argument JavaScript callbacks still run.
|
|
804
836
|
|
|
805
837
|
`resolveHeaders` is called on every dispatch (including the 401 retry). Merge order: `globalHeaders` → hook → per-request headers → principal credential. Use `ctx.operationId` / `ctx.method` / `ctx.url` to attach a one-shot captcha only on captcha ops. oat does not speak Turnstile.
|
|
806
838
|
|
|
@@ -998,6 +1030,8 @@ A 402 / plan-limit (`payment_required`, `*_plan_limit`) on create is not a backe
|
|
|
998
1030
|
|
|
999
1031
|
A documented feature-gate 403 is the exception: if create declares `x-feature-gate` and the body is `vars.type: feature_gate` (and `vars.feature` matches the tag when present), oat records a `COVERAGE_GAP` naming the tag rather than a seed defect. See [`x-feature-gate`](#x-feature-gate).
|
|
1000
1032
|
|
|
1033
|
+
When create is tagged `x-unique`, a first-variant seed **409** with a nonempty same-tenant list **adopts** that row (`world.seed` `COVERAGE_GAP` naming `x-unique` — the create could not insert). Unique-conflict checks still run against that row. Write-path oracles that need a body oat submitted stay skipped. 409 with an empty list stays `BLOCKED` (`could not seed`). A 409 without the tag is still today's seed failure. The seed 409 itself is not `create.unique-conflict-rejected` passing — that check is the explicit second POST. See [`x-unique`](#x-unique). Later variants that 409 only shorten the cohort. Generated values for unique body columns differ across variants (a suffix) without weakening `maxLength` / `pattern`; if the document cannot express two distinct values, oat records a gap and skips extra variants.
|
|
1034
|
+
|
|
1001
1035
|
`--seed` / `seed` makes the bodies identical across runs. It does not make server-assigned ids identical.
|
|
1002
1036
|
|
|
1003
1037
|
Teardown DELETEs created rows (or the `x-cleanup` route) newest-first. Failures and missing delete routes are printed as leftovers, not as check findings. `keepFixtures: true` skips this.
|
|
@@ -1119,6 +1153,7 @@ What each tag **unlocks** (otherwise the check cannot run):
|
|
|
1119
1153
|
| `x-soft-delete` | `softdelete.absent-from-default-list` |
|
|
1120
1154
|
| `x-invite` | `auth.invite-grants-then-revokes` |
|
|
1121
1155
|
| `x-wait` | `effects.side-effect-arrives` |
|
|
1156
|
+
| `x-unique` | `create.unique-conflict-rejected`, `update.unique-conflict-rejected` |
|
|
1122
1157
|
|
|
1123
1158
|
What each tag **sharpens** (the check already runs, but the verdict changes):
|
|
1124
1159
|
|
|
@@ -1168,13 +1203,16 @@ x-invite:
|
|
|
1168
1203
|
grantPointer: $.grant_id
|
|
1169
1204
|
tokenFrom: response # or outOfBand
|
|
1170
1205
|
tokenKind: org-invite # only when tokenFrom is outOfBand; default `${entity}-invite`
|
|
1206
|
+
acceptFrom: token # default. `link` = GET the OOB URL instead of POSTing accept JSON
|
|
1171
1207
|
```
|
|
1172
1208
|
|
|
1173
|
-
Put this on the invite operation. Config must give the invitee `inviteAs`. Defaults if omitted: `granteeField: key`, `tokenPointer: $.token`, `grantPointer: $.grant_id`, `tokenFrom: response`. All three of `invite` / `accept` / `revoke` (operationIds) are required or the tag is ignored.
|
|
1209
|
+
Put this on the invite operation. Config must give the invitee `inviteAs`. Defaults if omitted: `granteeField: key`, `tokenPointer: $.token`, `grantPointer: $.grant_id`, `tokenFrom: response`, `acceptFrom: token`. All three of `invite` / `accept` / `revoke` (operationIds) are required or the tag is ignored. `oat doctor` / `oat plan` print the accept mode.
|
|
1174
1210
|
|
|
1175
1211
|
`tokenFrom: response` (default) reads the accept token from the invite HTTP body at `tokenPointer`. Keep this for backends that still put the token in JSON.
|
|
1176
1212
|
|
|
1177
|
-
`tokenFrom: outOfBand` ignores the response token and calls `resolveOutOfBand({ address: inviteAs, kind })` after the invite POST. `kind` is `tokenKind` or `${entity}-invite` (`org` → `org-invite`, `project` → `project-invite`). Use this when the live profile must accept only the mailed token.
|
|
1213
|
+
`tokenFrom: outOfBand` ignores the response token and calls `resolveOutOfBand({ address: inviteAs, kind, scope, headers })` after the invite POST. `kind` is `tokenKind` or `${entity}-invite` (`org` → `org-invite`, `project` → `project-invite`). Use this when the live profile must accept only the mailed token.
|
|
1214
|
+
|
|
1215
|
+
`acceptFrom: token` (default) stuffs that string into the documented accept JSON / path as today. `acceptFrom: link` requires the string to be an absolute `http(s)` URL: oat GETs it (recorded, cookie-jar follow). The `accept` operationId is unused for that hop; `revoke` still uses the documented revoke operation. 2xx/3xx that leaves the grant readable continues the timeline; 4xx, 404, or an unreadable grant is a finding. A config that still POSTs `{ token }` with `tokenFrom: outOfBand` and no `acceptFrom` does not change meaning.
|
|
1178
1216
|
|
|
1179
1217
|
An invite operation is **not** the entity's fixture create, even when it is `POST` on the collection. oat will not seed it with a generated email. The invite check (and only that check) creates the grant, using `inviteAs` as `granteeField`. The check still runs when there is no non-invite create, as long as an item or list route exists.
|
|
1180
1218
|
|
|
@@ -1409,6 +1447,28 @@ Every 403 is not a feature gate. No tag, or a tag that disagrees with `vars.feat
|
|
|
1409
1447
|
|
|
1410
1448
|
**Fallback:** none. Without the tag, a 403 is a failed create.
|
|
1411
1449
|
|
|
1450
|
+
### `x-unique`
|
|
1451
|
+
|
|
1452
|
+
```yaml
|
|
1453
|
+
x-unique:
|
|
1454
|
+
- [email]
|
|
1455
|
+
- [workspace_id, slug]
|
|
1456
|
+
# or: { columns: [email] }
|
|
1457
|
+
# or a single string[] meaning one set: [email]
|
|
1458
|
+
```
|
|
1459
|
+
|
|
1460
|
+
Column sets that must stay unique. Honey/apps emit this as route meta → OpenAPI. `doctor` / `plan` show the effective sets per entity the way they show `x-feature-gate`.
|
|
1461
|
+
|
|
1462
|
+
Accepted shapes: a list of column sets, `{ columns: [...] }` objects, and a single `string[]` meaning one set. Each set is a non-empty list of column names; empty sets are dropped. Malformed / non-array → treat as absent (`null`), unique checks do not run, `doctor` records an `x-unique` gap. `[]` after filtering → explicit none, checks do not run. No tag → unique checks do not run, `doctor` says so.
|
|
1463
|
+
|
|
1464
|
+
Do not infer uniqueness from 409s, unique-looking names, or JSON Schema `uniqueItems`. Index names and product `error_key` / `vars.type` are not required; HTTP **409** is the unique-conflict class.
|
|
1465
|
+
|
|
1466
|
+
`create.unique-conflict-rejected` — after a known row (seeded or adopted), a second create with the same unique-set values is 409, list cardinality does not grow, and a documented 409 body still matches the 409 schema. **2xx is `BACKEND_BUG`** (evidence: both exchanges, unique columns, list before/after when list resolved; tear down a returned id). Probe each probeable set separately (one 2xx fails the check; do not collide every set in one request). Probeable = at least one column on the create JSON or form body; path/tenant/`x-generated` columns may fill set identity from scope, but a set with no body columns is skipped, not failed. Do not reuse `Idempotency-Key` / `Idempotence-Key` / `X-Idempotency-Key` (omit, or send a fresh key if the document requires it). 402 / documented feature-gate 403 on the probe is `COVERAGE_GAP`, not a unique pass. Other 4xx / 5xx is not a unique pass. Verdict is never `SECURITY`. Leftover/extra rows do not skip the check.
|
|
1467
|
+
|
|
1468
|
+
`update.unique-conflict-rejected` — PATCH a **different** row onto another row's unique-set values is 409 (same scoring). PATCH of a row's own unique values unchanged is not this check. Skip update sets whose columns are all `x-immutable`.
|
|
1469
|
+
|
|
1470
|
+
**Fallback:** none. Without the tag, unique checks do not run, and a seed 409 is a failed create.
|
|
1471
|
+
|
|
1412
1472
|
### `--profile` — cost gating
|
|
1413
1473
|
|
|
1414
1474
|
```bash
|
|
@@ -1467,7 +1527,7 @@ export default defineConfig({
|
|
|
1467
1527
|
|
|
1468
1528
|
## Checks
|
|
1469
1529
|
|
|
1470
|
-
|
|
1530
|
+
96 checks. A check that cannot run says so (`did not apply` + `needs`). A check that depends on a broken primitive is `BLOCKED`. A check that ran and stopped is inconclusive, not a pass.
|
|
1471
1531
|
|
|
1472
1532
|
Order is fixed (foundations first) so cascade suppression has a cause to point at. Mutating checks run alone; read-only checks may share in-flight requests under `maxInFlight`.
|
|
1473
1533
|
|
|
@@ -1553,6 +1613,8 @@ Order is fixed (foundations first) so cascade suppression has a cause to point a
|
|
|
1553
1613
|
| `tenant.filter-does-not-bypass-scope` | `filter=id.eq.<other tenant>` does not return that row | second principal, a filter, and a tagged or inferred tenant | `query.filter-selects-from-whole-set` |
|
|
1554
1614
|
| `auth.rank-is-monotonic` | a lower rank cannot do what a higher rank is denied | two same-tenant principals at different `rank` | `list.read-after-write` |
|
|
1555
1615
|
| `auth.invite-grants-then-revokes` | invite → accept grants; revoke takes it back | `x-invite` + peer with `inviteAs` | list, cross-tenant |
|
|
1616
|
+
| `create.unique-conflict-rejected` | a second create colliding a documented unique set is 409, not 2xx; the list does not grow | `x-unique` with a probeable create body column and a known row | — |
|
|
1617
|
+
| `update.unique-conflict-rejected` | PATCHing a **different** row onto another row's unique-set values is 409, not 2xx | `x-unique`, update, two known rows; skip all-`x-immutable` sets | — |
|
|
1556
1618
|
| `patch.immutable-field-rejected` | PATCHing an `x-immutable` field is rejected or ignored | `x-immutable` | — |
|
|
1557
1619
|
| `softdelete.absent-from-default-list` | a soft-deleted row is gone from the default list | `x-soft-delete` | `list.read-after-write` |
|
|
1558
1620
|
| `invalidation.declared-route-changes` | after a write, the other entity's listed route actually changes | `x-invalidate` naming another entity | list, persist |
|
|
@@ -1867,7 +1929,7 @@ const { doc: resolved, externalRefs } = dereference(doc)
|
|
|
1867
1929
|
const model = buildModel(resolved)
|
|
1868
1930
|
```
|
|
1869
1931
|
|
|
1870
|
-
Types exported: `OatConfig`, `Principal`, `AuthFlow`, `AuthRefresh`, `AuthStep`, `HookAuth`, `Hooks`, `Uploads`, `UploadRequest`, `UploadFile`, `HeaderRequest`, `InputRequest`, `OriginSpec`, `OutOfBandConfig`, `RunOptions`, `RunResult`, `Finding`, `Verdict`, `Actor`, `SpecModel`, `EntityModel`, `OperationModel`, `OpenApiDocument`, `AuthRefreshRequiredError`, `loadPersistedPrincipals`, `worstCaseWaitMs`, `allocateRunDir`, `DEFAULT_RUNS_ROOT`, matrix types.
|
|
1932
|
+
Types exported: `OatConfig`, `Principal`, `AuthFlow`, `AuthRefresh`, `AuthStep`, `HookAuth`, `Hooks`, `Uploads`, `UploadRequest`, `UploadFile`, `HeaderRequest`, `InputRequest`, `OriginSpec`, `OutOfBandConfig`, `OutOfBandRequest`, `TeardownPrincipalContext`, `RunOptions`, `RunResult`, `Finding`, `Verdict`, `Actor`, `SpecModel`, `EntityModel`, `OperationModel`, `OpenApiDocument`, `AuthRefreshRequiredError`, `loadPersistedPrincipals`, `worstCaseWaitMs`, `allocateRunDir`, `DEFAULT_RUNS_ROOT`, matrix types.
|
|
1871
1933
|
|
|
1872
1934
|
## CI
|
|
1873
1935
|
|
|
@@ -1956,6 +2018,7 @@ Comma-separated. Each is one named lie the demo API can tell. Primary check is w
|
|
|
1956
2018
|
| `ASYNC_RECEIPT_MISSING_ID` | `async.receipt-identifies-the-job` | receipt has no id |
|
|
1957
2019
|
| `PATCH_REPLACES` | `patch.minimality` | PATCH is implemented as replace |
|
|
1958
2020
|
| `IDEMPOTENCY_IGNORED` | `idempotency.replay-does-not-duplicate` | Idempotency-Key is ignored |
|
|
2021
|
+
| `UNIQUE_NOT_ENFORCED` | `create.unique-conflict-rejected` | a duplicate unique-set write is 2xx instead of 409 |
|
|
1959
2022
|
| `DELETE_MISSING_OK` | `delete.absent-record-returns-404` | DELETE missing returns 200 |
|
|
1960
2023
|
| `CONCURRENT_WRITE_LOST` | `concurrency.no-lost-update` | full-row write clobbers a parallel PATCH |
|
|
1961
2024
|
| `ENUM_NOT_VALIDATED` | `validation.enum-enforced` | enum is not enforced |
|
|
@@ -1980,10 +2043,10 @@ These are deliberate. An agent should not invent a flag for them.
|
|
|
1980
2043
|
- **No request timeout by default.** `fetch` waits until the server answers unless `network.requestTimeoutMs` is set. Watch `status=in_flight` and `idle_ms` on that request.
|
|
1981
2044
|
- **No retry on 5xx.** 429 is retried (up to 5, honouring `Retry-After`). One 401 → force refresh + single retry. A second 401 is evidence.
|
|
1982
2045
|
- **Network throws are not HTTP.** Offline / DNS / reset / timeout: 4 retries, then one wait (default 60s) for the link. If it does not come back, `net.unreachable` is recorded, remaining work stands down, the report is still written, exit `1`. Progress `status=network`. Failed attempts are journaled as `status: 0` with `{ error: "network", kind }`.
|
|
1983
|
-
- **No OpenAPI `security`.** Put credentials in `principals`. Cookie auth is a `headers: { cookie: "…" }` (or a flow that sets that header).
|
|
1984
|
-
- **No `servers[]`.** Always set `baseUrl`. Extra hosts are `origins[]`, each with its own `spec`.
|
|
2046
|
+
- **No OpenAPI `security`.** Put credentials in `principals`. Cookie auth is a `headers: { cookie: "…" }` (or a flow that sets that header / `saveAs: { credential: "cookie:session" }`). oat does not grow OpenAPI cookie `securitySchemes`.
|
|
2047
|
+
- **No `servers[]`.** Always set `baseUrl`. Extra hosts are `origins[]`, each with its own `spec`. An app origin that is HTML + `Set-Cookie` is a RequestStep with an absolute URL, not a dummy spec in `origins[]`.
|
|
1985
2048
|
- **No OCR.** Multipart and file parts are sent as dummy / pool / `each` / `resolveUpload` bytes. oat checks HTTP status and JSON responses, not whether a PDF is a real invoice.
|
|
1986
|
-
- **No webhook / callback / link-object following
|
|
2049
|
+
- **No webhook / callback / link-object following**, except the explicit auth / invite GET of a URL `resolveOutOfBand` returned. A hook-side `fetch` of that URL is not a recorded exchange.
|
|
1987
2050
|
- **External `$ref`s are not fetched.** In-document `$ref`s are.
|
|
1988
2051
|
- **`x-idempotent` is not the idempotency check.** The check keys off a documented `Idempotency-Key` header.
|
|
1989
2052
|
- **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.
|
package/dist/cli.js
CHANGED
|
@@ -258,6 +258,7 @@ export async function main() {
|
|
|
258
258
|
const { postgresAvailable, renderParserSuite, renderSuite, runExampleSpecSuite, runTagUnlockSuite, runParserSuite, runPayloadCatalogSuite, runCoverageReportSuite, runSeedContractSuite, runEffectsSuite, runTenantScopeSuite, runSuite, sqliteAvailable, d1Available, } = await import("./conformance/suite.js");
|
|
259
259
|
const { runFeatureGateSuite } = await import("./conformance/feature-gate.js");
|
|
260
260
|
const { runRateLimitSuite } = await import("./conformance/rate-limit.js");
|
|
261
|
+
const { runUniqueSuite } = await import("./conformance/unique.js");
|
|
261
262
|
const parser = renderParserSuite(runParserSuite());
|
|
262
263
|
process.stdout.write(parser.text);
|
|
263
264
|
/* The documented example is checked in the same breath: it is the only place a reader
|
|
@@ -281,6 +282,9 @@ export async function main() {
|
|
|
281
282
|
const rateLimit = renderParserSuite(await runRateLimitSuite());
|
|
282
283
|
process.stdout.write(rateLimit.text);
|
|
283
284
|
parser.failures += rateLimit.failures;
|
|
285
|
+
const unique = renderParserSuite(await runUniqueSuite());
|
|
286
|
+
process.stdout.write(unique.text);
|
|
287
|
+
parser.failures += unique.failures;
|
|
284
288
|
const seedContract = renderParserSuite(await runSeedContractSuite());
|
|
285
289
|
process.stdout.write(seedContract.text);
|
|
286
290
|
parser.failures += seedContract.failures;
|
|
@@ -10,14 +10,22 @@ export type { FieldType, FilterOp, FilterableField, FilterableFrom, QueryCapabil
|
|
|
10
10
|
interface StepBase {
|
|
11
11
|
/**
|
|
12
12
|
* Binds parts of the response into the flow's scope. Later steps interpolate them as
|
|
13
|
-
* `{name}`, and `rootsFromFlow` can promote them to path parameters.
|
|
14
|
-
*
|
|
13
|
+
* `{name}`, and `rootsFromFlow` can promote them to path parameters.
|
|
14
|
+
*
|
|
15
|
+
* Address forms:
|
|
16
|
+
* - `$.access_token` / `$.orgs.0.id` — JSON body (dot + numeric index only)
|
|
17
|
+
* - `cookie:<name>` — that cookie on `Set-Cookie` for this hop (and followed hops)
|
|
18
|
+
* - `header:<name>` — a non-set-cookie response header, case-insensitive
|
|
19
|
+
*
|
|
20
|
+
* Missing cookie / header / JSON path fails the auth step closed.
|
|
15
21
|
*/
|
|
16
22
|
saveAs?: Record<string, string>;
|
|
17
23
|
/**
|
|
18
24
|
* Binds values out of a JWT's claims. Tenancy commonly rides in the token rather than the
|
|
19
25
|
* response body, and a principal that can name its own tenant needs no roots configured. The
|
|
20
26
|
* signature is not verified — oat is reading its own credential, not trusting a third party.
|
|
27
|
+
* `token` is a JSON path into the response body (`$.access_token`) or a scope key already
|
|
28
|
+
* bound by `saveAs` (`credential`).
|
|
21
29
|
*/
|
|
22
30
|
saveClaimsFrom?: {
|
|
23
31
|
token: string;
|
|
@@ -40,19 +48,31 @@ export interface OperationStep extends StepBase {
|
|
|
40
48
|
/** Calls a raw path, for endpoints the document does not describe. */
|
|
41
49
|
export interface RequestStep extends StepBase {
|
|
42
50
|
method: string;
|
|
51
|
+
/**
|
|
52
|
+
* Path relative to this origin's `baseUrl`, or an absolute `http://` / `https://` URL.
|
|
53
|
+
* Absolute URLs are not joined to `baseUrl` — that is how a consume page on the app origin
|
|
54
|
+
* is a recorded hop when the spec origin is the API.
|
|
55
|
+
*/
|
|
43
56
|
path: string;
|
|
44
57
|
body?: unknown;
|
|
45
58
|
headers?: Record<string, string>;
|
|
46
59
|
query?: Record<string, string>;
|
|
47
|
-
/** Named `origins[]` entry. Omit to hit the primary `baseUrl`. */
|
|
60
|
+
/** Named `origins[]` entry. Omit to hit the primary `baseUrl`. Ignored for absolute URLs. */
|
|
48
61
|
origin?: string;
|
|
62
|
+
/**
|
|
63
|
+
* Redirect policy for this hop. Default `follow` uses a per-request cookie jar so a 303
|
|
64
|
+
* that sets `Set-Cookie` still leaves that cookie visible to `saveAs`. `manual` stops at
|
|
65
|
+
* the first 3xx (`expect: [303]` then reads cookies from that response).
|
|
66
|
+
*/
|
|
67
|
+
redirect?: "follow" | "manual";
|
|
49
68
|
}
|
|
50
69
|
/**
|
|
51
70
|
* A value that never travels over HTTP — an emailed verification token, an OTP, a magic link.
|
|
52
71
|
*
|
|
53
72
|
* oat declares the need and calls `hooks.resolveOutOfBand` to satisfy it, polling with backoff
|
|
54
73
|
* because such stores are usually eventually consistent. That hook is the entire coupling
|
|
55
|
-
* surface between oat and one specific backend.
|
|
74
|
+
* surface between oat and one specific backend. The hook returns a string (a token, or the
|
|
75
|
+
* mailed URL); oat records the consume hop only when a later RequestStep GETs that URL.
|
|
56
76
|
*/
|
|
57
77
|
export interface OutOfBandStep {
|
|
58
78
|
outOfBand: {
|
|
@@ -147,6 +167,16 @@ export interface OutOfBandRequest {
|
|
|
147
167
|
kind: string;
|
|
148
168
|
/** 1-based; oat retries with backoff until a value arrives or the attempts run out. */
|
|
149
169
|
attempt: number;
|
|
170
|
+
/** Flow scope at this step (interpolated binds + prior `saveAs`). */
|
|
171
|
+
scope: Record<string, string>;
|
|
172
|
+
/** Principal credential headers as they would be sent now (Authorization or Cookie). */
|
|
173
|
+
headers: Record<string, string>;
|
|
174
|
+
}
|
|
175
|
+
export interface TeardownPrincipalContext {
|
|
176
|
+
/** Live credential string — the last issued one, not a cleared snapshot. */
|
|
177
|
+
credential: string;
|
|
178
|
+
/** Live principal header map (Authorization or Cookie, whatever the flow uses). */
|
|
179
|
+
headers: Record<string, string>;
|
|
150
180
|
}
|
|
151
181
|
/**
|
|
152
182
|
* Poll schedule for `resolveOutOfBand` and `resolvePrincipalAuth`.
|
|
@@ -236,10 +266,12 @@ export interface Hooks {
|
|
|
236
266
|
resolveOutOfBand?: (request: OutOfBandRequest) => Promise<string | null>;
|
|
237
267
|
/**
|
|
238
268
|
* Removes a principal the run provisioned, and everything that principal created with it.
|
|
239
|
-
*
|
|
240
|
-
*
|
|
269
|
+
* `ctx.headers` is the live principal header map so authenticated delete-account is
|
|
270
|
+
* expressible without a tester-key god route. Address stays — some backends still key
|
|
271
|
+
* teardown on it. One-argument JavaScript callbacks still run (the second argument is
|
|
272
|
+
* extra); TypeScript configs should take `ctx`.
|
|
241
273
|
*/
|
|
242
|
-
teardownPrincipal?: (address: string) => Promise<void>;
|
|
274
|
+
teardownPrincipal?: (address: string, ctx: TeardownPrincipalContext) => Promise<void>;
|
|
243
275
|
/**
|
|
244
276
|
* Exact file or whole request. Return `null` to fall through to `uploads.each`, then pool, then a dummy.
|
|
245
277
|
* `UploadFile` replaces that field. `{ fields }` with a file replaces the whole request.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"define-config.d.ts","sourceRoot":"","sources":["../../src/config/define-config.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAA;AAEvF,YAAY,EACX,SAAS,EACT,QAAQ,EACR,eAAe,EACf,cAAc,EACd,iBAAiB,EACjB,aAAa,GACb,MAAM,+BAA+B,CAAA;AAItC,UAAU,QAAQ;IACjB
|
|
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;AAwIH,MAAM,UAAU,UAAU,CAAC,IAAmB;IAC7C,OAAO,UAAU,IAAI,IAAI,IAAI,OAAO,IAAI,CAAC,QAAQ,KAAK,QAAQ,IAAI,CAAC,CAAC,OAAO,IAAI,IAAI,CAAC,CAAA;AACrF,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,IAAmB;IAC7C,OAAO,OAAO,IAAI,IAAI,CAAA;AACvB,CAAC;AAyVD;;;;;;;;;;;;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, isAuthFlow, isHookAuth } from "./config/define-config.ts";
|
|
2
|
-
export type { AuthFlow, AuthRefresh, AuthStep, EntityConfig, FieldType, FilterOp, FilterableField, FilterableFrom, HeaderRequest, HookAuth, Hooks, InputRequest, OatConfig, OperationStep, OriginSpec, OutOfBandConfig, OutOfBandRequest, OutOfBandStep, Principal, PrincipalAuth, PrincipalAuthResult, ProfileSpec, QueryCapabilities, QueryCapabilitiesRequest, RateLimitSpec, RequestStep, SideEffectRequest, UploadFile, UploadRequest, UploadResolution, Uploads, } from "./config/define-config.ts";
|
|
2
|
+
export type { AuthFlow, AuthRefresh, AuthStep, EntityConfig, FieldType, FilterOp, FilterableField, FilterableFrom, HeaderRequest, HookAuth, Hooks, InputRequest, OatConfig, OperationStep, OriginSpec, OutOfBandConfig, OutOfBandRequest, OutOfBandStep, Principal, PrincipalAuth, PrincipalAuthResult, ProfileSpec, QueryCapabilities, QueryCapabilitiesRequest, RateLimitSpec, RequestStep, SideEffectRequest, TeardownPrincipalContext, UploadFile, UploadRequest, UploadResolution, Uploads, } from "./config/define-config.ts";
|
|
3
3
|
export { loadConfig } from "./config/load.ts";
|
|
4
4
|
export { loadPersistedPrincipals, parsePersistedPrincipals } from "./runtime/principals.ts";
|
|
5
5
|
export type { PersistedPrincipal } from "./runtime/principals.ts";
|
|
@@ -31,6 +31,13 @@ export declare const DEFECTS: {
|
|
|
31
31
|
* enumerating other tenants' identifiers.
|
|
32
32
|
*/
|
|
33
33
|
readonly EXISTENCE_LEAK_VIA_STATUS: "cross-tenant denial uses 403, revealing that the record exists";
|
|
34
|
+
/**
|
|
35
|
+
* Accepts a second create (or a colliding PATCH) of a documented unique column set.
|
|
36
|
+
*
|
|
37
|
+
* The document said these columns are unique. Returning 2xx instead of 409 is the backend
|
|
38
|
+
* ignoring that constraint — the unique-conflict checks exist to name that lie.
|
|
39
|
+
*/
|
|
40
|
+
readonly UNIQUE_NOT_ENFORCED: "a duplicate unique-set write is accepted instead of 409";
|
|
34
41
|
/**
|
|
35
42
|
* Accepts an Idempotency-Key and creates a second record anyway.
|
|
36
43
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"defects.d.ts","sourceRoot":"","sources":["../../src/reference/defects.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,eAAO,MAAM,OAAO;IACnB,iFAAiF;;IAEjF,qFAAqF;;IAErF,kEAAkE;;IAElE,iFAAiF;;IAEjF,wEAAwE;;IAExE,6CAA6C;;IAE7C,mFAAmF;;IAEnF;;;;;;OAMG;;IAEH;;;;;;OAMG;;IAEH;;;;;;;OAOG;;IAEH;;;;;;;OAOG;;IAEH;;;;;;OAMG;;IAEH;;;;;;;OAOG;;IAEH;;;;;;;OAOG;;IAEH;;;;;;OAMG;;IAGH;;;;OAIG;;IAGH;;;OAGG;;IAGH;;;;;;;;OAQG;;IAEH;;;;;;;OAOG;;IAEH,gDAAgD;;IAEhD,iCAAiC;;IAEjC,oDAAoD;;IAEpD,mCAAmC;;IAEnC,6EAA6E;;IAE7E,qCAAqC;;IAErC,4CAA4C;;IAE5C,+DAA+D;;IAE/D;;;;;;OAMG;;IAEH;;;;;;;;OAQG;;IAEH;;;;;OAKG;;IAEH,wDAAwD;;IAExD,gDAAgD;;IAEhD,iDAAiD;;IAEjD,yDAAyD;;IAEzD,sDAAsD;;IAEtD,6DAA6D;;IAE7D,yDAAyD;;IAEzD,iDAAiD;;IAEjD,gDAAgD;;IAEhD,uDAAuD;;IAEvD,iEAAiE;;IAEjE,2EAA2E;;IAE3E,kDAAkD;;IAElD,oDAAoD;;IAEpD,4DAA4D;;IAE5D;;;;;OAKG;;IAEH;;;;;OAKG;;IAEH,6DAA6D;;IAE7D,gDAAgD;;IAEhD,kDAAkD;;IAElD,8DAA8D;;IAE9D,6DAA6D;;IAE7D,+DAA+D;;IAE/D,8DAA8D;;IAE9D,+DAA+D;;IAE/D,yFAAyF;;IAEzF,uFAAuF;;IAEvF,mEAAmE;;IAEnE,+EAA+E;;IAE/E,mDAAmD;;IAEnD,2EAA2E;;IAE3E;;;;;;;OAOG;;IAEH;;;OAGG;;IAEH;;;OAGG;;IAEH;;;;OAIG;;IAEH;;;;;;OAMG;;IAEH,4EAA4E;;IAE5E,6DAA6D;;CAEpD,CAAA;AAEV,MAAM,MAAM,UAAU,GAAG,MAAM,OAAO,OAAO,CAAA;AAE7C,qBAAa,SAAS;IACrB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;gBAEjC,KAAK,GAAE,SAAS,MAAM,EAAO;IAIzC,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS;IAUpD,GAAG,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO;IAI9B,IAAI,IAAI,UAAU,EAAE;CAGpB;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,UAAU,CAE/D"}
|
|
1
|
+
{"version":3,"file":"defects.d.ts","sourceRoot":"","sources":["../../src/reference/defects.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,eAAO,MAAM,OAAO;IACnB,iFAAiF;;IAEjF,qFAAqF;;IAErF,kEAAkE;;IAElE,iFAAiF;;IAEjF,wEAAwE;;IAExE,6CAA6C;;IAE7C,mFAAmF;;IAEnF;;;;;;OAMG;;IAEH;;;;;OAKG;;IAEH;;;;;;OAMG;;IAEH;;;;;;;OAOG;;IAEH;;;;;;;OAOG;;IAEH;;;;;;OAMG;;IAEH;;;;;;;OAOG;;IAEH;;;;;;;OAOG;;IAEH;;;;;;OAMG;;IAGH;;;;OAIG;;IAGH;;;OAGG;;IAGH;;;;;;;;OAQG;;IAEH;;;;;;;OAOG;;IAEH,gDAAgD;;IAEhD,iCAAiC;;IAEjC,oDAAoD;;IAEpD,mCAAmC;;IAEnC,6EAA6E;;IAE7E,qCAAqC;;IAErC,4CAA4C;;IAE5C,+DAA+D;;IAE/D;;;;;;OAMG;;IAEH;;;;;;;;OAQG;;IAEH;;;;;OAKG;;IAEH,wDAAwD;;IAExD,gDAAgD;;IAEhD,iDAAiD;;IAEjD,yDAAyD;;IAEzD,sDAAsD;;IAEtD,6DAA6D;;IAE7D,yDAAyD;;IAEzD,iDAAiD;;IAEjD,gDAAgD;;IAEhD,uDAAuD;;IAEvD,iEAAiE;;IAEjE,2EAA2E;;IAE3E,kDAAkD;;IAElD,oDAAoD;;IAEpD,4DAA4D;;IAE5D;;;;;OAKG;;IAEH;;;;;OAKG;;IAEH,6DAA6D;;IAE7D,gDAAgD;;IAEhD,kDAAkD;;IAElD,8DAA8D;;IAE9D,6DAA6D;;IAE7D,+DAA+D;;IAE/D,8DAA8D;;IAE9D,+DAA+D;;IAE/D,yFAAyF;;IAEzF,uFAAuF;;IAEvF,mEAAmE;;IAEnE,+EAA+E;;IAE/E,mDAAmD;;IAEnD,2EAA2E;;IAE3E;;;;;;;OAOG;;IAEH;;;OAGG;;IAEH;;;OAGG;;IAEH;;;;OAIG;;IAEH;;;;;;OAMG;;IAEH,4EAA4E;;IAE5E,6DAA6D;;CAEpD,CAAA;AAEV,MAAM,MAAM,UAAU,GAAG,MAAM,OAAO,OAAO,CAAA;AAE7C,qBAAa,SAAS;IACrB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;gBAEjC,KAAK,GAAE,SAAS,MAAM,EAAO;IAIzC,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS;IAUpD,GAAG,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO;IAI9B,IAAI,IAAI,UAAU,EAAE;CAGpB;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,UAAU,CAE/D"}
|
|
@@ -31,6 +31,13 @@ export const DEFECTS = {
|
|
|
31
31
|
* enumerating other tenants' identifiers.
|
|
32
32
|
*/
|
|
33
33
|
EXISTENCE_LEAK_VIA_STATUS: "cross-tenant denial uses 403, revealing that the record exists",
|
|
34
|
+
/**
|
|
35
|
+
* Accepts a second create (or a colliding PATCH) of a documented unique column set.
|
|
36
|
+
*
|
|
37
|
+
* The document said these columns are unique. Returning 2xx instead of 409 is the backend
|
|
38
|
+
* ignoring that constraint — the unique-conflict checks exist to name that lie.
|
|
39
|
+
*/
|
|
40
|
+
UNIQUE_NOT_ENFORCED: "a duplicate unique-set write is accepted instead of 409",
|
|
34
41
|
/**
|
|
35
42
|
* Accepts an Idempotency-Key and creates a second record anyway.
|
|
36
43
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"defects.js","sourceRoot":"","sources":["../../src/reference/defects.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"defects.js","sourceRoot":"","sources":["../../src/reference/defects.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/reference/http.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAsC,KAAK,MAAM,EAAuB,MAAM,WAAW,CAAA;AAEhG,OAAO,EAAwB,KAAK,SAAS,EAAE,MAAM,cAAc,CAAA;AAInE,OAAO,EAAsB,KAAK,KAAK,EAAE,MAAM,gBAAgB,CAAA;AAE/D,UAAU,SAAS;IAClB,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,MAAM,CAAA;IACjB,8EAA8E;IAC9E,IAAI,EAAE,MAAM,CAAA;CACZ;AA0ID,MAAM,WAAW,eAAe;IAC/B,MAAM,EAAE,MAAM,CAAA;IACd,GAAG,EAAE,MAAM,CAAA;IACX,OAAO,EAAE,SAAS,CAAA;IAClB,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IAC1B,UAAU,EAAE,SAAS,EAAE,CAAA;CACvB;AAED;;;GAGG;AACH,wBAAsB,qBAAqB,CAC1C,OAAO,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,EAIrE,WAAW,EAAE,CAAC,OAAO,EAAE,SAAS,KAAK,OAAO,CAAC,KAAK,CAAC,GACjD,OAAO,CAAC,eAAe,CAAC,
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/reference/http.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAsC,KAAK,MAAM,EAAuB,MAAM,WAAW,CAAA;AAEhG,OAAO,EAAwB,KAAK,SAAS,EAAE,MAAM,cAAc,CAAA;AAInE,OAAO,EAAsB,KAAK,KAAK,EAAE,MAAM,gBAAgB,CAAA;AAE/D,UAAU,SAAS;IAClB,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,MAAM,CAAA;IACjB,8EAA8E;IAC9E,IAAI,EAAE,MAAM,CAAA;CACZ;AA0ID,MAAM,WAAW,eAAe;IAC/B,MAAM,EAAE,MAAM,CAAA;IACd,GAAG,EAAE,MAAM,CAAA;IACX,OAAO,EAAE,SAAS,CAAA;IAClB,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IAC1B,UAAU,EAAE,SAAS,EAAE,CAAA;CACvB;AAED;;;GAGG;AACH,wBAAsB,qBAAqB,CAC1C,OAAO,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,EAIrE,WAAW,EAAE,CAAC,OAAO,EAAE,SAAS,KAAK,OAAO,CAAC,KAAK,CAAC,GACjD,OAAO,CAAC,eAAe,CAAC,CAknB1B;AAsKD,uFAAuF;AACvF,wBAAsB,kBAAkB,CACvC,OAAO,GAAE;IAAE,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAO,GACxE,OAAO,CAAC,eAAe,CAAC,CAG1B;AAED,oEAAoE;AACpE,wBAAsB,kBAAkB,CACvC,OAAO,GAAE;IAAE,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAO,GACxE,OAAO,CAAC,eAAe,CAAC,CAI1B;AAED;;;;;;GAMG;AACH,wBAAsB,cAAc,CACnC,OAAO,GAAE;IACR,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAA;CACZ,GACJ,OAAO,CAAC,eAAe,CAAC,CA2B1B;AAED,gGAAgG;AAChG,wBAAsB,oBAAoB,CACzC,OAAO,GAAE;IAAE,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAO,GACxE,OAAO,CAAC,eAAe,CAAC,CAG1B"}
|
package/dist/reference/http.js
CHANGED
|
@@ -231,6 +231,41 @@ createStore) {
|
|
|
231
231
|
return record;
|
|
232
232
|
return { ...record, _internal_revision: 7, _shard: "shard-a" };
|
|
233
233
|
}
|
|
234
|
+
async function assertUnique(entity, record, exceptId, defects) {
|
|
235
|
+
if (defects.has("UNIQUE_NOT_ENFORCED"))
|
|
236
|
+
return;
|
|
237
|
+
const sets = entity.unique ?? [];
|
|
238
|
+
if (sets.length === 0)
|
|
239
|
+
return;
|
|
240
|
+
const scope = {};
|
|
241
|
+
for (const parent of entity.parents) {
|
|
242
|
+
const value = record[parent];
|
|
243
|
+
if (typeof value === "string")
|
|
244
|
+
scope[parent] = value;
|
|
245
|
+
}
|
|
246
|
+
const tenant = record[TENANT_FIELD];
|
|
247
|
+
if (typeof tenant === "string")
|
|
248
|
+
scope[TENANT_FIELD] = tenant;
|
|
249
|
+
const pageSize = Math.max(entity.maxLimit, 100);
|
|
250
|
+
const items = [];
|
|
251
|
+
for (let page = 1; page <= 200; page++) {
|
|
252
|
+
const chunk = await store.query(entity, scope, { limit: pageSize, page }, {
|
|
253
|
+
softDeleteField: entity.softDeleteField,
|
|
254
|
+
});
|
|
255
|
+
items.push(...chunk.items);
|
|
256
|
+
if (chunk.items.length < pageSize)
|
|
257
|
+
break;
|
|
258
|
+
}
|
|
259
|
+
for (const set of sets) {
|
|
260
|
+
const collides = items.some((row) => {
|
|
261
|
+
if (exceptId !== undefined && String(row[entity.identity]) === exceptId)
|
|
262
|
+
return false;
|
|
263
|
+
return set.every((col) => JSON.stringify(row[col]) === JSON.stringify(record[col]));
|
|
264
|
+
});
|
|
265
|
+
if (collides)
|
|
266
|
+
throw new HttpError(409, "conflict", `unique constraint violated (${set.join(", ")})`);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
234
269
|
function withDefaults(entity, input, scope) {
|
|
235
270
|
const record = { ...input };
|
|
236
271
|
record[entity.identity] = store.nextId(entity.name);
|
|
@@ -550,6 +585,7 @@ createStore) {
|
|
|
550
585
|
if (defects.has("CREATE_DROPS_FIELD"))
|
|
551
586
|
delete input.description;
|
|
552
587
|
const record = withDefaults(entity, input, { ...scope, project_id: principal.projectId });
|
|
588
|
+
await assertUnique(entity, record, undefined, defects);
|
|
553
589
|
const created = await store.insert(entity, record);
|
|
554
590
|
/* Progress starts on POST .../jobs/start, not on create. A seeded job that
|
|
555
591
|
* ticks for 60ms looks like a PATCH side effect once x-generated is stripped. */
|
|
@@ -595,6 +631,7 @@ createStore) {
|
|
|
595
631
|
for (const parent of entity.parents)
|
|
596
632
|
delete changes[parent];
|
|
597
633
|
}
|
|
634
|
+
await assertUnique(entity, { ...existing, ...changes }, itemId, defects);
|
|
598
635
|
const updated = await store.update(entity, itemId, changes);
|
|
599
636
|
return send(res, defects.has("RESPONSE_STATUS_UNDECLARED") ? 201 : 200, decorate(updated ?? { ...existing, ...changes }));
|
|
600
637
|
}
|