@demokit-ai/schema 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.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 SwaggerParser from '@apidevtools/swagger-parser';
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 { dereference = true } = options;
155
- let api;
156
- if (dereference) {
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
- return parseOpenAPIDocument(api, options);
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
- let parsed;
166
- try {
167
- parsed = JSON.parse(spec);
168
- } catch {
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 api;
179
- if (dereference) {
180
- api = await SwaggerParser.dereference(parsed);
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
- api = await SwaggerParser.validate(parsed);
177
+ schema = validationResult.specification;
183
178
  }
184
- return parseOpenAPIDocument(api, options);
179
+ return parseOpenAPIDocument(schema, options);
185
180
  }
186
181
  async function parseOpenAPIFromObject(spec, options = {}) {
187
- const { dereference = true } = options;
188
- let api;
189
- if (dereference) {
190
- api = await SwaggerParser.dereference(spec);
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
- api = await SwaggerParser.validate(spec);
197
+ schema = validationResult.specification;
193
198
  }
194
- return parseOpenAPIDocument(api, options);
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: doc.info?.title ?? "Untitled API",
212
- version: doc.info?.version ?? "1.0.0",
213
- description: doc.info?.description
217
+ title: docInfo.title ?? "Untitled API",
218
+ version: docInfo.version ?? "1.0.0",
219
+ description: docInfo.description
214
220
  };
215
- if (doc.servers && doc.servers.length > 0 && doc.servers[0]) {
216
- info.baseUrl = doc.servers[0].url;
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 schemas = doc.components?.schemas;
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(schema.type),
238
- description: schema.description
249
+ type: schemaTypeToModelType(s.type),
250
+ description: s.description
239
251
  };
240
- if (schema.type === "object" || schema.properties) {
252
+ if (s.type === "object" || s.properties) {
241
253
  model.type = "object";
242
254
  model.properties = {};
243
- model.required = schema.required || [];
244
- if (schema.properties) {
245
- for (const [propName, propSchema] of Object.entries(schema.properties)) {
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 (schema.additionalProperties !== void 0) {
254
- if (typeof schema.additionalProperties === "boolean") {
255
- model.additionalProperties = schema.additionalProperties;
256
- } else if ("$ref" in schema.additionalProperties) {
257
- model.additionalProperties = {
258
- $ref: schema.additionalProperties.$ref
259
- };
260
- } else {
261
- model.additionalProperties = parseSchemaToModel(
262
- `${name}AdditionalProps`,
263
- schema.additionalProperties
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 (schema.type === "array" && schema.items) {
282
+ if (s.type === "array" && s.items) {
269
283
  model.type = "array";
270
- if ("$ref" in schema.items) {
271
- model.items = { $ref: schema.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`, schema.items);
287
+ model.items = parseSchemaToModel(`${name}Item`, s.items);
274
288
  }
275
289
  }
276
- if (schema.enum) {
277
- model.enum = schema.enum;
290
+ if (s.enum) {
291
+ model.enum = s.enum;
278
292
  }
279
- if (schema.example !== void 0) {
280
- model.example = schema.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(schema.type),
310
+ type: schemaTypeToPropertyType(s.type),
296
311
  required,
297
- nullable: schema.nullable,
298
- description: schema.description,
299
- format: schema.format,
300
- enum: schema.enum,
301
- example: schema.example,
302
- default: schema.default,
303
- minimum: schema.minimum,
304
- maximum: schema.maximum,
305
- minLength: schema.minLength,
306
- maxLength: schema.maxLength,
307
- pattern: schema.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 (schema.type === "array" && schema.items) {
324
+ if (s.type === "array" && s.items) {
310
325
  prop.type = "array";
311
- if ("$ref" in schema.items) {
312
- prop.items = { $ref: schema.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`, schema.items);
329
+ prop.items = parseSchemaToModel(`${name}Item`, s.items);
315
330
  }
316
331
  }
317
- const xRelationship = schema["x-demokit-relationship"];
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
- if (!doc.paths) {
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(doc.paths)) {
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 paramObj = param;
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
- if (operation.requestBody && !("$ref" in operation.requestBody)) {
355
- requestBody = parseRequestBody(operation.requestBody);
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
- if (operation.responses) {
359
- for (const [statusCode, response] of Object.entries(operation.responses)) {
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 schema = param.schema;
400
+ const p = param;
401
+ const schema = p.schema;
379
402
  return {
380
- name: param.name,
381
- in: param.in,
382
- required: param.required ?? false,
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: param.description,
386
- example: param.example ?? schema?.example
408
+ description: p.description,
409
+ example: p.example ?? schema?.example
387
410
  };
388
411
  }
389
412
  function parseRequestBody(body) {
390
- const contentType = Object.keys(body.content || {})[0] || "application/json";
391
- const mediaType = body.content?.[contentType];
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?.schema) {
394
- if ("$ref" in mediaType.schema) {
395
- schema = { $ref: mediaType.schema.$ref };
396
- } else {
397
- schema = parseSchemaToModel("RequestBody", mediaType.schema);
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: body.required ?? false,
404
- description: body.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: response.description
440
+ description: r.description
411
441
  };
412
- if (response.content) {
442
+ const content = r.content;
443
+ if (content) {
413
444
  responseDef.content = {};
414
- for (const [contentType, mediaType] of Object.entries(response.content)) {
415
- if (mediaType.schema) {
416
- if ("$ref" in mediaType.schema) {
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: mediaType.schema.$ref
452
+ $ref: schema.$ref
419
453
  };
420
454
  } else {
421
455
  responseDef.content[contentType] = parseSchemaToModel(
422
456
  `Response${statusCode}`,
423
- mediaType.schema
457
+ schema
424
458
  );
425
459
  }
426
460
  }