@demokit-ai/schema 0.0.1 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +150 -120
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +150 -116
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.d.cts
CHANGED
|
@@ -379,6 +379,8 @@ declare function extractRefName(ref: string): string;
|
|
|
379
379
|
*
|
|
380
380
|
* Parses OpenAPI specs into DemoKit's internal schema representation
|
|
381
381
|
* with automatic relationship detection.
|
|
382
|
+
*
|
|
383
|
+
* Uses @scalar/openapi-parser - a modern, webpack-compatible parser.
|
|
382
384
|
*/
|
|
383
385
|
|
|
384
386
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -379,6 +379,8 @@ declare function extractRefName(ref: string): string;
|
|
|
379
379
|
*
|
|
380
380
|
* Parses OpenAPI specs into DemoKit's internal schema representation
|
|
381
381
|
* with automatic relationship detection.
|
|
382
|
+
*
|
|
383
|
+
* Uses @scalar/openapi-parser - a modern, webpack-compatible parser.
|
|
382
384
|
*/
|
|
383
385
|
|
|
384
386
|
/**
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { validate, dereference } from '@scalar/openapi-parser';
|
|
2
2
|
|
|
3
3
|
// src/parser.ts
|
|
4
4
|
|
|
@@ -151,47 +151,52 @@ function getModelDependencyOrder(models, relationships) {
|
|
|
151
151
|
|
|
152
152
|
// src/parser.ts
|
|
153
153
|
async function parseOpenAPIFromPath(pathOrUrl, options = {}) {
|
|
154
|
-
const
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
api = await SwaggerParser.dereference(pathOrUrl);
|
|
158
|
-
} else {
|
|
159
|
-
api = await SwaggerParser.parse(pathOrUrl);
|
|
154
|
+
const response = await fetch(pathOrUrl);
|
|
155
|
+
if (!response.ok) {
|
|
156
|
+
throw new Error(`Failed to fetch OpenAPI spec from ${pathOrUrl}: ${response.statusText}`);
|
|
160
157
|
}
|
|
161
|
-
|
|
158
|
+
const content = await response.text();
|
|
159
|
+
return parseOpenAPIFromString(content, options);
|
|
162
160
|
}
|
|
163
161
|
async function parseOpenAPIFromString(spec, options = {}) {
|
|
164
|
-
const { dereference = true } = options;
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
const api2 = await SwaggerParser.parse(spec);
|
|
170
|
-
if (dereference) {
|
|
171
|
-
return parseOpenAPIDocument(
|
|
172
|
-
await SwaggerParser.dereference(api2),
|
|
173
|
-
options
|
|
174
|
-
);
|
|
175
|
-
}
|
|
176
|
-
return parseOpenAPIDocument(api2, options);
|
|
162
|
+
const { dereference: shouldDereference = true } = options;
|
|
163
|
+
const validationResult = await validate(spec);
|
|
164
|
+
if (!validationResult.valid) {
|
|
165
|
+
const errorMessages = validationResult.errors?.map((e) => e.message).join(", ") || "Unknown validation error";
|
|
166
|
+
throw new Error(`Invalid OpenAPI specification: ${errorMessages}`);
|
|
177
167
|
}
|
|
178
|
-
let
|
|
179
|
-
if (
|
|
180
|
-
|
|
168
|
+
let schema;
|
|
169
|
+
if (shouldDereference) {
|
|
170
|
+
const derefResult = await dereference(spec);
|
|
171
|
+
if (derefResult.errors && derefResult.errors.length > 0) {
|
|
172
|
+
const errorMessages = derefResult.errors.map((e) => e.message).join(", ");
|
|
173
|
+
throw new Error(`Failed to dereference OpenAPI spec: ${errorMessages}`);
|
|
174
|
+
}
|
|
175
|
+
schema = derefResult.schema;
|
|
181
176
|
} else {
|
|
182
|
-
|
|
177
|
+
schema = validationResult.specification;
|
|
183
178
|
}
|
|
184
|
-
return parseOpenAPIDocument(
|
|
179
|
+
return parseOpenAPIDocument(schema, options);
|
|
185
180
|
}
|
|
186
181
|
async function parseOpenAPIFromObject(spec, options = {}) {
|
|
187
|
-
const { dereference = true } = options;
|
|
188
|
-
|
|
189
|
-
if (
|
|
190
|
-
|
|
182
|
+
const { dereference: shouldDereference = true } = options;
|
|
183
|
+
const validationResult = await validate(spec);
|
|
184
|
+
if (!validationResult.valid) {
|
|
185
|
+
const errorMessages = validationResult.errors?.map((e) => e.message).join(", ") || "Unknown validation error";
|
|
186
|
+
throw new Error(`Invalid OpenAPI specification: ${errorMessages}`);
|
|
187
|
+
}
|
|
188
|
+
let schema;
|
|
189
|
+
if (shouldDereference) {
|
|
190
|
+
const derefResult = await dereference(spec);
|
|
191
|
+
if (derefResult.errors && derefResult.errors.length > 0) {
|
|
192
|
+
const errorMessages = derefResult.errors.map((e) => e.message).join(", ");
|
|
193
|
+
throw new Error(`Failed to dereference OpenAPI spec: ${errorMessages}`);
|
|
194
|
+
}
|
|
195
|
+
schema = derefResult.schema;
|
|
191
196
|
} else {
|
|
192
|
-
|
|
197
|
+
schema = validationResult.specification;
|
|
193
198
|
}
|
|
194
|
-
return parseOpenAPIDocument(
|
|
199
|
+
return parseOpenAPIDocument(schema, options);
|
|
195
200
|
}
|
|
196
201
|
function parseOpenAPIDocument(doc, options = {}) {
|
|
197
202
|
const { detectRelationships: shouldDetect = true } = options;
|
|
@@ -207,24 +212,27 @@ function parseOpenAPIDocument(doc, options = {}) {
|
|
|
207
212
|
};
|
|
208
213
|
}
|
|
209
214
|
function parseInfo(doc) {
|
|
215
|
+
const docInfo = doc.info || {};
|
|
210
216
|
const info = {
|
|
211
|
-
title:
|
|
212
|
-
version:
|
|
213
|
-
description:
|
|
217
|
+
title: docInfo.title ?? "Untitled API",
|
|
218
|
+
version: docInfo.version ?? "1.0.0",
|
|
219
|
+
description: docInfo.description
|
|
214
220
|
};
|
|
215
|
-
|
|
216
|
-
|
|
221
|
+
const servers = doc.servers;
|
|
222
|
+
if (servers && servers.length > 0 && servers[0]) {
|
|
223
|
+
info.baseUrl = servers[0].url;
|
|
217
224
|
}
|
|
218
225
|
return info;
|
|
219
226
|
}
|
|
220
227
|
function parseModels(doc) {
|
|
221
228
|
const models = {};
|
|
222
|
-
const
|
|
229
|
+
const components = doc.components;
|
|
230
|
+
const schemas = components?.schemas;
|
|
223
231
|
if (!schemas) {
|
|
224
232
|
return models;
|
|
225
233
|
}
|
|
226
234
|
for (const [name, schema] of Object.entries(schemas)) {
|
|
227
|
-
if ("$ref" in schema) {
|
|
235
|
+
if (schema && typeof schema === "object" && "$ref" in schema) {
|
|
228
236
|
continue;
|
|
229
237
|
}
|
|
230
238
|
models[name] = parseSchemaToModel(name, schema);
|
|
@@ -232,17 +240,21 @@ function parseModels(doc) {
|
|
|
232
240
|
return models;
|
|
233
241
|
}
|
|
234
242
|
function parseSchemaToModel(name, schema) {
|
|
243
|
+
if (!schema || typeof schema !== "object") {
|
|
244
|
+
return { name, type: "object" };
|
|
245
|
+
}
|
|
246
|
+
const s = schema;
|
|
235
247
|
const model = {
|
|
236
248
|
name,
|
|
237
|
-
type: schemaTypeToModelType(
|
|
238
|
-
description:
|
|
249
|
+
type: schemaTypeToModelType(s.type),
|
|
250
|
+
description: s.description
|
|
239
251
|
};
|
|
240
|
-
if (
|
|
252
|
+
if (s.type === "object" || s.properties) {
|
|
241
253
|
model.type = "object";
|
|
242
254
|
model.properties = {};
|
|
243
|
-
model.required =
|
|
244
|
-
if (
|
|
245
|
-
for (const [propName, propSchema] of Object.entries(
|
|
255
|
+
model.required = s.required || [];
|
|
256
|
+
if (s.properties && typeof s.properties === "object") {
|
|
257
|
+
for (const [propName, propSchema] of Object.entries(s.properties)) {
|
|
246
258
|
model.properties[propName] = parseProperty(
|
|
247
259
|
propName,
|
|
248
260
|
propSchema,
|
|
@@ -250,39 +262,41 @@ function parseSchemaToModel(name, schema) {
|
|
|
250
262
|
);
|
|
251
263
|
}
|
|
252
264
|
}
|
|
253
|
-
if (
|
|
254
|
-
if (typeof
|
|
255
|
-
model.additionalProperties =
|
|
256
|
-
} else if (
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
+
if (s.additionalProperties !== void 0) {
|
|
266
|
+
if (typeof s.additionalProperties === "boolean") {
|
|
267
|
+
model.additionalProperties = s.additionalProperties;
|
|
268
|
+
} else if (s.additionalProperties && typeof s.additionalProperties === "object") {
|
|
269
|
+
if ("$ref" in s.additionalProperties) {
|
|
270
|
+
model.additionalProperties = {
|
|
271
|
+
$ref: s.additionalProperties.$ref
|
|
272
|
+
};
|
|
273
|
+
} else {
|
|
274
|
+
model.additionalProperties = parseSchemaToModel(
|
|
275
|
+
`${name}AdditionalProps`,
|
|
276
|
+
s.additionalProperties
|
|
277
|
+
);
|
|
278
|
+
}
|
|
265
279
|
}
|
|
266
280
|
}
|
|
267
281
|
}
|
|
268
|
-
if (
|
|
282
|
+
if (s.type === "array" && s.items) {
|
|
269
283
|
model.type = "array";
|
|
270
|
-
if ("$ref" in
|
|
271
|
-
model.items = { $ref:
|
|
284
|
+
if (typeof s.items === "object" && "$ref" in s.items) {
|
|
285
|
+
model.items = { $ref: s.items.$ref };
|
|
272
286
|
} else {
|
|
273
|
-
model.items = parseSchemaToModel(`${name}Item`,
|
|
287
|
+
model.items = parseSchemaToModel(`${name}Item`, s.items);
|
|
274
288
|
}
|
|
275
289
|
}
|
|
276
|
-
if (
|
|
277
|
-
model.enum =
|
|
290
|
+
if (s.enum) {
|
|
291
|
+
model.enum = s.enum;
|
|
278
292
|
}
|
|
279
|
-
if (
|
|
280
|
-
model.example =
|
|
293
|
+
if (s.example !== void 0) {
|
|
294
|
+
model.example = s.example;
|
|
281
295
|
}
|
|
282
296
|
return model;
|
|
283
297
|
}
|
|
284
298
|
function parseProperty(name, schema, required) {
|
|
285
|
-
if ("$ref" in schema) {
|
|
299
|
+
if (schema && typeof schema === "object" && "$ref" in schema) {
|
|
286
300
|
return {
|
|
287
301
|
name,
|
|
288
302
|
type: "object",
|
|
@@ -290,31 +304,32 @@ function parseProperty(name, schema, required) {
|
|
|
290
304
|
$ref: schema.$ref
|
|
291
305
|
};
|
|
292
306
|
}
|
|
307
|
+
const s = schema;
|
|
293
308
|
const prop = {
|
|
294
309
|
name,
|
|
295
|
-
type: schemaTypeToPropertyType(
|
|
310
|
+
type: schemaTypeToPropertyType(s.type),
|
|
296
311
|
required,
|
|
297
|
-
nullable:
|
|
298
|
-
description:
|
|
299
|
-
format:
|
|
300
|
-
enum:
|
|
301
|
-
example:
|
|
302
|
-
default:
|
|
303
|
-
minimum:
|
|
304
|
-
maximum:
|
|
305
|
-
minLength:
|
|
306
|
-
maxLength:
|
|
307
|
-
pattern:
|
|
312
|
+
nullable: s.nullable,
|
|
313
|
+
description: s.description,
|
|
314
|
+
format: s.format,
|
|
315
|
+
enum: s.enum,
|
|
316
|
+
example: s.example,
|
|
317
|
+
default: s.default,
|
|
318
|
+
minimum: s.minimum,
|
|
319
|
+
maximum: s.maximum,
|
|
320
|
+
minLength: s.minLength,
|
|
321
|
+
maxLength: s.maxLength,
|
|
322
|
+
pattern: s.pattern
|
|
308
323
|
};
|
|
309
|
-
if (
|
|
324
|
+
if (s.type === "array" && s.items) {
|
|
310
325
|
prop.type = "array";
|
|
311
|
-
if ("$ref" in
|
|
312
|
-
prop.items = { $ref:
|
|
326
|
+
if (typeof s.items === "object" && "$ref" in s.items) {
|
|
327
|
+
prop.items = { $ref: s.items.$ref };
|
|
313
328
|
} else {
|
|
314
|
-
prop.items = parseSchemaToModel(`${name}Item`,
|
|
329
|
+
prop.items = parseSchemaToModel(`${name}Item`, s.items);
|
|
315
330
|
}
|
|
316
331
|
}
|
|
317
|
-
const xRelationship =
|
|
332
|
+
const xRelationship = s["x-demokit-relationship"];
|
|
318
333
|
if (xRelationship && typeof xRelationship === "object") {
|
|
319
334
|
prop["x-demokit-relationship"] = xRelationship;
|
|
320
335
|
}
|
|
@@ -322,28 +337,32 @@ function parseProperty(name, schema, required) {
|
|
|
322
337
|
}
|
|
323
338
|
function parseEndpoints(doc) {
|
|
324
339
|
const endpoints = [];
|
|
325
|
-
|
|
340
|
+
const paths = doc.paths;
|
|
341
|
+
if (!paths) {
|
|
326
342
|
return endpoints;
|
|
327
343
|
}
|
|
328
344
|
const methods = ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"];
|
|
329
|
-
for (const [path, pathItem] of Object.entries(
|
|
330
|
-
if (!pathItem) continue;
|
|
345
|
+
for (const [path, pathItem] of Object.entries(paths)) {
|
|
346
|
+
if (!pathItem || typeof pathItem !== "object") continue;
|
|
331
347
|
for (const method of methods) {
|
|
332
348
|
const operation = pathItem[method.toLowerCase()];
|
|
333
|
-
if (!operation) continue;
|
|
349
|
+
if (!operation || typeof operation !== "object") continue;
|
|
334
350
|
endpoints.push(parseEndpoint(method, path, operation, pathItem));
|
|
335
351
|
}
|
|
336
352
|
}
|
|
337
353
|
return endpoints;
|
|
338
354
|
}
|
|
339
355
|
function parseEndpoint(method, path, operation, pathItem) {
|
|
340
|
-
const allParams = [...pathItem.parameters || [], ...operation.parameters || []];
|
|
341
356
|
const pathParams = [];
|
|
342
357
|
const queryParams = [];
|
|
358
|
+
const allParams = [
|
|
359
|
+
...pathItem.parameters || [],
|
|
360
|
+
...operation.parameters || []
|
|
361
|
+
];
|
|
343
362
|
for (const param of allParams) {
|
|
363
|
+
if (!param || typeof param !== "object") continue;
|
|
344
364
|
if ("$ref" in param) continue;
|
|
345
|
-
const
|
|
346
|
-
const parsed = parseParameter(paramObj);
|
|
365
|
+
const parsed = parseParameter(param);
|
|
347
366
|
if (parsed.in === "path") {
|
|
348
367
|
pathParams.push(parsed);
|
|
349
368
|
} else if (parsed.in === "query") {
|
|
@@ -351,12 +370,15 @@ function parseEndpoint(method, path, operation, pathItem) {
|
|
|
351
370
|
}
|
|
352
371
|
}
|
|
353
372
|
let requestBody;
|
|
354
|
-
|
|
355
|
-
|
|
373
|
+
const opRequestBody = operation.requestBody;
|
|
374
|
+
if (opRequestBody && typeof opRequestBody === "object" && !("$ref" in opRequestBody)) {
|
|
375
|
+
requestBody = parseRequestBody(opRequestBody);
|
|
356
376
|
}
|
|
357
377
|
const responses = {};
|
|
358
|
-
|
|
359
|
-
|
|
378
|
+
const opResponses = operation.responses;
|
|
379
|
+
if (opResponses && typeof opResponses === "object") {
|
|
380
|
+
for (const [statusCode, response] of Object.entries(opResponses)) {
|
|
381
|
+
if (!response || typeof response !== "object") continue;
|
|
360
382
|
if ("$ref" in response) continue;
|
|
361
383
|
responses[statusCode] = parseResponse(statusCode, response);
|
|
362
384
|
}
|
|
@@ -375,52 +397,64 @@ function parseEndpoint(method, path, operation, pathItem) {
|
|
|
375
397
|
};
|
|
376
398
|
}
|
|
377
399
|
function parseParameter(param) {
|
|
378
|
-
const
|
|
400
|
+
const p = param;
|
|
401
|
+
const schema = p.schema;
|
|
379
402
|
return {
|
|
380
|
-
name:
|
|
381
|
-
in:
|
|
382
|
-
required:
|
|
403
|
+
name: p.name,
|
|
404
|
+
in: p.in,
|
|
405
|
+
required: p.required ?? false,
|
|
383
406
|
type: schema ? schemaTypeToPropertyType(schema.type) : "string",
|
|
384
407
|
format: schema?.format,
|
|
385
|
-
description:
|
|
386
|
-
example:
|
|
408
|
+
description: p.description,
|
|
409
|
+
example: p.example ?? schema?.example
|
|
387
410
|
};
|
|
388
411
|
}
|
|
389
412
|
function parseRequestBody(body) {
|
|
390
|
-
const
|
|
391
|
-
const
|
|
413
|
+
const b = body;
|
|
414
|
+
const content = b.content;
|
|
415
|
+
const contentType = Object.keys(content || {})[0] || "application/json";
|
|
416
|
+
const mediaType = content?.[contentType];
|
|
392
417
|
let schema = { name: "Unknown", type: "object" };
|
|
393
|
-
if (mediaType
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
418
|
+
if (mediaType) {
|
|
419
|
+
const m = mediaType;
|
|
420
|
+
const mediaSchema = m.schema;
|
|
421
|
+
if (mediaSchema && typeof mediaSchema === "object") {
|
|
422
|
+
if ("$ref" in mediaSchema) {
|
|
423
|
+
schema = { $ref: mediaSchema.$ref };
|
|
424
|
+
} else {
|
|
425
|
+
schema = parseSchemaToModel("RequestBody", mediaSchema);
|
|
426
|
+
}
|
|
398
427
|
}
|
|
399
428
|
}
|
|
400
429
|
return {
|
|
401
430
|
contentType,
|
|
402
431
|
schema,
|
|
403
|
-
required:
|
|
404
|
-
description:
|
|
432
|
+
required: b.required ?? false,
|
|
433
|
+
description: b.description
|
|
405
434
|
};
|
|
406
435
|
}
|
|
407
436
|
function parseResponse(statusCode, response) {
|
|
437
|
+
const r = response;
|
|
408
438
|
const responseDef = {
|
|
409
439
|
statusCode,
|
|
410
|
-
description:
|
|
440
|
+
description: r.description
|
|
411
441
|
};
|
|
412
|
-
|
|
442
|
+
const content = r.content;
|
|
443
|
+
if (content) {
|
|
413
444
|
responseDef.content = {};
|
|
414
|
-
for (const [contentType, mediaType] of Object.entries(
|
|
415
|
-
if (mediaType
|
|
416
|
-
|
|
445
|
+
for (const [contentType, mediaType] of Object.entries(content)) {
|
|
446
|
+
if (!mediaType || typeof mediaType !== "object") continue;
|
|
447
|
+
const m = mediaType;
|
|
448
|
+
const schema = m.schema;
|
|
449
|
+
if (schema && typeof schema === "object") {
|
|
450
|
+
if ("$ref" in schema) {
|
|
417
451
|
responseDef.content[contentType] = {
|
|
418
|
-
$ref:
|
|
452
|
+
$ref: schema.$ref
|
|
419
453
|
};
|
|
420
454
|
} else {
|
|
421
455
|
responseDef.content[contentType] = parseSchemaToModel(
|
|
422
456
|
`Response${statusCode}`,
|
|
423
|
-
|
|
457
|
+
schema
|
|
424
458
|
);
|
|
425
459
|
}
|
|
426
460
|
}
|