@contractkit/openapi-to-ck 0.7.1 → 0.7.3

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 (42) hide show
  1. package/.turbo/turbo-build$colon$ci.log +24 -40
  2. package/.turbo/turbo-test$colon$ci.log +37 -77
  3. package/CHANGELOG.md +14 -0
  4. package/README.md +78 -0
  5. package/coverage/clover.xml +11 -11
  6. package/coverage/coverage-final.json +9 -9
  7. package/coverage/index.html +1 -1
  8. package/coverage/src/ast-to-ck.ts.html +1 -1
  9. package/coverage/src/circular-refs.ts.html +1 -1
  10. package/coverage/src/convert.ts.html +1 -1
  11. package/coverage/src/index.html +1 -1
  12. package/coverage/src/normalize.ts.html +1 -1
  13. package/coverage/src/paths-to-ast.ts.html +1 -1
  14. package/coverage/src/schema-to-ast.ts.html +1 -1
  15. package/coverage/src/tag-splitter.ts.html +1 -1
  16. package/coverage/src/warnings.ts.html +1 -1
  17. package/coverage/tests/helpers.ts.html +1 -1
  18. package/coverage/tests/index.html +1 -1
  19. package/package.json +4 -4
  20. package/.turbo/turbo-build.log +0 -20
  21. package/.turbo/turbo-format.log +0 -36
  22. package/.turbo/turbo-test.log +0 -19
  23. package/dist/chunk-CQGFQKBJ.js +0 -1681
  24. package/dist/chunk-CQGFQKBJ.js.map +0 -1
  25. package/dist/chunk-FNUU2DWY.js +0 -1779
  26. package/dist/chunk-FNUU2DWY.js.map +0 -1
  27. package/dist/chunk-LD2HAFZG.js +0 -1681
  28. package/dist/chunk-LD2HAFZG.js.map +0 -1
  29. package/dist/chunk-LQ2B3EJG.js +0 -1777
  30. package/dist/chunk-LQ2B3EJG.js.map +0 -1
  31. package/dist/chunk-M6JA6WY2.js +0 -1714
  32. package/dist/chunk-M6JA6WY2.js.map +0 -1
  33. package/dist/chunk-MQTKLXTN.js +0 -1681
  34. package/dist/chunk-MQTKLXTN.js.map +0 -1
  35. package/dist/chunk-NV7RGUUS.js +0 -1699
  36. package/dist/chunk-NV7RGUUS.js.map +0 -1
  37. package/dist/chunk-REM25FDE.js +0 -1779
  38. package/dist/chunk-REM25FDE.js.map +0 -1
  39. package/dist/chunk-SNW7GJOM.js +0 -1681
  40. package/dist/chunk-SNW7GJOM.js.map +0 -1
  41. package/dist/chunk-UWOSCBRG.js +0 -1681
  42. package/dist/chunk-UWOSCBRG.js.map +0 -1
@@ -1,1777 +0,0 @@
1
- var __defProp = Object.defineProperty;
2
- var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
-
4
- // src/normalize.ts
5
- function normalize(doc, warnings) {
6
- const version = detectVersion(doc);
7
- if (version === "2.0") {
8
- return normalizeSwagger2(doc, warnings);
9
- }
10
- if (version === "3.0") {
11
- return normalizeOas30(doc, warnings);
12
- }
13
- return doc;
14
- }
15
- __name(normalize, "normalize");
16
- function detectVersion(doc) {
17
- if (typeof doc.swagger === "string" && doc.swagger.startsWith("2")) return "2.0";
18
- if (typeof doc.openapi === "string") {
19
- if (doc.openapi.startsWith("3.0")) return "3.0";
20
- }
21
- return "3.1";
22
- }
23
- __name(detectVersion, "detectVersion");
24
- function normalizeSwagger2(doc, warnings) {
25
- const info = doc.info ?? {
26
- title: "Untitled",
27
- version: "0.0.0"
28
- };
29
- const basePath = doc.basePath ?? "";
30
- const schemes = doc.schemes ?? [
31
- "https"
32
- ];
33
- const host = doc.host ?? "localhost";
34
- const globalConsumes = doc.consumes ?? [
35
- "application/json"
36
- ];
37
- const globalProduces = doc.produces ?? [
38
- "application/json"
39
- ];
40
- const result = {
41
- openapi: "3.1.0",
42
- info: {
43
- title: info.title ?? "Untitled",
44
- version: info.version ?? "0.0.0",
45
- description: info.description
46
- },
47
- servers: [
48
- {
49
- url: `${schemes[0]}://${host}${basePath}`
50
- }
51
- ],
52
- paths: {},
53
- components: {
54
- schemas: {},
55
- securitySchemes: {}
56
- },
57
- tags: doc.tags ?? []
58
- };
59
- const definitions = doc.definitions ?? {};
60
- for (const [name, schema] of Object.entries(definitions)) {
61
- result.components.schemas[name] = normalizeNullable30(schema);
62
- }
63
- const secDefs = doc.securityDefinitions ?? {};
64
- for (const [name, scheme] of Object.entries(secDefs)) {
65
- result.components.securitySchemes[name] = convertSecurityScheme2(scheme);
66
- }
67
- const paths = doc.paths ?? {};
68
- for (const [path, pathItem] of Object.entries(paths)) {
69
- result.paths[path] = normalizePathItem2(pathItem, globalConsumes, globalProduces, warnings);
70
- }
71
- if (doc.security) {
72
- result.security = doc.security;
73
- }
74
- return result;
75
- }
76
- __name(normalizeSwagger2, "normalizeSwagger2");
77
- function normalizePathItem2(pathItem, globalConsumes, globalProduces, warnings) {
78
- const methods = [
79
- "get",
80
- "post",
81
- "put",
82
- "patch",
83
- "delete",
84
- "head",
85
- "options"
86
- ];
87
- const normalized = {};
88
- const pathParams = pathItem.parameters ?? [];
89
- for (const method of methods) {
90
- const op = pathItem[method];
91
- if (!op) continue;
92
- const opConsumes = op.consumes ?? globalConsumes;
93
- const opProduces = op.produces ?? globalProduces;
94
- const params = [
95
- ...pathParams,
96
- ...op.parameters ?? []
97
- ];
98
- const nonBodyParams = [];
99
- let requestBody;
100
- for (const param of params) {
101
- if (param.in === "body") {
102
- const contentType = opConsumes[0] ?? "application/json";
103
- requestBody = {
104
- description: param.description,
105
- required: param.required ?? true,
106
- content: {
107
- [contentType]: {
108
- schema: normalizeNullable30(param.schema ?? {})
109
- }
110
- }
111
- };
112
- } else if (param.in === "formData") {
113
- warnings.info(`#/paths/${encodePathSegment(method)}`, "formData parameters converted to multipart/form-data requestBody");
114
- if (!requestBody) {
115
- requestBody = {
116
- content: {
117
- "multipart/form-data": {
118
- schema: {
119
- type: "object",
120
- properties: {},
121
- required: []
122
- }
123
- }
124
- }
125
- };
126
- }
127
- const formSchema = requestBody.content["multipart/form-data"].schema;
128
- const props = formSchema.properties;
129
- props[param.name] = normalizeNullable30(param);
130
- if (param.required) {
131
- formSchema.required.push(param.name);
132
- }
133
- } else {
134
- const normalizedParam = {
135
- ...param
136
- };
137
- if (param.type) {
138
- normalizedParam.schema = normalizeNullable30({
139
- type: param.type,
140
- format: param.format,
141
- enum: param.enum,
142
- items: param.items,
143
- default: param.default,
144
- minimum: param.minimum,
145
- maximum: param.maximum,
146
- minLength: param.minLength,
147
- maxLength: param.maxLength,
148
- pattern: param.pattern
149
- });
150
- delete normalizedParam.type;
151
- delete normalizedParam.format;
152
- delete normalizedParam.enum;
153
- delete normalizedParam.items;
154
- }
155
- nonBodyParams.push(normalizedParam);
156
- }
157
- }
158
- const responses = {};
159
- const opResponses = op.responses ?? {};
160
- for (const [code, resp] of Object.entries(opResponses)) {
161
- const contentType = opProduces[0] ?? "application/json";
162
- const headers = convertResponseHeaders2(resp.headers);
163
- const responseEntry = {
164
- description: resp.description ?? ""
165
- };
166
- if (resp.schema) {
167
- responseEntry.content = {
168
- [contentType]: {
169
- schema: normalizeNullable30(resp.schema)
170
- }
171
- };
172
- }
173
- if (headers) {
174
- responseEntry.headers = headers;
175
- }
176
- responses[code] = responseEntry;
177
- }
178
- normalized[method] = {
179
- operationId: op.operationId,
180
- summary: op.summary,
181
- description: op.description,
182
- tags: op.tags,
183
- parameters: nonBodyParams.length > 0 ? nonBodyParams : void 0,
184
- requestBody,
185
- responses,
186
- security: op.security,
187
- deprecated: op.deprecated
188
- };
189
- }
190
- return normalized;
191
- }
192
- __name(normalizePathItem2, "normalizePathItem2");
193
- function convertResponseHeaders2(headers) {
194
- if (!headers) return void 0;
195
- const out = {};
196
- for (const [name, header] of Object.entries(headers)) {
197
- if (!header || typeof header !== "object") continue;
198
- const { description, type, format, items, ...rest } = header;
199
- const schema = {
200
- ...rest
201
- };
202
- if (type !== void 0) schema.type = type;
203
- if (format !== void 0) schema.format = format;
204
- if (items !== void 0) schema.items = items;
205
- const normalized = {};
206
- if (description !== void 0) normalized.description = description;
207
- if (Object.keys(schema).length > 0) normalized.schema = normalizeNullable30(schema);
208
- out[name] = normalized;
209
- }
210
- return Object.keys(out).length > 0 ? out : void 0;
211
- }
212
- __name(convertResponseHeaders2, "convertResponseHeaders2");
213
- function convertSecurityScheme2(scheme) {
214
- const type = scheme.type;
215
- if (type === "basic") {
216
- return {
217
- type: "http",
218
- scheme: "basic"
219
- };
220
- }
221
- if (type === "apiKey") {
222
- return {
223
- type: "apiKey",
224
- name: scheme.name,
225
- in: scheme.in
226
- };
227
- }
228
- if (type === "oauth2") {
229
- const flow = scheme.flow;
230
- const flows = {};
231
- if (flow === "implicit") {
232
- flows.implicit = {
233
- authorizationUrl: scheme.authorizationUrl,
234
- scopes: scheme.scopes ?? {}
235
- };
236
- } else if (flow === "password") {
237
- flows.password = {
238
- tokenUrl: scheme.tokenUrl,
239
- scopes: scheme.scopes ?? {}
240
- };
241
- } else if (flow === "application") {
242
- flows.clientCredentials = {
243
- tokenUrl: scheme.tokenUrl,
244
- scopes: scheme.scopes ?? {}
245
- };
246
- } else if (flow === "accessCode") {
247
- flows.authorizationCode = {
248
- authorizationUrl: scheme.authorizationUrl,
249
- tokenUrl: scheme.tokenUrl,
250
- scopes: scheme.scopes ?? {}
251
- };
252
- }
253
- return {
254
- type: "oauth2",
255
- flows
256
- };
257
- }
258
- return scheme;
259
- }
260
- __name(convertSecurityScheme2, "convertSecurityScheme2");
261
- function normalizeOas30(doc, _warnings) {
262
- if (doc.components?.schemas) {
263
- for (const [name, schema] of Object.entries(doc.components.schemas)) {
264
- doc.components.schemas[name] = normalizeNullable30(schema);
265
- }
266
- }
267
- if (doc.paths) {
268
- for (const pathItem of Object.values(doc.paths)) {
269
- normalizePathItemSchemas(pathItem);
270
- }
271
- }
272
- doc.openapi = "3.1.0";
273
- return doc;
274
- }
275
- __name(normalizeOas30, "normalizeOas30");
276
- function normalizePathItemSchemas(pathItem) {
277
- const methods = [
278
- "get",
279
- "post",
280
- "put",
281
- "patch",
282
- "delete",
283
- "head",
284
- "options"
285
- ];
286
- for (const method of methods) {
287
- const op = pathItem[method];
288
- if (!op) continue;
289
- const params = op.parameters ?? [];
290
- for (const param of params) {
291
- if (param.schema) {
292
- param.schema = normalizeNullable30(param.schema);
293
- }
294
- }
295
- const reqBody = op.requestBody;
296
- if (reqBody?.content) {
297
- for (const mediaType of Object.values(reqBody.content)) {
298
- if (mediaType.schema) {
299
- mediaType.schema = normalizeNullable30(mediaType.schema);
300
- }
301
- }
302
- }
303
- const responses = op.responses ?? {};
304
- for (const resp of Object.values(responses)) {
305
- if (resp.content) {
306
- for (const mediaType of Object.values(resp.content)) {
307
- if (mediaType.schema) {
308
- mediaType.schema = normalizeNullable30(mediaType.schema);
309
- }
310
- }
311
- }
312
- }
313
- }
314
- }
315
- __name(normalizePathItemSchemas, "normalizePathItemSchemas");
316
- function normalizeNullable30(schema) {
317
- if (!schema || typeof schema !== "object") return schema;
318
- const result = {
319
- ...schema
320
- };
321
- if (result.nullable === true && typeof result.type === "string") {
322
- result.type = [
323
- result.type,
324
- "null"
325
- ];
326
- delete result.nullable;
327
- }
328
- if (result.properties && typeof result.properties === "object") {
329
- const props = result.properties;
330
- for (const [key, val] of Object.entries(props)) {
331
- props[key] = normalizeNullable30(val);
332
- }
333
- }
334
- if (result.items && typeof result.items === "object" && !Array.isArray(result.items)) {
335
- result.items = normalizeNullable30(result.items);
336
- }
337
- if (result.additionalProperties && typeof result.additionalProperties === "object") {
338
- result.additionalProperties = normalizeNullable30(result.additionalProperties);
339
- }
340
- for (const combiner of [
341
- "allOf",
342
- "oneOf",
343
- "anyOf"
344
- ]) {
345
- if (Array.isArray(result[combiner])) {
346
- result[combiner] = result[combiner].map(normalizeNullable30);
347
- }
348
- }
349
- return result;
350
- }
351
- __name(normalizeNullable30, "normalizeNullable30");
352
- function encodePathSegment(s) {
353
- return s.replace(/~/g, "~0").replace(/\//g, "~1");
354
- }
355
- __name(encodePathSegment, "encodePathSegment");
356
-
357
- // src/circular-refs.ts
358
- function detectCircularRefs(schemas) {
359
- const circular = /* @__PURE__ */ new Set();
360
- const visiting = /* @__PURE__ */ new Set();
361
- const visited = /* @__PURE__ */ new Set();
362
- function visit(name) {
363
- if (visited.has(name)) return;
364
- if (visiting.has(name)) {
365
- circular.add(name);
366
- return;
367
- }
368
- visiting.add(name);
369
- const schema = schemas[name];
370
- if (schema && typeof schema === "object") {
371
- for (const ref of collectRefs(schema)) {
372
- const refName = extractRefName(ref);
373
- if (refName && schemas[refName]) {
374
- visit(refName);
375
- }
376
- }
377
- }
378
- visiting.delete(name);
379
- visited.add(name);
380
- }
381
- __name(visit, "visit");
382
- for (const name of Object.keys(schemas)) {
383
- visit(name);
384
- }
385
- return circular;
386
- }
387
- __name(detectCircularRefs, "detectCircularRefs");
388
- function collectRefs(obj) {
389
- const refs = [];
390
- function walk(val) {
391
- if (!val || typeof val !== "object") return;
392
- if (Array.isArray(val)) {
393
- for (const item of val) walk(item);
394
- return;
395
- }
396
- const record = val;
397
- if (typeof record.$ref === "string") {
398
- refs.push(record.$ref);
399
- }
400
- for (const v of Object.values(record)) {
401
- walk(v);
402
- }
403
- }
404
- __name(walk, "walk");
405
- walk(obj);
406
- return refs;
407
- }
408
- __name(collectRefs, "collectRefs");
409
- function extractRefName(ref) {
410
- const match = ref.match(/^#\/(?:components\/schemas|definitions)\/(.+)$/);
411
- return match?.[1];
412
- }
413
- __name(extractRefName, "extractRefName");
414
-
415
- // src/schema-to-ast.ts
416
- var LOC = {
417
- file: "",
418
- line: 0
419
- };
420
- var FORMAT_TO_SCALAR = {
421
- email: "email",
422
- uri: "url",
423
- url: "url",
424
- uuid: "uuid",
425
- date: "date",
426
- "date-time": "datetime",
427
- time: "time",
428
- binary: "binary",
429
- int64: "bigint"
430
- };
431
- function schemasToModels(schemas, ctx) {
432
- const models = [];
433
- for (const [name, schema] of Object.entries(schemas)) {
434
- const modelCtx = {
435
- ...ctx,
436
- path: `#/components/schemas/${name}`
437
- };
438
- const model = schemaToModel(name, schema, modelCtx);
439
- if (model) models.push(model);
440
- }
441
- models.push(...ctx.extractedModels);
442
- return models;
443
- }
444
- __name(schemasToModels, "schemasToModels");
445
- function schemaToModel(name, schema, ctx) {
446
- warnUnsupported(schema, ctx);
447
- const description = ctx.includeComments ? schema.description : void 0;
448
- if (schema.allOf && schema.allOf.length === 2) {
449
- const [first, second] = schema.allOf;
450
- const refMember = first?.$ref ? first : second?.$ref ? second : null;
451
- const objectMember = first?.$ref ? second : first;
452
- if (refMember?.$ref && objectMember?.properties) {
453
- const baseName = extractRefName(refMember.$ref);
454
- if (baseName) {
455
- const fields = schemaPropertiesToFields(objectMember, ctx);
456
- return {
457
- kind: "model",
458
- name,
459
- base: baseName,
460
- fields,
461
- description,
462
- loc: LOC
463
- };
464
- }
465
- }
466
- }
467
- if (schema.properties || schema.type === "object" && !schema.additionalProperties) {
468
- const fields = schemaPropertiesToFields(schema, ctx);
469
- return {
470
- kind: "model",
471
- name,
472
- fields,
473
- description,
474
- loc: LOC
475
- };
476
- }
477
- const typeNode = schemaToTypeNode(schema, ctx);
478
- return {
479
- kind: "model",
480
- name,
481
- fields: [],
482
- type: typeNode,
483
- description,
484
- loc: LOC
485
- };
486
- }
487
- __name(schemaToModel, "schemaToModel");
488
- function schemaToTypeNode(schema, ctx) {
489
- if (schema.$ref) {
490
- const refName = extractRefName(schema.$ref);
491
- if (refName) {
492
- if (ctx.circularRefs.has(refName)) {
493
- return {
494
- kind: "lazy",
495
- inner: {
496
- kind: "ref",
497
- name: refName
498
- }
499
- };
500
- }
501
- return {
502
- kind: "ref",
503
- name: refName
504
- };
505
- }
506
- ctx.warnings.warn(ctx.path, `Unresolvable $ref: ${schema.$ref}`);
507
- return {
508
- kind: "scalar",
509
- name: "unknown"
510
- };
511
- }
512
- if (schema.const !== void 0) {
513
- return {
514
- kind: "literal",
515
- value: schema.const
516
- };
517
- }
518
- if (schema.enum) {
519
- return {
520
- kind: "enum",
521
- values: schema.enum.map(String)
522
- };
523
- }
524
- if (schema.oneOf && schema.oneOf.length > 0) {
525
- if (schema.discriminator?.propertyName) {
526
- return toDiscriminatedUnion(schema.oneOf, schema.discriminator.propertyName, ctx);
527
- }
528
- return toUnion(schema.oneOf, ctx);
529
- }
530
- if (schema.anyOf && schema.anyOf.length > 0) {
531
- if (schema.discriminator?.propertyName) {
532
- return toDiscriminatedUnion(schema.anyOf, schema.discriminator.propertyName, ctx);
533
- }
534
- return toUnion(schema.anyOf, ctx);
535
- }
536
- if (schema.allOf && schema.allOf.length > 0) {
537
- if (schema.allOf.length === 1) {
538
- return schemaToTypeNode(schema.allOf[0], ctx);
539
- }
540
- return {
541
- kind: "intersection",
542
- members: schema.allOf.map((s) => schemaToTypeNode(s, ctx))
543
- };
544
- }
545
- const types = normalizeTypeField(schema);
546
- if (types === null) {
547
- if (schema.properties) {
548
- return schemaToInlineObject(schema, ctx);
549
- }
550
- return {
551
- kind: "scalar",
552
- name: "unknown"
553
- };
554
- }
555
- const { baseType, nullable } = types;
556
- let typeNode;
557
- switch (baseType) {
558
- case "string":
559
- typeNode = stringSchemaToType(schema);
560
- break;
561
- case "integer":
562
- typeNode = integerSchemaToType(schema);
563
- break;
564
- case "number":
565
- typeNode = numberSchemaToType(schema);
566
- break;
567
- case "boolean":
568
- typeNode = {
569
- kind: "scalar",
570
- name: "boolean"
571
- };
572
- break;
573
- case "null":
574
- typeNode = {
575
- kind: "scalar",
576
- name: "null"
577
- };
578
- break;
579
- case "array":
580
- typeNode = arraySchemaToType(schema, ctx);
581
- break;
582
- case "object":
583
- typeNode = objectSchemaToType(schema, ctx);
584
- break;
585
- default:
586
- ctx.warnings.warn(ctx.path, `Unknown type: ${baseType}`);
587
- typeNode = {
588
- kind: "scalar",
589
- name: "unknown"
590
- };
591
- }
592
- if (nullable) {
593
- return {
594
- kind: "union",
595
- members: [
596
- typeNode,
597
- {
598
- kind: "scalar",
599
- name: "null"
600
- }
601
- ]
602
- };
603
- }
604
- return typeNode;
605
- }
606
- __name(schemaToTypeNode, "schemaToTypeNode");
607
- function stringSchemaToType(schema) {
608
- if (schema.format) {
609
- const scalarName = FORMAT_TO_SCALAR[schema.format];
610
- if (scalarName) {
611
- return {
612
- kind: "scalar",
613
- name: scalarName
614
- };
615
- }
616
- }
617
- const mods = {};
618
- if (schema.minLength !== void 0 && schema.maxLength !== void 0 && schema.minLength === schema.maxLength) {
619
- mods.len = schema.minLength;
620
- } else {
621
- if (schema.minLength !== void 0) mods.min = schema.minLength;
622
- if (schema.maxLength !== void 0) mods.max = schema.maxLength;
623
- }
624
- if (schema.pattern) mods.regex = `/${schema.pattern}/`;
625
- if (schema.format && !FORMAT_TO_SCALAR[schema.format]) mods.format = schema.format;
626
- return {
627
- kind: "scalar",
628
- name: "string",
629
- ...mods
630
- };
631
- }
632
- __name(stringSchemaToType, "stringSchemaToType");
633
- function integerSchemaToType(schema) {
634
- const name = schema.format === "int64" ? "bigint" : "int";
635
- const mods = {};
636
- if (schema.minimum !== void 0) mods.min = schema.minimum;
637
- if (schema.maximum !== void 0) mods.max = schema.maximum;
638
- return {
639
- kind: "scalar",
640
- name,
641
- ...mods
642
- };
643
- }
644
- __name(integerSchemaToType, "integerSchemaToType");
645
- function numberSchemaToType(schema) {
646
- const mods = {};
647
- if (schema.minimum !== void 0) mods.min = schema.minimum;
648
- if (schema.maximum !== void 0) mods.max = schema.maximum;
649
- return {
650
- kind: "scalar",
651
- name: "number",
652
- ...mods
653
- };
654
- }
655
- __name(numberSchemaToType, "numberSchemaToType");
656
- function arraySchemaToType(schema, ctx) {
657
- if (schema.prefixItems && schema.prefixItems.length > 0) {
658
- return {
659
- kind: "tuple",
660
- items: schema.prefixItems.map((s) => schemaToTypeNode(s, ctx))
661
- };
662
- }
663
- const item = schema.items ? schemaToTypeNode(schema.items, ctx) : {
664
- kind: "scalar",
665
- name: "unknown"
666
- };
667
- const mods = {};
668
- if (schema.minItems !== void 0) mods.min = schema.minItems;
669
- if (schema.maxItems !== void 0) mods.max = schema.maxItems;
670
- return {
671
- kind: "array",
672
- item,
673
- ...mods
674
- };
675
- }
676
- __name(arraySchemaToType, "arraySchemaToType");
677
- function objectSchemaToType(schema, ctx) {
678
- if (schema.additionalProperties && typeof schema.additionalProperties === "object" && !schema.properties) {
679
- return {
680
- kind: "record",
681
- key: {
682
- kind: "scalar",
683
- name: "string"
684
- },
685
- value: schemaToTypeNode(schema.additionalProperties, ctx)
686
- };
687
- }
688
- if (schema.properties) {
689
- return schemaToInlineObject(schema, ctx);
690
- }
691
- return {
692
- kind: "scalar",
693
- name: "object"
694
- };
695
- }
696
- __name(objectSchemaToType, "objectSchemaToType");
697
- function schemaToInlineObject(schema, ctx) {
698
- const fields = schemaPropertiesToFields(schema, ctx);
699
- return {
700
- kind: "inlineObject",
701
- fields
702
- };
703
- }
704
- __name(schemaToInlineObject, "schemaToInlineObject");
705
- function schemaPropertiesToFields(schema, ctx) {
706
- const properties = schema.properties ?? {};
707
- const required = new Set(schema.required ?? []);
708
- const fields = [];
709
- for (const [name, propSchema] of Object.entries(properties)) {
710
- const propCtx = {
711
- ...ctx,
712
- path: `${ctx.path}/properties/${name}`
713
- };
714
- const fieldType = schemaToTypeNode(propSchema, propCtx);
715
- let nullable = false;
716
- let effectiveType = fieldType;
717
- if (fieldType.kind === "union") {
718
- const nonNull = fieldType.members.filter((m) => !(m.kind === "scalar" && m.name === "null"));
719
- if (nonNull.length < fieldType.members.length) {
720
- nullable = true;
721
- effectiveType = nonNull.length === 1 ? nonNull[0] : {
722
- kind: "union",
723
- members: nonNull
724
- };
725
- }
726
- }
727
- const visibility = propSchema.readOnly ? "readonly" : propSchema.writeOnly ? "writeonly" : "normal";
728
- fields.push({
729
- name,
730
- optional: !required.has(name),
731
- nullable,
732
- visibility,
733
- type: effectiveType,
734
- default: propSchema.default,
735
- deprecated: propSchema.deprecated,
736
- description: ctx.includeComments ? propSchema.description : void 0,
737
- loc: LOC
738
- });
739
- }
740
- return fields;
741
- }
742
- __name(schemaPropertiesToFields, "schemaPropertiesToFields");
743
- function normalizeTypeField(schema) {
744
- if (!schema.type) return null;
745
- if (typeof schema.type === "string") {
746
- return {
747
- baseType: schema.type,
748
- nullable: false
749
- };
750
- }
751
- if (Array.isArray(schema.type)) {
752
- const types = schema.type;
753
- const nonNull = types.filter((t) => t !== "null");
754
- const nullable = types.includes("null");
755
- if (nonNull.length === 1) {
756
- return {
757
- baseType: nonNull[0],
758
- nullable
759
- };
760
- }
761
- if (nonNull.length === 0) {
762
- return {
763
- baseType: "null",
764
- nullable: false
765
- };
766
- }
767
- return {
768
- baseType: nonNull[0],
769
- nullable
770
- };
771
- }
772
- return null;
773
- }
774
- __name(normalizeTypeField, "normalizeTypeField");
775
- function toUnion(schemas, ctx) {
776
- const members = schemas.map((s) => schemaToTypeNode(s, ctx));
777
- if (members.length === 1) return members[0];
778
- return {
779
- kind: "union",
780
- members
781
- };
782
- }
783
- __name(toUnion, "toUnion");
784
- function toDiscriminatedUnion(schemas, discriminator, ctx) {
785
- const members = schemas.map((s) => schemaToTypeNode(s, ctx));
786
- if (members.length === 1) return members[0];
787
- return {
788
- kind: "discriminatedUnion",
789
- discriminator,
790
- members
791
- };
792
- }
793
- __name(toDiscriminatedUnion, "toDiscriminatedUnion");
794
- function warnUnsupported(schema, ctx) {
795
- if (schema.xml) ctx.warnings.warn(ctx.path, "xml metadata is not supported, skipping");
796
- if (schema.externalDocs) ctx.warnings.info(ctx.path, "externalDocs is not supported, skipping");
797
- if (schema.not) ctx.warnings.warn(ctx.path, "not keyword is not supported, skipping");
798
- }
799
- __name(warnUnsupported, "warnUnsupported");
800
- function extractInlineModel(schema, suggestedName, ctx) {
801
- if (schema.$ref) {
802
- return {
803
- typeNode: schemaToTypeNode(schema, ctx)
804
- };
805
- }
806
- if (schema.properties || schema.type === "object" && schema.additionalProperties === void 0) {
807
- const fields = schemaPropertiesToFields(schema, ctx);
808
- const model = {
809
- kind: "model",
810
- name: suggestedName,
811
- fields,
812
- description: ctx.includeComments ? schema.description : void 0,
813
- loc: LOC
814
- };
815
- return {
816
- typeNode: {
817
- kind: "ref",
818
- name: suggestedName
819
- },
820
- model
821
- };
822
- }
823
- return {
824
- typeNode: schemaToTypeNode(schema, ctx)
825
- };
826
- }
827
- __name(extractInlineModel, "extractInlineModel");
828
- function sanitizeName(name, warnings) {
829
- const cleaned = name.replace(/[^a-zA-Z0-9_$]/g, " ").split(/\s+/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
830
- if (cleaned !== name) {
831
- warnings.info(`#/components/schemas/${name}`, `Schema name sanitized: "${name}" \u2192 "${cleaned}"`);
832
- }
833
- return cleaned || "UnnamedSchema";
834
- }
835
- __name(sanitizeName, "sanitizeName");
836
-
837
- // src/paths-to-ast.ts
838
- var LOC2 = {
839
- file: "",
840
- line: 0
841
- };
842
- var HTTP_METHODS = [
843
- "get",
844
- "post",
845
- "put",
846
- "patch",
847
- "delete"
848
- ];
849
- function pathsToRoutes(doc, ctx) {
850
- const routes = [];
851
- const routeTags = /* @__PURE__ */ new Map();
852
- const paths = doc.paths ?? {};
853
- for (const [path, pathItem] of Object.entries(paths)) {
854
- if (!pathItem) continue;
855
- const result = pathItemToRoute(path, pathItem, ctx);
856
- if (result) {
857
- routes.push(result.route);
858
- routeTags.set(result.route, result.tag);
859
- }
860
- }
861
- return {
862
- routes,
863
- routeTags
864
- };
865
- }
866
- __name(pathsToRoutes, "pathsToRoutes");
867
- function pathItemToRoute(path, pathItem, ctx) {
868
- const operations = [];
869
- let primaryTag = "default";
870
- const pathParams = (pathItem.parameters ?? []).filter((p) => p.in === "path");
871
- for (const method of HTTP_METHODS) {
872
- const op = pathItem[method];
873
- if (!op) continue;
874
- const opNode = operationToNode(method, op, path, ctx);
875
- operations.push(opNode);
876
- if (op.tags && op.tags.length > 0 && primaryTag === "default") {
877
- primaryTag = op.tags[0];
878
- }
879
- }
880
- if (operations.length === 0) return null;
881
- const params = buildPathParams(path, pathParams, pathItem, ctx);
882
- const route = {
883
- path,
884
- operations,
885
- loc: LOC2
886
- };
887
- if (params.length > 0) {
888
- route.params = {
889
- kind: "params",
890
- nodes: params
891
- };
892
- }
893
- if (pathItem.description && ctx.includeComments) {
894
- route.description = pathItem.description;
895
- }
896
- return {
897
- route,
898
- tag: primaryTag
899
- };
900
- }
901
- __name(pathItemToRoute, "pathItemToRoute");
902
- function operationToNode(method, op, path, ctx) {
903
- const pathPrefix = `#/paths/${encodePathSegment2(path)}/${method}`;
904
- const schemaCtx = makeSchemaCtx(ctx, pathPrefix);
905
- const node = {
906
- method,
907
- responses: [],
908
- loc: LOC2
909
- };
910
- if (op.operationId) {
911
- node.sdk = op.operationId;
912
- }
913
- if (op.description && ctx.includeComments) {
914
- node.description = op.description;
915
- }
916
- if (op.deprecated) {
917
- node.modifiers = [
918
- "deprecated"
919
- ];
920
- }
921
- const queryParams = [];
922
- const headerParams = [];
923
- for (const param of op.parameters ?? []) {
924
- if (param.in === "query") {
925
- queryParams.push(parameterToNode(param, schemaCtx));
926
- } else if (param.in === "header") {
927
- headerParams.push(parameterToNode(param, schemaCtx));
928
- }
929
- }
930
- if (queryParams.length > 0) {
931
- node.query = {
932
- kind: "params",
933
- nodes: queryParams
934
- };
935
- }
936
- if (headerParams.length > 0) {
937
- node.headers = {
938
- kind: "params",
939
- nodes: headerParams
940
- };
941
- }
942
- if (op.requestBody) {
943
- node.request = requestBodyToNode(op.requestBody, op.operationId ?? `${method}${toPascalCase(path)}`, schemaCtx, ctx);
944
- }
945
- const responses = op.responses ?? {};
946
- for (const [code, resp] of Object.entries(responses)) {
947
- const statusCode = parseInt(code, 10);
948
- if (isNaN(statusCode)) continue;
949
- const respNode = responseToNode(statusCode, resp, op.operationId ?? `${method}${toPascalCase(path)}`, schemaCtx, ctx);
950
- node.responses.push(respNode);
951
- }
952
- if (op.security !== void 0) {
953
- node.security = convertSecurity(op.security);
954
- }
955
- return node;
956
- }
957
- __name(operationToNode, "operationToNode");
958
- function buildPathParams(path, pathLevelParams, pathItem, ctx) {
959
- const schemaCtx = makeSchemaCtx(ctx, `#/paths/${encodePathSegment2(path)}`);
960
- const paramMap = /* @__PURE__ */ new Map();
961
- for (const p of pathLevelParams) {
962
- paramMap.set(p.name, p);
963
- }
964
- for (const method of HTTP_METHODS) {
965
- const op = pathItem[method];
966
- if (!op?.parameters) continue;
967
- for (const p of op.parameters) {
968
- if (p.in === "path" && !paramMap.has(p.name)) {
969
- paramMap.set(p.name, p);
970
- }
971
- }
972
- }
973
- const templateNames = [
974
- ...path.matchAll(/\{([^}]+)\}/g)
975
- ].map((m) => m[1]);
976
- return templateNames.map((name) => {
977
- const param = paramMap.get(name);
978
- if (param) {
979
- return parameterToNode(param, schemaCtx);
980
- }
981
- return {
982
- name,
983
- optional: false,
984
- nullable: false,
985
- type: {
986
- kind: "scalar",
987
- name: "string"
988
- },
989
- loc: LOC2
990
- };
991
- });
992
- }
993
- __name(buildPathParams, "buildPathParams");
994
- function parameterToNode(param, ctx) {
995
- const type = param.schema ? schemaToTypeNode(param.schema, ctx) : {
996
- kind: "scalar",
997
- name: "string"
998
- };
999
- return {
1000
- name: param.name,
1001
- optional: param.in !== "path" && !param.required,
1002
- nullable: false,
1003
- type,
1004
- description: ctx.includeComments ? param.description : void 0,
1005
- loc: LOC2
1006
- };
1007
- }
1008
- __name(parameterToNode, "parameterToNode");
1009
- function requestBodyToNode(reqBody, operationName, schemaCtx, ctx) {
1010
- const content = reqBody.content;
1011
- if (!content) return void 0;
1012
- const supported = /* @__PURE__ */ new Set([
1013
- "application/json",
1014
- "application/x-www-form-urlencoded",
1015
- "multipart/form-data"
1016
- ]);
1017
- const bodies = [];
1018
- for (const [contentType, mediaType] of Object.entries(content)) {
1019
- if (!supported.has(contentType) || !mediaType?.schema) continue;
1020
- const { typeNode, model } = extractInlineModel(mediaType.schema, `${toPascalCase(operationName)}Request`, schemaCtx);
1021
- if (model) {
1022
- ctx.extractedModels.push(model);
1023
- }
1024
- bodies.push({
1025
- contentType,
1026
- bodyType: typeNode
1027
- });
1028
- }
1029
- if (bodies.length === 0) return void 0;
1030
- return {
1031
- bodies
1032
- };
1033
- }
1034
- __name(requestBodyToNode, "requestBodyToNode");
1035
- function responseToNode(statusCode, resp, operationName, schemaCtx, ctx) {
1036
- const headers = convertResponseHeaders(resp.headers, schemaCtx);
1037
- if (!resp.content) {
1038
- return headers ? {
1039
- statusCode,
1040
- headers
1041
- } : {
1042
- statusCode
1043
- };
1044
- }
1045
- const [contentType, mediaType] = Object.entries(resp.content)[0] ?? [];
1046
- if (!contentType || !mediaType?.schema) {
1047
- return headers ? {
1048
- statusCode,
1049
- headers
1050
- } : {
1051
- statusCode
1052
- };
1053
- }
1054
- const { typeNode, model } = extractInlineModel(mediaType.schema, `${toPascalCase(operationName)}Response${statusCode}`, schemaCtx);
1055
- if (model) {
1056
- ctx.extractedModels.push(model);
1057
- }
1058
- return {
1059
- statusCode,
1060
- contentType,
1061
- bodyType: typeNode,
1062
- ...headers ? {
1063
- headers
1064
- } : {}
1065
- };
1066
- }
1067
- __name(responseToNode, "responseToNode");
1068
- function convertResponseHeaders(headers, schemaCtx) {
1069
- if (!headers) return void 0;
1070
- const out = [];
1071
- for (const [name, header] of Object.entries(headers)) {
1072
- if (!header) continue;
1073
- const type = header.schema ? schemaToTypeNode(header.schema, schemaCtx) : {
1074
- kind: "scalar",
1075
- name: "string"
1076
- };
1077
- out.push({
1078
- name,
1079
- optional: !header.required,
1080
- type,
1081
- description: schemaCtx.includeComments ? header.description : void 0
1082
- });
1083
- }
1084
- return out.length > 0 ? out : void 0;
1085
- }
1086
- __name(convertResponseHeaders, "convertResponseHeaders");
1087
- function convertSecurity(security) {
1088
- if (security.length === 0) {
1089
- return "none";
1090
- }
1091
- const allScopes = [];
1092
- for (const requirement of security) {
1093
- for (const scopes of Object.values(requirement)) {
1094
- allScopes.push(...scopes);
1095
- }
1096
- }
1097
- if (allScopes.length > 0) {
1098
- return {
1099
- roles: allScopes,
1100
- loc: LOC2
1101
- };
1102
- }
1103
- return {
1104
- roles: [],
1105
- loc: LOC2
1106
- };
1107
- }
1108
- __name(convertSecurity, "convertSecurity");
1109
- function makeSchemaCtx(ctx, path) {
1110
- return {
1111
- circularRefs: ctx.circularRefs,
1112
- warnings: ctx.warnings,
1113
- path,
1114
- includeComments: ctx.includeComments,
1115
- namedSchemas: ctx.namedSchemas,
1116
- extractedModels: ctx.extractedModels,
1117
- inlineCounter: 0
1118
- };
1119
- }
1120
- __name(makeSchemaCtx, "makeSchemaCtx");
1121
- function toPascalCase(input) {
1122
- return input.replace(/[^a-zA-Z0-9]/g, " ").split(/\s+/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
1123
- }
1124
- __name(toPascalCase, "toPascalCase");
1125
- function encodePathSegment2(s) {
1126
- return s.replace(/~/g, "~0").replace(/\//g, "~1");
1127
- }
1128
- __name(encodePathSegment2, "encodePathSegment");
1129
-
1130
- // src/tag-splitter.ts
1131
- function splitByTag(models, routes, routeTags) {
1132
- const routesByTag = /* @__PURE__ */ new Map();
1133
- for (const route of routes) {
1134
- const tag = routeTags.get(route) ?? "default";
1135
- const group = routesByTag.get(tag) ?? [];
1136
- group.push(route);
1137
- routesByTag.set(tag, group);
1138
- }
1139
- const modelsByTag = /* @__PURE__ */ new Map();
1140
- for (const [tag, tagRoutes] of routesByTag) {
1141
- const refs = /* @__PURE__ */ new Set();
1142
- for (const route of tagRoutes) {
1143
- collectRouteRefs(route, refs);
1144
- }
1145
- modelsByTag.set(tag, refs);
1146
- }
1147
- const modelNameToModel = new Map(models.map((m) => [
1148
- m.name,
1149
- m
1150
- ]));
1151
- const modelAssignment = /* @__PURE__ */ new Map();
1152
- for (const model of models) {
1153
- const tags = [];
1154
- for (const [tag, refs] of modelsByTag) {
1155
- if (refs.has(model.name)) {
1156
- tags.push(tag);
1157
- }
1158
- }
1159
- if (tags.length === 0) {
1160
- modelAssignment.set(model.name, "shared");
1161
- } else if (tags.length === 1) {
1162
- modelAssignment.set(model.name, tags[0]);
1163
- } else {
1164
- modelAssignment.set(model.name, "shared");
1165
- }
1166
- }
1167
- for (const model of models) {
1168
- if (modelAssignment.get(model.name) === "shared") {
1169
- const refs = /* @__PURE__ */ new Set();
1170
- collectModelRefs(model, refs);
1171
- for (const ref of refs) {
1172
- if (modelNameToModel.has(ref)) {
1173
- const currentTag = modelAssignment.get(ref);
1174
- if (currentTag && currentTag !== "shared") {
1175
- const otherTags = [
1176
- ...modelsByTag.entries()
1177
- ].filter(([t, r]) => t !== currentTag && r.has(ref)).map(([t]) => t);
1178
- if (otherTags.length > 0) {
1179
- modelAssignment.set(ref, "shared");
1180
- }
1181
- }
1182
- }
1183
- }
1184
- }
1185
- }
1186
- const result = /* @__PURE__ */ new Map();
1187
- const allTags = /* @__PURE__ */ new Set([
1188
- ...routesByTag.keys(),
1189
- ...new Set(modelAssignment.values())
1190
- ]);
1191
- for (const tag of allTags) {
1192
- const tagModels = models.filter((m) => modelAssignment.get(m.name) === tag);
1193
- const tagRoutes = routesByTag.get(tag) ?? [];
1194
- if (tagModels.length === 0 && tagRoutes.length === 0) continue;
1195
- const filename = sanitizeFilename(tag);
1196
- result.set(`${filename}.ck`, {
1197
- kind: "ckRoot",
1198
- meta: tag !== "shared" ? {
1199
- area: tag
1200
- } : {},
1201
- services: {},
1202
- models: tagModels,
1203
- routes: tagRoutes,
1204
- file: `${filename}.ck`
1205
- });
1206
- }
1207
- return result;
1208
- }
1209
- __name(splitByTag, "splitByTag");
1210
- function mergeIntoSingle(models, routes, filename = "api") {
1211
- return {
1212
- kind: "ckRoot",
1213
- meta: {},
1214
- services: {},
1215
- models,
1216
- routes,
1217
- file: `${filename}.ck`
1218
- };
1219
- }
1220
- __name(mergeIntoSingle, "mergeIntoSingle");
1221
- function collectRouteRefs(route, refs) {
1222
- if (route.params) {
1223
- collectParamSourceRefs(route.params, refs);
1224
- }
1225
- for (const op of route.operations) {
1226
- if (op.query) collectParamSourceRefs(op.query, refs);
1227
- if (op.headers) collectParamSourceRefs(op.headers, refs);
1228
- if (op.request) {
1229
- for (const body of op.request.bodies) collectTypeRefs(body.bodyType, refs);
1230
- }
1231
- for (const resp of op.responses) {
1232
- if (resp.bodyType) collectTypeRefs(resp.bodyType, refs);
1233
- }
1234
- }
1235
- }
1236
- __name(collectRouteRefs, "collectRouteRefs");
1237
- function collectParamSourceRefs(source, refs) {
1238
- if (typeof source === "string") {
1239
- refs.add(source);
1240
- return;
1241
- }
1242
- if (Array.isArray(source)) {
1243
- for (const param of source) {
1244
- if (param && typeof param === "object" && "type" in param) {
1245
- collectTypeRefs(param.type, refs);
1246
- }
1247
- }
1248
- return;
1249
- }
1250
- if (source && typeof source === "object" && "kind" in source) {
1251
- collectTypeRefs(source, refs);
1252
- }
1253
- }
1254
- __name(collectParamSourceRefs, "collectParamSourceRefs");
1255
- function collectTypeRefs(type, refs) {
1256
- switch (type.kind) {
1257
- case "ref":
1258
- refs.add(type.name);
1259
- break;
1260
- case "array":
1261
- collectTypeRefs(type.item, refs);
1262
- break;
1263
- case "tuple":
1264
- for (const item of type.items) collectTypeRefs(item, refs);
1265
- break;
1266
- case "record":
1267
- collectTypeRefs(type.key, refs);
1268
- collectTypeRefs(type.value, refs);
1269
- break;
1270
- case "union":
1271
- case "discriminatedUnion":
1272
- case "intersection":
1273
- for (const member of type.members) collectTypeRefs(member, refs);
1274
- break;
1275
- case "inlineObject":
1276
- for (const field of type.fields) collectTypeRefs(field.type, refs);
1277
- break;
1278
- case "lazy":
1279
- collectTypeRefs(type.inner, refs);
1280
- break;
1281
- }
1282
- }
1283
- __name(collectTypeRefs, "collectTypeRefs");
1284
- function collectModelRefs(model, refs) {
1285
- if (model.bases) for (const b of model.bases) refs.add(b);
1286
- if (model.type) collectTypeRefs(model.type, refs);
1287
- for (const field of model.fields) {
1288
- collectTypeRefs(field.type, refs);
1289
- }
1290
- }
1291
- __name(collectModelRefs, "collectModelRefs");
1292
- function sanitizeFilename(tag) {
1293
- return tag.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "") || "default";
1294
- }
1295
- __name(sanitizeFilename, "sanitizeFilename");
1296
-
1297
- // src/ast-to-ck.ts
1298
- var INDENT = " ";
1299
- function astToCk(root, options = {}) {
1300
- const { includeComments = true } = options;
1301
- const ctx = {
1302
- includeComments
1303
- };
1304
- const parts = [];
1305
- const optionsBlock = serializeOptions(root);
1306
- if (optionsBlock) parts.push(optionsBlock);
1307
- for (const model of root.models) {
1308
- parts.push(serializeModel(model, ctx));
1309
- }
1310
- for (const route of root.routes) {
1311
- parts.push(serializeRoute(route, ctx));
1312
- }
1313
- return parts.join("\n\n") + "\n";
1314
- }
1315
- __name(astToCk, "astToCk");
1316
- function serializeOptions(root) {
1317
- const hasKeys = Object.keys(root.meta).length > 0;
1318
- const hasServices = root.services && Object.keys(root.services).length > 0;
1319
- const hasSecurity = root.security !== void 0;
1320
- if (!hasKeys && !hasServices && !hasSecurity) return null;
1321
- const lines = [
1322
- "options {"
1323
- ];
1324
- if (hasKeys) {
1325
- lines.push(`${INDENT}keys: {`);
1326
- for (const [key, value] of Object.entries(root.meta)) {
1327
- lines.push(`${INDENT}${INDENT}${key}: ${value}`);
1328
- }
1329
- lines.push(`${INDENT}}`);
1330
- }
1331
- if (hasServices) {
1332
- lines.push(`${INDENT}services: {`);
1333
- for (const [name, path] of Object.entries(root.services)) {
1334
- lines.push(`${INDENT}${INDENT}${name}: "${path}"`);
1335
- }
1336
- lines.push(`${INDENT}}`);
1337
- }
1338
- if (hasSecurity) {
1339
- lines.push(`${INDENT}security: {`);
1340
- if (root.security === "none") {
1341
- lines.push(`${INDENT}${INDENT}none`);
1342
- } else {
1343
- const sec = root.security;
1344
- if (sec.roles && sec.roles.length > 0) {
1345
- lines.push(`${INDENT}${INDENT}roles: [${sec.roles.join(", ")}]`);
1346
- }
1347
- }
1348
- lines.push(`${INDENT}}`);
1349
- }
1350
- lines.push("}");
1351
- return lines.join("\n");
1352
- }
1353
- __name(serializeOptions, "serializeOptions");
1354
- function serializeModel(model, ctx) {
1355
- const parts = [];
1356
- const prefixes = [];
1357
- if (model.inputCase && model.inputCase !== "camel") {
1358
- prefixes.push(`format(input=${model.inputCase})`);
1359
- }
1360
- if (model.mode && model.mode !== "strict") {
1361
- prefixes.push(`mode(${model.mode})`);
1362
- }
1363
- if (model.deprecated) {
1364
- prefixes.push("deprecated");
1365
- }
1366
- const prefix = prefixes.length > 0 ? prefixes.join(" ") + " " : "";
1367
- const comment = ctx.includeComments && model.description ? ` # ${model.description}` : "";
1368
- if (model.type) {
1369
- parts.push(`contract ${prefix}${model.name}: ${serializeType(model.type)}${comment}`);
1370
- return parts.join("");
1371
- }
1372
- if (model.bases && model.bases.length > 0) {
1373
- parts.push(`contract ${prefix}${model.name}: ${model.bases.join(" & ")} & {${comment}`);
1374
- } else {
1375
- parts.push(`contract ${prefix}${model.name}: {${comment}`);
1376
- }
1377
- for (const field of model.fields) {
1378
- parts.push(serializeField(field, 1, ctx));
1379
- }
1380
- parts.push("}");
1381
- return parts.join("\n");
1382
- }
1383
- __name(serializeModel, "serializeModel");
1384
- function serializeField(field, depth, ctx) {
1385
- const indent = INDENT.repeat(depth);
1386
- const optional = field.optional ? "?" : "";
1387
- const visibility = field.visibility !== "normal" ? `${field.visibility} ` : "";
1388
- const deprecated = field.deprecated ? "deprecated " : "";
1389
- let typeStr = serializeType(field.type);
1390
- if (field.nullable && !typeContainsNull(field.type)) {
1391
- typeStr = `${typeStr} | null`;
1392
- }
1393
- const defaultVal = field.default !== void 0 ? ` = ${serializeDefault(field.default)}` : "";
1394
- const comment = ctx.includeComments && field.description ? ` # ${field.description}` : "";
1395
- return `${indent}${field.name}${optional}: ${deprecated}${visibility}${typeStr}${defaultVal}${comment}`;
1396
- }
1397
- __name(serializeField, "serializeField");
1398
- function typeContainsNull(type) {
1399
- if (type.kind === "scalar" && type.name === "null") return true;
1400
- if (type.kind === "union") return type.members.some(typeContainsNull);
1401
- return false;
1402
- }
1403
- __name(typeContainsNull, "typeContainsNull");
1404
- function serializeDefault(value) {
1405
- if (typeof value === "string") {
1406
- if (/^[a-zA-Z_$][a-zA-Z0-9_$\-.]*$/.test(value)) return value;
1407
- return `"${value}"`;
1408
- }
1409
- return String(value);
1410
- }
1411
- __name(serializeDefault, "serializeDefault");
1412
- function serializeType(type) {
1413
- switch (type.kind) {
1414
- case "scalar":
1415
- return serializeScalar(type);
1416
- case "array":
1417
- return serializeArray(type);
1418
- case "tuple":
1419
- return `tuple(${type.items.map(serializeType).join(", ")})`;
1420
- case "record":
1421
- return `record(${serializeType(type.key)}, ${serializeType(type.value)})`;
1422
- case "enum":
1423
- return `enum(${type.values.join(", ")})`;
1424
- case "literal":
1425
- return serializeLiteral(type);
1426
- case "union":
1427
- return type.members.map(serializeType).join(" | ");
1428
- case "discriminatedUnion":
1429
- return `discriminated(by=${type.discriminator}, ${type.members.map(serializeType).join(" | ")})`;
1430
- case "intersection":
1431
- return type.members.map(serializeType).join(" & ");
1432
- case "ref":
1433
- return type.name;
1434
- case "inlineObject":
1435
- return serializeInlineObject(type);
1436
- case "lazy":
1437
- return `lazy(${serializeType(type.inner)})`;
1438
- }
1439
- }
1440
- __name(serializeType, "serializeType");
1441
- function serializeScalar(type) {
1442
- const args = [];
1443
- if (type.len !== void 0) args.push(`length=${type.len}`);
1444
- if (type.min !== void 0) args.push(typeof type.min === "string" ? `min="${type.min}"` : `min=${type.min}`);
1445
- if (type.max !== void 0) args.push(typeof type.max === "string" ? `max="${type.max}"` : `max=${type.max}`);
1446
- if (type.regex !== void 0) args.push(`regex=${type.regex}`);
1447
- if (type.format !== void 0) args.push(`format=${type.format}`);
1448
- if (args.length === 0) return type.name;
1449
- return `${type.name}(${args.join(", ")})`;
1450
- }
1451
- __name(serializeScalar, "serializeScalar");
1452
- function serializeArray(type) {
1453
- const args = [
1454
- serializeType(type.item)
1455
- ];
1456
- if (type.min !== void 0) args.push(`min=${type.min}`);
1457
- if (type.max !== void 0) args.push(`max=${type.max}`);
1458
- return `array(${args.join(", ")})`;
1459
- }
1460
- __name(serializeArray, "serializeArray");
1461
- function serializeLiteral(type) {
1462
- if (typeof type.value === "string") return `literal("${type.value}")`;
1463
- return `literal(${type.value})`;
1464
- }
1465
- __name(serializeLiteral, "serializeLiteral");
1466
- function serializeInlineObject(type) {
1467
- const modePrefix = type.mode ? `mode(${type.mode}) ` : "";
1468
- if (type.fields.length === 0) return `${modePrefix}{}`;
1469
- const lines = [
1470
- `${modePrefix}{`
1471
- ];
1472
- for (const field of type.fields) {
1473
- lines.push(serializeField(field, 2, {
1474
- includeComments: true
1475
- }));
1476
- }
1477
- lines.push(`${INDENT}}`);
1478
- return lines.join("\n");
1479
- }
1480
- __name(serializeInlineObject, "serializeInlineObject");
1481
- function serializeRoute(route, ctx) {
1482
- const lines = [];
1483
- const modStr = serializeModifiers(route.modifiers);
1484
- const comment = ctx.includeComments && route.description ? ` # ${route.description}` : "";
1485
- lines.push(`operation${modStr} ${route.path}: {${comment}`);
1486
- if (route.params) {
1487
- serializeParamSource(lines, "params", route.params, route.paramsMode, 1, ctx);
1488
- }
1489
- if (route.security !== void 0) {
1490
- serializeSecurityBlock(lines, route.security, 1, ctx);
1491
- }
1492
- for (const op of route.operations) {
1493
- serializeOperation(lines, op, 1, ctx);
1494
- }
1495
- lines.push("}");
1496
- return lines.join("\n");
1497
- }
1498
- __name(serializeRoute, "serializeRoute");
1499
- function serializeOperation(lines, op, depth, ctx) {
1500
- const indent = INDENT.repeat(depth);
1501
- const modStr = serializeModifiers(op.modifiers);
1502
- const comment = ctx.includeComments && op.description ? ` # ${op.description}` : "";
1503
- lines.push(`${indent}${op.method}${modStr}: {${comment}`);
1504
- const inner = INDENT.repeat(depth + 1);
1505
- if (op.service) {
1506
- lines.push(`${inner}service: ${op.service}`);
1507
- }
1508
- if (op.sdk) {
1509
- lines.push(`${inner}sdk: ${op.sdk}`);
1510
- }
1511
- if (op.signature) {
1512
- const sigComment = ctx.includeComments && op.signatureDescription ? ` # ${op.signatureDescription}` : "";
1513
- lines.push(`${inner}signature: ${op.signature}${sigComment}`);
1514
- }
1515
- if (op.security !== void 0) {
1516
- serializeSecurityBlock(lines, op.security, depth + 1, ctx);
1517
- }
1518
- if (op.query) {
1519
- serializeParamSource(lines, "query", op.query, op.queryMode, depth + 1, ctx);
1520
- }
1521
- if (op.headers) {
1522
- serializeParamSource(lines, "headers", op.headers, op.headersMode, depth + 1, ctx);
1523
- }
1524
- if (op.request) {
1525
- serializeRequest(lines, op.request, depth + 1);
1526
- }
1527
- if (op.responses.length > 0) {
1528
- serializeResponses(lines, op.responses, depth + 1);
1529
- }
1530
- lines.push(`${indent}}`);
1531
- return lines;
1532
- }
1533
- __name(serializeOperation, "serializeOperation");
1534
- function serializeModifiers(modifiers) {
1535
- if (!modifiers || modifiers.length === 0) return "";
1536
- return `(${modifiers.join(", ")})`;
1537
- }
1538
- __name(serializeModifiers, "serializeModifiers");
1539
- function serializeParamSource(lines, keyword, source, mode, depth, ctx) {
1540
- const indent = INDENT.repeat(depth);
1541
- if (source.kind === "ref") {
1542
- lines.push(`${indent}${keyword}: ${source.name}`);
1543
- return;
1544
- }
1545
- if (source.kind === "type") {
1546
- lines.push(`${indent}${keyword}: ${serializeType(source.node)}`);
1547
- return;
1548
- }
1549
- const modeStr = mode ? `mode(${mode}) ` : "";
1550
- lines.push(`${indent}${keyword}: ${modeStr}{`);
1551
- for (const param of source.nodes) {
1552
- const optional = param.optional ? "?" : "";
1553
- let typeStr = serializeType(param.type);
1554
- if (param.nullable && !typeContainsNull(param.type)) {
1555
- typeStr = `${typeStr} | null`;
1556
- }
1557
- const defaultVal = param.default !== void 0 ? ` = ${serializeDefault(param.default)}` : "";
1558
- const comment = ctx.includeComments && param.description ? ` # ${param.description}` : "";
1559
- lines.push(`${INDENT.repeat(depth + 1)}${param.name}${optional}: ${typeStr}${defaultVal}${comment}`);
1560
- }
1561
- lines.push(`${indent}}`);
1562
- }
1563
- __name(serializeParamSource, "serializeParamSource");
1564
- function serializeRequest(lines, request, depth) {
1565
- const indent = INDENT.repeat(depth);
1566
- lines.push(`${indent}request: {`);
1567
- for (const body of request.bodies) {
1568
- lines.push(`${INDENT.repeat(depth + 1)}${body.contentType}: ${serializeType(body.bodyType)}`);
1569
- }
1570
- lines.push(`${indent}}`);
1571
- }
1572
- __name(serializeRequest, "serializeRequest");
1573
- function serializeResponses(lines, responses, depth) {
1574
- const indent = INDENT.repeat(depth);
1575
- lines.push(`${indent}response: {`);
1576
- for (const resp of responses) {
1577
- const hasBody = resp.bodyType && resp.contentType;
1578
- const hasHeaders = resp.headers && resp.headers.length > 0;
1579
- if (hasBody || hasHeaders) {
1580
- lines.push(`${INDENT.repeat(depth + 1)}${resp.statusCode}: {`);
1581
- if (hasBody) {
1582
- lines.push(`${INDENT.repeat(depth + 2)}${resp.contentType}: ${serializeType(resp.bodyType)}`);
1583
- }
1584
- if (hasHeaders) {
1585
- lines.push(`${INDENT.repeat(depth + 2)}headers: {`);
1586
- for (const h of resp.headers) {
1587
- const opt = h.optional ? "?" : "";
1588
- const trail = h.description ? ` # ${h.description}` : "";
1589
- lines.push(`${INDENT.repeat(depth + 3)}${h.name}${opt}: ${serializeType(h.type)}${trail}`);
1590
- }
1591
- lines.push(`${INDENT.repeat(depth + 2)}}`);
1592
- }
1593
- lines.push(`${INDENT.repeat(depth + 1)}}`);
1594
- } else {
1595
- lines.push(`${INDENT.repeat(depth + 1)}${resp.statusCode}:`);
1596
- }
1597
- }
1598
- lines.push(`${indent}}`);
1599
- }
1600
- __name(serializeResponses, "serializeResponses");
1601
- function serializeSecurityBlock(lines, security, depth, ctx) {
1602
- const indent = INDENT.repeat(depth);
1603
- if (security === "none") {
1604
- lines.push(`${indent}security: none`);
1605
- return;
1606
- }
1607
- const sec = security;
1608
- if (sec.roles && sec.roles.length > 0) {
1609
- const rolesComment = ctx.includeComments && sec.rolesDescription ? ` # ${sec.rolesDescription}` : "";
1610
- lines.push(`${indent}security: {`);
1611
- lines.push(`${INDENT.repeat(depth + 1)}roles: [${sec.roles.join(", ")}]${rolesComment}`);
1612
- lines.push(`${indent}}`);
1613
- } else {
1614
- lines.push(`${indent}security: {}`);
1615
- }
1616
- }
1617
- __name(serializeSecurityBlock, "serializeSecurityBlock");
1618
-
1619
- // src/convert.ts
1620
- import { readFileSync } from "fs";
1621
- import { parse as parseYaml } from "yaml";
1622
-
1623
- // src/warnings.ts
1624
- var WarningCollector = class {
1625
- static {
1626
- __name(this, "WarningCollector");
1627
- }
1628
- warnings = [];
1629
- onWarning;
1630
- constructor(onWarning) {
1631
- this.onWarning = onWarning;
1632
- }
1633
- warn(path, message) {
1634
- this.add({
1635
- path,
1636
- message,
1637
- severity: "warn"
1638
- });
1639
- }
1640
- info(path, message) {
1641
- this.add({
1642
- path,
1643
- message,
1644
- severity: "info"
1645
- });
1646
- }
1647
- add(warning) {
1648
- this.warnings.push(warning);
1649
- this.onWarning?.(warning);
1650
- }
1651
- };
1652
-
1653
- // src/convert.ts
1654
- async function convertOpenApiToCk(options) {
1655
- const { split = "by-tag", includeComments = true } = options;
1656
- const warnings = new WarningCollector(options.onWarning);
1657
- const rawDoc = await parseInput(options.input);
1658
- const doc = normalize(rawDoc, warnings);
1659
- const schemas = sanitizeSchemaNames(doc, warnings);
1660
- const circularRefs = detectCircularRefs(schemas);
1661
- const extractedModels = [];
1662
- const schemaCtx = {
1663
- circularRefs,
1664
- warnings,
1665
- path: "#/components/schemas",
1666
- includeComments,
1667
- namedSchemas: schemas,
1668
- extractedModels,
1669
- inlineCounter: 0
1670
- };
1671
- const models = schemasToModels(schemas, schemaCtx);
1672
- const { routes, routeTags } = pathsToRoutes(doc, {
1673
- circularRefs,
1674
- warnings,
1675
- includeComments,
1676
- namedSchemas: schemas,
1677
- extractedModels,
1678
- globalSecurity: doc.security
1679
- });
1680
- const files = /* @__PURE__ */ new Map();
1681
- if (split === "by-tag") {
1682
- const ckRoots = splitByTag(models, routes, routeTags);
1683
- for (const [filename, root] of ckRoots) {
1684
- files.set(filename, astToCk(root, {
1685
- includeComments
1686
- }));
1687
- }
1688
- } else {
1689
- const root = mergeIntoSingle(models, routes);
1690
- files.set("api.ck", astToCk(root, {
1691
- includeComments
1692
- }));
1693
- }
1694
- return {
1695
- files,
1696
- warnings: warnings.warnings
1697
- };
1698
- }
1699
- __name(convertOpenApiToCk, "convertOpenApiToCk");
1700
- async function parseInput(input) {
1701
- if (typeof input === "object") {
1702
- return input;
1703
- }
1704
- try {
1705
- const content = readFileSync(input, "utf-8");
1706
- return parseJsonOrYaml(content);
1707
- } catch {
1708
- return parseJsonOrYaml(input);
1709
- }
1710
- }
1711
- __name(parseInput, "parseInput");
1712
- function parseJsonOrYaml(content) {
1713
- try {
1714
- return JSON.parse(content);
1715
- } catch {
1716
- return parseYaml(content);
1717
- }
1718
- }
1719
- __name(parseJsonOrYaml, "parseJsonOrYaml");
1720
- function sanitizeSchemaNames(doc, warnings) {
1721
- const original = doc.components?.schemas ?? {};
1722
- const sanitized = {};
1723
- const nameMap = /* @__PURE__ */ new Map();
1724
- for (const name of Object.keys(original)) {
1725
- const clean = sanitizeName(name, warnings);
1726
- if (sanitized[clean]) {
1727
- warnings.warn(`#/components/schemas/${name}`, `Name collision after sanitization: "${name}" and another schema both map to "${clean}"`);
1728
- let i = 2;
1729
- while (sanitized[`${clean}${i}`]) i++;
1730
- nameMap.set(name, `${clean}${i}`);
1731
- sanitized[`${clean}${i}`] = original[name];
1732
- } else {
1733
- nameMap.set(name, clean);
1734
- sanitized[clean] = original[name];
1735
- }
1736
- }
1737
- if (nameMap.size > 0) {
1738
- updateRefs(doc, nameMap);
1739
- }
1740
- return sanitized;
1741
- }
1742
- __name(sanitizeSchemaNames, "sanitizeSchemaNames");
1743
- function updateRefs(obj, nameMap) {
1744
- if (!obj || typeof obj !== "object") return;
1745
- if (Array.isArray(obj)) {
1746
- for (const item of obj) updateRefs(item, nameMap);
1747
- return;
1748
- }
1749
- const record = obj;
1750
- if (typeof record.$ref === "string") {
1751
- const match = record.$ref.match(/^#\/components\/schemas\/(.+)$/);
1752
- if (match?.[1] && nameMap.has(match[1])) {
1753
- record.$ref = `#/components/schemas/${nameMap.get(match[1])}`;
1754
- }
1755
- }
1756
- for (const value of Object.values(record)) {
1757
- updateRefs(value, nameMap);
1758
- }
1759
- }
1760
- __name(updateRefs, "updateRefs");
1761
-
1762
- export {
1763
- __name,
1764
- normalize,
1765
- detectCircularRefs,
1766
- extractRefName,
1767
- schemasToModels,
1768
- schemaToTypeNode,
1769
- sanitizeName,
1770
- pathsToRoutes,
1771
- splitByTag,
1772
- mergeIntoSingle,
1773
- astToCk,
1774
- serializeType,
1775
- convertOpenApiToCk
1776
- };
1777
- //# sourceMappingURL=chunk-LQ2B3EJG.js.map