@nifrajs/schema 3.3.0 → 3.5.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/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/openapi-import.d.ts +73 -0
- package/dist/openapi-import.d.ts.map +1 -0
- package/dist/openapi-import.js +302 -0
- package/dist/openapi-import.js.map +1 -0
- package/dist/openapi.d.ts +8 -0
- package/dist/openapi.d.ts.map +1 -1
- package/dist/openapi.js +54 -15
- package/dist/openapi.js.map +1 -1
- package/package.json +2 -2
- package/src/index.ts +16 -1
- package/src/openapi-import.ts +410 -0
- package/src/openapi.ts +61 -13
package/dist/index.d.ts
CHANGED
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
*/
|
|
9
9
|
export { fromTypeBox, type NifraSchema } from "./adapter.js";
|
|
10
10
|
export { registerFormat } from "./formats.js";
|
|
11
|
-
export { type OpenAPIDocument, type OpenAPIInfo, toOpenAPI } from "./openapi.js";
|
|
11
|
+
export { type OpenAPIDocument, type OpenAPIInfo, toOpenAPI, toOpenAPIFromEvidence, } from "./openapi.js";
|
|
12
|
+
export { diffOpenApiInventory, type ImportedApiInventory, type ImportedRoute, type ImportedRouteQuery, type ImportOpenAPIOptions, importOpenAPI, OpenAPIImportError, type OpenApiInventoryDrift, } from "./openapi-import.js";
|
|
12
13
|
export { decodeCursor, encodeCursor, type Page, paginate } from "./pagination.js";
|
|
13
14
|
export { t } from "./t.js";
|
|
14
15
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,WAAW,EAAE,KAAK,WAAW,EAAE,MAAM,cAAc,CAAA;AAI5D,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAC7C,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,WAAW,EAAE,KAAK,WAAW,EAAE,MAAM,cAAc,CAAA;AAI5D,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAC7C,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,WAAW,EAChB,SAAS,EACT,qBAAqB,GACtB,MAAM,cAAc,CAAA;AACrB,OAAO,EACL,oBAAoB,EACpB,KAAK,oBAAoB,EACzB,KAAK,aAAa,EAClB,KAAK,kBAAkB,EACvB,KAAK,oBAAoB,EACzB,aAAa,EACb,kBAAkB,EAClB,KAAK,qBAAqB,GAC3B,MAAM,qBAAqB,CAAA;AAE5B,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,KAAK,IAAI,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AACjF,OAAO,EAAE,CAAC,EAAE,MAAM,QAAQ,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -11,7 +11,8 @@ export { fromTypeBox } from "./adapter.js";
|
|
|
11
11
|
// install themselves lazily from the validate path (see ./adapter.ts) - NOT as an import side
|
|
12
12
|
// effect of this module, which a production bundler would tree-shake away.
|
|
13
13
|
export { registerFormat } from "./formats.js";
|
|
14
|
-
export { toOpenAPI } from "./openapi.js";
|
|
14
|
+
export { toOpenAPI, toOpenAPIFromEvidence, } from "./openapi.js";
|
|
15
|
+
export { diffOpenApiInventory, importOpenAPI, OpenAPIImportError, } from "./openapi-import.js";
|
|
15
16
|
// Cursor-pagination runtime helpers - pair with `t.paginated` / `t.pageQuery`.
|
|
16
17
|
export { decodeCursor, encodeCursor, paginate } from "./pagination.js";
|
|
17
18
|
export { t } from "./t.js";
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,WAAW,EAAoB,MAAM,cAAc,CAAA;AAC5D,gGAAgG;AAChG,8FAA8F;AAC9F,2EAA2E;AAC3E,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAC7C,OAAO,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,WAAW,EAAoB,MAAM,cAAc,CAAA;AAC5D,gGAAgG;AAChG,8FAA8F;AAC9F,2EAA2E;AAC3E,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAC7C,OAAO,EAGL,SAAS,EACT,qBAAqB,GACtB,MAAM,cAAc,CAAA;AACrB,OAAO,EACL,oBAAoB,EAKpB,aAAa,EACb,kBAAkB,GAEnB,MAAM,qBAAqB,CAAA;AAC5B,+EAA+E;AAC/E,OAAO,EAAE,YAAY,EAAE,YAAY,EAAa,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AACjF,OAAO,EAAE,CAAC,EAAE,MAAM,QAAQ,CAAA"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenAPI 3.x inventory import - the structural half of an export/import roundtrip.
|
|
3
|
+
*
|
|
4
|
+
* `toOpenAPI` renders a Nifra app (or contract) as an OpenAPI 3.1 document. This module reads such
|
|
5
|
+
* a document back into a canonical route inventory: method, Nifra-templated path (`{id}` back to
|
|
6
|
+
* `:id`), required query names, body presence, and response statuses. `diffOpenApiInventory` then
|
|
7
|
+
* compares two inventories, so `export -> import -> diff` proves the cycle survives in CI.
|
|
8
|
+
*
|
|
9
|
+
* Deliberate boundaries, all fail-closed or explicitly documented:
|
|
10
|
+
*
|
|
11
|
+
* - Offline and ref-opaque. `$ref`s are never resolved and nothing is fetched. A `$ref` parameter
|
|
12
|
+
* cannot yield a name without resolution, so it throws with the location; `$ref` bodies and
|
|
13
|
+
* response schemas only assert presence, which needs no resolution.
|
|
14
|
+
* - No schema reconstruction. JSON Schema is not turned back into `t` builders here - that is a
|
|
15
|
+
* separate codegen step needing consumer evidence. The inventory proves the route table survived;
|
|
16
|
+
* field-level types ride along in the source document, not in this comparison.
|
|
17
|
+
* - No auth interpretation. `security`, `securitySchemes`, and `servers` are never read. Importing
|
|
18
|
+
* an API must not silently adopt its auth model.
|
|
19
|
+
* - Templated paths degrade conservatively. `/files/*path` exports as `/files/{path}`, indistinguishable
|
|
20
|
+
* from a single-segment `:path`. Import yields `:path` and records a note for every OpenAPI template;
|
|
21
|
+
* a greedy remainder needs a manual `*path` rewrite.
|
|
22
|
+
* - Nifra parameter names follow `/^[A-Za-z_][A-Za-z0-9_]*$/`; `__proto__`, `constructor`, and
|
|
23
|
+
* `prototype` are rejected. Mixed templates are converted only when their boundaries are representable.
|
|
24
|
+
* - Bounded. Path/operation/parameter counts are capped (`ImportOpenAPIOptions`); a document over
|
|
25
|
+
* the caps throws rather than allocating.
|
|
26
|
+
*/
|
|
27
|
+
export declare class OpenAPIImportError extends Error {
|
|
28
|
+
constructor(message: string);
|
|
29
|
+
}
|
|
30
|
+
export interface ImportOpenAPIOptions {
|
|
31
|
+
/** Maximum entries under `paths`. Default 5000. */
|
|
32
|
+
readonly maxPaths?: number;
|
|
33
|
+
/** Maximum operations on one path item. Default 8. */
|
|
34
|
+
readonly maxOperationsPerPath?: number;
|
|
35
|
+
/** Maximum merged parameters per operation. Default 100. */
|
|
36
|
+
readonly maxParameters?: number;
|
|
37
|
+
}
|
|
38
|
+
export interface ImportedRouteQuery {
|
|
39
|
+
readonly name: string;
|
|
40
|
+
readonly required: boolean;
|
|
41
|
+
}
|
|
42
|
+
export interface ImportedRoute {
|
|
43
|
+
/** Lowercase HTTP method as it appears in the document. */
|
|
44
|
+
readonly method: string;
|
|
45
|
+
/** Nifra-templated path (`/users/:id`). */
|
|
46
|
+
readonly path: string;
|
|
47
|
+
readonly operationId?: string;
|
|
48
|
+
/** Template parameter names in path order. */
|
|
49
|
+
readonly pathParams: readonly string[];
|
|
50
|
+
readonly query: readonly ImportedRouteQuery[];
|
|
51
|
+
readonly hasBody: boolean;
|
|
52
|
+
readonly requestBodyRequired: boolean;
|
|
53
|
+
/** Response status keys (`"200"`, `"404"`, `"default"`). */
|
|
54
|
+
readonly responseStatuses: readonly string[];
|
|
55
|
+
/** Per-route degradations (wildcard rewrites). */
|
|
56
|
+
readonly notes: readonly string[];
|
|
57
|
+
}
|
|
58
|
+
export interface ImportedApiInventory {
|
|
59
|
+
readonly title: string;
|
|
60
|
+
readonly version: string;
|
|
61
|
+
readonly routes: readonly ImportedRoute[];
|
|
62
|
+
readonly warnings: readonly string[];
|
|
63
|
+
}
|
|
64
|
+
export interface OpenApiInventoryDrift {
|
|
65
|
+
/** `METHOD /path`, or `inventory` for route-set level drift. */
|
|
66
|
+
readonly route: string;
|
|
67
|
+
readonly message: string;
|
|
68
|
+
}
|
|
69
|
+
/** Parse an OpenAPI 3.0/3.1 document into a canonical route inventory. Throws on invalid input. */
|
|
70
|
+
export declare function importOpenAPI(document: unknown, options?: ImportOpenAPIOptions): ImportedApiInventory;
|
|
71
|
+
/** Compare two inventories. Empty means the export/import cycle preserved the route table. */
|
|
72
|
+
export declare function diffOpenApiInventory(expected: ImportedApiInventory, actual: ImportedApiInventory): readonly OpenApiInventoryDrift[];
|
|
73
|
+
//# sourceMappingURL=openapi-import.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openapi-import.d.ts","sourceRoot":"","sources":["../src/openapi-import.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,qBAAa,kBAAmB,SAAQ,KAAK;gBAC/B,OAAO,EAAE,MAAM;CAI5B;AAED,MAAM,WAAW,oBAAoB;IACnC,mDAAmD;IACnD,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;IAC1B,sDAAsD;IACtD,QAAQ,CAAC,oBAAoB,CAAC,EAAE,MAAM,CAAA;IACtC,4DAA4D;IAC5D,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAA;CAChC;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAA;CAC3B;AAED,MAAM,WAAW,aAAa;IAC5B,2DAA2D;IAC3D,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,2CAA2C;IAC3C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,8CAA8C;IAC9C,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAA;IACtC,QAAQ,CAAC,KAAK,EAAE,SAAS,kBAAkB,EAAE,CAAA;IAC7C,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,QAAQ,CAAC,mBAAmB,EAAE,OAAO,CAAA;IACrC,4DAA4D;IAC5D,QAAQ,CAAC,gBAAgB,EAAE,SAAS,MAAM,EAAE,CAAA;IAC5C,kDAAkD;IAClD,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAA;CAClC;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,MAAM,EAAE,SAAS,aAAa,EAAE,CAAA;IACzC,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAA;CACrC;AAED,MAAM,WAAW,qBAAqB;IACpC,gEAAgE;IAChE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CACzB;AAgJD,mGAAmG;AACnG,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,OAAO,EACjB,OAAO,GAAE,oBAAyB,GACjC,oBAAoB,CA+HtB;AAMD,8FAA8F;AAC9F,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,oBAAoB,EAC9B,MAAM,EAAE,oBAAoB,GAC3B,SAAS,qBAAqB,EAAE,CAgDlC"}
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenAPI 3.x inventory import - the structural half of an export/import roundtrip.
|
|
3
|
+
*
|
|
4
|
+
* `toOpenAPI` renders a Nifra app (or contract) as an OpenAPI 3.1 document. This module reads such
|
|
5
|
+
* a document back into a canonical route inventory: method, Nifra-templated path (`{id}` back to
|
|
6
|
+
* `:id`), required query names, body presence, and response statuses. `diffOpenApiInventory` then
|
|
7
|
+
* compares two inventories, so `export -> import -> diff` proves the cycle survives in CI.
|
|
8
|
+
*
|
|
9
|
+
* Deliberate boundaries, all fail-closed or explicitly documented:
|
|
10
|
+
*
|
|
11
|
+
* - Offline and ref-opaque. `$ref`s are never resolved and nothing is fetched. A `$ref` parameter
|
|
12
|
+
* cannot yield a name without resolution, so it throws with the location; `$ref` bodies and
|
|
13
|
+
* response schemas only assert presence, which needs no resolution.
|
|
14
|
+
* - No schema reconstruction. JSON Schema is not turned back into `t` builders here - that is a
|
|
15
|
+
* separate codegen step needing consumer evidence. The inventory proves the route table survived;
|
|
16
|
+
* field-level types ride along in the source document, not in this comparison.
|
|
17
|
+
* - No auth interpretation. `security`, `securitySchemes`, and `servers` are never read. Importing
|
|
18
|
+
* an API must not silently adopt its auth model.
|
|
19
|
+
* - Templated paths degrade conservatively. `/files/*path` exports as `/files/{path}`, indistinguishable
|
|
20
|
+
* from a single-segment `:path`. Import yields `:path` and records a note for every OpenAPI template;
|
|
21
|
+
* a greedy remainder needs a manual `*path` rewrite.
|
|
22
|
+
* - Nifra parameter names follow `/^[A-Za-z_][A-Za-z0-9_]*$/`; `__proto__`, `constructor`, and
|
|
23
|
+
* `prototype` are rejected. Mixed templates are converted only when their boundaries are representable.
|
|
24
|
+
* - Bounded. Path/operation/parameter counts are capped (`ImportOpenAPIOptions`); a document over
|
|
25
|
+
* the caps throws rather than allocating.
|
|
26
|
+
*/
|
|
27
|
+
export class OpenAPIImportError extends Error {
|
|
28
|
+
constructor(message) {
|
|
29
|
+
super(`openapi import: ${message}`);
|
|
30
|
+
this.name = "OpenAPIImportError";
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
const SUPPORTED_METHODS = new Set([
|
|
34
|
+
"get",
|
|
35
|
+
"put",
|
|
36
|
+
"post",
|
|
37
|
+
"delete",
|
|
38
|
+
"patch",
|
|
39
|
+
"head",
|
|
40
|
+
"options",
|
|
41
|
+
"trace",
|
|
42
|
+
]);
|
|
43
|
+
const PATH_ITEM_METADATA = new Set(["$ref", "summary", "description", "servers", "parameters"]);
|
|
44
|
+
const RESPONSE_KEY = /^(?:[1-5][0-9]{2}|default)$/;
|
|
45
|
+
const TEMPLATE_EXPRESSION = /\{([^{}]*)\}/g;
|
|
46
|
+
const NIFRA_PARAM = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
47
|
+
const RESERVED_PARAM_NAMES = new Set(["__proto__", "constructor", "prototype"]);
|
|
48
|
+
function isRecord(value) {
|
|
49
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
50
|
+
}
|
|
51
|
+
function normalizedLimit(value, fallback, label) {
|
|
52
|
+
const max = value ?? fallback;
|
|
53
|
+
if (!Number.isSafeInteger(max) || max < 0)
|
|
54
|
+
throw new OpenAPIImportError(`${label} limit must be a finite safe integer >= 0`);
|
|
55
|
+
return max;
|
|
56
|
+
}
|
|
57
|
+
function assertLimit(count, max, label) {
|
|
58
|
+
if (count > max)
|
|
59
|
+
throw new OpenAPIImportError(`${label} count ${count} exceeds limit ${max}`);
|
|
60
|
+
}
|
|
61
|
+
/** Collect own keys while stopping as soon as a bounded collection is over its cap. */
|
|
62
|
+
function ownKeysWithinLimit(record, max, label, include = () => true) {
|
|
63
|
+
const keys = [];
|
|
64
|
+
for (const key in record) {
|
|
65
|
+
if (!Object.hasOwn(record, key) || !include(key))
|
|
66
|
+
continue;
|
|
67
|
+
if (keys.length >= max)
|
|
68
|
+
throw new OpenAPIImportError(`${label} count ${keys.length + 1} exceeds limit ${max}`);
|
|
69
|
+
keys.push(key);
|
|
70
|
+
}
|
|
71
|
+
return keys;
|
|
72
|
+
}
|
|
73
|
+
function toNifraPath(openapiPath) {
|
|
74
|
+
const segments = openapiPath.split("/");
|
|
75
|
+
let templated = false;
|
|
76
|
+
const pathParams = [];
|
|
77
|
+
const out = segments.map((segment) => {
|
|
78
|
+
if (!segment.includes("{") && !segment.includes("}"))
|
|
79
|
+
return segment;
|
|
80
|
+
const matches = [...segment.matchAll(TEMPLATE_EXPRESSION)];
|
|
81
|
+
if (matches.length === 0)
|
|
82
|
+
throw new OpenAPIImportError(`unparseable path template ${openapiPath}`);
|
|
83
|
+
let cursor = 0;
|
|
84
|
+
let converted = "";
|
|
85
|
+
for (const match of matches) {
|
|
86
|
+
if (match.index === undefined)
|
|
87
|
+
throw new OpenAPIImportError(`unparseable path template ${openapiPath}`);
|
|
88
|
+
const start = match.index;
|
|
89
|
+
const end = start + match[0].length;
|
|
90
|
+
const name = match[1] ?? "";
|
|
91
|
+
const previous = segment[start - 1];
|
|
92
|
+
const next = segment[end];
|
|
93
|
+
// Nifra's mixed grammar needs a non-identifier delimiter after a parameter. A terminal
|
|
94
|
+
// `foo{bar}` or adjacent `{a}{b}` cannot be represented without changing the capture.
|
|
95
|
+
if (next === "{" ||
|
|
96
|
+
(next !== undefined && /[A-Za-z0-9_]/.test(next)) ||
|
|
97
|
+
(next === undefined && previous !== undefined && /[A-Za-z0-9_]/.test(previous)))
|
|
98
|
+
throw new OpenAPIImportError(`path template ${openapiPath} is not representable in Nifra`);
|
|
99
|
+
if (!NIFRA_PARAM.test(name) || RESERVED_PARAM_NAMES.has(name))
|
|
100
|
+
throw new OpenAPIImportError(`invalid path parameter ${name}`);
|
|
101
|
+
if (pathParams.includes(name))
|
|
102
|
+
throw new OpenAPIImportError(`duplicate path parameter ${name} on ${openapiPath}`);
|
|
103
|
+
pathParams.push(name);
|
|
104
|
+
converted += `${segment.slice(cursor, start)}:${name}`;
|
|
105
|
+
cursor = end;
|
|
106
|
+
templated = true;
|
|
107
|
+
}
|
|
108
|
+
const tail = segment.slice(cursor);
|
|
109
|
+
if (tail.includes("{") || tail.includes("}"))
|
|
110
|
+
throw new OpenAPIImportError(`unparseable path template ${openapiPath}`);
|
|
111
|
+
return converted + tail;
|
|
112
|
+
});
|
|
113
|
+
return { path: out.join("/"), templated, pathParams: Object.freeze(pathParams) };
|
|
114
|
+
}
|
|
115
|
+
function mergedParameters(pathItem, operation, max) {
|
|
116
|
+
const merged = new Map();
|
|
117
|
+
for (const source of [pathItem.parameters, operation.parameters]) {
|
|
118
|
+
if (source === undefined)
|
|
119
|
+
continue;
|
|
120
|
+
if (!Array.isArray(source))
|
|
121
|
+
throw new OpenAPIImportError("parameters must be an array");
|
|
122
|
+
assertLimit(source.length, max, "parameters");
|
|
123
|
+
for (const entry of source) {
|
|
124
|
+
if (!isRecord(entry))
|
|
125
|
+
throw new OpenAPIImportError("parameter entries must be objects");
|
|
126
|
+
if ("$ref" in entry)
|
|
127
|
+
throw new OpenAPIImportError("parameter $refs must be inlined before import - refs are never resolved");
|
|
128
|
+
const { name, in: location, required, } = entry;
|
|
129
|
+
if (typeof name !== "string" || name === "")
|
|
130
|
+
throw new OpenAPIImportError("parameters need a non-empty name");
|
|
131
|
+
if (typeof location !== "string" || location === "")
|
|
132
|
+
throw new OpenAPIImportError(`parameter ${name} needs an 'in' location`);
|
|
133
|
+
merged.set(`${location} ${name}`, {
|
|
134
|
+
name,
|
|
135
|
+
location,
|
|
136
|
+
required: required === true,
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
assertLimit(merged.size, max, "merged parameters");
|
|
141
|
+
return [...merged.values()];
|
|
142
|
+
}
|
|
143
|
+
/** Parse an OpenAPI 3.0/3.1 document into a canonical route inventory. Throws on invalid input. */
|
|
144
|
+
export function importOpenAPI(document, options = {}) {
|
|
145
|
+
const maxPaths = normalizedLimit(options.maxPaths, 5000, "maxPaths");
|
|
146
|
+
const maxOperations = normalizedLimit(options.maxOperationsPerPath, 8, "maxOperationsPerPath");
|
|
147
|
+
const maxParameters = normalizedLimit(options.maxParameters, 100, "maxParameters");
|
|
148
|
+
if (!isRecord(document))
|
|
149
|
+
throw new OpenAPIImportError("document must be an object");
|
|
150
|
+
const { openapi, info, paths } = document;
|
|
151
|
+
if (typeof openapi !== "string" || !/^3\.(0|1)\.\d+$/.test(openapi))
|
|
152
|
+
throw new OpenAPIImportError(`unsupported OpenAPI version ${JSON.stringify(openapi)} - 3.0.x and 3.1.x only`);
|
|
153
|
+
if (!isRecord(info))
|
|
154
|
+
throw new OpenAPIImportError("document.info must be an object");
|
|
155
|
+
const title = typeof info.title === "string" ? info.title : "";
|
|
156
|
+
const version = typeof info.version === "string" ? info.version : "";
|
|
157
|
+
if (paths === undefined)
|
|
158
|
+
return { title, version, routes: Object.freeze([]), warnings: Object.freeze([]) };
|
|
159
|
+
if (!isRecord(paths))
|
|
160
|
+
throw new OpenAPIImportError("document.paths must be an object");
|
|
161
|
+
const pathKeys = ownKeysWithinLimit(paths, maxPaths, "paths");
|
|
162
|
+
const warnings = [];
|
|
163
|
+
const routes = [];
|
|
164
|
+
for (const openapiPath of pathKeys) {
|
|
165
|
+
if (!openapiPath.startsWith("/"))
|
|
166
|
+
throw new OpenAPIImportError(`path ${openapiPath} must start with /`);
|
|
167
|
+
const pathItem = paths[openapiPath];
|
|
168
|
+
if (!isRecord(pathItem))
|
|
169
|
+
throw new OpenAPIImportError(`path item ${openapiPath} must be an object`);
|
|
170
|
+
if (Object.hasOwn(pathItem, "$ref"))
|
|
171
|
+
throw new OpenAPIImportError(`path item $refs must be inlined before import - refs are never resolved`);
|
|
172
|
+
const operationKeys = ownKeysWithinLimit(pathItem, maxOperations, `operations on ${openapiPath}`, (key) => key !== "parameters" && !PATH_ITEM_METADATA.has(key) && !key.startsWith("x-"));
|
|
173
|
+
const { path, templated, pathParams } = toNifraPath(openapiPath);
|
|
174
|
+
const seenMethods = new Set();
|
|
175
|
+
for (const rawMethod of operationKeys) {
|
|
176
|
+
const method = rawMethod.toLowerCase();
|
|
177
|
+
if (!SUPPORTED_METHODS.has(method))
|
|
178
|
+
throw new OpenAPIImportError(`unsupported method ${rawMethod} on ${openapiPath}`);
|
|
179
|
+
if (seenMethods.has(method))
|
|
180
|
+
throw new OpenAPIImportError(`duplicate method ${rawMethod} on ${openapiPath}`);
|
|
181
|
+
seenMethods.add(method);
|
|
182
|
+
const rawOperation = pathItem[rawMethod];
|
|
183
|
+
if (!isRecord(rawOperation))
|
|
184
|
+
throw new OpenAPIImportError(`operation ${rawMethod} ${openapiPath} must be an object`);
|
|
185
|
+
const params = mergedParameters(pathItem, rawOperation, maxParameters);
|
|
186
|
+
const query = [];
|
|
187
|
+
for (const param of params) {
|
|
188
|
+
if (param.location === "query")
|
|
189
|
+
query.push({ name: param.name, required: param.required });
|
|
190
|
+
else if (param.location !== "path" && param.location !== "header")
|
|
191
|
+
warnings.push(`${method.toUpperCase()} ${path}: '${param.location}' parameter '${param.name}' skipped`);
|
|
192
|
+
else if (param.location === "header")
|
|
193
|
+
warnings.push(`${method.toUpperCase()} ${path}: header parameters are not inventoried`);
|
|
194
|
+
}
|
|
195
|
+
const declaredPathParams = new Set(params.filter((param) => param.location === "path").map((param) => param.name));
|
|
196
|
+
for (const name of pathParams) {
|
|
197
|
+
if (!declaredPathParams.has(name))
|
|
198
|
+
throw new OpenAPIImportError(`path parameter ${name} is not declared on ${method.toUpperCase()} ${path}`);
|
|
199
|
+
}
|
|
200
|
+
for (const name of declaredPathParams) {
|
|
201
|
+
if (!pathParams.includes(name))
|
|
202
|
+
throw new OpenAPIImportError(`declared path parameter ${name} is not in ${path}`);
|
|
203
|
+
}
|
|
204
|
+
const rawBody = rawOperation.requestBody;
|
|
205
|
+
const hasBody = rawBody !== undefined;
|
|
206
|
+
if (hasBody && !isRecord(rawBody))
|
|
207
|
+
throw new OpenAPIImportError(`requestBody on ${method.toUpperCase()} ${path} must be an object`);
|
|
208
|
+
const rawResponses = rawOperation.responses;
|
|
209
|
+
if (rawResponses === undefined)
|
|
210
|
+
throw new OpenAPIImportError(`responses are required on ${method.toUpperCase()} ${path}`);
|
|
211
|
+
if (!isRecord(rawResponses))
|
|
212
|
+
throw new OpenAPIImportError(`responses on ${method.toUpperCase()} ${path} must be an object`);
|
|
213
|
+
const responseStatuses = [];
|
|
214
|
+
for (const status of Object.keys(rawResponses)) {
|
|
215
|
+
if (!RESPONSE_KEY.test(status))
|
|
216
|
+
throw new OpenAPIImportError(`invalid response status ${status} on ${method.toUpperCase()} ${path}`);
|
|
217
|
+
if (!isRecord(rawResponses[status]))
|
|
218
|
+
throw new OpenAPIImportError(`response ${status} on ${method.toUpperCase()} ${path} must be an object`);
|
|
219
|
+
responseStatuses.push(status);
|
|
220
|
+
}
|
|
221
|
+
const notes = templated
|
|
222
|
+
? [
|
|
223
|
+
"OpenAPI path templates import as single-segment :params - greedy remainders (*rest) cannot be distinguished and need manual rewrites",
|
|
224
|
+
]
|
|
225
|
+
: [];
|
|
226
|
+
const operationId = rawOperation.operationId;
|
|
227
|
+
routes.push(Object.freeze({
|
|
228
|
+
method,
|
|
229
|
+
path,
|
|
230
|
+
...(typeof operationId === "string" && operationId !== "" ? { operationId } : {}),
|
|
231
|
+
pathParams,
|
|
232
|
+
query: Object.freeze(query),
|
|
233
|
+
hasBody,
|
|
234
|
+
requestBodyRequired: hasBody && rawBody.required === true,
|
|
235
|
+
responseStatuses: Object.freeze(responseStatuses),
|
|
236
|
+
notes: Object.freeze(notes),
|
|
237
|
+
}));
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
if (routes.some((route) => route.pathParams.length > 0))
|
|
241
|
+
warnings.push("OpenAPI path templates import as single-segment :params - greedy remainders (*rest) cannot be distinguished and need manual rewrites; see route notes");
|
|
242
|
+
return {
|
|
243
|
+
title,
|
|
244
|
+
version,
|
|
245
|
+
routes: Object.freeze(routes),
|
|
246
|
+
warnings: Object.freeze(warnings),
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
function routeKey(method, path) {
|
|
250
|
+
return `${method.toUpperCase()} ${path}`;
|
|
251
|
+
}
|
|
252
|
+
/** Compare two inventories. Empty means the export/import cycle preserved the route table. */
|
|
253
|
+
export function diffOpenApiInventory(expected, actual) {
|
|
254
|
+
const drift = [];
|
|
255
|
+
const actualByKey = new Map(actual.routes.map((route) => [routeKey(route.method, route.path), route]));
|
|
256
|
+
const expectedKeys = new Set();
|
|
257
|
+
for (const want of expected.routes) {
|
|
258
|
+
const key = routeKey(want.method, want.path);
|
|
259
|
+
expectedKeys.add(key);
|
|
260
|
+
const got = actualByKey.get(key);
|
|
261
|
+
if (got === undefined) {
|
|
262
|
+
drift.push({ route: key, message: "route missing after import" });
|
|
263
|
+
continue;
|
|
264
|
+
}
|
|
265
|
+
const wantQuery = new Map(want.query.map((param) => [param.name, param.required]));
|
|
266
|
+
const gotQuery = new Map(got.query.map((param) => [param.name, param.required]));
|
|
267
|
+
for (const [name, required] of wantQuery) {
|
|
268
|
+
if (!gotQuery.has(name))
|
|
269
|
+
drift.push({ route: key, message: `query parameter '${name}' lost` });
|
|
270
|
+
else if (gotQuery.get(name) !== required)
|
|
271
|
+
drift.push({ route: key, message: `query parameter '${name}' required flag flipped` });
|
|
272
|
+
}
|
|
273
|
+
for (const name of gotQuery.keys()) {
|
|
274
|
+
if (!wantQuery.has(name))
|
|
275
|
+
drift.push({ route: key, message: `unexpected query parameter '${name}'` });
|
|
276
|
+
}
|
|
277
|
+
if (want.hasBody !== got.hasBody)
|
|
278
|
+
drift.push({
|
|
279
|
+
route: key,
|
|
280
|
+
message: want.hasBody ? "request body lost" : "unexpected request body",
|
|
281
|
+
});
|
|
282
|
+
else if (want.hasBody && want.requestBodyRequired !== got.requestBodyRequired)
|
|
283
|
+
drift.push({ route: key, message: "request body required flag flipped" });
|
|
284
|
+
const wantStatuses = new Set(want.responseStatuses);
|
|
285
|
+
const gotStatuses = new Set(got.responseStatuses);
|
|
286
|
+
for (const status of wantStatuses) {
|
|
287
|
+
if (!gotStatuses.has(status))
|
|
288
|
+
drift.push({ route: key, message: `response status ${status} lost` });
|
|
289
|
+
}
|
|
290
|
+
for (const status of gotStatuses) {
|
|
291
|
+
if (!wantStatuses.has(status))
|
|
292
|
+
drift.push({ route: key, message: `unexpected response status ${status}` });
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
for (const got of actual.routes) {
|
|
296
|
+
const key = routeKey(got.method, got.path);
|
|
297
|
+
if (!expectedKeys.has(key))
|
|
298
|
+
drift.push({ route: key, message: "unexpected route after import" });
|
|
299
|
+
}
|
|
300
|
+
return Object.freeze(drift);
|
|
301
|
+
}
|
|
302
|
+
//# sourceMappingURL=openapi-import.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openapi-import.js","sourceRoot":"","sources":["../src/openapi-import.ts"],"names":[],"mappings":"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"}
|
package/dist/openapi.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ContractShape } from "@nifrajs/core/contract";
|
|
2
|
+
import { type ProjectEvidenceSnapshot } from "@nifrajs/core/evidence";
|
|
2
3
|
import { type JsonSchema } from "@nifrajs/core/reflection";
|
|
3
4
|
import type { Server } from "@nifrajs/core/server";
|
|
4
5
|
/**
|
|
@@ -39,6 +40,11 @@ export interface ToOpenAPIOptions {
|
|
|
39
40
|
readonly securitySchemes?: Readonly<Record<string, Record<string, unknown>>>;
|
|
40
41
|
/** Document-wide security requirement; a per-operation `security` (incl. `[]` for public) overrides it. */
|
|
41
42
|
readonly security?: readonly SecurityRequirement[];
|
|
43
|
+
/**
|
|
44
|
+
* Reuse a canonical token-only route-evidence pass. This is useful for build artifacts and CLI
|
|
45
|
+
* projections that already evaluated the project; it never supplies runtime validators.
|
|
46
|
+
*/
|
|
47
|
+
readonly evidence?: ProjectEvidenceSnapshot;
|
|
42
48
|
/**
|
|
43
49
|
* Per-operation overrides, shallow-merged over the generated operation. Keyed by `operationId`
|
|
44
50
|
* (contract op name) or `"METHOD /path"` (e.g. `"GET /users/:id"`). The escape hatch for detail that
|
|
@@ -99,5 +105,7 @@ export interface OpenAPIDocument {
|
|
|
99
105
|
}
|
|
100
106
|
/** Generate an OpenAPI 3.1 document from a contract or a running app. See the module doc for the detail model. */
|
|
101
107
|
export declare function toOpenAPI(input: ContractShape | Server, options?: ToOpenAPIOptions): OpenAPIDocument;
|
|
108
|
+
/** Generate OpenAPI from an existing canonical project-evidence snapshot without loading a server. */
|
|
109
|
+
export declare function toOpenAPIFromEvidence(evidence: ProjectEvidenceSnapshot, options?: Omit<ToOpenAPIOptions, "evidence">): OpenAPIDocument;
|
|
102
110
|
export {};
|
|
103
111
|
//# sourceMappingURL=openapi.d.ts.map
|
package/dist/openapi.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openapi.d.ts","sourceRoot":"","sources":["../src/openapi.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"openapi.d.ts","sourceRoot":"","sources":["../src/openapi.ts"],"names":[],"mappings":"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"}
|
package/dist/openapi.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { reflectedRoutesFromEvidence } from "@nifrajs/core/evidence";
|
|
1
2
|
import { reflectRoutes, reflectSchema, } from "@nifrajs/core/reflection";
|
|
2
3
|
/**
|
|
3
4
|
* Collects schemas that carry a `$id` into `components.schemas`, returning a `$ref` in their place -
|
|
@@ -49,23 +50,52 @@ class SchemaStore {
|
|
|
49
50
|
return { $ref: `#/components/schemas/${componentName}` };
|
|
50
51
|
}
|
|
51
52
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
53
|
+
const NIFRA_PARAM = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
54
|
+
const MIXED_PARAM = /:([A-Za-z_][A-Za-z0-9_]*)/g;
|
|
55
|
+
/** Names captured by a route segment under Nifra's parameter grammar. */
|
|
56
|
+
function segmentParams(segment) {
|
|
56
57
|
if (segment.startsWith("*"))
|
|
57
|
-
return segment.length > 1 ? segment.slice(1) : "wildcard";
|
|
58
|
+
return [segment.length > 1 ? segment.slice(1) : "wildcard"];
|
|
59
|
+
const names = [];
|
|
60
|
+
for (const match of segment.matchAll(MIXED_PARAM)) {
|
|
61
|
+
const name = match[1];
|
|
62
|
+
if (name === undefined || match.index === undefined)
|
|
63
|
+
continue;
|
|
64
|
+
const previous = segment[match.index - 1];
|
|
65
|
+
const atEnd = match.index + match[0].length === segment.length;
|
|
66
|
+
// `things:batchGet` is an established literal action path. The router only treats a terminal
|
|
67
|
+
// colon after an identifier as a parameter when a literal suffix follows it.
|
|
68
|
+
if (previous !== undefined && /[A-Za-z0-9_]/.test(previous) && atEnd)
|
|
69
|
+
continue;
|
|
70
|
+
names.push(name);
|
|
71
|
+
}
|
|
72
|
+
return names;
|
|
73
|
+
}
|
|
74
|
+
/** Name of a wholly dynamic path segment, or `undefined` for static/mixed segments. */
|
|
75
|
+
function segmentParam(segment) {
|
|
76
|
+
const names = segmentParams(segment);
|
|
77
|
+
if (segment.startsWith("*") && names.length === 1)
|
|
78
|
+
return names[0];
|
|
79
|
+
if (segment.startsWith(":") && NIFRA_PARAM.test(segment.slice(1)) && names.length === 1)
|
|
80
|
+
return names[0];
|
|
58
81
|
return undefined;
|
|
59
82
|
}
|
|
83
|
+
/** Convert one Nifra route segment, including mixed forms, to OpenAPI templates. */
|
|
84
|
+
function toTemplatedSegment(segment) {
|
|
85
|
+
const whole = segmentParam(segment);
|
|
86
|
+
if (whole !== undefined)
|
|
87
|
+
return `{${whole}}`;
|
|
88
|
+
return segment.replace(MIXED_PARAM, (match, name, offset) => {
|
|
89
|
+
const previous = segment[offset - 1];
|
|
90
|
+
const atEnd = offset + match.length === segment.length;
|
|
91
|
+
if (previous !== undefined && /[A-Za-z0-9_]/.test(previous) && atEnd)
|
|
92
|
+
return match;
|
|
93
|
+
return `{${name}}`;
|
|
94
|
+
});
|
|
95
|
+
}
|
|
60
96
|
/** `/users/:id/*rest` → `/users/{id}/{rest}` (OpenAPI path templating). */
|
|
61
97
|
function toTemplatedPath(path) {
|
|
62
|
-
return path
|
|
63
|
-
.split("/")
|
|
64
|
-
.map((segment) => {
|
|
65
|
-
const param = segmentParam(segment);
|
|
66
|
-
return param === undefined ? segment : `{${param}}`;
|
|
67
|
-
})
|
|
68
|
-
.join("/");
|
|
98
|
+
return path.split("/").map(toTemplatedSegment).join("/");
|
|
69
99
|
}
|
|
70
100
|
function pathParameters(path, paramsSchema) {
|
|
71
101
|
const params = [];
|
|
@@ -77,8 +107,7 @@ function pathParameters(path, paramsSchema) {
|
|
|
77
107
|
}
|
|
78
108
|
}
|
|
79
109
|
for (const segment of path.split("/")) {
|
|
80
|
-
const name
|
|
81
|
-
if (name !== undefined) {
|
|
110
|
+
for (const name of segmentParams(segment)) {
|
|
82
111
|
// Merge the declared constraint (uuid format, integer type, etc.) when present;
|
|
83
112
|
// fall back to the bare { type: "string" } derived from the URL pattern.
|
|
84
113
|
const schema = fieldSchemas.get(name) ?? { type: "string" };
|
|
@@ -235,7 +264,10 @@ export function toOpenAPI(input, options = {}) {
|
|
|
235
264
|
const paths = {};
|
|
236
265
|
const store = new SchemaStore();
|
|
237
266
|
if (isApp(input)) {
|
|
238
|
-
|
|
267
|
+
const routes = options.evidence === undefined
|
|
268
|
+
? reflectRoutes(input)
|
|
269
|
+
: reflectedRoutesFromEvidence(options.evidence);
|
|
270
|
+
for (const route of routes) {
|
|
239
271
|
addOperation(paths, route.method, {
|
|
240
272
|
path: route.path,
|
|
241
273
|
body: route.schema?.body,
|
|
@@ -303,4 +335,11 @@ export function toOpenAPI(input, options = {}) {
|
|
|
303
335
|
...(Object.keys(components).length > 0 ? { components } : {}),
|
|
304
336
|
};
|
|
305
337
|
}
|
|
338
|
+
/** Generate OpenAPI from an existing canonical project-evidence snapshot without loading a server. */
|
|
339
|
+
export function toOpenAPIFromEvidence(evidence, options = {}) {
|
|
340
|
+
const app = {
|
|
341
|
+
routes: () => reflectedRoutesFromEvidence(evidence),
|
|
342
|
+
};
|
|
343
|
+
return toOpenAPI(app, { ...options, evidence });
|
|
344
|
+
}
|
|
306
345
|
//# sourceMappingURL=openapi.js.map
|
package/dist/openapi.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openapi.js","sourceRoot":"","sources":["../src/openapi.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"openapi.js","sourceRoot":"","sources":["../src/openapi.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nifrajs/schema",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.5.0",
|
|
4
4
|
"description": "Built-in schema builder `t` - TypeBox-backed (free JSON Schema + compiled validators), implements Standard Schema so nifra routes validate it natively.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"bun",
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"@sinclair/typebox": "^0.34.49"
|
|
49
49
|
},
|
|
50
50
|
"peerDependencies": {
|
|
51
|
-
"@nifrajs/core": "^3.
|
|
51
|
+
"@nifrajs/core": "^3.5.0"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
54
|
"@nifrajs/core": "workspace:*",
|
package/src/index.ts
CHANGED
|
@@ -11,7 +11,22 @@ export { fromTypeBox, type NifraSchema } from "./adapter.ts"
|
|
|
11
11
|
// install themselves lazily from the validate path (see ./adapter.ts) - NOT as an import side
|
|
12
12
|
// effect of this module, which a production bundler would tree-shake away.
|
|
13
13
|
export { registerFormat } from "./formats.ts"
|
|
14
|
-
export {
|
|
14
|
+
export {
|
|
15
|
+
type OpenAPIDocument,
|
|
16
|
+
type OpenAPIInfo,
|
|
17
|
+
toOpenAPI,
|
|
18
|
+
toOpenAPIFromEvidence,
|
|
19
|
+
} from "./openapi.ts"
|
|
20
|
+
export {
|
|
21
|
+
diffOpenApiInventory,
|
|
22
|
+
type ImportedApiInventory,
|
|
23
|
+
type ImportedRoute,
|
|
24
|
+
type ImportedRouteQuery,
|
|
25
|
+
type ImportOpenAPIOptions,
|
|
26
|
+
importOpenAPI,
|
|
27
|
+
OpenAPIImportError,
|
|
28
|
+
type OpenApiInventoryDrift,
|
|
29
|
+
} from "./openapi-import.ts"
|
|
15
30
|
// Cursor-pagination runtime helpers - pair with `t.paginated` / `t.pageQuery`.
|
|
16
31
|
export { decodeCursor, encodeCursor, type Page, paginate } from "./pagination.ts"
|
|
17
32
|
export { t } from "./t.ts"
|
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenAPI 3.x inventory import - the structural half of an export/import roundtrip.
|
|
3
|
+
*
|
|
4
|
+
* `toOpenAPI` renders a Nifra app (or contract) as an OpenAPI 3.1 document. This module reads such
|
|
5
|
+
* a document back into a canonical route inventory: method, Nifra-templated path (`{id}` back to
|
|
6
|
+
* `:id`), required query names, body presence, and response statuses. `diffOpenApiInventory` then
|
|
7
|
+
* compares two inventories, so `export -> import -> diff` proves the cycle survives in CI.
|
|
8
|
+
*
|
|
9
|
+
* Deliberate boundaries, all fail-closed or explicitly documented:
|
|
10
|
+
*
|
|
11
|
+
* - Offline and ref-opaque. `$ref`s are never resolved and nothing is fetched. A `$ref` parameter
|
|
12
|
+
* cannot yield a name without resolution, so it throws with the location; `$ref` bodies and
|
|
13
|
+
* response schemas only assert presence, which needs no resolution.
|
|
14
|
+
* - No schema reconstruction. JSON Schema is not turned back into `t` builders here - that is a
|
|
15
|
+
* separate codegen step needing consumer evidence. The inventory proves the route table survived;
|
|
16
|
+
* field-level types ride along in the source document, not in this comparison.
|
|
17
|
+
* - No auth interpretation. `security`, `securitySchemes`, and `servers` are never read. Importing
|
|
18
|
+
* an API must not silently adopt its auth model.
|
|
19
|
+
* - Templated paths degrade conservatively. `/files/*path` exports as `/files/{path}`, indistinguishable
|
|
20
|
+
* from a single-segment `:path`. Import yields `:path` and records a note for every OpenAPI template;
|
|
21
|
+
* a greedy remainder needs a manual `*path` rewrite.
|
|
22
|
+
* - Nifra parameter names follow `/^[A-Za-z_][A-Za-z0-9_]*$/`; `__proto__`, `constructor`, and
|
|
23
|
+
* `prototype` are rejected. Mixed templates are converted only when their boundaries are representable.
|
|
24
|
+
* - Bounded. Path/operation/parameter counts are capped (`ImportOpenAPIOptions`); a document over
|
|
25
|
+
* the caps throws rather than allocating.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
export class OpenAPIImportError extends Error {
|
|
29
|
+
constructor(message: string) {
|
|
30
|
+
super(`openapi import: ${message}`)
|
|
31
|
+
this.name = "OpenAPIImportError"
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface ImportOpenAPIOptions {
|
|
36
|
+
/** Maximum entries under `paths`. Default 5000. */
|
|
37
|
+
readonly maxPaths?: number
|
|
38
|
+
/** Maximum operations on one path item. Default 8. */
|
|
39
|
+
readonly maxOperationsPerPath?: number
|
|
40
|
+
/** Maximum merged parameters per operation. Default 100. */
|
|
41
|
+
readonly maxParameters?: number
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface ImportedRouteQuery {
|
|
45
|
+
readonly name: string
|
|
46
|
+
readonly required: boolean
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface ImportedRoute {
|
|
50
|
+
/** Lowercase HTTP method as it appears in the document. */
|
|
51
|
+
readonly method: string
|
|
52
|
+
/** Nifra-templated path (`/users/:id`). */
|
|
53
|
+
readonly path: string
|
|
54
|
+
readonly operationId?: string
|
|
55
|
+
/** Template parameter names in path order. */
|
|
56
|
+
readonly pathParams: readonly string[]
|
|
57
|
+
readonly query: readonly ImportedRouteQuery[]
|
|
58
|
+
readonly hasBody: boolean
|
|
59
|
+
readonly requestBodyRequired: boolean
|
|
60
|
+
/** Response status keys (`"200"`, `"404"`, `"default"`). */
|
|
61
|
+
readonly responseStatuses: readonly string[]
|
|
62
|
+
/** Per-route degradations (wildcard rewrites). */
|
|
63
|
+
readonly notes: readonly string[]
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface ImportedApiInventory {
|
|
67
|
+
readonly title: string
|
|
68
|
+
readonly version: string
|
|
69
|
+
readonly routes: readonly ImportedRoute[]
|
|
70
|
+
readonly warnings: readonly string[]
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export interface OpenApiInventoryDrift {
|
|
74
|
+
/** `METHOD /path`, or `inventory` for route-set level drift. */
|
|
75
|
+
readonly route: string
|
|
76
|
+
readonly message: string
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const SUPPORTED_METHODS: ReadonlySet<string> = new Set([
|
|
80
|
+
"get",
|
|
81
|
+
"put",
|
|
82
|
+
"post",
|
|
83
|
+
"delete",
|
|
84
|
+
"patch",
|
|
85
|
+
"head",
|
|
86
|
+
"options",
|
|
87
|
+
"trace",
|
|
88
|
+
])
|
|
89
|
+
const PATH_ITEM_METADATA = new Set(["$ref", "summary", "description", "servers", "parameters"])
|
|
90
|
+
const RESPONSE_KEY = /^(?:[1-5][0-9]{2}|default)$/
|
|
91
|
+
const TEMPLATE_EXPRESSION = /\{([^{}]*)\}/g
|
|
92
|
+
const NIFRA_PARAM = /^[A-Za-z_][A-Za-z0-9_]*$/
|
|
93
|
+
const RESERVED_PARAM_NAMES = new Set(["__proto__", "constructor", "prototype"])
|
|
94
|
+
|
|
95
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
96
|
+
return typeof value === "object" && value !== null && !Array.isArray(value)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function normalizedLimit(value: number | undefined, fallback: number, label: string): number {
|
|
100
|
+
const max = value ?? fallback
|
|
101
|
+
if (!Number.isSafeInteger(max) || max < 0)
|
|
102
|
+
throw new OpenAPIImportError(`${label} limit must be a finite safe integer >= 0`)
|
|
103
|
+
return max
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function assertLimit(count: number, max: number, label: string): void {
|
|
107
|
+
if (count > max) throw new OpenAPIImportError(`${label} count ${count} exceeds limit ${max}`)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** Collect own keys while stopping as soon as a bounded collection is over its cap. */
|
|
111
|
+
function ownKeysWithinLimit(
|
|
112
|
+
record: Record<string, unknown>,
|
|
113
|
+
max: number,
|
|
114
|
+
label: string,
|
|
115
|
+
include: (key: string) => boolean = () => true,
|
|
116
|
+
): string[] {
|
|
117
|
+
const keys: string[] = []
|
|
118
|
+
for (const key in record) {
|
|
119
|
+
if (!Object.hasOwn(record, key) || !include(key)) continue
|
|
120
|
+
if (keys.length >= max)
|
|
121
|
+
throw new OpenAPIImportError(`${label} count ${keys.length + 1} exceeds limit ${max}`)
|
|
122
|
+
keys.push(key)
|
|
123
|
+
}
|
|
124
|
+
return keys
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function toNifraPath(openapiPath: string): {
|
|
128
|
+
readonly path: string
|
|
129
|
+
readonly templated: boolean
|
|
130
|
+
readonly pathParams: readonly string[]
|
|
131
|
+
} {
|
|
132
|
+
const segments = openapiPath.split("/")
|
|
133
|
+
let templated = false
|
|
134
|
+
const pathParams: string[] = []
|
|
135
|
+
const out = segments.map((segment) => {
|
|
136
|
+
if (!segment.includes("{") && !segment.includes("}")) return segment
|
|
137
|
+
const matches = [...segment.matchAll(TEMPLATE_EXPRESSION)]
|
|
138
|
+
if (matches.length === 0)
|
|
139
|
+
throw new OpenAPIImportError(`unparseable path template ${openapiPath}`)
|
|
140
|
+
let cursor = 0
|
|
141
|
+
let converted = ""
|
|
142
|
+
for (const match of matches) {
|
|
143
|
+
if (match.index === undefined)
|
|
144
|
+
throw new OpenAPIImportError(`unparseable path template ${openapiPath}`)
|
|
145
|
+
const start = match.index
|
|
146
|
+
const end = start + match[0].length
|
|
147
|
+
const name = match[1] ?? ""
|
|
148
|
+
const previous = segment[start - 1]
|
|
149
|
+
const next = segment[end]
|
|
150
|
+
// Nifra's mixed grammar needs a non-identifier delimiter after a parameter. A terminal
|
|
151
|
+
// `foo{bar}` or adjacent `{a}{b}` cannot be represented without changing the capture.
|
|
152
|
+
if (
|
|
153
|
+
next === "{" ||
|
|
154
|
+
(next !== undefined && /[A-Za-z0-9_]/.test(next)) ||
|
|
155
|
+
(next === undefined && previous !== undefined && /[A-Za-z0-9_]/.test(previous))
|
|
156
|
+
)
|
|
157
|
+
throw new OpenAPIImportError(`path template ${openapiPath} is not representable in Nifra`)
|
|
158
|
+
if (!NIFRA_PARAM.test(name) || RESERVED_PARAM_NAMES.has(name))
|
|
159
|
+
throw new OpenAPIImportError(`invalid path parameter ${name}`)
|
|
160
|
+
if (pathParams.includes(name))
|
|
161
|
+
throw new OpenAPIImportError(`duplicate path parameter ${name} on ${openapiPath}`)
|
|
162
|
+
pathParams.push(name)
|
|
163
|
+
converted += `${segment.slice(cursor, start)}:${name}`
|
|
164
|
+
cursor = end
|
|
165
|
+
templated = true
|
|
166
|
+
}
|
|
167
|
+
const tail = segment.slice(cursor)
|
|
168
|
+
if (tail.includes("{") || tail.includes("}"))
|
|
169
|
+
throw new OpenAPIImportError(`unparseable path template ${openapiPath}`)
|
|
170
|
+
return converted + tail
|
|
171
|
+
})
|
|
172
|
+
return { path: out.join("/"), templated, pathParams: Object.freeze(pathParams) }
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
interface MergedParameter {
|
|
176
|
+
readonly name: string
|
|
177
|
+
readonly location: string
|
|
178
|
+
readonly required: boolean
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function mergedParameters(
|
|
182
|
+
pathItem: Record<string, unknown>,
|
|
183
|
+
operation: Record<string, unknown>,
|
|
184
|
+
max: number,
|
|
185
|
+
): MergedParameter[] {
|
|
186
|
+
const merged = new Map<string, MergedParameter>()
|
|
187
|
+
for (const source of [pathItem.parameters, operation.parameters]) {
|
|
188
|
+
if (source === undefined) continue
|
|
189
|
+
if (!Array.isArray(source)) throw new OpenAPIImportError("parameters must be an array")
|
|
190
|
+
assertLimit(source.length, max, "parameters")
|
|
191
|
+
for (const entry of source) {
|
|
192
|
+
if (!isRecord(entry)) throw new OpenAPIImportError("parameter entries must be objects")
|
|
193
|
+
if ("$ref" in entry)
|
|
194
|
+
throw new OpenAPIImportError(
|
|
195
|
+
"parameter $refs must be inlined before import - refs are never resolved",
|
|
196
|
+
)
|
|
197
|
+
const {
|
|
198
|
+
name,
|
|
199
|
+
in: location,
|
|
200
|
+
required,
|
|
201
|
+
} = entry as {
|
|
202
|
+
name?: unknown
|
|
203
|
+
in?: unknown
|
|
204
|
+
required?: unknown
|
|
205
|
+
}
|
|
206
|
+
if (typeof name !== "string" || name === "")
|
|
207
|
+
throw new OpenAPIImportError("parameters need a non-empty name")
|
|
208
|
+
if (typeof location !== "string" || location === "")
|
|
209
|
+
throw new OpenAPIImportError(`parameter ${name} needs an 'in' location`)
|
|
210
|
+
merged.set(`${location} ${name}`, {
|
|
211
|
+
name,
|
|
212
|
+
location,
|
|
213
|
+
required: required === true,
|
|
214
|
+
})
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
assertLimit(merged.size, max, "merged parameters")
|
|
218
|
+
return [...merged.values()]
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/** Parse an OpenAPI 3.0/3.1 document into a canonical route inventory. Throws on invalid input. */
|
|
222
|
+
export function importOpenAPI(
|
|
223
|
+
document: unknown,
|
|
224
|
+
options: ImportOpenAPIOptions = {},
|
|
225
|
+
): ImportedApiInventory {
|
|
226
|
+
const maxPaths = normalizedLimit(options.maxPaths, 5000, "maxPaths")
|
|
227
|
+
const maxOperations = normalizedLimit(options.maxOperationsPerPath, 8, "maxOperationsPerPath")
|
|
228
|
+
const maxParameters = normalizedLimit(options.maxParameters, 100, "maxParameters")
|
|
229
|
+
if (!isRecord(document)) throw new OpenAPIImportError("document must be an object")
|
|
230
|
+
const { openapi, info, paths } = document
|
|
231
|
+
if (typeof openapi !== "string" || !/^3\.(0|1)\.\d+$/.test(openapi))
|
|
232
|
+
throw new OpenAPIImportError(
|
|
233
|
+
`unsupported OpenAPI version ${JSON.stringify(openapi)} - 3.0.x and 3.1.x only`,
|
|
234
|
+
)
|
|
235
|
+
if (!isRecord(info)) throw new OpenAPIImportError("document.info must be an object")
|
|
236
|
+
const title = typeof info.title === "string" ? info.title : ""
|
|
237
|
+
const version = typeof info.version === "string" ? info.version : ""
|
|
238
|
+
if (paths === undefined)
|
|
239
|
+
return { title, version, routes: Object.freeze([]), warnings: Object.freeze([]) }
|
|
240
|
+
if (!isRecord(paths)) throw new OpenAPIImportError("document.paths must be an object")
|
|
241
|
+
const pathKeys = ownKeysWithinLimit(paths, maxPaths, "paths")
|
|
242
|
+
const warnings: string[] = []
|
|
243
|
+
const routes: ImportedRoute[] = []
|
|
244
|
+
for (const openapiPath of pathKeys) {
|
|
245
|
+
if (!openapiPath.startsWith("/"))
|
|
246
|
+
throw new OpenAPIImportError(`path ${openapiPath} must start with /`)
|
|
247
|
+
const pathItem = paths[openapiPath]
|
|
248
|
+
if (!isRecord(pathItem))
|
|
249
|
+
throw new OpenAPIImportError(`path item ${openapiPath} must be an object`)
|
|
250
|
+
if (Object.hasOwn(pathItem, "$ref"))
|
|
251
|
+
throw new OpenAPIImportError(
|
|
252
|
+
`path item $refs must be inlined before import - refs are never resolved`,
|
|
253
|
+
)
|
|
254
|
+
const operationKeys = ownKeysWithinLimit(
|
|
255
|
+
pathItem,
|
|
256
|
+
maxOperations,
|
|
257
|
+
`operations on ${openapiPath}`,
|
|
258
|
+
(key) => key !== "parameters" && !PATH_ITEM_METADATA.has(key) && !key.startsWith("x-"),
|
|
259
|
+
)
|
|
260
|
+
const { path, templated, pathParams } = toNifraPath(openapiPath)
|
|
261
|
+
const seenMethods = new Set<string>()
|
|
262
|
+
for (const rawMethod of operationKeys) {
|
|
263
|
+
const method = rawMethod.toLowerCase()
|
|
264
|
+
if (!SUPPORTED_METHODS.has(method))
|
|
265
|
+
throw new OpenAPIImportError(`unsupported method ${rawMethod} on ${openapiPath}`)
|
|
266
|
+
if (seenMethods.has(method))
|
|
267
|
+
throw new OpenAPIImportError(`duplicate method ${rawMethod} on ${openapiPath}`)
|
|
268
|
+
seenMethods.add(method)
|
|
269
|
+
const rawOperation = pathItem[rawMethod]
|
|
270
|
+
if (!isRecord(rawOperation))
|
|
271
|
+
throw new OpenAPIImportError(`operation ${rawMethod} ${openapiPath} must be an object`)
|
|
272
|
+
const params = mergedParameters(pathItem, rawOperation, maxParameters)
|
|
273
|
+
const query: ImportedRouteQuery[] = []
|
|
274
|
+
for (const param of params) {
|
|
275
|
+
if (param.location === "query") query.push({ name: param.name, required: param.required })
|
|
276
|
+
else if (param.location !== "path" && param.location !== "header")
|
|
277
|
+
warnings.push(
|
|
278
|
+
`${method.toUpperCase()} ${path}: '${param.location}' parameter '${param.name}' skipped`,
|
|
279
|
+
)
|
|
280
|
+
else if (param.location === "header")
|
|
281
|
+
warnings.push(`${method.toUpperCase()} ${path}: header parameters are not inventoried`)
|
|
282
|
+
}
|
|
283
|
+
const declaredPathParams = new Set(
|
|
284
|
+
params.filter((param) => param.location === "path").map((param) => param.name),
|
|
285
|
+
)
|
|
286
|
+
for (const name of pathParams) {
|
|
287
|
+
if (!declaredPathParams.has(name))
|
|
288
|
+
throw new OpenAPIImportError(
|
|
289
|
+
`path parameter ${name} is not declared on ${method.toUpperCase()} ${path}`,
|
|
290
|
+
)
|
|
291
|
+
}
|
|
292
|
+
for (const name of declaredPathParams) {
|
|
293
|
+
if (!pathParams.includes(name))
|
|
294
|
+
throw new OpenAPIImportError(`declared path parameter ${name} is not in ${path}`)
|
|
295
|
+
}
|
|
296
|
+
const rawBody = rawOperation.requestBody
|
|
297
|
+
const hasBody = rawBody !== undefined
|
|
298
|
+
if (hasBody && !isRecord(rawBody))
|
|
299
|
+
throw new OpenAPIImportError(
|
|
300
|
+
`requestBody on ${method.toUpperCase()} ${path} must be an object`,
|
|
301
|
+
)
|
|
302
|
+
const rawResponses = rawOperation.responses
|
|
303
|
+
if (rawResponses === undefined)
|
|
304
|
+
throw new OpenAPIImportError(`responses are required on ${method.toUpperCase()} ${path}`)
|
|
305
|
+
if (!isRecord(rawResponses))
|
|
306
|
+
throw new OpenAPIImportError(
|
|
307
|
+
`responses on ${method.toUpperCase()} ${path} must be an object`,
|
|
308
|
+
)
|
|
309
|
+
const responseStatuses: string[] = []
|
|
310
|
+
for (const status of Object.keys(rawResponses)) {
|
|
311
|
+
if (!RESPONSE_KEY.test(status))
|
|
312
|
+
throw new OpenAPIImportError(
|
|
313
|
+
`invalid response status ${status} on ${method.toUpperCase()} ${path}`,
|
|
314
|
+
)
|
|
315
|
+
if (!isRecord(rawResponses[status]))
|
|
316
|
+
throw new OpenAPIImportError(
|
|
317
|
+
`response ${status} on ${method.toUpperCase()} ${path} must be an object`,
|
|
318
|
+
)
|
|
319
|
+
responseStatuses.push(status)
|
|
320
|
+
}
|
|
321
|
+
const notes = templated
|
|
322
|
+
? [
|
|
323
|
+
"OpenAPI path templates import as single-segment :params - greedy remainders (*rest) cannot be distinguished and need manual rewrites",
|
|
324
|
+
]
|
|
325
|
+
: []
|
|
326
|
+
const operationId = rawOperation.operationId
|
|
327
|
+
routes.push(
|
|
328
|
+
Object.freeze({
|
|
329
|
+
method,
|
|
330
|
+
path,
|
|
331
|
+
...(typeof operationId === "string" && operationId !== "" ? { operationId } : {}),
|
|
332
|
+
pathParams,
|
|
333
|
+
query: Object.freeze(query),
|
|
334
|
+
hasBody,
|
|
335
|
+
requestBodyRequired: hasBody && (rawBody as Record<string, unknown>).required === true,
|
|
336
|
+
responseStatuses: Object.freeze(responseStatuses),
|
|
337
|
+
notes: Object.freeze(notes),
|
|
338
|
+
}),
|
|
339
|
+
)
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
if (routes.some((route) => route.pathParams.length > 0))
|
|
343
|
+
warnings.push(
|
|
344
|
+
"OpenAPI path templates import as single-segment :params - greedy remainders (*rest) cannot be distinguished and need manual rewrites; see route notes",
|
|
345
|
+
)
|
|
346
|
+
return {
|
|
347
|
+
title,
|
|
348
|
+
version,
|
|
349
|
+
routes: Object.freeze(routes),
|
|
350
|
+
warnings: Object.freeze(warnings),
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function routeKey(method: string, path: string): string {
|
|
355
|
+
return `${method.toUpperCase()} ${path}`
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/** Compare two inventories. Empty means the export/import cycle preserved the route table. */
|
|
359
|
+
export function diffOpenApiInventory(
|
|
360
|
+
expected: ImportedApiInventory,
|
|
361
|
+
actual: ImportedApiInventory,
|
|
362
|
+
): readonly OpenApiInventoryDrift[] {
|
|
363
|
+
const drift: OpenApiInventoryDrift[] = []
|
|
364
|
+
const actualByKey = new Map(
|
|
365
|
+
actual.routes.map((route) => [routeKey(route.method, route.path), route]),
|
|
366
|
+
)
|
|
367
|
+
const expectedKeys = new Set<string>()
|
|
368
|
+
for (const want of expected.routes) {
|
|
369
|
+
const key = routeKey(want.method, want.path)
|
|
370
|
+
expectedKeys.add(key)
|
|
371
|
+
const got = actualByKey.get(key)
|
|
372
|
+
if (got === undefined) {
|
|
373
|
+
drift.push({ route: key, message: "route missing after import" })
|
|
374
|
+
continue
|
|
375
|
+
}
|
|
376
|
+
const wantQuery = new Map(want.query.map((param) => [param.name, param.required]))
|
|
377
|
+
const gotQuery = new Map(got.query.map((param) => [param.name, param.required]))
|
|
378
|
+
for (const [name, required] of wantQuery) {
|
|
379
|
+
if (!gotQuery.has(name)) drift.push({ route: key, message: `query parameter '${name}' lost` })
|
|
380
|
+
else if (gotQuery.get(name) !== required)
|
|
381
|
+
drift.push({ route: key, message: `query parameter '${name}' required flag flipped` })
|
|
382
|
+
}
|
|
383
|
+
for (const name of gotQuery.keys()) {
|
|
384
|
+
if (!wantQuery.has(name))
|
|
385
|
+
drift.push({ route: key, message: `unexpected query parameter '${name}'` })
|
|
386
|
+
}
|
|
387
|
+
if (want.hasBody !== got.hasBody)
|
|
388
|
+
drift.push({
|
|
389
|
+
route: key,
|
|
390
|
+
message: want.hasBody ? "request body lost" : "unexpected request body",
|
|
391
|
+
})
|
|
392
|
+
else if (want.hasBody && want.requestBodyRequired !== got.requestBodyRequired)
|
|
393
|
+
drift.push({ route: key, message: "request body required flag flipped" })
|
|
394
|
+
const wantStatuses = new Set(want.responseStatuses)
|
|
395
|
+
const gotStatuses = new Set(got.responseStatuses)
|
|
396
|
+
for (const status of wantStatuses) {
|
|
397
|
+
if (!gotStatuses.has(status))
|
|
398
|
+
drift.push({ route: key, message: `response status ${status} lost` })
|
|
399
|
+
}
|
|
400
|
+
for (const status of gotStatuses) {
|
|
401
|
+
if (!wantStatuses.has(status))
|
|
402
|
+
drift.push({ route: key, message: `unexpected response status ${status}` })
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
for (const got of actual.routes) {
|
|
406
|
+
const key = routeKey(got.method, got.path)
|
|
407
|
+
if (!expectedKeys.has(key)) drift.push({ route: key, message: "unexpected route after import" })
|
|
408
|
+
}
|
|
409
|
+
return Object.freeze(drift)
|
|
410
|
+
}
|
package/src/openapi.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ContractShape } from "@nifrajs/core/contract"
|
|
2
|
+
import { type ProjectEvidenceSnapshot, reflectedRoutesFromEvidence } from "@nifrajs/core/evidence"
|
|
2
3
|
import {
|
|
3
4
|
type JsonSchema,
|
|
4
5
|
type ReflectedRouteSchema,
|
|
@@ -50,6 +51,11 @@ export interface ToOpenAPIOptions {
|
|
|
50
51
|
readonly securitySchemes?: Readonly<Record<string, Record<string, unknown>>>
|
|
51
52
|
/** Document-wide security requirement; a per-operation `security` (incl. `[]` for public) overrides it. */
|
|
52
53
|
readonly security?: readonly SecurityRequirement[]
|
|
54
|
+
/**
|
|
55
|
+
* Reuse a canonical token-only route-evidence pass. This is useful for build artifacts and CLI
|
|
56
|
+
* projections that already evaluated the project; it never supplies runtime validators.
|
|
57
|
+
*/
|
|
58
|
+
readonly evidence?: ProjectEvidenceSnapshot
|
|
53
59
|
/**
|
|
54
60
|
* Per-operation overrides, shallow-merged over the generated operation. Keyed by `operationId`
|
|
55
61
|
* (contract op name) or `"METHOD /path"` (e.g. `"GET /users/:id"`). The escape hatch for detail that
|
|
@@ -175,22 +181,50 @@ class SchemaStore {
|
|
|
175
181
|
}
|
|
176
182
|
}
|
|
177
183
|
|
|
178
|
-
|
|
184
|
+
const NIFRA_PARAM = /^[A-Za-z_][A-Za-z0-9_]*$/
|
|
185
|
+
const MIXED_PARAM = /:([A-Za-z_][A-Za-z0-9_]*)/g
|
|
186
|
+
|
|
187
|
+
/** Names captured by a route segment under Nifra's parameter grammar. */
|
|
188
|
+
function segmentParams(segment: string): readonly string[] {
|
|
189
|
+
if (segment.startsWith("*")) return [segment.length > 1 ? segment.slice(1) : "wildcard"]
|
|
190
|
+
const names: string[] = []
|
|
191
|
+
for (const match of segment.matchAll(MIXED_PARAM)) {
|
|
192
|
+
const name = match[1]
|
|
193
|
+
if (name === undefined || match.index === undefined) continue
|
|
194
|
+
const previous = segment[match.index - 1]
|
|
195
|
+
const atEnd = match.index + match[0].length === segment.length
|
|
196
|
+
// `things:batchGet` is an established literal action path. The router only treats a terminal
|
|
197
|
+
// colon after an identifier as a parameter when a literal suffix follows it.
|
|
198
|
+
if (previous !== undefined && /[A-Za-z0-9_]/.test(previous) && atEnd) continue
|
|
199
|
+
names.push(name)
|
|
200
|
+
}
|
|
201
|
+
return names
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/** Name of a wholly dynamic path segment, or `undefined` for static/mixed segments. */
|
|
179
205
|
function segmentParam(segment: string): string | undefined {
|
|
180
|
-
|
|
181
|
-
if (segment.startsWith("*")
|
|
206
|
+
const names = segmentParams(segment)
|
|
207
|
+
if (segment.startsWith("*") && names.length === 1) return names[0]
|
|
208
|
+
if (segment.startsWith(":") && NIFRA_PARAM.test(segment.slice(1)) && names.length === 1)
|
|
209
|
+
return names[0]
|
|
182
210
|
return undefined
|
|
183
211
|
}
|
|
184
212
|
|
|
213
|
+
/** Convert one Nifra route segment, including mixed forms, to OpenAPI templates. */
|
|
214
|
+
function toTemplatedSegment(segment: string): string {
|
|
215
|
+
const whole = segmentParam(segment)
|
|
216
|
+
if (whole !== undefined) return `{${whole}}`
|
|
217
|
+
return segment.replace(MIXED_PARAM, (match, name: string, offset: number) => {
|
|
218
|
+
const previous = segment[offset - 1]
|
|
219
|
+
const atEnd = offset + match.length === segment.length
|
|
220
|
+
if (previous !== undefined && /[A-Za-z0-9_]/.test(previous) && atEnd) return match
|
|
221
|
+
return `{${name}}`
|
|
222
|
+
})
|
|
223
|
+
}
|
|
224
|
+
|
|
185
225
|
/** `/users/:id/*rest` → `/users/{id}/{rest}` (OpenAPI path templating). */
|
|
186
226
|
function toTemplatedPath(path: string): string {
|
|
187
|
-
return path
|
|
188
|
-
.split("/")
|
|
189
|
-
.map((segment) => {
|
|
190
|
-
const param = segmentParam(segment)
|
|
191
|
-
return param === undefined ? segment : `{${param}}`
|
|
192
|
-
})
|
|
193
|
-
.join("/")
|
|
227
|
+
return path.split("/").map(toTemplatedSegment).join("/")
|
|
194
228
|
}
|
|
195
229
|
|
|
196
230
|
function pathParameters(path: string, paramsSchema?: SchemaReflection): OpenAPIParameter[] {
|
|
@@ -203,8 +237,7 @@ function pathParameters(path: string, paramsSchema?: SchemaReflection): OpenAPIP
|
|
|
203
237
|
}
|
|
204
238
|
}
|
|
205
239
|
for (const segment of path.split("/")) {
|
|
206
|
-
const name
|
|
207
|
-
if (name !== undefined) {
|
|
240
|
+
for (const name of segmentParams(segment)) {
|
|
208
241
|
// Merge the declared constraint (uuid format, integer type, etc.) when present;
|
|
209
242
|
// fall back to the bare { type: "string" } derived from the URL pattern.
|
|
210
243
|
const schema = fieldSchemas.get(name) ?? { type: "string" as const }
|
|
@@ -402,7 +435,11 @@ export function toOpenAPI(
|
|
|
402
435
|
const store = new SchemaStore()
|
|
403
436
|
|
|
404
437
|
if (isApp(input)) {
|
|
405
|
-
|
|
438
|
+
const routes =
|
|
439
|
+
options.evidence === undefined
|
|
440
|
+
? reflectRoutes(input)
|
|
441
|
+
: reflectedRoutesFromEvidence(options.evidence)
|
|
442
|
+
for (const route of routes) {
|
|
406
443
|
addOperation(
|
|
407
444
|
paths,
|
|
408
445
|
route.method,
|
|
@@ -485,3 +522,14 @@ export function toOpenAPI(
|
|
|
485
522
|
...(Object.keys(components).length > 0 ? { components } : {}),
|
|
486
523
|
}
|
|
487
524
|
}
|
|
525
|
+
|
|
526
|
+
/** Generate OpenAPI from an existing canonical project-evidence snapshot without loading a server. */
|
|
527
|
+
export function toOpenAPIFromEvidence(
|
|
528
|
+
evidence: ProjectEvidenceSnapshot,
|
|
529
|
+
options: Omit<ToOpenAPIOptions, "evidence"> = {},
|
|
530
|
+
): OpenAPIDocument {
|
|
531
|
+
const app = {
|
|
532
|
+
routes: () => reflectedRoutesFromEvidence(evidence),
|
|
533
|
+
} as unknown as Server
|
|
534
|
+
return toOpenAPI(app, { ...options, evidence })
|
|
535
|
+
}
|