@lssm/lib.contracts-transformers 0.0.0-canary-20251220041653 → 0.0.0-canary-20251221114240

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.
Files changed (60) hide show
  1. package/README.md +2 -2
  2. package/dist/common/index.d.ts +2 -2
  3. package/dist/common/types.d.ts +14 -10
  4. package/dist/common/types.d.ts.map +1 -1
  5. package/dist/index.d.ts +6 -5
  6. package/dist/index.js +3 -2
  7. package/dist/openapi/differ.d.ts +6 -6
  8. package/dist/openapi/differ.d.ts.map +1 -1
  9. package/dist/openapi/differ.js +11 -4
  10. package/dist/openapi/differ.js.map +1 -1
  11. package/dist/openapi/exporter.d.ts +8 -8
  12. package/dist/openapi/exporter.d.ts.map +1 -1
  13. package/dist/openapi/exporter.js +4 -4
  14. package/dist/openapi/exporter.js.map +1 -1
  15. package/dist/openapi/importer/analyzer.js +28 -0
  16. package/dist/openapi/importer/analyzer.js.map +1 -0
  17. package/dist/openapi/importer/events.js +36 -0
  18. package/dist/openapi/importer/events.js.map +1 -0
  19. package/dist/openapi/importer/generator.js +71 -0
  20. package/dist/openapi/importer/generator.js.map +1 -0
  21. package/dist/openapi/importer/index.d.ts +17 -0
  22. package/dist/openapi/importer/index.d.ts.map +1 -0
  23. package/dist/openapi/importer/index.js +154 -0
  24. package/dist/openapi/importer/index.js.map +1 -0
  25. package/dist/openapi/importer/models.js +19 -0
  26. package/dist/openapi/importer/models.js.map +1 -0
  27. package/dist/openapi/importer/schemas.js +80 -0
  28. package/dist/openapi/importer/schemas.js.map +1 -0
  29. package/dist/openapi/index.d.ts +5 -4
  30. package/dist/openapi/index.js +4 -2
  31. package/dist/openapi/parser/document.d.ts +20 -0
  32. package/dist/openapi/parser/document.d.ts.map +1 -0
  33. package/dist/openapi/parser/document.js +95 -0
  34. package/dist/openapi/parser/document.js.map +1 -0
  35. package/dist/openapi/parser/index.js +5 -0
  36. package/dist/openapi/parser/operation.js +59 -0
  37. package/dist/openapi/parser/operation.js.map +1 -0
  38. package/dist/openapi/parser/parameters.js +37 -0
  39. package/dist/openapi/parser/parameters.js.map +1 -0
  40. package/dist/openapi/parser/resolvers.js +63 -0
  41. package/dist/openapi/parser/resolvers.js.map +1 -0
  42. package/dist/openapi/parser/utils.d.ts +19 -0
  43. package/dist/openapi/parser/utils.d.ts.map +1 -0
  44. package/dist/openapi/parser/utils.js +48 -0
  45. package/dist/openapi/parser/utils.js.map +1 -0
  46. package/dist/openapi/parser.js +6 -232
  47. package/dist/openapi/schema-converter.d.ts +7 -1
  48. package/dist/openapi/schema-converter.d.ts.map +1 -1
  49. package/dist/openapi/schema-converter.js +117 -20
  50. package/dist/openapi/schema-converter.js.map +1 -1
  51. package/dist/openapi/types.d.ts +14 -20
  52. package/dist/openapi/types.d.ts.map +1 -1
  53. package/package.json +5 -5
  54. package/dist/openapi/importer.d.ts +0 -16
  55. package/dist/openapi/importer.d.ts.map +0 -1
  56. package/dist/openapi/importer.js +0 -265
  57. package/dist/openapi/importer.js.map +0 -1
  58. package/dist/openapi/parser.d.ts +0 -32
  59. package/dist/openapi/parser.d.ts.map +0 -1
  60. package/dist/openapi/parser.js.map +0 -1
@@ -58,25 +58,6 @@ interface OpenApiExportOptions {
58
58
  /** Additional OpenAPI extensions */
59
59
  extensions?: Record<string, unknown>;
60
60
  }
61
- /**
62
- * Options for importing from OpenAPI.
63
- */
64
- interface OpenApiImportOptions {
65
- /** Prefix for generated spec names */
66
- prefix?: string;
67
- /** Only import operations with these tags */
68
- tags?: string[];
69
- /** Exclude operations with these operationIds */
70
- exclude?: string[];
71
- /** Include operations with these operationIds (overrides exclude) */
72
- include?: string[];
73
- /** Default stability for imported specs */
74
- defaultStability?: 'experimental' | 'beta' | 'stable' | 'deprecated';
75
- /** Default owners for imported specs */
76
- defaultOwners?: string[];
77
- /** Default auth level for imported specs */
78
- defaultAuth?: 'anonymous' | 'user' | 'admin';
79
- }
80
61
  /**
81
62
  * Options for parsing OpenAPI documents.
82
63
  */
@@ -174,6 +155,19 @@ interface ParseResult {
174
155
  servers: OpenApiServer[];
175
156
  /** Parse warnings */
176
157
  warnings: string[];
158
+ /** Parsed events (webhooks) */
159
+ events: ParsedEvent[];
160
+ }
161
+ /**
162
+ * Parsed event (webhook).
163
+ */
164
+ interface ParsedEvent {
165
+ /** Event name */
166
+ name: string;
167
+ /** Event description */
168
+ description?: string;
169
+ /** Event payload schema */
170
+ payload: OpenApiSchema;
177
171
  }
178
172
  /**
179
173
  * OpenAPI-specific transport hints.
@@ -218,5 +212,5 @@ interface ContractSpecOpenApiDocument {
218
212
  };
219
213
  }
220
214
  //#endregion
221
- export { ContractSpecOpenApiDocument, HttpMethod, OpenApiDocument, OpenApiExportOptions, OpenApiImportOptions, OpenApiOperation, OpenApiParameter, OpenApiParseOptions, OpenApiSchema, OpenApiServer, OpenApiSource, OpenApiTransportHints, OpenApiVersion, ParameterLocation, ParseResult, ParsedOperation, ParsedParameter };
215
+ export { ContractSpecOpenApiDocument, HttpMethod, OpenApiDocument, OpenApiExportOptions, OpenApiOperation, OpenApiParameter, OpenApiParseOptions, OpenApiSchema, OpenApiServer, OpenApiSource, OpenApiTransportHints, OpenApiVersion, ParameterLocation, ParseResult, ParsedOperation, ParsedParameter };
222
216
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","names":[],"sources":["../../src/openapi/types.ts"],"sourcesContent":[],"mappings":";;;;;AAeA;AAKA;AAOA;AACI,KAlBQ,cAAA,GAkBE,KAAA,GAAA,KAAA;;;;AAGiB,KAhBnB,eAAA,GAAkB,SAAA,CAAU,QAgBT,GAhBoB,WAAA,CAAY,QAgBhC;AAK/B;;;AAGI,KAnBQ,gBAAA,GACR,SAAA,CAAU,eAkBA,GAjBV,WAAA,CAAY,eAiBF;;;AAMd;AAaY,KA/BA,aAAA,GACR,SAAA,CAAU,YA8Be,GA7BzB,WAAA,CAAY,YA6Ba,GA5BzB,SAAA,CAAU,eA4Be,GA3BzB,WAAA,CAAY,eA2Ba;AAK7B;AAgBA;AAgBA;AAoBiB,KA/EL,gBAAA,GACR,SAAA,CAAU,eA8EsB,GA7EhC,WAAA,CAAY,eA6EoB,GA5EhC,SAAA,CAAU,eA4EsB,GA3EhC,WAAA,CAAY,eA2EoB;AAYpC;;;AAgBe,KAlGH,UAAA,GAkGG,KAAA,GAAA,MAAA,GAAA,KAAA,GAAA,QAAA,GAAA,OAAA,GAAA,MAAA,GAAA,SAAA,GAAA,OAAA;;;;AAgBA,KArGH,iBAAA,GAqGG,MAAA,GAAA,OAAA,GAAA,QAAA,GAAA,QAAA;;;;AAmBE,UAnHA,aAAA,CAmHe;EAkBf,GAAA,EAAA,MAAA;EAEL,WAAA,CAAA,EAAA,MAAA;EAED,SAAA,CAAA,EAtIG,MAsIH,CAAA,MAAA,EAAA;IAQG,OAAA,EAAA,MAAA;IAEY,IAAA,CAAA,EAAA,MAAA,EAAA;IAAf,WAAA,CAAA,EAAA,MAAA;EAEA,CAAA,CAAA;;AAQX;AAgBA;AAYA;AAOY,UAhLK,oBAAA,CAgLL;EACY;EAAf,KAAA,CAAA,EAAA,MAAA;EAEmB;EAAf,OAAA,CAAA,EAAA,MAAA;EAAM;;;YA3KP;;eAEG;;;;;UAME,oBAAA;;;;;;;;;;;;;;;;;;;UAoBA,mBAAA;;;;;;;;;;;UAYA,eAAA;;;;UAIP;;;;;;;;;;cAUI;;eAEC;;gBAEC;;gBAEA;;;;YAIJ;;;;aAIC;;aAIE;;;;;;aAOF;;;;;;;;;;;UAYI,eAAA;;;;MAIX;;;;;;UAMI;;;;;;;UAQO,WAAA;;YAEL;;WAED;;;;;;;;cAQG;;WAEH,eAAe;;WAEf;;;;;;;UAQM,qBAAA,SAA8B;;;;;;;;;;;;;;;UAgB9B,aAAA,SAAsB;;;kBAGrB;;;;;;;;UASD,2BAAA;;;;;;;YAOL;SACH,eAAe;;aAEX,eAAe"}
1
+ {"version":3,"file":"types.d.ts","names":[],"sources":["../../src/openapi/types.ts"],"sourcesContent":[],"mappings":";;;;;;AAiDA;;AAEI,KA5BQ,cAAA,GA4BI,KAAA,GAAA,KAAA;;;;AAOJ,KA9BA,eAAA,GAAkB,SAAA,CAAU,QA8BlB,GA9B6B,WAAA,CAAY,QA8BzC;AAatB;AAKA;AAgBA;AAgBiB,KA3EL,gBAAA,GACR,SAAA,CAAU,eA0EsB,GAzEhC,WAAA,CAAY,eAyEoB;AAYpC;;;AAgBe,KAhGH,aAAA,GACR,SAAA,CAAU,YA+FC,GA9FX,WAAA,CAAY,YA8FD,GA7FX,SAAA,CAAU,eA6FC,GA5FX,WAAA,CAAY,eA4FD;;;;AAgBA,KAvGH,gBAAA,GACR,SAAA,CAAU,eAsGC,GArGX,WAAA,CAAY,eAqGD,GApGX,SAAA,CAAU,eAoGC,GAnGX,WAAA,CAAY,eAmGD;;;;AAmBE,KAjHL,UAAA,GAiHoB,KAAA,GAAA,MAI1B,GAAA,KAAA,GAAA,QAMI,GAAA,OAAa,GAAA,MAAA,GAAA,SAAA,GAAA,OAAA;AAQvB;;;AAYc,KAlIF,iBAAA,GAkIE,MAAA,GAAA,OAAA,GAAA,QAAA,GAAA,QAAA;;;;AAQJ,UArIO,aAAA,CAqIP;EAAW,GAAA,EAAA,MAAA;EAMJ,WAAA,CAAA,EAAW,MAAA;EAYX,SAAA,CAAA,EApJH,MAoJG,CAAA,MAAsB,EAAA;IAgBtB,OAAA,EAAA,MAAc;IAYd,IAAA,CAAA,EAAA,MAAA,EAAA;IAOL,WAAA,CAAA,EAAA,MAAA;EACY,CAAA,CAAA;;;;;UA3KP,oBAAA;;;;;;;;YAQL;;eAEG;;;;;UAME,mBAAA;;;;;;;;;;;UAYA,eAAA;;;;UAIP;;;;;;;;;;cAUI;;eAEC;;gBAEC;;gBAEA;;;;YAIJ;;;;aAIC;;aAIE;;;;;;aAOF;;;;;;;;;;;UAYI,eAAA;;;;MAIX;;;;;;UAMI;;;;;;;UAQO,WAAA;;YAEL;;WAED;;;;;;;;cAQG;;WAEH,eAAe;;WAEf;;;;UAID;;;;;UAMO,WAAA;;;;;;WAMN;;;;;UAMM,qBAAA,SAA8B;;;;;;;;;;;;;;;UAgB9B,aAAA,SAAsB;;;kBAGrB;;;;;;;;UASD,2BAAA;;;;;;;YAOL;SACH,eAAe;;aAEX,eAAe"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lssm/lib.contracts-transformers",
3
- "version": "0.0.0-canary-20251220041653",
3
+ "version": "0.0.0-canary-20251221114240",
4
4
  "description": "Contract format transformations: import/export between ContractSpec and external formats (OpenAPI, AsyncAPI, etc.)",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -17,15 +17,15 @@
17
17
  "test": "bun test"
18
18
  },
19
19
  "dependencies": {
20
- "@lssm/lib.contracts": "0.0.0-canary-20251220041653",
21
- "@lssm/lib.schema": "0.0.0-canary-20251220041653",
20
+ "@lssm/lib.contracts": "0.0.0-canary-20251221114240",
21
+ "@lssm/lib.schema": "0.0.0-canary-20251221114240",
22
22
  "openapi-types": "^12.1.3",
23
23
  "yaml": "^2.7.1",
24
24
  "zod": "^4.1.13"
25
25
  },
26
26
  "devDependencies": {
27
- "@lssm/tool.tsdown": "0.0.0-canary-20251220041653",
28
- "@lssm/tool.typescript": "0.0.0-canary-20251220041653",
27
+ "@lssm/tool.tsdown": "0.0.0-canary-20251221114240",
28
+ "@lssm/tool.typescript": "0.0.0-canary-20251221114240",
29
29
  "tsdown": "^0.18.1",
30
30
  "typescript": "^5.9.3"
31
31
  },
@@ -1,16 +0,0 @@
1
- import { ImportResult } from "../common/types.js";
2
- import { OpenApiImportOptions, ParseResult, ParsedOperation } from "./types.js";
3
-
4
- //#region src/openapi/importer.d.ts
5
-
6
- /**
7
- * Import operations from a parsed OpenAPI document.
8
- */
9
- declare function importFromOpenApi(parseResult: ParseResult, options?: OpenApiImportOptions): ImportResult;
10
- /**
11
- * Import a single operation to ContractSpec code.
12
- */
13
- declare function importOperation(operation: ParsedOperation, options?: OpenApiImportOptions): string;
14
- //#endregion
15
- export { importFromOpenApi, importOperation };
16
- //# sourceMappingURL=importer.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"importer.d.ts","names":[],"sources":["../../src/openapi/importer.ts"],"sourcesContent":[],"mappings":";;;;;;;;AAyagB,iBAjIA,iBAAA,CAkIH,WACF,EAlII,WAkIJ,EAAA,OAAyB,CAAA,EAjIzB,oBAiIyB,CAAA,EAhIjC,YAgIiC;;;;iBAFpB,eAAA,YACH,2BACF"}
@@ -1,265 +0,0 @@
1
- import { toFileName, toPascalCase, toSpecName, toValidIdentifier } from "../common/utils.js";
2
- import { generateImports, generateSchemaModelCode } from "./schema-converter.js";
3
-
4
- //#region src/openapi/importer.ts
5
- /**
6
- * HTTP methods that typically indicate a command (state-changing).
7
- */
8
- const COMMAND_METHODS = [
9
- "post",
10
- "put",
11
- "delete",
12
- "patch"
13
- ];
14
- /**
15
- * Determine if an operation is a command or query based on HTTP method.
16
- */
17
- function inferOpKind(method) {
18
- return COMMAND_METHODS.includes(method.toLowerCase()) ? "command" : "query";
19
- }
20
- /**
21
- * Determine auth level based on security requirements.
22
- */
23
- function inferAuthLevel(operation, defaultAuth) {
24
- if (!operation.security || operation.security.length === 0) return defaultAuth;
25
- for (const sec of operation.security) if (Object.keys(sec).length === 0) return "anonymous";
26
- return "user";
27
- }
28
- /**
29
- * Build a merged input schema from all parameter sources.
30
- */
31
- function buildInputSchema(operation) {
32
- const fields = [];
33
- for (const param of operation.pathParams) fields.push({
34
- name: param.name,
35
- schema: param.schema,
36
- required: true
37
- });
38
- for (const param of operation.queryParams) fields.push({
39
- name: param.name,
40
- schema: param.schema,
41
- required: param.required
42
- });
43
- const excludedHeaders = [
44
- "authorization",
45
- "content-type",
46
- "accept",
47
- "user-agent"
48
- ];
49
- for (const param of operation.headerParams) if (!excludedHeaders.includes(param.name.toLowerCase())) fields.push({
50
- name: param.name,
51
- schema: param.schema,
52
- required: param.required
53
- });
54
- if (operation.requestBody?.schema) {
55
- const bodySchema = operation.requestBody.schema;
56
- if (!("$ref" in bodySchema)) {
57
- const schemaObj = bodySchema;
58
- const properties = schemaObj["properties"];
59
- const required = schemaObj["required"] ?? [];
60
- if (properties) for (const [propName, propSchema] of Object.entries(properties)) fields.push({
61
- name: propName,
62
- schema: propSchema,
63
- required: required.includes(propName)
64
- });
65
- } else fields.push({
66
- name: "body",
67
- schema: bodySchema,
68
- required: operation.requestBody.required
69
- });
70
- }
71
- if (fields.length === 0) return {
72
- schema: null,
73
- fields: []
74
- };
75
- return {
76
- schema: {
77
- type: "object",
78
- properties: fields.reduce((acc, f) => {
79
- acc[f.name] = f.schema;
80
- return acc;
81
- }, {}),
82
- required: fields.filter((f) => f.required).map((f) => f.name)
83
- },
84
- fields
85
- };
86
- }
87
- /**
88
- * Get the output schema from the operation responses.
89
- */
90
- function getOutputSchema(operation) {
91
- for (const code of [
92
- "200",
93
- "201",
94
- "202",
95
- "204"
96
- ]) {
97
- const response = operation.responses[code];
98
- if (response?.schema) return response.schema;
99
- }
100
- for (const [code, response] of Object.entries(operation.responses)) if (code.startsWith("2") && response.schema) return response.schema;
101
- return null;
102
- }
103
- /**
104
- * Generate ContractSpec TypeScript code for an operation.
105
- */
106
- function generateSpecCode(operation, options, inputModel, outputModel) {
107
- const specName = toSpecName(operation.operationId, options.prefix);
108
- const kind = inferOpKind(operation.method);
109
- const auth = inferAuthLevel(operation, options.defaultAuth ?? "user");
110
- const lines = [];
111
- lines.push("import { defineCommand, defineQuery } from '@lssm/lib.contracts';");
112
- if (inputModel || outputModel) lines.push(generateImports([...inputModel?.fields ?? [], ...outputModel?.fields ?? []]));
113
- lines.push("");
114
- if (inputModel && inputModel.code) {
115
- lines.push("// Input schema");
116
- lines.push(inputModel.code);
117
- lines.push("");
118
- }
119
- if (outputModel && outputModel.code) {
120
- lines.push("// Output schema");
121
- lines.push(outputModel.code);
122
- lines.push("");
123
- }
124
- const defineFunc = kind === "command" ? "defineCommand" : "defineQuery";
125
- const safeName = toValidIdentifier(toPascalCase(operation.operationId));
126
- lines.push(`/**`);
127
- lines.push(` * ${operation.summary ?? operation.operationId}`);
128
- if (operation.description) {
129
- lines.push(` *`);
130
- lines.push(` * ${operation.description}`);
131
- }
132
- lines.push(` *`);
133
- lines.push(` * @source OpenAPI: ${operation.method.toUpperCase()} ${operation.path}`);
134
- lines.push(` */`);
135
- lines.push(`export const ${safeName}Spec = ${defineFunc}({`);
136
- lines.push(" meta: {");
137
- lines.push(` name: '${specName}',`);
138
- lines.push(" version: 1,");
139
- lines.push(` stability: '${options.defaultStability ?? "stable"}',`);
140
- lines.push(` owners: [${(options.defaultOwners ?? []).map((o) => `'${o}'`).join(", ")}],`);
141
- lines.push(` tags: [${operation.tags.map((t) => `'${t}'`).join(", ")}],`);
142
- lines.push(` description: ${JSON.stringify(operation.summary ?? operation.operationId)},`);
143
- lines.push(` goal: ${JSON.stringify(operation.description ?? "Imported from OpenAPI")},`);
144
- lines.push(` context: 'Imported from OpenAPI: ${operation.method.toUpperCase()} ${operation.path}',`);
145
- lines.push(" },");
146
- lines.push(" io: {");
147
- if (inputModel) lines.push(` input: ${inputModel.name},`);
148
- else lines.push(" input: null,");
149
- if (outputModel) lines.push(` output: ${outputModel.name},`);
150
- else lines.push(" output: null, // TODO: Define output schema");
151
- lines.push(" },");
152
- lines.push(" policy: {");
153
- lines.push(` auth: '${auth}',`);
154
- lines.push(" },");
155
- lines.push(" transport: {");
156
- lines.push(" rest: {");
157
- lines.push(` method: '${operation.method.toUpperCase()}',`);
158
- lines.push(` path: '${operation.path}',`);
159
- lines.push(" },");
160
- lines.push(" },");
161
- lines.push("});");
162
- return lines.join("\n");
163
- }
164
- /**
165
- * Import operations from a parsed OpenAPI document.
166
- */
167
- function importFromOpenApi(parseResult, options = {}) {
168
- const { tags, exclude = [], include } = options;
169
- const specs = [];
170
- const skipped = [];
171
- const errors = [];
172
- for (const operation of parseResult.operations) {
173
- if (tags && tags.length > 0) {
174
- if (!operation.tags.some((t) => tags.includes(t))) {
175
- skipped.push({
176
- sourceId: operation.operationId,
177
- reason: `No matching tags (has: ${operation.tags.join(", ")})`
178
- });
179
- continue;
180
- }
181
- }
182
- if (include && include.length > 0) {
183
- if (!include.includes(operation.operationId)) {
184
- skipped.push({
185
- sourceId: operation.operationId,
186
- reason: "Not in include list"
187
- });
188
- continue;
189
- }
190
- } else if (exclude.includes(operation.operationId)) {
191
- skipped.push({
192
- sourceId: operation.operationId,
193
- reason: "In exclude list"
194
- });
195
- continue;
196
- }
197
- if (operation.deprecated && options.defaultStability !== "deprecated") {
198
- skipped.push({
199
- sourceId: operation.operationId,
200
- reason: "Deprecated operation"
201
- });
202
- continue;
203
- }
204
- try {
205
- const { schema: inputSchema } = buildInputSchema(operation);
206
- const inputModel = inputSchema ? generateSchemaModelCode(inputSchema, `${operation.operationId}Input`) : null;
207
- const outputSchema = getOutputSchema(operation);
208
- const code = generateSpecCode(operation, options, inputModel, outputSchema ? generateSchemaModelCode(outputSchema, `${operation.operationId}Output`) : null);
209
- const fileName = toFileName(toSpecName(operation.operationId, options.prefix));
210
- const transportHints = { rest: {
211
- method: operation.method.toUpperCase(),
212
- path: operation.path,
213
- params: {
214
- path: operation.pathParams.map((p) => p.name),
215
- query: operation.queryParams.map((p) => p.name),
216
- header: operation.headerParams.map((p) => p.name),
217
- cookie: operation.cookieParams.map((p) => p.name)
218
- }
219
- } };
220
- const source = {
221
- type: "openapi",
222
- sourceId: operation.operationId,
223
- operationId: operation.operationId,
224
- openApiVersion: parseResult.version,
225
- importedAt: /* @__PURE__ */ new Date()
226
- };
227
- specs.push({
228
- spec: {},
229
- code,
230
- fileName,
231
- source,
232
- transportHints
233
- });
234
- } catch (error) {
235
- errors.push({
236
- sourceId: operation.operationId,
237
- error: error instanceof Error ? error.message : String(error)
238
- });
239
- }
240
- }
241
- return {
242
- specs,
243
- skipped,
244
- errors,
245
- summary: {
246
- total: parseResult.operations.length,
247
- imported: specs.length,
248
- skipped: skipped.length,
249
- errors: errors.length
250
- }
251
- };
252
- }
253
- /**
254
- * Import a single operation to ContractSpec code.
255
- */
256
- function importOperation(operation, options = {}) {
257
- const { schema: inputSchema } = buildInputSchema(operation);
258
- const inputModel = inputSchema ? generateSchemaModelCode(inputSchema, `${operation.operationId}Input`) : null;
259
- const outputSchema = getOutputSchema(operation);
260
- return generateSpecCode(operation, options, inputModel, outputSchema ? generateSchemaModelCode(outputSchema, `${operation.operationId}Output`) : null);
261
- }
262
-
263
- //#endregion
264
- export { importFromOpenApi, importOperation };
265
- //# sourceMappingURL=importer.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"importer.js","names":["fields: {\n name: string;\n schema: OpenApiSchema;\n required: boolean;\n }[]","lines: string[]","specs: ImportedSpec[]","skipped: ImportResult['skipped']","errors: ImportResult['errors']","transportHints: OpenApiTransportHints","source: OpenApiSource"],"sources":["../../src/openapi/importer.ts"],"sourcesContent":["/**\n * Import OpenAPI specifications into ContractSpec format.\n * Converts OpenAPI operations to ContractSpec command/query definitions.\n */\n\nimport type {\n OpenApiImportOptions,\n ParseResult,\n ParsedOperation,\n OpenApiSchema,\n OpenApiTransportHints,\n OpenApiSource,\n} from './types';\nimport type { ImportResult, ImportedSpec } from '../common/types';\nimport {\n toSpecName,\n toFileName,\n toPascalCase,\n toValidIdentifier,\n} from '../common/utils';\nimport {\n generateSchemaModelCode,\n generateImports,\n type GeneratedModel,\n} from './schema-converter';\n\n/**\n * HTTP methods that typically indicate a command (state-changing).\n */\nconst COMMAND_METHODS = ['post', 'put', 'delete', 'patch'];\n\n/**\n * Determine if an operation is a command or query based on HTTP method.\n */\nfunction inferOpKind(method: string): 'command' | 'query' {\n return COMMAND_METHODS.includes(method.toLowerCase()) ? 'command' : 'query';\n}\n\n/**\n * Determine auth level based on security requirements.\n */\nfunction inferAuthLevel(\n operation: ParsedOperation,\n defaultAuth: 'anonymous' | 'user' | 'admin'\n): 'anonymous' | 'user' | 'admin' {\n if (!operation.security || operation.security.length === 0) {\n // Check if any security scheme is present\n return defaultAuth;\n }\n\n // If there's an empty security requirement, it's anonymous\n for (const sec of operation.security) {\n if (Object.keys(sec).length === 0) {\n return 'anonymous';\n }\n }\n\n return 'user';\n}\n\n/**\n * Build a merged input schema from all parameter sources.\n */\nfunction buildInputSchema(operation: ParsedOperation): {\n schema: OpenApiSchema | null;\n fields: { name: string; schema: OpenApiSchema; required: boolean }[];\n} {\n const fields: {\n name: string;\n schema: OpenApiSchema;\n required: boolean;\n }[] = [];\n\n // Add path parameters\n for (const param of operation.pathParams) {\n fields.push({\n name: param.name,\n schema: param.schema,\n required: true, // Path params are always required\n });\n }\n\n // Add query parameters\n for (const param of operation.queryParams) {\n fields.push({\n name: param.name,\n schema: param.schema,\n required: param.required,\n });\n }\n\n // Add header parameters (excluding common headers)\n const excludedHeaders = [\n 'authorization',\n 'content-type',\n 'accept',\n 'user-agent',\n ];\n for (const param of operation.headerParams) {\n if (!excludedHeaders.includes(param.name.toLowerCase())) {\n fields.push({\n name: param.name,\n schema: param.schema,\n required: param.required,\n });\n }\n }\n\n // Add request body fields\n if (operation.requestBody?.schema) {\n const bodySchema = operation.requestBody.schema;\n if (!('$ref' in bodySchema)) {\n const schemaObj = bodySchema as Record<string, unknown>;\n const properties = schemaObj['properties'] as\n | Record<string, OpenApiSchema>\n | undefined;\n const required = (schemaObj['required'] as string[]) ?? [];\n\n if (properties) {\n for (const [propName, propSchema] of Object.entries(properties)) {\n fields.push({\n name: propName,\n schema: propSchema,\n required: required.includes(propName),\n });\n }\n }\n } else {\n // Reference to a schema - add as a single field\n fields.push({\n name: 'body',\n schema: bodySchema,\n required: operation.requestBody.required,\n });\n }\n }\n\n if (fields.length === 0) {\n return { schema: null, fields: [] };\n }\n\n // Build a merged schema\n const mergedSchema: OpenApiSchema = {\n type: 'object',\n properties: fields.reduce(\n (acc, f) => {\n acc[f.name] = f.schema;\n return acc;\n },\n {} as Record<string, OpenApiSchema>\n ),\n required: fields.filter((f) => f.required).map((f) => f.name),\n } as unknown as OpenApiSchema;\n\n return { schema: mergedSchema, fields };\n}\n\n/**\n * Get the output schema from the operation responses.\n */\nfunction getOutputSchema(operation: ParsedOperation): OpenApiSchema | null {\n // Prefer 200, then 201, then 2xx responses\n const successCodes = ['200', '201', '202', '204'];\n\n for (const code of successCodes) {\n const response = operation.responses[code];\n if (response?.schema) {\n return response.schema;\n }\n }\n\n // Check for any 2xx response\n for (const [code, response] of Object.entries(operation.responses)) {\n if (code.startsWith('2') && response.schema) {\n return response.schema;\n }\n }\n\n return null;\n}\n\n/**\n * Generate ContractSpec TypeScript code for an operation.\n */\nfunction generateSpecCode(\n operation: ParsedOperation,\n options: OpenApiImportOptions,\n inputModel: GeneratedModel | null,\n outputModel: GeneratedModel | null\n): string {\n const specName = toSpecName(operation.operationId, options.prefix);\n const kind = inferOpKind(operation.method);\n const auth = inferAuthLevel(operation, options.defaultAuth ?? 'user');\n\n const lines: string[] = [];\n\n // Imports\n lines.push(\n \"import { defineCommand, defineQuery } from '@lssm/lib.contracts';\"\n );\n if (inputModel || outputModel) {\n lines.push(\n generateImports([\n ...(inputModel?.fields ?? []),\n ...(outputModel?.fields ?? []),\n ])\n );\n }\n lines.push('');\n\n // Generate input model if present\n if (inputModel && inputModel.code) {\n lines.push('// Input schema');\n lines.push(inputModel.code);\n lines.push('');\n }\n\n // Generate output model if present\n if (outputModel && outputModel.code) {\n lines.push('// Output schema');\n lines.push(outputModel.code);\n lines.push('');\n }\n\n // Generate spec\n const defineFunc = kind === 'command' ? 'defineCommand' : 'defineQuery';\n const safeName = toValidIdentifier(toPascalCase(operation.operationId));\n\n lines.push(`/**`);\n lines.push(` * ${operation.summary ?? operation.operationId}`);\n if (operation.description) {\n lines.push(` *`);\n lines.push(` * ${operation.description}`);\n }\n lines.push(` *`);\n lines.push(\n ` * @source OpenAPI: ${operation.method.toUpperCase()} ${operation.path}`\n );\n lines.push(` */`);\n lines.push(`export const ${safeName}Spec = ${defineFunc}({`);\n\n // Meta\n lines.push(' meta: {');\n lines.push(` name: '${specName}',`);\n lines.push(' version: 1,');\n lines.push(` stability: '${options.defaultStability ?? 'stable'}',`);\n lines.push(\n ` owners: [${(options.defaultOwners ?? []).map((o) => `'${o}'`).join(', ')}],`\n );\n lines.push(` tags: [${operation.tags.map((t) => `'${t}'`).join(', ')}],`);\n lines.push(\n ` description: ${JSON.stringify(operation.summary ?? operation.operationId)},`\n );\n lines.push(\n ` goal: ${JSON.stringify(operation.description ?? 'Imported from OpenAPI')},`\n );\n lines.push(\n ` context: 'Imported from OpenAPI: ${operation.method.toUpperCase()} ${operation.path}',`\n );\n lines.push(' },');\n\n // IO\n lines.push(' io: {');\n if (inputModel) {\n lines.push(` input: ${inputModel.name},`);\n } else {\n lines.push(' input: null,');\n }\n if (outputModel) {\n lines.push(` output: ${outputModel.name},`);\n } else {\n lines.push(' output: null, // TODO: Define output schema');\n }\n lines.push(' },');\n\n // Policy\n lines.push(' policy: {');\n lines.push(` auth: '${auth}',`);\n lines.push(' },');\n\n // Transport hints\n lines.push(' transport: {');\n lines.push(' rest: {');\n lines.push(` method: '${operation.method.toUpperCase()}',`);\n lines.push(` path: '${operation.path}',`);\n lines.push(' },');\n lines.push(' },');\n\n lines.push('});');\n\n return lines.join('\\n');\n}\n\n/**\n * Import operations from a parsed OpenAPI document.\n */\nexport function importFromOpenApi(\n parseResult: ParseResult,\n options: OpenApiImportOptions = {}\n): ImportResult {\n const { tags, exclude = [], include } = options;\n const specs: ImportedSpec[] = [];\n const skipped: ImportResult['skipped'] = [];\n const errors: ImportResult['errors'] = [];\n\n for (const operation of parseResult.operations) {\n // Filter by tags if specified\n if (tags && tags.length > 0) {\n const hasMatchingTag = operation.tags.some((t) => tags.includes(t));\n if (!hasMatchingTag) {\n skipped.push({\n sourceId: operation.operationId,\n reason: `No matching tags (has: ${operation.tags.join(', ')})`,\n });\n continue;\n }\n }\n\n // Filter by include/exclude\n if (include && include.length > 0) {\n if (!include.includes(operation.operationId)) {\n skipped.push({\n sourceId: operation.operationId,\n reason: 'Not in include list',\n });\n continue;\n }\n } else if (exclude.includes(operation.operationId)) {\n skipped.push({\n sourceId: operation.operationId,\n reason: 'In exclude list',\n });\n continue;\n }\n\n // Skip deprecated operations by default\n if (operation.deprecated && options.defaultStability !== 'deprecated') {\n skipped.push({\n sourceId: operation.operationId,\n reason: 'Deprecated operation',\n });\n continue;\n }\n\n try {\n // Build input schema\n const { schema: inputSchema } = buildInputSchema(operation);\n const inputModel = inputSchema\n ? generateSchemaModelCode(inputSchema, `${operation.operationId}Input`)\n : null;\n\n // Get output schema\n const outputSchema = getOutputSchema(operation);\n const outputModel = outputSchema\n ? generateSchemaModelCode(\n outputSchema,\n `${operation.operationId}Output`\n )\n : null;\n\n // Generate spec code\n const code = generateSpecCode(\n operation,\n options,\n inputModel,\n outputModel\n );\n const specName = toSpecName(operation.operationId, options.prefix);\n const fileName = toFileName(specName);\n\n // Build transport hints\n const transportHints: OpenApiTransportHints = {\n rest: {\n method:\n operation.method.toUpperCase() as OpenApiTransportHints['rest']['method'],\n path: operation.path,\n params: {\n path: operation.pathParams.map((p) => p.name),\n query: operation.queryParams.map((p) => p.name),\n header: operation.headerParams.map((p) => p.name),\n cookie: operation.cookieParams.map((p) => p.name),\n },\n },\n };\n\n // Build source info\n const source: OpenApiSource = {\n type: 'openapi',\n sourceId: operation.operationId,\n operationId: operation.operationId,\n openApiVersion: parseResult.version,\n importedAt: new Date(),\n };\n\n specs.push({\n spec: {} as ImportedSpec['spec'], // Placeholder - actual spec would be built at runtime\n code,\n fileName,\n source,\n transportHints,\n });\n } catch (error) {\n errors.push({\n sourceId: operation.operationId,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n return {\n specs,\n skipped,\n errors,\n summary: {\n total: parseResult.operations.length,\n imported: specs.length,\n skipped: skipped.length,\n errors: errors.length,\n },\n };\n}\n\n/**\n * Import a single operation to ContractSpec code.\n */\nexport function importOperation(\n operation: ParsedOperation,\n options: OpenApiImportOptions = {}\n): string {\n const { schema: inputSchema } = buildInputSchema(operation);\n const inputModel = inputSchema\n ? generateSchemaModelCode(inputSchema, `${operation.operationId}Input`)\n : null;\n\n const outputSchema = getOutputSchema(operation);\n const outputModel = outputSchema\n ? generateSchemaModelCode(outputSchema, `${operation.operationId}Output`)\n : null;\n\n return generateSpecCode(operation, options, inputModel, outputModel);\n}\n"],"mappings":"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"}
@@ -1,32 +0,0 @@
1
- import { OpenApiDocument, OpenApiParseOptions, OpenApiVersion, ParseResult } from "./types.js";
2
-
3
- //#region src/openapi/parser.d.ts
4
-
5
- /**
6
- * Parse an OpenAPI document from a string (JSON or YAML).
7
- */
8
- declare function parseOpenApiString(content: string, format?: 'json' | 'yaml'): OpenApiDocument;
9
- /**
10
- * Detect the format of content (JSON or YAML).
11
- */
12
- declare function detectFormat(content: string): 'json' | 'yaml';
13
- /**
14
- * Detect OpenAPI version from document.
15
- */
16
- declare function detectVersion(doc: OpenApiDocument): OpenApiVersion;
17
- /**
18
- * Parse an OpenAPI document into a structured result.
19
- */
20
- declare function parseOpenApiDocument(doc: OpenApiDocument, _options?: OpenApiParseOptions): ParseResult;
21
- /**
22
- * Parse OpenAPI from a file path or URL.
23
- * Note: This is an async function that requires I/O adapters.
24
- * For pure parsing, use parseOpenApiString or parseOpenApiDocument.
25
- */
26
- declare function parseOpenApi(source: string, options?: OpenApiParseOptions & {
27
- fetch?: typeof globalThis.fetch;
28
- readFile?: (path: string) => Promise<string>;
29
- }): Promise<ParseResult>;
30
- //#endregion
31
- export { detectFormat, detectVersion, parseOpenApi, parseOpenApiDocument, parseOpenApiString };
32
- //# sourceMappingURL=parser.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"parser.d.ts","names":[],"sources":["../../src/openapi/parser.ts"],"sourcesContent":[],"mappings":";;;;AA0DA;AAqNA;;AAEY,iBA/OI,kBAAA,CA+OJ,OAAA,EAAA,MAAA,EAAA,MAAA,CAAA,EAAA,MAAA,GAAA,MAAA,CAAA,EA5OT,eA4OS;;;AA2EZ;AAEW,iBA/SK,YAAA,CA+SL,OAAA,EAAA,MAAA,CAAA,EAAA,MAAA,GAAA,MAAA;;;;AAIR,iBAxSa,aAAA,CAwSb,GAAA,EAxSgC,eAwShC,CAAA,EAxSkD,cAwSlD;;;;iBAnFa,oBAAA,MACT,4BACK,sBACT;;;;;;iBA0EmB,YAAA,2BAEX;iBACQ,UAAA,CAAW;+BACG;IAE9B,QAAQ"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"parser.js","names":["HTTP_METHODS: HttpMethod[]","parseYaml","current: unknown","resolved: OpenAPIV3.ParameterObject | OpenAPIV3_1.ParameterObject","parsed: ParsedParameter","requestBody: ParsedOperation['requestBody']","responses: ParsedOperation['responses']","warnings: string[]","operations: ParsedOperation[]","schemas: Record<string, OpenApiSchema>","servers: OpenApiServer[]","content: string","format: 'json' | 'yaml'"],"sources":["../../src/openapi/parser.ts"],"sourcesContent":["/**\n * OpenAPI document parser.\n * Parses OpenAPI 3.x documents from JSON/YAML files or URLs.\n */\n\nimport { parse as parseYaml } from 'yaml';\nimport type {\n OpenApiDocument,\n OpenApiParseOptions,\n ParseResult,\n ParsedOperation,\n ParsedParameter,\n HttpMethod,\n OpenApiVersion,\n OpenApiSchema,\n OpenApiParameter,\n OpenApiServer,\n} from './types';\nimport type { OpenAPIV3, OpenAPIV3_1 } from 'openapi-types';\n\nconst HTTP_METHODS: HttpMethod[] = [\n 'get',\n 'post',\n 'put',\n 'delete',\n 'patch',\n 'head',\n 'options',\n 'trace',\n];\n\n/**\n * Parse an OpenAPI document from a string (JSON or YAML).\n */\nexport function parseOpenApiString(\n content: string,\n format: 'json' | 'yaml' = 'json'\n): OpenApiDocument {\n if (format === 'yaml') {\n return parseYaml(content) as OpenApiDocument;\n }\n return JSON.parse(content) as OpenApiDocument;\n}\n\n/**\n * Detect the format of content (JSON or YAML).\n */\nexport function detectFormat(content: string): 'json' | 'yaml' {\n const trimmed = content.trim();\n if (trimmed.startsWith('{') || trimmed.startsWith('[')) {\n return 'json';\n }\n return 'yaml';\n}\n\n/**\n * Detect OpenAPI version from document.\n */\nexport function detectVersion(doc: OpenApiDocument): OpenApiVersion {\n const version = doc.openapi;\n if (version.startsWith('3.1')) {\n return '3.1';\n }\n return '3.0';\n}\n\n/**\n * Check if a value is a reference object.\n */\nfunction isReference(\n obj: unknown\n): obj is OpenAPIV3.ReferenceObject | OpenAPIV3_1.ReferenceObject {\n return typeof obj === 'object' && obj !== null && '$ref' in obj;\n}\n\n/**\n * Resolve a $ref reference in the document.\n */\nfunction resolveRef<T>(doc: OpenApiDocument, ref: string): T | undefined {\n // Only support local refs for now\n if (!ref.startsWith('#/')) {\n return undefined;\n }\n\n const path = ref.slice(2).split('/');\n let current: unknown = doc;\n\n for (const part of path) {\n if (current === null || current === undefined) return undefined;\n if (typeof current !== 'object') return undefined;\n current = (current as Record<string, unknown>)[part];\n }\n\n return current as T;\n}\n\n/**\n * Resolve a schema, following $ref if needed.\n */\nfunction resolveSchema(\n doc: OpenApiDocument,\n schema: OpenApiSchema | undefined\n): OpenApiSchema | undefined {\n if (!schema) return undefined;\n if (isReference(schema)) {\n return resolveRef<OpenApiSchema>(doc, schema.$ref) ?? schema;\n }\n return schema;\n}\n\n/**\n * Parse parameters from an operation.\n */\nfunction parseParameters(\n doc: OpenApiDocument,\n params: OpenApiParameter[] | undefined\n): {\n path: ParsedParameter[];\n query: ParsedParameter[];\n header: ParsedParameter[];\n cookie: ParsedParameter[];\n} {\n const result = {\n path: [] as ParsedParameter[],\n query: [] as ParsedParameter[],\n header: [] as ParsedParameter[],\n cookie: [] as ParsedParameter[],\n };\n\n if (!params) return result;\n\n for (const param of params) {\n let resolved: OpenAPIV3.ParameterObject | OpenAPIV3_1.ParameterObject;\n\n if (isReference(param)) {\n const ref = resolveRef<\n OpenAPIV3.ParameterObject | OpenAPIV3_1.ParameterObject\n >(doc, param.$ref);\n if (!ref) continue;\n resolved = ref;\n } else {\n resolved = param;\n }\n\n const parsed: ParsedParameter = {\n name: resolved.name,\n in: resolved.in as ParsedParameter['in'],\n required: resolved.required ?? resolved.in === 'path',\n description: resolved.description,\n schema: resolved.schema as OpenApiSchema,\n deprecated: resolved.deprecated ?? false,\n };\n\n result[resolved.in as keyof typeof result]?.push(parsed);\n }\n\n return result;\n}\n\n/**\n * Generate an operationId if not present.\n */\nfunction generateOperationId(method: HttpMethod, path: string): string {\n // Convert path to camelCase operationId\n const pathParts = path\n .split('/')\n .filter(Boolean)\n .map((part) => {\n // Remove path parameters\n if (part.startsWith('{') && part.endsWith('}')) {\n return (\n 'By' + part.slice(1, -1).charAt(0).toUpperCase() + part.slice(2, -1)\n );\n }\n return part.charAt(0).toUpperCase() + part.slice(1);\n });\n\n return method + pathParts.join('');\n}\n\n/**\n * Parse a single operation.\n */\nfunction parseOperation(\n doc: OpenApiDocument,\n method: HttpMethod,\n path: string,\n operation: OpenAPIV3.OperationObject | OpenAPIV3_1.OperationObject,\n pathParams: OpenApiParameter[] | undefined\n): ParsedOperation {\n // Merge path-level and operation-level parameters\n const allParams = [...(pathParams ?? []), ...(operation.parameters ?? [])];\n const params = parseParameters(doc, allParams as OpenApiParameter[]);\n\n // Parse request body\n let requestBody: ParsedOperation['requestBody'];\n if (operation.requestBody) {\n const body = isReference(operation.requestBody)\n ? resolveRef<OpenAPIV3.RequestBodyObject | OpenAPIV3_1.RequestBodyObject>(\n doc,\n operation.requestBody.$ref\n )\n : operation.requestBody;\n\n if (body) {\n const contentType =\n Object.keys(body.content ?? {})[0] ?? 'application/json';\n const content = body.content?.[contentType];\n if (content?.schema) {\n requestBody = {\n required: body.required ?? false,\n schema:\n resolveSchema(doc, content.schema as OpenApiSchema) ??\n ({} as OpenApiSchema),\n contentType,\n };\n }\n }\n }\n\n // Parse responses\n const responses: ParsedOperation['responses'] = {};\n for (const [status, response] of Object.entries(operation.responses ?? {})) {\n const resolved = isReference(response)\n ? resolveRef<OpenAPIV3.ResponseObject | OpenAPIV3_1.ResponseObject>(\n doc,\n response.$ref\n )\n : response;\n\n if (resolved) {\n const contentType = Object.keys(resolved.content ?? {})[0];\n const content = contentType ? resolved.content?.[contentType] : undefined;\n\n responses[status] = {\n description: resolved.description,\n schema: content?.schema\n ? resolveSchema(doc, content.schema as OpenApiSchema)\n : undefined,\n contentType,\n };\n }\n }\n\n // Check for x-contractspec extension\n const contractSpecMeta = (operation as Record<string, unknown>)?.[\n 'x-contractspec'\n ] as ParsedOperation['contractSpecMeta'];\n\n return {\n operationId: operation.operationId ?? generateOperationId(method, path),\n method,\n path,\n summary: operation.summary,\n description: operation.description,\n tags: operation.tags ?? [],\n pathParams: params.path,\n queryParams: params.query,\n headerParams: params.header,\n cookieParams: params.cookie,\n requestBody,\n responses,\n deprecated: operation.deprecated ?? false,\n security: operation.security as ParsedOperation['security'],\n contractSpecMeta,\n };\n}\n\n/**\n * Parse an OpenAPI document into a structured result.\n */\nexport function parseOpenApiDocument(\n doc: OpenApiDocument,\n _options: OpenApiParseOptions = {}\n): ParseResult {\n const version = detectVersion(doc);\n const warnings: string[] = [];\n const operations: ParsedOperation[] = [];\n\n // Parse operations from paths\n for (const [path, pathItem] of Object.entries(doc.paths ?? {})) {\n if (!pathItem) continue;\n\n // Get path-level parameters\n const pathParams = (pathItem as OpenAPIV3.PathItemObject).parameters;\n\n for (const method of HTTP_METHODS) {\n const operation = (pathItem as Record<string, unknown>)[method] as\n | OpenAPIV3.OperationObject\n | OpenAPIV3_1.OperationObject\n | undefined;\n\n if (operation) {\n try {\n operations.push(\n parseOperation(\n doc,\n method,\n path,\n operation,\n pathParams as OpenApiParameter[]\n )\n );\n } catch (error) {\n warnings.push(\n `Failed to parse ${method.toUpperCase()} ${path}: ${error}`\n );\n }\n }\n }\n }\n\n // Extract component schemas\n const schemas: Record<string, OpenApiSchema> = {};\n const components = doc.components;\n if (components?.schemas) {\n for (const [name, schema] of Object.entries(components.schemas)) {\n schemas[name] = schema as OpenApiSchema;\n }\n }\n\n // Parse servers\n const servers: OpenApiServer[] = (doc.servers ?? []).map((s) => ({\n url: s.url,\n description: s.description,\n variables: s.variables as OpenApiServer['variables'],\n }));\n\n return {\n document: doc,\n version,\n info: {\n title: doc.info.title,\n version: doc.info.version,\n description: doc.info.description,\n },\n operations,\n schemas,\n servers,\n warnings,\n };\n}\n\n/**\n * Parse OpenAPI from a file path or URL.\n * Note: This is an async function that requires I/O adapters.\n * For pure parsing, use parseOpenApiString or parseOpenApiDocument.\n */\nexport async function parseOpenApi(\n source: string,\n options: OpenApiParseOptions & {\n fetch?: typeof globalThis.fetch;\n readFile?: (path: string) => Promise<string>;\n } = {}\n): Promise<ParseResult> {\n const {\n fetch: fetchFn = globalThis.fetch,\n readFile,\n timeout = 30000,\n } = options;\n\n let content: string;\n let format: 'json' | 'yaml';\n\n if (source.startsWith('http://') || source.startsWith('https://')) {\n // Fetch from URL\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n try {\n const response = await fetchFn(source, { signal: controller.signal });\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n content = await response.text();\n } finally {\n clearTimeout(timeoutId);\n }\n\n // Detect format from URL or content\n if (source.endsWith('.yaml') || source.endsWith('.yml')) {\n format = 'yaml';\n } else if (source.endsWith('.json')) {\n format = 'json';\n } else {\n format = detectFormat(content);\n }\n } else {\n // Read from file\n if (!readFile) {\n throw new Error('readFile adapter required for file paths');\n }\n content = await readFile(source);\n\n // Detect format from extension or content\n if (source.endsWith('.yaml') || source.endsWith('.yml')) {\n format = 'yaml';\n } else if (source.endsWith('.json')) {\n format = 'json';\n } else {\n format = detectFormat(content);\n }\n }\n\n const doc = parseOpenApiString(content, format);\n return parseOpenApiDocument(doc, options);\n}\n"],"mappings":";;;;;;;AAoBA,MAAMA,eAA6B;CACjC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;;;;AAKD,SAAgB,mBACd,SACA,SAA0B,QACT;AACjB,KAAI,WAAW,OACb,QAAOC,MAAU,QAAQ;AAE3B,QAAO,KAAK,MAAM,QAAQ;;;;;AAM5B,SAAgB,aAAa,SAAkC;CAC7D,MAAM,UAAU,QAAQ,MAAM;AAC9B,KAAI,QAAQ,WAAW,IAAI,IAAI,QAAQ,WAAW,IAAI,CACpD,QAAO;AAET,QAAO;;;;;AAMT,SAAgB,cAAc,KAAsC;AAElE,KADgB,IAAI,QACR,WAAW,MAAM,CAC3B,QAAO;AAET,QAAO;;;;;AAMT,SAAS,YACP,KACgE;AAChE,QAAO,OAAO,QAAQ,YAAY,QAAQ,QAAQ,UAAU;;;;;AAM9D,SAAS,WAAc,KAAsB,KAA4B;AAEvE,KAAI,CAAC,IAAI,WAAW,KAAK,CACvB;CAGF,MAAM,OAAO,IAAI,MAAM,EAAE,CAAC,MAAM,IAAI;CACpC,IAAIC,UAAmB;AAEvB,MAAK,MAAM,QAAQ,MAAM;AACvB,MAAI,YAAY,QAAQ,YAAY,OAAW,QAAO;AACtD,MAAI,OAAO,YAAY,SAAU,QAAO;AACxC,YAAW,QAAoC;;AAGjD,QAAO;;;;;AAMT,SAAS,cACP,KACA,QAC2B;AAC3B,KAAI,CAAC,OAAQ,QAAO;AACpB,KAAI,YAAY,OAAO,CACrB,QAAO,WAA0B,KAAK,OAAO,KAAK,IAAI;AAExD,QAAO;;;;;AAMT,SAAS,gBACP,KACA,QAMA;CACA,MAAM,SAAS;EACb,MAAM,EAAE;EACR,OAAO,EAAE;EACT,QAAQ,EAAE;EACV,QAAQ,EAAE;EACX;AAED,KAAI,CAAC,OAAQ,QAAO;AAEpB,MAAK,MAAM,SAAS,QAAQ;EAC1B,IAAIC;AAEJ,MAAI,YAAY,MAAM,EAAE;GACtB,MAAM,MAAM,WAEV,KAAK,MAAM,KAAK;AAClB,OAAI,CAAC,IAAK;AACV,cAAW;QAEX,YAAW;EAGb,MAAMC,SAA0B;GAC9B,MAAM,SAAS;GACf,IAAI,SAAS;GACb,UAAU,SAAS,YAAY,SAAS,OAAO;GAC/C,aAAa,SAAS;GACtB,QAAQ,SAAS;GACjB,YAAY,SAAS,cAAc;GACpC;AAED,SAAO,SAAS,KAA4B,KAAK,OAAO;;AAG1D,QAAO;;;;;AAMT,SAAS,oBAAoB,QAAoB,MAAsB;AAerE,QAAO,SAbW,KACf,MAAM,IAAI,CACV,OAAO,QAAQ,CACf,KAAK,SAAS;AAEb,MAAI,KAAK,WAAW,IAAI,IAAI,KAAK,SAAS,IAAI,CAC5C,QACE,OAAO,KAAK,MAAM,GAAG,GAAG,CAAC,OAAO,EAAE,CAAC,aAAa,GAAG,KAAK,MAAM,GAAG,GAAG;AAGxE,SAAO,KAAK,OAAO,EAAE,CAAC,aAAa,GAAG,KAAK,MAAM,EAAE;GACnD,CAEsB,KAAK,GAAG;;;;;AAMpC,SAAS,eACP,KACA,QACA,MACA,WACA,YACiB;CAGjB,MAAM,SAAS,gBAAgB,KADb,CAAC,GAAI,cAAc,EAAE,EAAG,GAAI,UAAU,cAAc,EAAE,CAAE,CACN;CAGpE,IAAIC;AACJ,KAAI,UAAU,aAAa;EACzB,MAAM,OAAO,YAAY,UAAU,YAAY,GAC3C,WACE,KACA,UAAU,YAAY,KACvB,GACD,UAAU;AAEd,MAAI,MAAM;GACR,MAAM,cACJ,OAAO,KAAK,KAAK,WAAW,EAAE,CAAC,CAAC,MAAM;GACxC,MAAM,UAAU,KAAK,UAAU;AAC/B,OAAI,SAAS,OACX,eAAc;IACZ,UAAU,KAAK,YAAY;IAC3B,QACE,cAAc,KAAK,QAAQ,OAAwB,IAClD,EAAE;IACL;IACD;;;CAMP,MAAMC,YAA0C,EAAE;AAClD,MAAK,MAAM,CAAC,QAAQ,aAAa,OAAO,QAAQ,UAAU,aAAa,EAAE,CAAC,EAAE;EAC1E,MAAM,WAAW,YAAY,SAAS,GAClC,WACE,KACA,SAAS,KACV,GACD;AAEJ,MAAI,UAAU;GACZ,MAAM,cAAc,OAAO,KAAK,SAAS,WAAW,EAAE,CAAC,CAAC;GACxD,MAAM,UAAU,cAAc,SAAS,UAAU,eAAe;AAEhE,aAAU,UAAU;IAClB,aAAa,SAAS;IACtB,QAAQ,SAAS,SACb,cAAc,KAAK,QAAQ,OAAwB,GACnD;IACJ;IACD;;;CAKL,MAAM,mBAAoB,YACxB;AAGF,QAAO;EACL,aAAa,UAAU,eAAe,oBAAoB,QAAQ,KAAK;EACvE;EACA;EACA,SAAS,UAAU;EACnB,aAAa,UAAU;EACvB,MAAM,UAAU,QAAQ,EAAE;EAC1B,YAAY,OAAO;EACnB,aAAa,OAAO;EACpB,cAAc,OAAO;EACrB,cAAc,OAAO;EACrB;EACA;EACA,YAAY,UAAU,cAAc;EACpC,UAAU,UAAU;EACpB;EACD;;;;;AAMH,SAAgB,qBACd,KACA,WAAgC,EAAE,EACrB;CACb,MAAM,UAAU,cAAc,IAAI;CAClC,MAAMC,WAAqB,EAAE;CAC7B,MAAMC,aAAgC,EAAE;AAGxC,MAAK,MAAM,CAAC,MAAM,aAAa,OAAO,QAAQ,IAAI,SAAS,EAAE,CAAC,EAAE;AAC9D,MAAI,CAAC,SAAU;EAGf,MAAM,aAAc,SAAsC;AAE1D,OAAK,MAAM,UAAU,cAAc;GACjC,MAAM,YAAa,SAAqC;AAKxD,OAAI,UACF,KAAI;AACF,eAAW,KACT,eACE,KACA,QACA,MACA,WACA,WACD,CACF;YACM,OAAO;AACd,aAAS,KACP,mBAAmB,OAAO,aAAa,CAAC,GAAG,KAAK,IAAI,QACrD;;;;CAOT,MAAMC,UAAyC,EAAE;CACjD,MAAM,aAAa,IAAI;AACvB,KAAI,YAAY,QACd,MAAK,MAAM,CAAC,MAAM,WAAW,OAAO,QAAQ,WAAW,QAAQ,CAC7D,SAAQ,QAAQ;CAKpB,MAAMC,WAA4B,IAAI,WAAW,EAAE,EAAE,KAAK,OAAO;EAC/D,KAAK,EAAE;EACP,aAAa,EAAE;EACf,WAAW,EAAE;EACd,EAAE;AAEH,QAAO;EACL,UAAU;EACV;EACA,MAAM;GACJ,OAAO,IAAI,KAAK;GAChB,SAAS,IAAI,KAAK;GAClB,aAAa,IAAI,KAAK;GACvB;EACD;EACA;EACA;EACA;EACD;;;;;;;AAQH,eAAsB,aACpB,QACA,UAGI,EAAE,EACgB;CACtB,MAAM,EACJ,OAAO,UAAU,WAAW,OAC5B,UACA,UAAU,QACR;CAEJ,IAAIC;CACJ,IAAIC;AAEJ,KAAI,OAAO,WAAW,UAAU,IAAI,OAAO,WAAW,WAAW,EAAE;EAEjE,MAAM,aAAa,IAAI,iBAAiB;EACxC,MAAM,YAAY,iBAAiB,WAAW,OAAO,EAAE,QAAQ;AAE/D,MAAI;GACF,MAAM,WAAW,MAAM,QAAQ,QAAQ,EAAE,QAAQ,WAAW,QAAQ,CAAC;AACrE,OAAI,CAAC,SAAS,GACZ,OAAM,IAAI,MAAM,QAAQ,SAAS,OAAO,IAAI,SAAS,aAAa;AAEpE,aAAU,MAAM,SAAS,MAAM;YACvB;AACR,gBAAa,UAAU;;AAIzB,MAAI,OAAO,SAAS,QAAQ,IAAI,OAAO,SAAS,OAAO,CACrD,UAAS;WACA,OAAO,SAAS,QAAQ,CACjC,UAAS;MAET,UAAS,aAAa,QAAQ;QAE3B;AAEL,MAAI,CAAC,SACH,OAAM,IAAI,MAAM,2CAA2C;AAE7D,YAAU,MAAM,SAAS,OAAO;AAGhC,MAAI,OAAO,SAAS,QAAQ,IAAI,OAAO,SAAS,OAAO,CACrD,UAAS;WACA,OAAO,SAAS,QAAQ,CACjC,UAAS;MAET,UAAS,aAAa,QAAQ;;AAKlC,QAAO,qBADK,mBAAmB,SAAS,OAAO,EACd,QAAQ"}