@lovrozagar/oat 0.2.1 → 0.3.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 +60 -1
- package/dist/cli.js +17 -1
- package/dist/reference/http.d.ts.map +1 -1
- package/dist/reference/http.js +2 -2
- package/dist/reference/http.js.map +1 -1
- package/dist/report/console.d.ts.map +1 -1
- package/dist/report/console.js +18 -0
- package/dist/report/console.js.map +1 -1
- package/dist/runtime/checks.d.ts.map +1 -1
- package/dist/runtime/checks.js +122 -31
- package/dist/runtime/checks.js.map +1 -1
- package/dist/runtime/feature-gate.d.ts +23 -0
- package/dist/runtime/feature-gate.d.ts.map +1 -0
- package/dist/runtime/feature-gate.js +68 -0
- package/dist/runtime/feature-gate.js.map +1 -0
- package/dist/runtime/fixture.d.ts +28 -1
- package/dist/runtime/fixture.d.ts.map +1 -1
- package/dist/runtime/fixture.js +551 -90
- package/dist/runtime/fixture.js.map +1 -1
- package/dist/runtime/run.d.ts.map +1 -1
- package/dist/runtime/run.js +135 -41
- package/dist/runtime/run.js.map +1 -1
- package/dist/runtime/world.d.ts +17 -1
- package/dist/runtime/world.d.ts.map +1 -1
- package/dist/runtime/world.js +56 -4
- package/dist/runtime/world.js.map +1 -1
- package/dist/spec/extensions.d.ts +7 -0
- package/dist/spec/extensions.d.ts.map +1 -1
- package/dist/spec/extensions.js +10 -0
- package/dist/spec/extensions.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 +7 -2
- package/dist/spec/graph.js.map +1 -1
- package/dist/spec/load.d.ts.map +1 -1
- package/dist/spec/load.js +19 -4
- package/dist/spec/load.js.map +1 -1
- package/labs/annotated-openapi.yaml +6 -0
- package/package.json +3 -2
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* When a 403 is the document, not a defect.
|
|
3
|
+
*
|
|
4
|
+
* `x-feature-gate` names a plan key. A free principal hitting that route and receiving
|
|
5
|
+
* `vars.type === "feature_gate"` is the product working as published. Treating that the same as
|
|
6
|
+
* a broken create is the class of mistake that turns a correct backend into `world.seed` BLOCKED.
|
|
7
|
+
*/
|
|
8
|
+
import type { OperationObject } from "../spec/types.ts";
|
|
9
|
+
import type { OperationModel } from "../spec/graph.ts";
|
|
10
|
+
import type { Exchange } from "./client.ts";
|
|
11
|
+
import type { FindingCollector } from "./finding.ts";
|
|
12
|
+
import type { SchemaValidator } from "./validate.ts";
|
|
13
|
+
/** Forbidden status unless a later abstraction names another one — the documented default. */
|
|
14
|
+
export declare const FEATURE_GATE_STATUS = 403;
|
|
15
|
+
export declare function isDocumentedFeatureGateDenial(op: Pick<OperationModel, "featureGate">, status: number, body: unknown): boolean;
|
|
16
|
+
export declare function describeFeatureGate(op: Pick<OperationModel, "featureGate">, body: unknown): string;
|
|
17
|
+
export declare function featureGateVars(body: unknown): Record<string, unknown> | null;
|
|
18
|
+
/**
|
|
19
|
+
* A gate 403 still has to match the documented error schema. Coverage is "this 403 is
|
|
20
|
+
* expected"; it is not a free pass for an undeclared body.
|
|
21
|
+
*/
|
|
22
|
+
export declare function reportFeatureGateSchemaDrift(findings: FindingCollector, validator: SchemaValidator, op: OperationModel, raw: OperationObject | undefined, exchange: Exchange, entity: string): void;
|
|
23
|
+
//# sourceMappingURL=feature-gate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"feature-gate.d.ts","sourceRoot":"","sources":["../../src/runtime/feature-gate.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA;AACvD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AACtD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAC3C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AACpD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAEpD,8FAA8F;AAC9F,eAAO,MAAM,mBAAmB,MAAM,CAAA;AAEtC,wBAAgB,6BAA6B,CAC5C,EAAE,EAAE,IAAI,CAAC,cAAc,EAAE,aAAa,CAAC,EACvC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,OAAO,GACX,OAAO,CAYT;AAED,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,IAAI,CAAC,cAAc,EAAE,aAAa,CAAC,EAAE,IAAI,EAAE,OAAO,GAAG,MAAM,CAQlG;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAK7E;AAED;;;GAGG;AACH,wBAAgB,4BAA4B,CAC3C,QAAQ,EAAE,gBAAgB,EAC1B,SAAS,EAAE,eAAe,EAC1B,EAAE,EAAE,cAAc,EAClB,GAAG,EAAE,eAAe,GAAG,SAAS,EAChC,QAAQ,EAAE,QAAQ,EAClB,MAAM,EAAE,MAAM,GACZ,IAAI,CAwBN"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* When a 403 is the document, not a defect.
|
|
3
|
+
*
|
|
4
|
+
* `x-feature-gate` names a plan key. A free principal hitting that route and receiving
|
|
5
|
+
* `vars.type === "feature_gate"` is the product working as published. Treating that the same as
|
|
6
|
+
* a broken create is the class of mistake that turns a correct backend into `world.seed` BLOCKED.
|
|
7
|
+
*/
|
|
8
|
+
/** Forbidden status unless a later abstraction names another one — the documented default. */
|
|
9
|
+
export const FEATURE_GATE_STATUS = 403;
|
|
10
|
+
export function isDocumentedFeatureGateDenial(op, status, body) {
|
|
11
|
+
if (op.featureGate === null)
|
|
12
|
+
return false;
|
|
13
|
+
if (status !== FEATURE_GATE_STATUS)
|
|
14
|
+
return false;
|
|
15
|
+
if (body === null || typeof body !== "object")
|
|
16
|
+
return false;
|
|
17
|
+
const vars = body.vars;
|
|
18
|
+
if (vars === null || typeof vars !== "object")
|
|
19
|
+
return false;
|
|
20
|
+
const rec = vars;
|
|
21
|
+
if (rec.type !== "feature_gate")
|
|
22
|
+
return false;
|
|
23
|
+
/* A string that disagrees with the tag is backend/tag drift — keep today's failure. Absent
|
|
24
|
+
* `vars.feature` is enough together with `type`; do not invent a mismatch. */
|
|
25
|
+
if (typeof rec.feature === "string" && rec.feature !== op.featureGate)
|
|
26
|
+
return false;
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
export function describeFeatureGate(op, body) {
|
|
30
|
+
const key = op.featureGate ?? "unknown";
|
|
31
|
+
const vars = featureGateVars(body);
|
|
32
|
+
if (vars === null)
|
|
33
|
+
return `x-feature-gate: ${key}`;
|
|
34
|
+
const extras = ["current_plan", "required_plan", "feature"]
|
|
35
|
+
.filter((name) => typeof vars[name] === "string")
|
|
36
|
+
.map((name) => `${name}: ${vars[name]}`);
|
|
37
|
+
return extras.length === 0 ? `x-feature-gate: ${key}` : `x-feature-gate: ${key} (${extras.join(", ")})`;
|
|
38
|
+
}
|
|
39
|
+
export function featureGateVars(body) {
|
|
40
|
+
if (body === null || typeof body !== "object")
|
|
41
|
+
return null;
|
|
42
|
+
const vars = body.vars;
|
|
43
|
+
if (vars === null || typeof vars !== "object")
|
|
44
|
+
return null;
|
|
45
|
+
return vars;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* A gate 403 still has to match the documented error schema. Coverage is "this 403 is
|
|
49
|
+
* expected"; it is not a free pass for an undeclared body.
|
|
50
|
+
*/
|
|
51
|
+
export function reportFeatureGateSchemaDrift(findings, validator, op, raw, exchange, entity) {
|
|
52
|
+
if (raw === undefined)
|
|
53
|
+
return;
|
|
54
|
+
if (!validator.documents(raw, exchange.status))
|
|
55
|
+
return;
|
|
56
|
+
const result = validator.validate(op.operationId, raw, exchange.status, exchange.responseBody);
|
|
57
|
+
if (result.ok)
|
|
58
|
+
return;
|
|
59
|
+
if (findings.findings.some((finding) => finding.check === "schema.error-response-matches-document" &&
|
|
60
|
+
finding.entity === entity &&
|
|
61
|
+
finding.evidence.some((prior) => prior.seq === exchange.seq))) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
findings.spec("schema.error-response-matches-document", entity, `${exchange.status} error body does not match its documented schema`, `${op.operationId} returned ${exchange.status} with a body that fails the schema the ` +
|
|
65
|
+
`document declares for it: ${result.errors.join("; ")}. Clients that parse errors ` +
|
|
66
|
+
"from the spec will not understand this response.", [exchange]);
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=feature-gate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"feature-gate.js","sourceRoot":"","sources":["../../src/runtime/feature-gate.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAQH,8FAA8F;AAC9F,MAAM,CAAC,MAAM,mBAAmB,GAAG,GAAG,CAAA;AAEtC,MAAM,UAAU,6BAA6B,CAC5C,EAAuC,EACvC,MAAc,EACd,IAAa;IAEb,IAAI,EAAE,CAAC,WAAW,KAAK,IAAI;QAAE,OAAO,KAAK,CAAA;IACzC,IAAI,MAAM,KAAK,mBAAmB;QAAE,OAAO,KAAK,CAAA;IAChD,IAAI,IAAI,KAAK,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAA;IAC3D,MAAM,IAAI,GAAI,IAAgC,CAAC,IAAI,CAAA;IACnD,IAAI,IAAI,KAAK,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAA;IAC3D,MAAM,GAAG,GAAG,IAA+B,CAAA;IAC3C,IAAI,GAAG,CAAC,IAAI,KAAK,cAAc;QAAE,OAAO,KAAK,CAAA;IAC7C;kFAC8E;IAC9E,IAAI,OAAO,GAAG,CAAC,OAAO,KAAK,QAAQ,IAAI,GAAG,CAAC,OAAO,KAAK,EAAE,CAAC,WAAW;QAAE,OAAO,KAAK,CAAA;IACnF,OAAO,IAAI,CAAA;AACZ,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,EAAuC,EAAE,IAAa;IACzF,MAAM,GAAG,GAAG,EAAE,CAAC,WAAW,IAAI,SAAS,CAAA;IACvC,MAAM,IAAI,GAAG,eAAe,CAAC,IAAI,CAAC,CAAA;IAClC,IAAI,IAAI,KAAK,IAAI;QAAE,OAAO,mBAAmB,GAAG,EAAE,CAAA;IAClD,MAAM,MAAM,GAAG,CAAC,cAAc,EAAE,eAAe,EAAE,SAAS,CAAC;SACzD,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,KAAK,QAAQ,CAAC;SAChD,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,IAAI,KAAK,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IACzC,OAAO,MAAM,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,mBAAmB,GAAG,EAAE,CAAC,CAAC,CAAC,mBAAmB,GAAG,KAAK,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAA;AACxG,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,IAAa;IAC5C,IAAI,IAAI,KAAK,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAA;IAC1D,MAAM,IAAI,GAAI,IAAgC,CAAC,IAAI,CAAA;IACnD,IAAI,IAAI,KAAK,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAA;IAC1D,OAAO,IAA+B,CAAA;AACvC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,4BAA4B,CAC3C,QAA0B,EAC1B,SAA0B,EAC1B,EAAkB,EAClB,GAAgC,EAChC,QAAkB,EAClB,MAAc;IAEd,IAAI,GAAG,KAAK,SAAS;QAAE,OAAM;IAC7B,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,GAAG,EAAE,QAAQ,CAAC,MAAM,CAAC;QAAE,OAAM;IACtD,MAAM,MAAM,GAAG,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAC,WAAW,EAAE,GAAG,EAAE,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,YAAY,CAAC,CAAA;IAC9F,IAAI,MAAM,CAAC,EAAE;QAAE,OAAM;IACrB,IACC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CACrB,CAAC,OAAO,EAAE,EAAE,CACX,OAAO,CAAC,KAAK,KAAK,wCAAwC;QAC1D,OAAO,CAAC,MAAM,KAAK,MAAM;QACzB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,KAAK,QAAQ,CAAC,GAAG,CAAC,CAC7D,EACA,CAAC;QACF,OAAM;IACP,CAAC;IACD,QAAQ,CAAC,IAAI,CACZ,wCAAwC,EACxC,MAAM,EACN,GAAG,QAAQ,CAAC,MAAM,kDAAkD,EACpE,GAAG,EAAE,CAAC,WAAW,aAAa,QAAQ,CAAC,MAAM,yCAAyC;QACrF,6BAA6B,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,8BAA8B;QACnF,kDAAkD,EACnD,CAAC,QAAQ,CAAC,CACV,CAAA;AACF,CAAC"}
|
|
@@ -5,6 +5,10 @@
|
|
|
5
5
|
* order, escaping needs metacharacters. oat seeds a *discriminating cohort* — instances shaped so
|
|
6
6
|
* that every query assertion has signal — and derives everything from a seed so a failing run is
|
|
7
7
|
* exactly reproducible.
|
|
8
|
+
*
|
|
9
|
+
* After `$ref` inlining the same object identity can cycle, and empty schemas (`{}`, `true`,
|
|
10
|
+
* `additionalProperties: {}`) mean "any value", not "walk this object again". Generation therefore
|
|
11
|
+
* keys a WeakSet on every node and treats those open shapes as a scalar or `{}`.
|
|
8
12
|
*/
|
|
9
13
|
export type Variant = "baseline" | "lexical-first" | "lexical-last" | "null-heavy" | "unicode" | "metacharacter" | "boundary";
|
|
10
14
|
export declare const COHORT: readonly Variant[];
|
|
@@ -15,6 +19,29 @@ export interface CohortMember {
|
|
|
15
19
|
variant: Variant;
|
|
16
20
|
body: Record<string, unknown>;
|
|
17
21
|
}
|
|
18
|
-
|
|
22
|
+
/** Thrown when a walk still overflows; the pointer names the node that blew the stack. */
|
|
23
|
+
export declare class FixtureOverflow extends Error {
|
|
24
|
+
readonly operationId: string;
|
|
25
|
+
readonly pointer: string;
|
|
26
|
+
constructor(operationId: string, pointer: string);
|
|
27
|
+
}
|
|
28
|
+
export declare function overflowFrom(error: unknown, operationId: string, pointer?: string): FixtureOverflow;
|
|
29
|
+
export declare function isOverflowError(error: unknown): boolean;
|
|
30
|
+
export declare function buildCohort(bodySchema: Schema | boolean, seed: number, variants?: readonly Variant[], operationId?: string): CohortMember[];
|
|
31
|
+
/**
|
|
32
|
+
* One create body, or a gap if a required field cannot be honoured.
|
|
33
|
+
*
|
|
34
|
+
* Optional fields that cannot match `format`/`pattern` are omitted. Required ones must not fall
|
|
35
|
+
* back to `"Quarterly Report N"` — that is what 400s every email/handle/url field.
|
|
36
|
+
*/
|
|
37
|
+
export declare function generateBody(bodySchema: Schema | boolean, variant: Variant, rand: () => number, index: number, operationId?: string): {
|
|
38
|
+
body: Record<string, unknown>;
|
|
39
|
+
missingRequired: string[];
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* `{}` / `true` / a lone `additionalProperties: {}` is "any JSON", not an object to descend.
|
|
43
|
+
* Walking it again is what turns `z.unknown()` into a stack overflow.
|
|
44
|
+
*/
|
|
45
|
+
export declare function isVacuous(schema: unknown): boolean;
|
|
19
46
|
export {};
|
|
20
47
|
//# sourceMappingURL=fixture.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fixture.d.ts","sourceRoot":"","sources":["../../src/runtime/fixture.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"fixture.d.ts","sourceRoot":"","sources":["../../src/runtime/fixture.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,MAAM,MAAM,OAAO,GAChB,UAAU,GACV,eAAe,GACf,cAAc,GACd,YAAY,GACZ,SAAS,GACT,eAAe,GACf,UAAU,CAAA;AAEb,eAAO,MAAM,MAAM,EAAE,SAAS,OAAO,EAQpC,CAAA;AAED,8DAA8D;AAC9D,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,MAAM,CAQrD;AAED,KAAK,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;AAgBrC,MAAM,WAAW,YAAY;IAC5B,OAAO,EAAE,OAAO,CAAA;IAChB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAC7B;AAED,0FAA0F;AAC1F,qBAAa,eAAgB,SAAQ,KAAK;IAExC,QAAQ,CAAC,WAAW,EAAE,MAAM;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM;gBADf,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM;CAKzB;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,SAAM,GAAG,eAAe,CAKhG;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAGvD;AAED,wBAAgB,WAAW,CAC1B,UAAU,EAAE,MAAM,GAAG,OAAO,EAC5B,IAAI,EAAE,MAAM,EACZ,QAAQ,GAAE,SAAS,OAAO,EAAW,EACrC,WAAW,SAAY,GACrB,YAAY,EAAE,CAmBhB;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAC3B,UAAU,EAAE,MAAM,GAAG,OAAO,EAC5B,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,MAAM,MAAM,EAClB,KAAK,EAAE,MAAM,EACb,WAAW,SAAY,GACrB;IAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,eAAe,EAAE,MAAM,EAAE,CAAA;CAAE,CAc9D;AAwcD;;;GAGG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAgBlD"}
|