@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 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
@@ -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,EAAE,KAAK,eAAe,EAAE,KAAK,WAAW,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAEhF,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,KAAK,IAAI,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AACjF,OAAO,EAAE,CAAC,EAAE,MAAM,QAAQ,CAAA"}
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,EAA0C,SAAS,EAAE,MAAM,cAAc,CAAA;AAChF,+EAA+E;AAC/E,OAAO,EAAE,YAAY,EAAE,YAAY,EAAa,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AACjF,OAAO,EAAE,CAAC,EAAE,MAAM,QAAQ,CAAA"}
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
@@ -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
- /** Name of a path segment's parameter, or `undefined` for a static segment. */
53
- function segmentParam(segment) {
54
- if (segment.startsWith(":"))
55
- return segment.slice(1);
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 = segmentParam(segment);
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
- for (const route of reflectRoutes(input)) {
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
@@ -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.0",
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.3.0"
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 { type OpenAPIDocument, type OpenAPIInfo, toOpenAPI } from "./openapi.ts"
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
- /** Name of a path segment's parameter, or `undefined` for a static segment. */
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
- if (segment.startsWith(":")) return segment.slice(1)
181
- if (segment.startsWith("*")) return segment.length > 1 ? segment.slice(1) : "wildcard"
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 = segmentParam(segment)
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
- for (const route of reflectRoutes(input)) {
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
+ }