@demokit-ai/schema 0.0.1
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.cjs +461 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +459 -0
- package/dist/index.d.ts +459 -0
- package/dist/index.js +444 -0
- package/dist/index.js.map +1 -0
- package/package.json +54 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,461 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var SwaggerParser = require('@apidevtools/swagger-parser');
|
|
4
|
+
|
|
5
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
6
|
+
|
|
7
|
+
var SwaggerParser__default = /*#__PURE__*/_interopDefault(SwaggerParser);
|
|
8
|
+
|
|
9
|
+
// src/parser.ts
|
|
10
|
+
|
|
11
|
+
// src/types.ts
|
|
12
|
+
function isSchemaRef(value) {
|
|
13
|
+
return typeof value === "object" && value !== null && "$ref" in value && typeof value.$ref === "string";
|
|
14
|
+
}
|
|
15
|
+
function extractRefName(ref) {
|
|
16
|
+
const parts = ref.split("/");
|
|
17
|
+
return parts[parts.length - 1] ?? ref;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// src/relationships.ts
|
|
21
|
+
var ID_FIELD_PATTERNS = [
|
|
22
|
+
// Standard patterns: userId -> User, customerId -> Customer
|
|
23
|
+
{ pattern: /^(.+)Id$/, targetField: "id" },
|
|
24
|
+
{ pattern: /^(.+)_id$/, targetField: "id" },
|
|
25
|
+
{ pattern: /^(.+)ID$/, targetField: "id" },
|
|
26
|
+
// UUID patterns: userUuid -> User, customerUUID -> Customer
|
|
27
|
+
{ pattern: /^(.+)Uuid$/, targetField: "uuid" },
|
|
28
|
+
{ pattern: /^(.+)UUID$/, targetField: "uuid" },
|
|
29
|
+
{ pattern: /^(.+)_uuid$/, targetField: "uuid" }
|
|
30
|
+
];
|
|
31
|
+
function prefixToModelName(prefix) {
|
|
32
|
+
return prefix.charAt(0).toUpperCase() + prefix.slice(1);
|
|
33
|
+
}
|
|
34
|
+
function detectRelationshipFromNaming(property, availableModels) {
|
|
35
|
+
const fieldName = property.name;
|
|
36
|
+
for (const { pattern, targetField } of ID_FIELD_PATTERNS) {
|
|
37
|
+
const match = fieldName.match(pattern);
|
|
38
|
+
if (match && match[1]) {
|
|
39
|
+
const prefix = match[1];
|
|
40
|
+
const modelName = prefixToModelName(prefix);
|
|
41
|
+
if (availableModels.has(modelName)) {
|
|
42
|
+
return { model: modelName, field: targetField };
|
|
43
|
+
}
|
|
44
|
+
const pluralModel = modelName + "s";
|
|
45
|
+
if (availableModels.has(pluralModel)) {
|
|
46
|
+
return { model: pluralModel, field: targetField };
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
function detectRelationshipFromExtension(property) {
|
|
53
|
+
const extension = property["x-demokit-relationship"];
|
|
54
|
+
if (extension && typeof extension === "object") {
|
|
55
|
+
return {
|
|
56
|
+
model: extension.model,
|
|
57
|
+
field: extension.field || "id"
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
function detectRelationshipFromRef(property, availableModels) {
|
|
63
|
+
if (property.$ref) {
|
|
64
|
+
const modelName = extractRefName(property.$ref);
|
|
65
|
+
if (availableModels.has(modelName)) {
|
|
66
|
+
return { model: modelName, field: "id" };
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
if (property.items && isSchemaRef(property.items)) {
|
|
70
|
+
const modelName = extractRefName(property.items.$ref);
|
|
71
|
+
if (availableModels.has(modelName)) {
|
|
72
|
+
return { model: modelName, field: "id" };
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
function determineRelationshipType(property, _fromModel) {
|
|
78
|
+
if (property.type === "array") {
|
|
79
|
+
return "one-to-many";
|
|
80
|
+
}
|
|
81
|
+
return "many-to-one";
|
|
82
|
+
}
|
|
83
|
+
function detectRelationships(models) {
|
|
84
|
+
const relationships = [];
|
|
85
|
+
const availableModels = new Set(Object.keys(models));
|
|
86
|
+
for (const [modelName, model] of Object.entries(models)) {
|
|
87
|
+
if (!model.properties) continue;
|
|
88
|
+
for (const [propName, property] of Object.entries(model.properties)) {
|
|
89
|
+
let target = null;
|
|
90
|
+
let detectionMethod = "inferred";
|
|
91
|
+
target = detectRelationshipFromExtension(property);
|
|
92
|
+
if (target) {
|
|
93
|
+
detectionMethod = "x-demokit-extension";
|
|
94
|
+
}
|
|
95
|
+
if (!target) {
|
|
96
|
+
target = detectRelationshipFromRef(property, availableModels);
|
|
97
|
+
if (target) {
|
|
98
|
+
detectionMethod = "explicit-ref";
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
if (!target) {
|
|
102
|
+
target = detectRelationshipFromNaming(property, availableModels);
|
|
103
|
+
if (target) {
|
|
104
|
+
detectionMethod = "naming-convention";
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
if (target) {
|
|
108
|
+
if (target.model === modelName && target.field === "id" && propName === "id") {
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
property.relationshipTo = target;
|
|
112
|
+
relationships.push({
|
|
113
|
+
from: { model: modelName, field: propName },
|
|
114
|
+
to: target,
|
|
115
|
+
type: determineRelationshipType(property),
|
|
116
|
+
required: property.required ?? false,
|
|
117
|
+
detectedBy: detectionMethod
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return relationships;
|
|
123
|
+
}
|
|
124
|
+
function getRelationshipsForModel(modelName, relationships) {
|
|
125
|
+
return {
|
|
126
|
+
outgoing: relationships.filter((r) => r.from.model === modelName),
|
|
127
|
+
incoming: relationships.filter((r) => r.to.model === modelName)
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
function isModelReferenced(modelName, relationships) {
|
|
131
|
+
return relationships.some((r) => r.to.model === modelName);
|
|
132
|
+
}
|
|
133
|
+
function getModelDependencyOrder(models, relationships) {
|
|
134
|
+
const modelNames = Object.keys(models);
|
|
135
|
+
const visited = /* @__PURE__ */ new Set();
|
|
136
|
+
const order = [];
|
|
137
|
+
function visit(name, path = /* @__PURE__ */ new Set()) {
|
|
138
|
+
if (visited.has(name)) return;
|
|
139
|
+
if (path.has(name)) {
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
path.add(name);
|
|
143
|
+
const deps = relationships.filter((r) => r.from.model === name).map((r) => r.to.model).filter((dep) => modelNames.includes(dep));
|
|
144
|
+
for (const dep of deps) {
|
|
145
|
+
visit(dep, new Set(path));
|
|
146
|
+
}
|
|
147
|
+
if (!visited.has(name)) {
|
|
148
|
+
visited.add(name);
|
|
149
|
+
order.push(name);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
for (const name of modelNames) {
|
|
153
|
+
visit(name);
|
|
154
|
+
}
|
|
155
|
+
return order;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// src/parser.ts
|
|
159
|
+
async function parseOpenAPIFromPath(pathOrUrl, options = {}) {
|
|
160
|
+
const { dereference = true } = options;
|
|
161
|
+
let api;
|
|
162
|
+
if (dereference) {
|
|
163
|
+
api = await SwaggerParser__default.default.dereference(pathOrUrl);
|
|
164
|
+
} else {
|
|
165
|
+
api = await SwaggerParser__default.default.parse(pathOrUrl);
|
|
166
|
+
}
|
|
167
|
+
return parseOpenAPIDocument(api, options);
|
|
168
|
+
}
|
|
169
|
+
async function parseOpenAPIFromString(spec, options = {}) {
|
|
170
|
+
const { dereference = true } = options;
|
|
171
|
+
let parsed;
|
|
172
|
+
try {
|
|
173
|
+
parsed = JSON.parse(spec);
|
|
174
|
+
} catch {
|
|
175
|
+
const api2 = await SwaggerParser__default.default.parse(spec);
|
|
176
|
+
if (dereference) {
|
|
177
|
+
return parseOpenAPIDocument(
|
|
178
|
+
await SwaggerParser__default.default.dereference(api2),
|
|
179
|
+
options
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
return parseOpenAPIDocument(api2, options);
|
|
183
|
+
}
|
|
184
|
+
let api;
|
|
185
|
+
if (dereference) {
|
|
186
|
+
api = await SwaggerParser__default.default.dereference(parsed);
|
|
187
|
+
} else {
|
|
188
|
+
api = await SwaggerParser__default.default.validate(parsed);
|
|
189
|
+
}
|
|
190
|
+
return parseOpenAPIDocument(api, options);
|
|
191
|
+
}
|
|
192
|
+
async function parseOpenAPIFromObject(spec, options = {}) {
|
|
193
|
+
const { dereference = true } = options;
|
|
194
|
+
let api;
|
|
195
|
+
if (dereference) {
|
|
196
|
+
api = await SwaggerParser__default.default.dereference(spec);
|
|
197
|
+
} else {
|
|
198
|
+
api = await SwaggerParser__default.default.validate(spec);
|
|
199
|
+
}
|
|
200
|
+
return parseOpenAPIDocument(api, options);
|
|
201
|
+
}
|
|
202
|
+
function parseOpenAPIDocument(doc, options = {}) {
|
|
203
|
+
const { detectRelationships: shouldDetect = true } = options;
|
|
204
|
+
const info = parseInfo(doc);
|
|
205
|
+
const models = parseModels(doc);
|
|
206
|
+
const endpoints = parseEndpoints(doc);
|
|
207
|
+
const relationships = shouldDetect ? detectRelationships(models) : [];
|
|
208
|
+
return {
|
|
209
|
+
info,
|
|
210
|
+
endpoints,
|
|
211
|
+
models,
|
|
212
|
+
relationships
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
function parseInfo(doc) {
|
|
216
|
+
const info = {
|
|
217
|
+
title: doc.info?.title ?? "Untitled API",
|
|
218
|
+
version: doc.info?.version ?? "1.0.0",
|
|
219
|
+
description: doc.info?.description
|
|
220
|
+
};
|
|
221
|
+
if (doc.servers && doc.servers.length > 0 && doc.servers[0]) {
|
|
222
|
+
info.baseUrl = doc.servers[0].url;
|
|
223
|
+
}
|
|
224
|
+
return info;
|
|
225
|
+
}
|
|
226
|
+
function parseModels(doc) {
|
|
227
|
+
const models = {};
|
|
228
|
+
const schemas = doc.components?.schemas;
|
|
229
|
+
if (!schemas) {
|
|
230
|
+
return models;
|
|
231
|
+
}
|
|
232
|
+
for (const [name, schema] of Object.entries(schemas)) {
|
|
233
|
+
if ("$ref" in schema) {
|
|
234
|
+
continue;
|
|
235
|
+
}
|
|
236
|
+
models[name] = parseSchemaToModel(name, schema);
|
|
237
|
+
}
|
|
238
|
+
return models;
|
|
239
|
+
}
|
|
240
|
+
function parseSchemaToModel(name, schema) {
|
|
241
|
+
const model = {
|
|
242
|
+
name,
|
|
243
|
+
type: schemaTypeToModelType(schema.type),
|
|
244
|
+
description: schema.description
|
|
245
|
+
};
|
|
246
|
+
if (schema.type === "object" || schema.properties) {
|
|
247
|
+
model.type = "object";
|
|
248
|
+
model.properties = {};
|
|
249
|
+
model.required = schema.required || [];
|
|
250
|
+
if (schema.properties) {
|
|
251
|
+
for (const [propName, propSchema] of Object.entries(schema.properties)) {
|
|
252
|
+
model.properties[propName] = parseProperty(
|
|
253
|
+
propName,
|
|
254
|
+
propSchema,
|
|
255
|
+
model.required.includes(propName)
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
if (schema.additionalProperties !== void 0) {
|
|
260
|
+
if (typeof schema.additionalProperties === "boolean") {
|
|
261
|
+
model.additionalProperties = schema.additionalProperties;
|
|
262
|
+
} else if ("$ref" in schema.additionalProperties) {
|
|
263
|
+
model.additionalProperties = {
|
|
264
|
+
$ref: schema.additionalProperties.$ref
|
|
265
|
+
};
|
|
266
|
+
} else {
|
|
267
|
+
model.additionalProperties = parseSchemaToModel(
|
|
268
|
+
`${name}AdditionalProps`,
|
|
269
|
+
schema.additionalProperties
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
if (schema.type === "array" && schema.items) {
|
|
275
|
+
model.type = "array";
|
|
276
|
+
if ("$ref" in schema.items) {
|
|
277
|
+
model.items = { $ref: schema.items.$ref };
|
|
278
|
+
} else {
|
|
279
|
+
model.items = parseSchemaToModel(`${name}Item`, schema.items);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
if (schema.enum) {
|
|
283
|
+
model.enum = schema.enum;
|
|
284
|
+
}
|
|
285
|
+
if (schema.example !== void 0) {
|
|
286
|
+
model.example = schema.example;
|
|
287
|
+
}
|
|
288
|
+
return model;
|
|
289
|
+
}
|
|
290
|
+
function parseProperty(name, schema, required) {
|
|
291
|
+
if ("$ref" in schema) {
|
|
292
|
+
return {
|
|
293
|
+
name,
|
|
294
|
+
type: "object",
|
|
295
|
+
required,
|
|
296
|
+
$ref: schema.$ref
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
const prop = {
|
|
300
|
+
name,
|
|
301
|
+
type: schemaTypeToPropertyType(schema.type),
|
|
302
|
+
required,
|
|
303
|
+
nullable: schema.nullable,
|
|
304
|
+
description: schema.description,
|
|
305
|
+
format: schema.format,
|
|
306
|
+
enum: schema.enum,
|
|
307
|
+
example: schema.example,
|
|
308
|
+
default: schema.default,
|
|
309
|
+
minimum: schema.minimum,
|
|
310
|
+
maximum: schema.maximum,
|
|
311
|
+
minLength: schema.minLength,
|
|
312
|
+
maxLength: schema.maxLength,
|
|
313
|
+
pattern: schema.pattern
|
|
314
|
+
};
|
|
315
|
+
if (schema.type === "array" && schema.items) {
|
|
316
|
+
prop.type = "array";
|
|
317
|
+
if ("$ref" in schema.items) {
|
|
318
|
+
prop.items = { $ref: schema.items.$ref };
|
|
319
|
+
} else {
|
|
320
|
+
prop.items = parseSchemaToModel(`${name}Item`, schema.items);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
const xRelationship = schema["x-demokit-relationship"];
|
|
324
|
+
if (xRelationship && typeof xRelationship === "object") {
|
|
325
|
+
prop["x-demokit-relationship"] = xRelationship;
|
|
326
|
+
}
|
|
327
|
+
return prop;
|
|
328
|
+
}
|
|
329
|
+
function parseEndpoints(doc) {
|
|
330
|
+
const endpoints = [];
|
|
331
|
+
if (!doc.paths) {
|
|
332
|
+
return endpoints;
|
|
333
|
+
}
|
|
334
|
+
const methods = ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"];
|
|
335
|
+
for (const [path, pathItem] of Object.entries(doc.paths)) {
|
|
336
|
+
if (!pathItem) continue;
|
|
337
|
+
for (const method of methods) {
|
|
338
|
+
const operation = pathItem[method.toLowerCase()];
|
|
339
|
+
if (!operation) continue;
|
|
340
|
+
endpoints.push(parseEndpoint(method, path, operation, pathItem));
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
return endpoints;
|
|
344
|
+
}
|
|
345
|
+
function parseEndpoint(method, path, operation, pathItem) {
|
|
346
|
+
const allParams = [...pathItem.parameters || [], ...operation.parameters || []];
|
|
347
|
+
const pathParams = [];
|
|
348
|
+
const queryParams = [];
|
|
349
|
+
for (const param of allParams) {
|
|
350
|
+
if ("$ref" in param) continue;
|
|
351
|
+
const paramObj = param;
|
|
352
|
+
const parsed = parseParameter(paramObj);
|
|
353
|
+
if (parsed.in === "path") {
|
|
354
|
+
pathParams.push(parsed);
|
|
355
|
+
} else if (parsed.in === "query") {
|
|
356
|
+
queryParams.push(parsed);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
let requestBody;
|
|
360
|
+
if (operation.requestBody && !("$ref" in operation.requestBody)) {
|
|
361
|
+
requestBody = parseRequestBody(operation.requestBody);
|
|
362
|
+
}
|
|
363
|
+
const responses = {};
|
|
364
|
+
if (operation.responses) {
|
|
365
|
+
for (const [statusCode, response] of Object.entries(operation.responses)) {
|
|
366
|
+
if ("$ref" in response) continue;
|
|
367
|
+
responses[statusCode] = parseResponse(statusCode, response);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
return {
|
|
371
|
+
method,
|
|
372
|
+
path,
|
|
373
|
+
operationId: operation.operationId,
|
|
374
|
+
summary: operation.summary,
|
|
375
|
+
description: operation.description,
|
|
376
|
+
pathParams,
|
|
377
|
+
queryParams,
|
|
378
|
+
requestBody,
|
|
379
|
+
responses,
|
|
380
|
+
tags: operation.tags || []
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
function parseParameter(param) {
|
|
384
|
+
const schema = param.schema;
|
|
385
|
+
return {
|
|
386
|
+
name: param.name,
|
|
387
|
+
in: param.in,
|
|
388
|
+
required: param.required ?? false,
|
|
389
|
+
type: schema ? schemaTypeToPropertyType(schema.type) : "string",
|
|
390
|
+
format: schema?.format,
|
|
391
|
+
description: param.description,
|
|
392
|
+
example: param.example ?? schema?.example
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
function parseRequestBody(body) {
|
|
396
|
+
const contentType = Object.keys(body.content || {})[0] || "application/json";
|
|
397
|
+
const mediaType = body.content?.[contentType];
|
|
398
|
+
let schema = { name: "Unknown", type: "object" };
|
|
399
|
+
if (mediaType?.schema) {
|
|
400
|
+
if ("$ref" in mediaType.schema) {
|
|
401
|
+
schema = { $ref: mediaType.schema.$ref };
|
|
402
|
+
} else {
|
|
403
|
+
schema = parseSchemaToModel("RequestBody", mediaType.schema);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
return {
|
|
407
|
+
contentType,
|
|
408
|
+
schema,
|
|
409
|
+
required: body.required ?? false,
|
|
410
|
+
description: body.description
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
function parseResponse(statusCode, response) {
|
|
414
|
+
const responseDef = {
|
|
415
|
+
statusCode,
|
|
416
|
+
description: response.description
|
|
417
|
+
};
|
|
418
|
+
if (response.content) {
|
|
419
|
+
responseDef.content = {};
|
|
420
|
+
for (const [contentType, mediaType] of Object.entries(response.content)) {
|
|
421
|
+
if (mediaType.schema) {
|
|
422
|
+
if ("$ref" in mediaType.schema) {
|
|
423
|
+
responseDef.content[contentType] = {
|
|
424
|
+
$ref: mediaType.schema.$ref
|
|
425
|
+
};
|
|
426
|
+
} else {
|
|
427
|
+
responseDef.content[contentType] = parseSchemaToModel(
|
|
428
|
+
`Response${statusCode}`,
|
|
429
|
+
mediaType.schema
|
|
430
|
+
);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
return responseDef;
|
|
436
|
+
}
|
|
437
|
+
function schemaTypeToModelType(type) {
|
|
438
|
+
if (!type) return "object";
|
|
439
|
+
if (Array.isArray(type)) return type[0];
|
|
440
|
+
return type;
|
|
441
|
+
}
|
|
442
|
+
function schemaTypeToPropertyType(type) {
|
|
443
|
+
if (!type) return "object";
|
|
444
|
+
if (Array.isArray(type)) return type[0];
|
|
445
|
+
return type;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
exports.detectRelationshipFromExtension = detectRelationshipFromExtension;
|
|
449
|
+
exports.detectRelationshipFromNaming = detectRelationshipFromNaming;
|
|
450
|
+
exports.detectRelationshipFromRef = detectRelationshipFromRef;
|
|
451
|
+
exports.detectRelationships = detectRelationships;
|
|
452
|
+
exports.extractRefName = extractRefName;
|
|
453
|
+
exports.getModelDependencyOrder = getModelDependencyOrder;
|
|
454
|
+
exports.getRelationshipsForModel = getRelationshipsForModel;
|
|
455
|
+
exports.isModelReferenced = isModelReferenced;
|
|
456
|
+
exports.isSchemaRef = isSchemaRef;
|
|
457
|
+
exports.parseOpenAPIFromObject = parseOpenAPIFromObject;
|
|
458
|
+
exports.parseOpenAPIFromPath = parseOpenAPIFromPath;
|
|
459
|
+
exports.parseOpenAPIFromString = parseOpenAPIFromString;
|
|
460
|
+
//# sourceMappingURL=index.cjs.map
|
|
461
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/types.ts","../src/relationships.ts","../src/parser.ts"],"names":["SwaggerParser","api"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Core types for DemoKit schema representation\n *\n * These types represent a parsed API schema with relationship detection,\n * independent of the source format (OpenAPI, GraphQL, etc.)\n */\n\n/**\n * The main schema representation for DemoKit\n * Contains all information needed for fixture generation\n */\nexport interface DemokitSchema {\n /**\n * Metadata about the API\n */\n info: SchemaInfo\n\n /**\n * All API endpoints discovered from the spec\n */\n endpoints: Endpoint[]\n\n /**\n * All data models (schemas) from the spec\n */\n models: Record<string, DataModel>\n\n /**\n * Detected relationships between models\n */\n relationships: Relationship[]\n}\n\n/**\n * API metadata\n */\nexport interface SchemaInfo {\n /**\n * API title from the spec\n */\n title: string\n\n /**\n * API version\n */\n version: string\n\n /**\n * Optional description\n */\n description?: string\n\n /**\n * Base URL for the API (if specified)\n */\n baseUrl?: string\n}\n\n/**\n * An API endpoint\n */\nexport interface Endpoint {\n /**\n * HTTP method (GET, POST, PUT, PATCH, DELETE)\n */\n method: HttpMethod\n\n /**\n * URL path with parameter placeholders\n * @example \"/users/{id}\" or \"/orders/{orderId}/items\"\n */\n path: string\n\n /**\n * Operation ID from OpenAPI (if available)\n */\n operationId?: string\n\n /**\n * Human-readable summary\n */\n summary?: string\n\n /**\n * Detailed description\n */\n description?: string\n\n /**\n * Path parameters\n */\n pathParams: ParameterDef[]\n\n /**\n * Query parameters\n */\n queryParams: ParameterDef[]\n\n /**\n * Request body schema (for POST/PUT/PATCH)\n */\n requestBody?: RequestBody\n\n /**\n * Response schemas by status code\n */\n responses: Record<string, ResponseDef>\n\n /**\n * Tags for grouping endpoints\n */\n tags: string[]\n}\n\n/**\n * HTTP methods supported\n */\nexport type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS'\n\n/**\n * A request or response body definition\n */\nexport interface RequestBody {\n /**\n * Content type (usually application/json)\n */\n contentType: string\n\n /**\n * Reference to the model name, or inline schema\n */\n schema: SchemaRef | DataModel\n\n /**\n * Whether the body is required\n */\n required: boolean\n\n /**\n * Description of the body\n */\n description?: string\n}\n\n/**\n * A response definition\n */\nexport interface ResponseDef {\n /**\n * HTTP status code\n */\n statusCode: string\n\n /**\n * Description of the response\n */\n description?: string\n\n /**\n * Content type to schema mapping\n */\n content?: Record<string, SchemaRef | DataModel>\n}\n\n/**\n * A parameter definition (path or query)\n */\nexport interface ParameterDef {\n /**\n * Parameter name\n */\n name: string\n\n /**\n * Where the parameter is located\n */\n in: 'path' | 'query' | 'header' | 'cookie'\n\n /**\n * Whether the parameter is required\n */\n required: boolean\n\n /**\n * Parameter type\n */\n type: PropertyType\n\n /**\n * Optional format hint\n */\n format?: string\n\n /**\n * Description\n */\n description?: string\n\n /**\n * Example value\n */\n example?: unknown\n}\n\n/**\n * Reference to another schema/model\n */\nexport interface SchemaRef {\n /**\n * The referenced model name\n */\n $ref: string\n}\n\n/**\n * A data model representing an object schema\n */\nexport interface DataModel {\n /**\n * Model name (from the schema component name)\n */\n name: string\n\n /**\n * The type of this model\n */\n type: ModelType\n\n /**\n * Description from the spec\n */\n description?: string\n\n /**\n * Properties for object types\n */\n properties?: Record<string, PropertyDef>\n\n /**\n * Required property names\n */\n required?: string[]\n\n /**\n * For array types, the item schema\n */\n items?: SchemaRef | DataModel\n\n /**\n * Enum values for enum types\n */\n enum?: unknown[]\n\n /**\n * Example value from the spec\n */\n example?: unknown\n\n /**\n * Additional properties schema (for dictionaries)\n */\n additionalProperties?: boolean | SchemaRef | DataModel\n}\n\n/**\n * Model types\n */\nexport type ModelType = 'object' | 'array' | 'string' | 'number' | 'integer' | 'boolean' | 'null'\n\n/**\n * A property definition within a model\n */\nexport interface PropertyDef {\n /**\n * Property name\n */\n name: string\n\n /**\n * Property type\n */\n type: PropertyType\n\n /**\n * Format hint (uuid, email, date-time, etc.)\n */\n format?: string\n\n /**\n * Description\n */\n description?: string\n\n /**\n * Whether this property is required\n */\n required?: boolean\n\n /**\n * Whether this property is nullable\n */\n nullable?: boolean\n\n /**\n * Enum values for string enums\n */\n enum?: unknown[]\n\n /**\n * For array types, the item schema\n */\n items?: SchemaRef | DataModel\n\n /**\n * Reference to another model (for $ref properties)\n */\n $ref?: string\n\n /**\n * Example value\n */\n example?: unknown\n\n /**\n * Default value\n */\n default?: unknown\n\n /**\n * Minimum value (for numbers)\n */\n minimum?: number\n\n /**\n * Maximum value (for numbers)\n */\n maximum?: number\n\n /**\n * Min length (for strings)\n */\n minLength?: number\n\n /**\n * Max length (for strings)\n */\n maxLength?: number\n\n /**\n * Pattern (regex for strings)\n */\n pattern?: string\n\n /**\n * Detected relationship to another model\n * Set by relationship detection, not directly from spec\n */\n relationshipTo?: RelationshipTarget\n\n /**\n * Custom extension for explicit relationship hints\n * @example \"x-demokit-relationship\": { \"model\": \"User\", \"field\": \"id\" }\n */\n 'x-demokit-relationship'?: RelationshipTarget\n}\n\n/**\n * Property types\n */\nexport type PropertyType =\n | 'string'\n | 'number'\n | 'integer'\n | 'boolean'\n | 'array'\n | 'object'\n | 'null'\n\n/**\n * A detected relationship between two models\n */\nexport interface Relationship {\n /**\n * The source side of the relationship\n */\n from: RelationshipSide\n\n /**\n * The target side of the relationship\n */\n to: RelationshipSide\n\n /**\n * Type of relationship\n */\n type: RelationshipType\n\n /**\n * Whether the relationship is required (not nullable)\n */\n required: boolean\n\n /**\n * How this relationship was detected\n */\n detectedBy: RelationshipDetectionMethod\n}\n\n/**\n * One side of a relationship\n */\nexport interface RelationshipSide {\n /**\n * Model name\n */\n model: string\n\n /**\n * Field name\n */\n field: string\n}\n\n/**\n * Target for a relationship from a property\n */\nexport interface RelationshipTarget {\n /**\n * Target model name\n */\n model: string\n\n /**\n * Target field (usually \"id\")\n */\n field: string\n}\n\n/**\n * Types of relationships between models\n */\nexport type RelationshipType =\n | 'one-to-one'\n | 'one-to-many'\n | 'many-to-one'\n | 'many-to-many'\n\n/**\n * How a relationship was detected\n */\nexport type RelationshipDetectionMethod =\n | 'explicit-ref' // OpenAPI $ref\n | 'naming-convention' // userId -> User.id pattern\n | 'x-demokit-extension' // x-demokit-relationship extension\n | 'inferred' // AI or heuristic inference\n\n/**\n * Helper type to check if something is a schema reference\n */\nexport function isSchemaRef(value: unknown): value is SchemaRef {\n return (\n typeof value === 'object' &&\n value !== null &&\n '$ref' in value &&\n typeof (value as SchemaRef).$ref === 'string'\n )\n}\n\n/**\n * Extract the model name from a $ref string\n * @example \"#/components/schemas/User\" -> \"User\"\n */\nexport function extractRefName(ref: string): string {\n const parts = ref.split('/')\n return parts[parts.length - 1] ?? ref\n}\n","/**\n * Relationship detection for DemoKit schemas\n *\n * Detects relationships between models using:\n * 1. Explicit $ref references in OpenAPI\n * 2. Naming conventions (userId -> User.id)\n * 3. x-demokit-relationship extension\n */\n\nimport type {\n DataModel,\n PropertyDef,\n Relationship,\n RelationshipTarget,\n RelationshipDetectionMethod,\n RelationshipType,\n} from './types'\nimport { isSchemaRef, extractRefName } from './types'\n\n/**\n * Common ID field naming patterns\n * Maps suffix patterns to implied relationships\n */\nconst ID_FIELD_PATTERNS = [\n // Standard patterns: userId -> User, customerId -> Customer\n { pattern: /^(.+)Id$/, targetField: 'id' },\n { pattern: /^(.+)_id$/, targetField: 'id' },\n { pattern: /^(.+)ID$/, targetField: 'id' },\n\n // UUID patterns: userUuid -> User, customerUUID -> Customer\n { pattern: /^(.+)Uuid$/, targetField: 'uuid' },\n { pattern: /^(.+)UUID$/, targetField: 'uuid' },\n { pattern: /^(.+)_uuid$/, targetField: 'uuid' },\n]\n\n/**\n * Convert a field name prefix to a model name\n * @example \"user\" -> \"User\", \"orderItem\" -> \"OrderItem\"\n */\nfunction prefixToModelName(prefix: string): string {\n return prefix.charAt(0).toUpperCase() + prefix.slice(1)\n}\n\n/**\n * Check if a property looks like a foreign key based on naming conventions\n */\nexport function detectRelationshipFromNaming(\n property: PropertyDef,\n availableModels: Set<string>\n): RelationshipTarget | null {\n const fieldName = property.name\n\n for (const { pattern, targetField } of ID_FIELD_PATTERNS) {\n const match = fieldName.match(pattern)\n if (match && match[1]) {\n const prefix = match[1]\n const modelName = prefixToModelName(prefix)\n\n // Check if this model exists in the schema\n if (availableModels.has(modelName)) {\n return { model: modelName, field: targetField }\n }\n\n // Also check for plural forms\n const pluralModel = modelName + 's'\n if (availableModels.has(pluralModel)) {\n return { model: pluralModel, field: targetField }\n }\n }\n }\n\n return null\n}\n\n/**\n * Check if a property has an explicit x-demokit-relationship extension\n */\nexport function detectRelationshipFromExtension(\n property: PropertyDef\n): RelationshipTarget | null {\n const extension = property['x-demokit-relationship']\n if (extension && typeof extension === 'object') {\n return {\n model: extension.model,\n field: extension.field || 'id',\n }\n }\n return null\n}\n\n/**\n * Check if a property is a $ref to another model\n */\nexport function detectRelationshipFromRef(\n property: PropertyDef,\n availableModels: Set<string>\n): RelationshipTarget | null {\n if (property.$ref) {\n const modelName = extractRefName(property.$ref)\n if (availableModels.has(modelName)) {\n return { model: modelName, field: 'id' }\n }\n }\n\n // Check if items is a $ref (for arrays of references)\n if (property.items && isSchemaRef(property.items)) {\n const modelName = extractRefName(property.items.$ref)\n if (availableModels.has(modelName)) {\n return { model: modelName, field: 'id' }\n }\n }\n\n return null\n}\n\n/**\n * Detect the relationship type based on property and target\n */\nfunction determineRelationshipType(\n property: PropertyDef,\n _fromModel: DataModel\n): RelationshipType {\n // If the property is an array, it's one-to-many or many-to-many\n if (property.type === 'array') {\n // For simplicity, treat array references as one-to-many\n // (the \"one\" side has the array of references)\n return 'one-to-many'\n }\n\n // Single reference is many-to-one (many records can reference one target)\n return 'many-to-one'\n}\n\n/**\n * Detect all relationships in a set of models\n */\nexport function detectRelationships(\n models: Record<string, DataModel>\n): Relationship[] {\n const relationships: Relationship[] = []\n const availableModels = new Set(Object.keys(models))\n\n for (const [modelName, model] of Object.entries(models)) {\n if (!model.properties) continue\n\n for (const [propName, property] of Object.entries(model.properties)) {\n let target: RelationshipTarget | null = null\n let detectionMethod: RelationshipDetectionMethod = 'inferred'\n\n // Priority 1: Explicit x-demokit-relationship extension\n target = detectRelationshipFromExtension(property)\n if (target) {\n detectionMethod = 'x-demokit-extension'\n }\n\n // Priority 2: $ref references\n if (!target) {\n target = detectRelationshipFromRef(property, availableModels)\n if (target) {\n detectionMethod = 'explicit-ref'\n }\n }\n\n // Priority 3: Naming conventions\n if (!target) {\n target = detectRelationshipFromNaming(property, availableModels)\n if (target) {\n detectionMethod = 'naming-convention'\n }\n }\n\n if (target) {\n // Don't create self-referential relationships for simple IDs\n if (target.model === modelName && target.field === 'id' && propName === 'id') {\n continue\n }\n\n // Add the detected relationship info to the property\n property.relationshipTo = target\n\n relationships.push({\n from: { model: modelName, field: propName },\n to: target,\n type: determineRelationshipType(property, model),\n required: property.required ?? false,\n detectedBy: detectionMethod,\n })\n }\n }\n }\n\n return relationships\n}\n\n/**\n * Find all relationships for a specific model\n */\nexport function getRelationshipsForModel(\n modelName: string,\n relationships: Relationship[]\n): {\n outgoing: Relationship[]\n incoming: Relationship[]\n} {\n return {\n outgoing: relationships.filter(r => r.from.model === modelName),\n incoming: relationships.filter(r => r.to.model === modelName),\n }\n}\n\n/**\n * Check if a model is referenced by other models\n */\nexport function isModelReferenced(\n modelName: string,\n relationships: Relationship[]\n): boolean {\n return relationships.some(r => r.to.model === modelName)\n}\n\n/**\n * Get the dependency order for models based on relationships\n * Returns models in order such that dependencies come before dependents\n */\nexport function getModelDependencyOrder(\n models: Record<string, DataModel>,\n relationships: Relationship[]\n): string[] {\n const modelNames = Object.keys(models)\n const visited = new Set<string>()\n const order: string[] = []\n\n function visit(name: string, path: Set<string> = new Set()) {\n if (visited.has(name)) return\n if (path.has(name)) {\n // Circular dependency - skip to break the cycle\n return\n }\n\n path.add(name)\n\n // Visit all models that this model depends on first\n const deps = relationships\n .filter(r => r.from.model === name)\n .map(r => r.to.model)\n .filter(dep => modelNames.includes(dep))\n\n for (const dep of deps) {\n visit(dep, new Set(path))\n }\n\n if (!visited.has(name)) {\n visited.add(name)\n order.push(name)\n }\n }\n\n for (const name of modelNames) {\n visit(name)\n }\n\n return order\n}\n","/**\n * OpenAPI 3.x parser for DemoKit\n *\n * Parses OpenAPI specs into DemoKit's internal schema representation\n * with automatic relationship detection.\n */\n\nimport SwaggerParser from '@apidevtools/swagger-parser'\nimport type { OpenAPI, OpenAPIV3, OpenAPIV3_1 } from 'openapi-types'\nimport type {\n DemokitSchema,\n SchemaInfo,\n Endpoint,\n DataModel,\n PropertyDef,\n HttpMethod,\n ParameterDef,\n RequestBody,\n ResponseDef,\n PropertyType,\n ModelType,\n} from './types'\n// extractRefName is used in relationships.ts, re-exported from index.ts\nimport { detectRelationships } from './relationships'\n\ntype OpenAPIDocument = OpenAPIV3.Document | OpenAPIV3_1.Document\ntype SchemaObject = OpenAPIV3.SchemaObject | OpenAPIV3_1.SchemaObject\ntype ReferenceObject = OpenAPIV3.ReferenceObject | OpenAPIV3_1.ReferenceObject\ntype ParameterObject = OpenAPIV3.ParameterObject | OpenAPIV3_1.ParameterObject\ntype RequestBodyObject = OpenAPIV3.RequestBodyObject | OpenAPIV3_1.RequestBodyObject\ntype ResponseObject = OpenAPIV3.ResponseObject | OpenAPIV3_1.ResponseObject\ntype MediaTypeObject = OpenAPIV3.MediaTypeObject | OpenAPIV3_1.MediaTypeObject\n\n/**\n * Options for parsing OpenAPI specs\n */\nexport interface ParseOptions {\n /**\n * Whether to dereference all $ref pointers\n * @default true\n */\n dereference?: boolean\n\n /**\n * Whether to detect relationships automatically\n * @default true\n */\n detectRelationships?: boolean\n\n /**\n * Whether to include response schemas in models\n * @default true\n */\n includeResponses?: boolean\n}\n\n/**\n * Parse an OpenAPI spec from a file path or URL\n */\nexport async function parseOpenAPIFromPath(\n pathOrUrl: string,\n options: ParseOptions = {}\n): Promise<DemokitSchema> {\n const { dereference = true } = options\n\n let api: OpenAPI.Document\n\n if (dereference) {\n api = await SwaggerParser.dereference(pathOrUrl)\n } else {\n api = await SwaggerParser.parse(pathOrUrl)\n }\n\n return parseOpenAPIDocument(api as OpenAPIDocument, options)\n}\n\n/**\n * Parse an OpenAPI spec from a string (JSON or YAML)\n */\nexport async function parseOpenAPIFromString(\n spec: string,\n options: ParseOptions = {}\n): Promise<DemokitSchema> {\n const { dereference = true } = options\n\n // Parse the string as JSON or YAML\n let parsed: unknown\n try {\n parsed = JSON.parse(spec)\n } catch {\n // If JSON parse fails, try YAML via SwaggerParser\n const api = await SwaggerParser.parse(spec as unknown as string)\n if (dereference) {\n return parseOpenAPIDocument(\n (await SwaggerParser.dereference(api)) as OpenAPIDocument,\n options\n )\n }\n return parseOpenAPIDocument(api as OpenAPIDocument, options)\n }\n\n let api: OpenAPI.Document\n if (dereference) {\n api = await SwaggerParser.dereference(parsed as OpenAPI.Document)\n } else {\n api = await SwaggerParser.validate(parsed as OpenAPI.Document)\n }\n\n return parseOpenAPIDocument(api as OpenAPIDocument, options)\n}\n\n/**\n * Parse an OpenAPI spec from an already-parsed object\n */\nexport async function parseOpenAPIFromObject(\n spec: Record<string, unknown>,\n options: ParseOptions = {}\n): Promise<DemokitSchema> {\n const { dereference = true } = options\n\n let api: OpenAPI.Document\n if (dereference) {\n api = await SwaggerParser.dereference(spec as OpenAPI.Document)\n } else {\n api = await SwaggerParser.validate(spec as OpenAPI.Document)\n }\n\n return parseOpenAPIDocument(api as OpenAPIDocument, options)\n}\n\n/**\n * Parse an OpenAPI document into DemokitSchema\n */\nfunction parseOpenAPIDocument(\n doc: OpenAPIDocument,\n options: ParseOptions = {}\n): DemokitSchema {\n const { detectRelationships: shouldDetect = true } = options\n\n // Parse info\n const info = parseInfo(doc)\n\n // Parse models from components/schemas\n const models = parseModels(doc)\n\n // Parse endpoints from paths\n const endpoints = parseEndpoints(doc)\n\n // Detect relationships\n const relationships = shouldDetect ? detectRelationships(models) : []\n\n return {\n info,\n endpoints,\n models,\n relationships,\n }\n}\n\n/**\n * Parse API info from the document\n */\nfunction parseInfo(doc: OpenAPIDocument): SchemaInfo {\n const info: SchemaInfo = {\n title: doc.info?.title ?? 'Untitled API',\n version: doc.info?.version ?? '1.0.0',\n description: doc.info?.description,\n }\n\n // Try to extract base URL from servers\n if (doc.servers && doc.servers.length > 0 && doc.servers[0]) {\n info.baseUrl = doc.servers[0].url\n }\n\n return info\n}\n\n/**\n * Parse all models from components/schemas\n */\nfunction parseModels(doc: OpenAPIDocument): Record<string, DataModel> {\n const models: Record<string, DataModel> = {}\n\n const schemas = doc.components?.schemas\n if (!schemas) {\n return models\n }\n\n for (const [name, schema] of Object.entries(schemas)) {\n // Skip reference objects (shouldn't happen after dereference)\n if ('$ref' in schema) {\n continue\n }\n\n models[name] = parseSchemaToModel(name, schema as SchemaObject)\n }\n\n return models\n}\n\n/**\n * Parse a schema object into a DataModel\n */\nfunction parseSchemaToModel(name: string, schema: SchemaObject): DataModel {\n const model: DataModel = {\n name,\n type: schemaTypeToModelType(schema.type),\n description: schema.description,\n }\n\n // Parse properties for object types\n if (schema.type === 'object' || schema.properties) {\n model.type = 'object'\n model.properties = {}\n model.required = schema.required || []\n\n if (schema.properties) {\n for (const [propName, propSchema] of Object.entries(schema.properties)) {\n model.properties[propName] = parseProperty(\n propName,\n propSchema as SchemaObject | ReferenceObject,\n model.required.includes(propName)\n )\n }\n }\n\n if (schema.additionalProperties !== undefined) {\n if (typeof schema.additionalProperties === 'boolean') {\n model.additionalProperties = schema.additionalProperties\n } else if ('$ref' in schema.additionalProperties) {\n model.additionalProperties = {\n $ref: (schema.additionalProperties as ReferenceObject).$ref,\n }\n } else {\n model.additionalProperties = parseSchemaToModel(\n `${name}AdditionalProps`,\n schema.additionalProperties as SchemaObject\n )\n }\n }\n }\n\n // Parse array items\n if (schema.type === 'array' && schema.items) {\n model.type = 'array'\n if ('$ref' in schema.items) {\n model.items = { $ref: (schema.items as ReferenceObject).$ref }\n } else {\n model.items = parseSchemaToModel(`${name}Item`, schema.items as SchemaObject)\n }\n }\n\n // Parse enum values\n if (schema.enum) {\n model.enum = schema.enum\n }\n\n // Parse example\n if (schema.example !== undefined) {\n model.example = schema.example\n }\n\n return model\n}\n\n/**\n * Parse a property schema into a PropertyDef\n */\nfunction parseProperty(\n name: string,\n schema: SchemaObject | ReferenceObject,\n required: boolean\n): PropertyDef {\n // Handle $ref\n if ('$ref' in schema) {\n return {\n name,\n type: 'object',\n required,\n $ref: schema.$ref,\n }\n }\n\n const prop: PropertyDef = {\n name,\n type: schemaTypeToPropertyType(schema.type),\n required,\n nullable: (schema as Record<string, unknown>).nullable as boolean | undefined,\n description: schema.description,\n format: schema.format,\n enum: schema.enum,\n example: schema.example,\n default: schema.default,\n minimum: schema.minimum,\n maximum: schema.maximum,\n minLength: schema.minLength,\n maxLength: schema.maxLength,\n pattern: schema.pattern,\n }\n\n // Parse array items\n if (schema.type === 'array' && schema.items) {\n prop.type = 'array'\n if ('$ref' in schema.items) {\n prop.items = { $ref: (schema.items as ReferenceObject).$ref }\n } else {\n prop.items = parseSchemaToModel(`${name}Item`, schema.items as SchemaObject)\n }\n }\n\n // Check for x-demokit-relationship extension\n const xRelationship = (schema as Record<string, unknown>)['x-demokit-relationship']\n if (xRelationship && typeof xRelationship === 'object') {\n prop['x-demokit-relationship'] = xRelationship as { model: string; field: string }\n }\n\n return prop\n}\n\n/**\n * Parse all endpoints from paths\n */\nfunction parseEndpoints(doc: OpenAPIDocument): Endpoint[] {\n const endpoints: Endpoint[] = []\n\n if (!doc.paths) {\n return endpoints\n }\n\n const methods: HttpMethod[] = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS']\n\n for (const [path, pathItem] of Object.entries(doc.paths)) {\n if (!pathItem) continue\n\n for (const method of methods) {\n const operation = pathItem[method.toLowerCase() as keyof typeof pathItem] as\n | OpenAPIV3.OperationObject\n | OpenAPIV3_1.OperationObject\n | undefined\n\n if (!operation) continue\n\n endpoints.push(parseEndpoint(method, path, operation, pathItem))\n }\n }\n\n return endpoints\n}\n\n/**\n * Parse a single endpoint\n */\nfunction parseEndpoint(\n method: HttpMethod,\n path: string,\n operation: OpenAPIV3.OperationObject | OpenAPIV3_1.OperationObject,\n pathItem: OpenAPIV3.PathItemObject | OpenAPIV3_1.PathItemObject\n): Endpoint {\n // Combine path-level and operation-level parameters\n const allParams = [...(pathItem.parameters || []), ...(operation.parameters || [])]\n\n const pathParams: ParameterDef[] = []\n const queryParams: ParameterDef[] = []\n\n for (const param of allParams) {\n if ('$ref' in param) continue // Skip refs\n const paramObj = param as ParameterObject\n const parsed = parseParameter(paramObj)\n\n if (parsed.in === 'path') {\n pathParams.push(parsed)\n } else if (parsed.in === 'query') {\n queryParams.push(parsed)\n }\n }\n\n // Parse request body\n let requestBody: RequestBody | undefined\n if (operation.requestBody && !('$ref' in operation.requestBody)) {\n requestBody = parseRequestBody(operation.requestBody as RequestBodyObject)\n }\n\n // Parse responses\n const responses: Record<string, ResponseDef> = {}\n if (operation.responses) {\n for (const [statusCode, response] of Object.entries(operation.responses)) {\n if ('$ref' in response) continue\n responses[statusCode] = parseResponse(statusCode, response as ResponseObject)\n }\n }\n\n return {\n method,\n path,\n operationId: operation.operationId,\n summary: operation.summary,\n description: operation.description,\n pathParams,\n queryParams,\n requestBody,\n responses,\n tags: operation.tags || [],\n }\n}\n\n/**\n * Parse a parameter object\n */\nfunction parseParameter(param: ParameterObject): ParameterDef {\n const schema = param.schema as SchemaObject | undefined\n\n return {\n name: param.name,\n in: param.in as 'path' | 'query' | 'header' | 'cookie',\n required: param.required ?? false,\n type: schema ? schemaTypeToPropertyType(schema.type) : 'string',\n format: schema?.format,\n description: param.description,\n example: param.example ?? schema?.example,\n }\n}\n\n/**\n * Parse a request body object\n */\nfunction parseRequestBody(body: RequestBodyObject): RequestBody {\n const contentType = Object.keys(body.content || {})[0] || 'application/json'\n const mediaType = body.content?.[contentType] as MediaTypeObject | undefined\n\n let schema: RequestBody['schema'] = { name: 'Unknown', type: 'object' }\n\n if (mediaType?.schema) {\n if ('$ref' in mediaType.schema) {\n schema = { $ref: (mediaType.schema as ReferenceObject).$ref }\n } else {\n schema = parseSchemaToModel('RequestBody', mediaType.schema as SchemaObject)\n }\n }\n\n return {\n contentType,\n schema,\n required: body.required ?? false,\n description: body.description,\n }\n}\n\n/**\n * Parse a response object\n */\nfunction parseResponse(statusCode: string, response: ResponseObject): ResponseDef {\n const responseDef: ResponseDef = {\n statusCode,\n description: response.description,\n }\n\n if (response.content) {\n responseDef.content = {}\n\n for (const [contentType, mediaType] of Object.entries(response.content)) {\n if (mediaType.schema) {\n if ('$ref' in mediaType.schema) {\n responseDef.content[contentType] = {\n $ref: (mediaType.schema as ReferenceObject).$ref,\n }\n } else {\n responseDef.content[contentType] = parseSchemaToModel(\n `Response${statusCode}`,\n mediaType.schema as SchemaObject\n )\n }\n }\n }\n }\n\n return responseDef\n}\n\n/**\n * Convert OpenAPI schema type to ModelType\n */\nfunction schemaTypeToModelType(\n type: string | string[] | undefined\n): ModelType {\n if (!type) return 'object'\n if (Array.isArray(type)) return type[0] as ModelType\n return type as ModelType\n}\n\n/**\n * Convert OpenAPI schema type to PropertyType\n */\nfunction schemaTypeToPropertyType(\n type: string | string[] | undefined\n): PropertyType {\n if (!type) return 'object'\n if (Array.isArray(type)) return type[0] as PropertyType\n return type as PropertyType\n}\n"]}
|