@contractkit/openapi-to-ck 0.10.2 → 0.12.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/.turbo/turbo-build$colon$ci.log +7 -7
- package/.turbo/turbo-test$colon$ci.log +24 -20
- package/CHANGELOG.md +172 -0
- package/README.md +30 -8
- package/dist/ast-to-ck.d.ts +32 -16
- package/dist/ast-to-ck.d.ts.map +1 -1
- package/dist/{chunk-JPI3AQ7V.js → chunk-U7V7LFBV.js} +219 -391
- package/dist/chunk-U7V7LFBV.js.map +1 -0
- package/dist/convert.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/normalize.d.ts +6 -2
- package/dist/normalize.d.ts.map +1 -1
- package/dist/paths-to-ast.d.ts +2 -0
- package/dist/paths-to-ast.d.ts.map +1 -1
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +18 -3
- package/dist/plugin.js.map +1 -1
- package/dist/schema-to-ast.d.ts +13 -1
- package/dist/schema-to-ast.d.ts.map +1 -1
- package/dist/tag-splitter.d.ts.map +1 -1
- package/dist/types.d.ts +36 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +4 -5
- package/src/ast-to-ck.ts +29 -453
- package/src/convert.ts +57 -3
- package/src/normalize.ts +87 -11
- package/src/paths-to-ast.ts +92 -11
- package/src/plugin.ts +17 -2
- package/src/schema-to-ast.ts +71 -7
- package/src/tag-splitter.ts +21 -16
- package/src/types.ts +36 -0
- package/tests/__snapshots__/kitchen-sink.ck +102 -0
- package/tests/ast-to-ck.test.ts +34 -17
- package/tests/component-refs.test.ts +114 -0
- package/tests/coverage.test.ts +246 -0
- package/tests/error-responses.test.ts +94 -0
- package/tests/fixtures/kitchen-sink-3.1.json +100 -0
- package/tests/helpers.ts +40 -0
- package/tests/kitchen-sink.test.ts +116 -0
- package/tests/schema-to-ast.test.ts +11 -2
- package/dist/chunk-JPI3AQ7V.js.map +0 -1
|
@@ -4,15 +4,65 @@ var __name = (target, value) => __defProp(target, "name", { value, configurable:
|
|
|
4
4
|
// src/normalize.ts
|
|
5
5
|
function normalize(doc, warnings) {
|
|
6
6
|
const version = detectVersion(doc);
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
if (version === "3.0") {
|
|
11
|
-
return normalizeOas30(doc, warnings);
|
|
12
|
-
}
|
|
13
|
-
return doc;
|
|
7
|
+
const normalized = version === "2.0" ? normalizeSwagger2(doc, warnings) : version === "3.0" ? normalizeOas30(doc, warnings) : doc;
|
|
8
|
+
dereferenceComponents(normalized, warnings);
|
|
9
|
+
return normalized;
|
|
14
10
|
}
|
|
15
11
|
__name(normalize, "normalize");
|
|
12
|
+
var DEREF_SECTIONS = [
|
|
13
|
+
"parameters",
|
|
14
|
+
"requestBodies",
|
|
15
|
+
"responses",
|
|
16
|
+
"headers"
|
|
17
|
+
];
|
|
18
|
+
var MAX_REF_DEPTH = 10;
|
|
19
|
+
function dereferenceComponents(doc, warnings) {
|
|
20
|
+
const components = doc.components;
|
|
21
|
+
const resolve = /* @__PURE__ */ __name((node, path, depth = 0) => {
|
|
22
|
+
if (!node || typeof node !== "object") return node;
|
|
23
|
+
if (Array.isArray(node)) return node.map((item) => resolve(item, path, depth));
|
|
24
|
+
const obj = node;
|
|
25
|
+
const ref = obj.$ref;
|
|
26
|
+
if (typeof ref === "string") {
|
|
27
|
+
const match = /^#\/components\/([^/]+)\/(.+)$/.exec(ref);
|
|
28
|
+
const section = match?.[1];
|
|
29
|
+
if (section && section !== "schemas" && DEREF_SECTIONS.includes(section)) {
|
|
30
|
+
if (depth >= MAX_REF_DEPTH) {
|
|
31
|
+
warnings.warn(path, `$ref chain too deep to resolve: ${ref}`);
|
|
32
|
+
return obj;
|
|
33
|
+
}
|
|
34
|
+
const target = components?.[section]?.[decodeRefToken(match[2])];
|
|
35
|
+
if (target === void 0) {
|
|
36
|
+
warnings.warn(path, `unresolved $ref '${ref}' \u2014 the component is not defined`);
|
|
37
|
+
return obj;
|
|
38
|
+
}
|
|
39
|
+
const siblings = {
|
|
40
|
+
...obj
|
|
41
|
+
};
|
|
42
|
+
delete siblings.$ref;
|
|
43
|
+
const resolved = resolve(target, path, depth + 1);
|
|
44
|
+
return {
|
|
45
|
+
...resolved,
|
|
46
|
+
...siblings
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
return obj;
|
|
50
|
+
}
|
|
51
|
+
const out = {};
|
|
52
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
53
|
+
out[key] = key === "schema" || key === "schemas" ? value : resolve(value, `${path}/${key}`, depth);
|
|
54
|
+
}
|
|
55
|
+
return out;
|
|
56
|
+
}, "resolve");
|
|
57
|
+
for (const [path, pathItem] of Object.entries(doc.paths ?? {})) {
|
|
58
|
+
doc.paths[path] = resolve(pathItem, `#/paths/${path}`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
__name(dereferenceComponents, "dereferenceComponents");
|
|
62
|
+
function decodeRefToken(token) {
|
|
63
|
+
return decodeURIComponent(token).replace(/~1/g, "/").replace(/~0/g, "~");
|
|
64
|
+
}
|
|
65
|
+
__name(decodeRefToken, "decodeRefToken");
|
|
16
66
|
function detectVersion(doc) {
|
|
17
67
|
if (typeof doc.swagger === "string" && doc.swagger.startsWith("2")) return "2.0";
|
|
18
68
|
if (typeof doc.openapi === "string") {
|
|
@@ -82,7 +132,8 @@ function normalizePathItem2(pathItem, globalConsumes, globalProduces, warnings)
|
|
|
82
132
|
"patch",
|
|
83
133
|
"delete",
|
|
84
134
|
"head",
|
|
85
|
-
"options"
|
|
135
|
+
"options",
|
|
136
|
+
"trace"
|
|
86
137
|
];
|
|
87
138
|
const normalized = {};
|
|
88
139
|
const pathParams = pathItem.parameters ?? [];
|
|
@@ -281,7 +332,8 @@ function normalizePathItemSchemas(pathItem) {
|
|
|
281
332
|
"patch",
|
|
282
333
|
"delete",
|
|
283
334
|
"head",
|
|
284
|
-
"options"
|
|
335
|
+
"options",
|
|
336
|
+
"trace"
|
|
285
337
|
];
|
|
286
338
|
for (const method of methods) {
|
|
287
339
|
const op = pathItem[method];
|
|
@@ -419,14 +471,20 @@ var LOC = {
|
|
|
419
471
|
};
|
|
420
472
|
var FORMAT_TO_SCALAR = {
|
|
421
473
|
email: "email",
|
|
474
|
+
"idn-email": "email",
|
|
422
475
|
uri: "url",
|
|
476
|
+
"uri-reference": "url",
|
|
477
|
+
iri: "url",
|
|
478
|
+
"iri-reference": "url",
|
|
423
479
|
url: "url",
|
|
424
480
|
uuid: "uuid",
|
|
425
481
|
date: "date",
|
|
426
482
|
"date-time": "datetime",
|
|
427
483
|
time: "time",
|
|
484
|
+
duration: "duration",
|
|
428
485
|
binary: "binary",
|
|
429
|
-
int64: "bigint"
|
|
486
|
+
int64: "bigint",
|
|
487
|
+
decimal: "decimal"
|
|
430
488
|
};
|
|
431
489
|
function schemasToModels(schemas, ctx) {
|
|
432
490
|
const models = [];
|
|
@@ -472,6 +530,11 @@ function schemaToModel(name, schema, ctx) {
|
|
|
472
530
|
kind: "model",
|
|
473
531
|
name,
|
|
474
532
|
fields,
|
|
533
|
+
// `additionalProperties: true` says unknown keys are allowed, which is `mode(loose)`.
|
|
534
|
+
// `false` and absent both match `.ck`'s `strict` default, so neither needs a mode.
|
|
535
|
+
...schema.additionalProperties === true ? {
|
|
536
|
+
mode: "loose"
|
|
537
|
+
} : {},
|
|
475
538
|
description,
|
|
476
539
|
loc: LOC
|
|
477
540
|
};
|
|
@@ -488,10 +551,11 @@ function schemaToModel(name, schema, ctx) {
|
|
|
488
551
|
}
|
|
489
552
|
__name(schemaToModel, "schemaToModel");
|
|
490
553
|
function schemaToTypeNode(schema, ctx) {
|
|
554
|
+
warnUnrepresentableConstraints(schema, ctx);
|
|
491
555
|
if (schema.$ref) {
|
|
492
556
|
const refName = extractRefName(schema.$ref);
|
|
493
557
|
if (refName) {
|
|
494
|
-
if (ctx.circularRefs.has(refName)) {
|
|
558
|
+
if (ctx.insideModel && ctx.circularRefs.has(refName)) {
|
|
495
559
|
return {
|
|
496
560
|
kind: "lazy",
|
|
497
561
|
inner: {
|
|
@@ -607,6 +671,17 @@ function schemaToTypeNode(schema, ctx) {
|
|
|
607
671
|
}
|
|
608
672
|
__name(schemaToTypeNode, "schemaToTypeNode");
|
|
609
673
|
function stringSchemaToType(schema) {
|
|
674
|
+
if (schema.format === "decimal") {
|
|
675
|
+
const mods2 = {};
|
|
676
|
+
if (schema["x-contractkit-min"] !== void 0) mods2.min = schema["x-contractkit-min"];
|
|
677
|
+
if (schema["x-contractkit-max"] !== void 0) mods2.max = schema["x-contractkit-max"];
|
|
678
|
+
if (schema["x-contractkit-scale"] !== void 0) mods2.scale = schema["x-contractkit-scale"];
|
|
679
|
+
return {
|
|
680
|
+
kind: "scalar",
|
|
681
|
+
name: "decimal",
|
|
682
|
+
...mods2
|
|
683
|
+
};
|
|
684
|
+
}
|
|
610
685
|
if (schema.format) {
|
|
611
686
|
const scalarName = FORMAT_TO_SCALAR[schema.format];
|
|
612
687
|
if (scalarName) {
|
|
@@ -623,7 +698,7 @@ function stringSchemaToType(schema) {
|
|
|
623
698
|
if (schema.minLength !== void 0) mods.min = schema.minLength;
|
|
624
699
|
if (schema.maxLength !== void 0) mods.max = schema.maxLength;
|
|
625
700
|
}
|
|
626
|
-
if (schema.pattern) mods.regex =
|
|
701
|
+
if (schema.pattern) mods.regex = schema.pattern;
|
|
627
702
|
if (schema.format && !FORMAT_TO_SCALAR[schema.format]) mods.format = schema.format;
|
|
628
703
|
return {
|
|
629
704
|
kind: "scalar",
|
|
@@ -645,6 +720,16 @@ function integerSchemaToType(schema) {
|
|
|
645
720
|
}
|
|
646
721
|
__name(integerSchemaToType, "integerSchemaToType");
|
|
647
722
|
function numberSchemaToType(schema) {
|
|
723
|
+
if (schema.format === "decimal") {
|
|
724
|
+
const mods2 = {};
|
|
725
|
+
if (schema.minimum !== void 0) mods2.min = String(schema.minimum);
|
|
726
|
+
if (schema.maximum !== void 0) mods2.max = String(schema.maximum);
|
|
727
|
+
return {
|
|
728
|
+
kind: "scalar",
|
|
729
|
+
name: "decimal",
|
|
730
|
+
...mods2
|
|
731
|
+
};
|
|
732
|
+
}
|
|
648
733
|
const mods = {};
|
|
649
734
|
if (schema.minimum !== void 0) mods.min = schema.minimum;
|
|
650
735
|
if (schema.maximum !== void 0) mods.max = schema.maximum;
|
|
@@ -793,12 +878,24 @@ function toDiscriminatedUnion(schemas, discriminator, ctx) {
|
|
|
793
878
|
};
|
|
794
879
|
}
|
|
795
880
|
__name(toDiscriminatedUnion, "toDiscriminatedUnion");
|
|
881
|
+
var UNREPRESENTABLE_KEYWORDS = [
|
|
882
|
+
"exclusiveMinimum",
|
|
883
|
+
"exclusiveMaximum",
|
|
884
|
+
"multipleOf",
|
|
885
|
+
"uniqueItems"
|
|
886
|
+
];
|
|
796
887
|
function warnUnsupported(schema, ctx) {
|
|
797
888
|
if (schema.xml) ctx.warnings.warn(ctx.path, "xml metadata is not supported, skipping");
|
|
798
889
|
if (schema.externalDocs) ctx.warnings.info(ctx.path, "externalDocs is not supported, skipping");
|
|
799
890
|
if (schema.not) ctx.warnings.warn(ctx.path, "not keyword is not supported, skipping");
|
|
800
891
|
}
|
|
801
892
|
__name(warnUnsupported, "warnUnsupported");
|
|
893
|
+
function warnUnrepresentableConstraints(schema, ctx) {
|
|
894
|
+
for (const keyword of UNREPRESENTABLE_KEYWORDS) {
|
|
895
|
+
if (schema[keyword] !== void 0) ctx.warnings.warn(ctx.path, `${keyword} has no .ck equivalent, dropping the constraint`);
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
__name(warnUnrepresentableConstraints, "warnUnrepresentableConstraints");
|
|
802
899
|
function extractInlineModel(schema, suggestedName, ctx) {
|
|
803
900
|
if (schema.$ref) {
|
|
804
901
|
return {
|
|
@@ -806,7 +903,10 @@ function extractInlineModel(schema, suggestedName, ctx) {
|
|
|
806
903
|
};
|
|
807
904
|
}
|
|
808
905
|
if (schema.properties || schema.type === "object" && schema.additionalProperties === void 0) {
|
|
809
|
-
const fields = schemaPropertiesToFields(schema,
|
|
906
|
+
const fields = schemaPropertiesToFields(schema, {
|
|
907
|
+
...ctx,
|
|
908
|
+
insideModel: true
|
|
909
|
+
});
|
|
810
910
|
const model = {
|
|
811
911
|
kind: "model",
|
|
812
912
|
name: suggestedName,
|
|
@@ -829,10 +929,11 @@ function extractInlineModel(schema, suggestedName, ctx) {
|
|
|
829
929
|
__name(extractInlineModel, "extractInlineModel");
|
|
830
930
|
function sanitizeName(name, warnings) {
|
|
831
931
|
const cleaned = name.replace(/[^a-zA-Z0-9_$]/g, " ").split(/\s+/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
832
|
-
|
|
833
|
-
|
|
932
|
+
const safe = /^[0-9]/.test(cleaned) ? `_${cleaned}` : cleaned;
|
|
933
|
+
if (safe !== name) {
|
|
934
|
+
warnings.info(`#/components/schemas/${name}`, `Schema name sanitized: "${name}" \u2192 "${safe}"`);
|
|
834
935
|
}
|
|
835
|
-
return
|
|
936
|
+
return safe || "UnnamedSchema";
|
|
836
937
|
}
|
|
837
938
|
__name(sanitizeName, "sanitizeName");
|
|
838
939
|
|
|
@@ -841,6 +942,11 @@ var LOC2 = {
|
|
|
841
942
|
file: "",
|
|
842
943
|
line: 0
|
|
843
944
|
};
|
|
945
|
+
var MIME_RE = /^[a-z0-9][a-z0-9.+_-]*\/[a-z0-9][a-z0-9.+_-]*$/i;
|
|
946
|
+
function toNameText(summary) {
|
|
947
|
+
return summary.replace(/[}#]/g, " ").replace(/\s+/g, " ").trim();
|
|
948
|
+
}
|
|
949
|
+
__name(toNameText, "toNameText");
|
|
844
950
|
var HTTP_METHODS = [
|
|
845
951
|
"get",
|
|
846
952
|
"post",
|
|
@@ -848,6 +954,11 @@ var HTTP_METHODS = [
|
|
|
848
954
|
"patch",
|
|
849
955
|
"delete"
|
|
850
956
|
];
|
|
957
|
+
var UNSUPPORTED_METHODS = [
|
|
958
|
+
"head",
|
|
959
|
+
"options",
|
|
960
|
+
"trace"
|
|
961
|
+
];
|
|
851
962
|
function pathsToRoutes(doc, ctx) {
|
|
852
963
|
const routes = [];
|
|
853
964
|
const routeTags = /* @__PURE__ */ new Map();
|
|
@@ -870,6 +981,11 @@ function pathItemToRoute(path, pathItem, ctx) {
|
|
|
870
981
|
const operations = [];
|
|
871
982
|
let primaryTag = "default";
|
|
872
983
|
const pathParams = (pathItem.parameters ?? []).filter((p) => p.in === "path");
|
|
984
|
+
for (const method of UNSUPPORTED_METHODS) {
|
|
985
|
+
if (pathItem[method]) {
|
|
986
|
+
ctx.warnings.warn(`#/paths/${encodePathSegment2(path)}/${method}`, `\`${method}\` operations have no .ck equivalent; dropped`);
|
|
987
|
+
}
|
|
988
|
+
}
|
|
873
989
|
for (const method of HTTP_METHODS) {
|
|
874
990
|
const op = pathItem[method];
|
|
875
991
|
if (!op) continue;
|
|
@@ -912,6 +1028,11 @@ function operationToNode(method, op, path, ctx) {
|
|
|
912
1028
|
if (op.operationId) {
|
|
913
1029
|
node.sdk = op.operationId;
|
|
914
1030
|
}
|
|
1031
|
+
if (op.summary) {
|
|
1032
|
+
const name = toNameText(op.summary);
|
|
1033
|
+
if (name) node.name = name;
|
|
1034
|
+
else ctx.warnings.warn(`${pathPrefix}/summary`, "summary has no content `.ck` can carry as a name; dropped");
|
|
1035
|
+
}
|
|
915
1036
|
if (op.description && ctx.includeComments) {
|
|
916
1037
|
node.description = op.description;
|
|
917
1038
|
}
|
|
@@ -923,10 +1044,16 @@ function operationToNode(method, op, path, ctx) {
|
|
|
923
1044
|
const queryParams = [];
|
|
924
1045
|
const headerParams = [];
|
|
925
1046
|
for (const param of op.parameters ?? []) {
|
|
1047
|
+
if (!param?.name) {
|
|
1048
|
+
ctx.warnings.warn(`${pathPrefix}/parameters`, "skipped a parameter with no name (an unresolved $ref?)");
|
|
1049
|
+
continue;
|
|
1050
|
+
}
|
|
926
1051
|
if (param.in === "query") {
|
|
927
1052
|
queryParams.push(parameterToNode(param, schemaCtx));
|
|
928
1053
|
} else if (param.in === "header") {
|
|
929
1054
|
headerParams.push(parameterToNode(param, schemaCtx));
|
|
1055
|
+
} else if (param.in === "cookie") {
|
|
1056
|
+
ctx.warnings.warn(`${pathPrefix}/parameters/${param.name}`, "cookie parameters have no `.ck` equivalent; dropped");
|
|
930
1057
|
}
|
|
931
1058
|
}
|
|
932
1059
|
if (queryParams.length > 0) {
|
|
@@ -946,13 +1073,17 @@ function operationToNode(method, op, path, ctx) {
|
|
|
946
1073
|
}
|
|
947
1074
|
const responses = op.responses ?? {};
|
|
948
1075
|
for (const [code, resp] of Object.entries(responses)) {
|
|
1076
|
+
if (!/^\d{3}$/.test(code)) {
|
|
1077
|
+
ctx.warnings.warn(`${pathPrefix}/responses/${code}`, `response key '${code}' is not a numeric status code; dropped`);
|
|
1078
|
+
continue;
|
|
1079
|
+
}
|
|
949
1080
|
const statusCode = parseInt(code, 10);
|
|
950
|
-
if (isNaN(statusCode)) continue;
|
|
951
1081
|
const respNode = responseToNode(statusCode, resp, op.operationId ?? `${method}${toPascalCase(path)}`, schemaCtx, ctx);
|
|
952
1082
|
node.responses.push(respNode);
|
|
953
1083
|
}
|
|
954
|
-
|
|
955
|
-
|
|
1084
|
+
const security = op.security ?? ctx.globalSecurity;
|
|
1085
|
+
if (security !== void 0) {
|
|
1086
|
+
node.security = convertSecurity(security);
|
|
956
1087
|
}
|
|
957
1088
|
return node;
|
|
958
1089
|
}
|
|
@@ -1011,14 +1142,13 @@ __name(parameterToNode, "parameterToNode");
|
|
|
1011
1142
|
function requestBodyToNode(reqBody, operationName, schemaCtx, ctx) {
|
|
1012
1143
|
const content = reqBody.content;
|
|
1013
1144
|
if (!content) return void 0;
|
|
1014
|
-
const supported = /* @__PURE__ */ new Set([
|
|
1015
|
-
"application/json",
|
|
1016
|
-
"application/x-www-form-urlencoded",
|
|
1017
|
-
"multipart/form-data"
|
|
1018
|
-
]);
|
|
1019
1145
|
const bodies = [];
|
|
1020
1146
|
for (const [contentType, mediaType] of Object.entries(content)) {
|
|
1021
|
-
if (!
|
|
1147
|
+
if (!MIME_RE.test(contentType)) {
|
|
1148
|
+
ctx.warnings.warn(`${schemaCtx.path}/requestBody/content`, `content type '${contentType}' is not a plain type/subtype; skipped`);
|
|
1149
|
+
continue;
|
|
1150
|
+
}
|
|
1151
|
+
if (!mediaType?.schema) continue;
|
|
1022
1152
|
const { typeNode, model } = extractInlineModel(mediaType.schema, `${toPascalCase(operationName)}Request`, schemaCtx);
|
|
1023
1153
|
if (model) {
|
|
1024
1154
|
ctx.extractedModels.push(model);
|
|
@@ -1034,14 +1164,24 @@ function requestBodyToNode(reqBody, operationName, schemaCtx, ctx) {
|
|
|
1034
1164
|
};
|
|
1035
1165
|
}
|
|
1036
1166
|
__name(requestBodyToNode, "requestBodyToNode");
|
|
1167
|
+
function shouldDocument(statusCode, braced, resp, ctx) {
|
|
1168
|
+
if (!braced) return false;
|
|
1169
|
+
if (resp["x-contractkit-emit"] === "documented") return true;
|
|
1170
|
+
return statusCode >= 400 && ctx.errorResponses === "documented";
|
|
1171
|
+
}
|
|
1172
|
+
__name(shouldDocument, "shouldDocument");
|
|
1037
1173
|
function responseToNode(statusCode, resp, operationName, schemaCtx, ctx) {
|
|
1038
1174
|
const headers = convertResponseHeaders(resp.headers, schemaCtx);
|
|
1175
|
+
const documented = /* @__PURE__ */ __name((braced) => shouldDocument(statusCode, braced, resp, ctx) ? {
|
|
1176
|
+
emit: "documented"
|
|
1177
|
+
} : {}, "documented");
|
|
1039
1178
|
const empty = /* @__PURE__ */ __name(() => ({
|
|
1040
1179
|
statusCode,
|
|
1041
1180
|
bodies: [],
|
|
1042
1181
|
...headers ? {
|
|
1043
1182
|
headers,
|
|
1044
|
-
hasBlock: true
|
|
1183
|
+
hasBlock: true,
|
|
1184
|
+
...documented(true)
|
|
1045
1185
|
} : {}
|
|
1046
1186
|
}), "empty");
|
|
1047
1187
|
if (!resp.content) return empty();
|
|
@@ -1063,7 +1203,8 @@ function responseToNode(statusCode, resp, operationName, schemaCtx, ctx) {
|
|
|
1063
1203
|
hasBlock: true,
|
|
1064
1204
|
...headers ? {
|
|
1065
1205
|
headers
|
|
1066
|
-
} : {}
|
|
1206
|
+
} : {},
|
|
1207
|
+
...documented(true)
|
|
1067
1208
|
};
|
|
1068
1209
|
}
|
|
1069
1210
|
__name(responseToNode, "responseToNode");
|
|
@@ -1103,7 +1244,9 @@ function makeSchemaCtx(ctx, path) {
|
|
|
1103
1244
|
includeComments: ctx.includeComments,
|
|
1104
1245
|
namedSchemas: ctx.namedSchemas,
|
|
1105
1246
|
extractedModels: ctx.extractedModels,
|
|
1106
|
-
inlineCounter: 0
|
|
1247
|
+
inlineCounter: 0,
|
|
1248
|
+
// A response body, request body, param or header names an already-imported model.
|
|
1249
|
+
insideModel: false
|
|
1107
1250
|
};
|
|
1108
1251
|
}
|
|
1109
1252
|
__name(makeSchemaCtx, "makeSchemaCtx");
|
|
@@ -1224,20 +1367,16 @@ function collectRouteRefs(route, refs) {
|
|
|
1224
1367
|
}
|
|
1225
1368
|
__name(collectRouteRefs, "collectRouteRefs");
|
|
1226
1369
|
function collectParamSourceRefs(source, refs) {
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
return;
|
|
1238
|
-
}
|
|
1239
|
-
if (source && typeof source === "object" && "kind" in source) {
|
|
1240
|
-
collectTypeRefs(source, refs);
|
|
1370
|
+
switch (source.kind) {
|
|
1371
|
+
case "ref":
|
|
1372
|
+
refs.add(source.name);
|
|
1373
|
+
return;
|
|
1374
|
+
case "params":
|
|
1375
|
+
for (const param of source.nodes) collectTypeRefs(param.type, refs);
|
|
1376
|
+
return;
|
|
1377
|
+
case "type":
|
|
1378
|
+
collectTypeRefs(source.node, refs);
|
|
1379
|
+
return;
|
|
1241
1380
|
}
|
|
1242
1381
|
}
|
|
1243
1382
|
__name(collectParamSourceRefs, "collectParamSourceRefs");
|
|
@@ -1284,347 +1423,12 @@ function sanitizeFilename(tag) {
|
|
|
1284
1423
|
__name(sanitizeFilename, "sanitizeFilename");
|
|
1285
1424
|
|
|
1286
1425
|
// src/ast-to-ck.ts
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
return text.replace(/\s+/g, " ").trim();
|
|
1291
|
-
}
|
|
1292
|
-
__name(singleLineComment, "singleLineComment");
|
|
1293
|
-
function quoteEnumValue(value) {
|
|
1294
|
-
if (IDENT_RE.test(value)) return value;
|
|
1295
|
-
if (!value.includes('"')) return `"${value}"`;
|
|
1296
|
-
if (!value.includes("'")) return `'${value}'`;
|
|
1297
|
-
return `"${value}"`;
|
|
1298
|
-
}
|
|
1299
|
-
__name(quoteEnumValue, "quoteEnumValue");
|
|
1300
|
-
function astToCk(root, options = {}) {
|
|
1301
|
-
const { includeComments = true } = options;
|
|
1302
|
-
const ctx = {
|
|
1303
|
-
includeComments
|
|
1304
|
-
};
|
|
1305
|
-
const parts = [];
|
|
1306
|
-
const optionsBlock = serializeOptions(root);
|
|
1307
|
-
if (optionsBlock) parts.push(optionsBlock);
|
|
1308
|
-
for (const model of root.models) {
|
|
1309
|
-
parts.push(serializeModel(model, ctx));
|
|
1310
|
-
}
|
|
1311
|
-
for (const route of root.routes) {
|
|
1312
|
-
parts.push(serializeRoute(route, ctx));
|
|
1313
|
-
}
|
|
1314
|
-
return parts.join("\n\n") + "\n";
|
|
1426
|
+
import { printCk, printType } from "@contractkit/core";
|
|
1427
|
+
function astToCk(root, _options = {}) {
|
|
1428
|
+
return printCk(root);
|
|
1315
1429
|
}
|
|
1316
1430
|
__name(astToCk, "astToCk");
|
|
1317
|
-
|
|
1318
|
-
const hasKeys = Object.keys(root.meta).length > 0;
|
|
1319
|
-
const hasServices = root.services && Object.keys(root.services).length > 0;
|
|
1320
|
-
const hasSecurity = root.security !== void 0;
|
|
1321
|
-
if (!hasKeys && !hasServices && !hasSecurity) return null;
|
|
1322
|
-
const lines = [
|
|
1323
|
-
"options {"
|
|
1324
|
-
];
|
|
1325
|
-
if (hasKeys) {
|
|
1326
|
-
lines.push(`${INDENT}keys: {`);
|
|
1327
|
-
for (const [key, value] of Object.entries(root.meta)) {
|
|
1328
|
-
lines.push(`${INDENT}${INDENT}${key}: ${value}`);
|
|
1329
|
-
}
|
|
1330
|
-
lines.push(`${INDENT}}`);
|
|
1331
|
-
}
|
|
1332
|
-
if (hasServices) {
|
|
1333
|
-
lines.push(`${INDENT}services: {`);
|
|
1334
|
-
for (const [name, path] of Object.entries(root.services)) {
|
|
1335
|
-
lines.push(`${INDENT}${INDENT}${name}: "${path}"`);
|
|
1336
|
-
}
|
|
1337
|
-
lines.push(`${INDENT}}`);
|
|
1338
|
-
}
|
|
1339
|
-
if (hasSecurity) {
|
|
1340
|
-
lines.push(`${INDENT}security: {`);
|
|
1341
|
-
if (root.security === "none") {
|
|
1342
|
-
lines.push(`${INDENT}${INDENT}none`);
|
|
1343
|
-
} else {
|
|
1344
|
-
const sec = root.security;
|
|
1345
|
-
if (sec.policy !== void 0) {
|
|
1346
|
-
const value = sec.policy === false ? "none" : sec.policy;
|
|
1347
|
-
lines.push(`${INDENT}${INDENT}policy: ${value}`);
|
|
1348
|
-
}
|
|
1349
|
-
}
|
|
1350
|
-
lines.push(`${INDENT}}`);
|
|
1351
|
-
}
|
|
1352
|
-
lines.push("}");
|
|
1353
|
-
return lines.join("\n");
|
|
1354
|
-
}
|
|
1355
|
-
__name(serializeOptions, "serializeOptions");
|
|
1356
|
-
function serializeModel(model, ctx) {
|
|
1357
|
-
const parts = [];
|
|
1358
|
-
const prefixes = [];
|
|
1359
|
-
if (model.inputCase && model.inputCase !== "camel") {
|
|
1360
|
-
prefixes.push(`format(input=${model.inputCase})`);
|
|
1361
|
-
}
|
|
1362
|
-
if (model.mode && model.mode !== "strict") {
|
|
1363
|
-
prefixes.push(`mode(${model.mode})`);
|
|
1364
|
-
}
|
|
1365
|
-
if (model.deprecated) {
|
|
1366
|
-
prefixes.push("deprecated");
|
|
1367
|
-
}
|
|
1368
|
-
const prefix = prefixes.length > 0 ? prefixes.join(" ") + " " : "";
|
|
1369
|
-
const comment = ctx.includeComments && model.description ? ` # ${singleLineComment(model.description)}` : "";
|
|
1370
|
-
if (model.type) {
|
|
1371
|
-
parts.push(`contract ${prefix}${model.name}: ${serializeType(model.type)}${comment}`);
|
|
1372
|
-
return parts.join("");
|
|
1373
|
-
}
|
|
1374
|
-
if (model.bases && model.bases.length > 0) {
|
|
1375
|
-
parts.push(`contract ${prefix}${model.name}: ${model.bases.join(" & ")} & {${comment}`);
|
|
1376
|
-
} else {
|
|
1377
|
-
parts.push(`contract ${prefix}${model.name}: {${comment}`);
|
|
1378
|
-
}
|
|
1379
|
-
for (const field of model.fields) {
|
|
1380
|
-
parts.push(serializeField(field, 1, ctx));
|
|
1381
|
-
}
|
|
1382
|
-
parts.push("}");
|
|
1383
|
-
return parts.join("\n");
|
|
1384
|
-
}
|
|
1385
|
-
__name(serializeModel, "serializeModel");
|
|
1386
|
-
function serializeField(field, depth, ctx) {
|
|
1387
|
-
const indent = INDENT.repeat(depth);
|
|
1388
|
-
const optional = field.optional ? "?" : "";
|
|
1389
|
-
const visibility = field.visibility !== "normal" ? `${field.visibility} ` : "";
|
|
1390
|
-
const deprecated = field.deprecated ? "deprecated " : "";
|
|
1391
|
-
let typeStr = serializeType(field.type);
|
|
1392
|
-
if (field.nullable && !typeContainsNull(field.type)) {
|
|
1393
|
-
typeStr = `${typeStr} | null`;
|
|
1394
|
-
}
|
|
1395
|
-
const defaultVal = field.default !== void 0 ? ` = ${serializeDefault(field.default)}` : "";
|
|
1396
|
-
const comment = ctx.includeComments && field.description ? ` # ${singleLineComment(field.description)}` : "";
|
|
1397
|
-
return `${indent}${field.name}${optional}: ${deprecated}${visibility}${typeStr}${defaultVal}${comment}`;
|
|
1398
|
-
}
|
|
1399
|
-
__name(serializeField, "serializeField");
|
|
1400
|
-
function typeContainsNull(type) {
|
|
1401
|
-
if (type.kind === "scalar" && type.name === "null") return true;
|
|
1402
|
-
if (type.kind === "union") return type.members.some(typeContainsNull);
|
|
1403
|
-
return false;
|
|
1404
|
-
}
|
|
1405
|
-
__name(typeContainsNull, "typeContainsNull");
|
|
1406
|
-
function serializeDefault(value) {
|
|
1407
|
-
if (typeof value === "string") {
|
|
1408
|
-
if (/^[a-zA-Z_$][a-zA-Z0-9_$\-.]*$/.test(value)) return value;
|
|
1409
|
-
return `"${value}"`;
|
|
1410
|
-
}
|
|
1411
|
-
return String(value);
|
|
1412
|
-
}
|
|
1413
|
-
__name(serializeDefault, "serializeDefault");
|
|
1414
|
-
function serializeType(type) {
|
|
1415
|
-
switch (type.kind) {
|
|
1416
|
-
case "scalar":
|
|
1417
|
-
return serializeScalar(type);
|
|
1418
|
-
case "array":
|
|
1419
|
-
return serializeArray(type);
|
|
1420
|
-
case "tuple":
|
|
1421
|
-
return `tuple(${type.items.map(serializeType).join(", ")})`;
|
|
1422
|
-
case "record":
|
|
1423
|
-
return `record(${serializeType(type.key)}, ${serializeType(type.value)})`;
|
|
1424
|
-
case "enum":
|
|
1425
|
-
return `enum(${type.values.map(quoteEnumValue).join(", ")})`;
|
|
1426
|
-
case "literal":
|
|
1427
|
-
return serializeLiteral(type);
|
|
1428
|
-
case "union":
|
|
1429
|
-
return type.members.map(serializeType).join(" | ");
|
|
1430
|
-
case "discriminatedUnion":
|
|
1431
|
-
return `discriminated(by=${type.discriminator}, ${type.members.map(serializeType).join(" | ")})`;
|
|
1432
|
-
case "intersection":
|
|
1433
|
-
return type.members.map(serializeType).join(" & ");
|
|
1434
|
-
case "ref":
|
|
1435
|
-
return type.name;
|
|
1436
|
-
case "inlineObject":
|
|
1437
|
-
return serializeInlineObject(type);
|
|
1438
|
-
case "lazy":
|
|
1439
|
-
return `lazy(${serializeType(type.inner)})`;
|
|
1440
|
-
}
|
|
1441
|
-
}
|
|
1442
|
-
__name(serializeType, "serializeType");
|
|
1443
|
-
function serializeScalar(type) {
|
|
1444
|
-
const args = [];
|
|
1445
|
-
if (type.len !== void 0) args.push(`length=${type.len}`);
|
|
1446
|
-
if (type.min !== void 0) args.push(typeof type.min === "string" ? `min="${type.min}"` : `min=${type.min}`);
|
|
1447
|
-
if (type.max !== void 0) args.push(typeof type.max === "string" ? `max="${type.max}"` : `max=${type.max}`);
|
|
1448
|
-
if (type.regex !== void 0) args.push(`regex=${type.regex}`);
|
|
1449
|
-
if (type.format !== void 0) args.push(`format=${type.format}`);
|
|
1450
|
-
if (args.length === 0) return type.name;
|
|
1451
|
-
return `${type.name}(${args.join(", ")})`;
|
|
1452
|
-
}
|
|
1453
|
-
__name(serializeScalar, "serializeScalar");
|
|
1454
|
-
function serializeArray(type) {
|
|
1455
|
-
const args = [
|
|
1456
|
-
serializeType(type.item)
|
|
1457
|
-
];
|
|
1458
|
-
if (type.min !== void 0) args.push(`min=${type.min}`);
|
|
1459
|
-
if (type.max !== void 0) args.push(`max=${type.max}`);
|
|
1460
|
-
return `array(${args.join(", ")})`;
|
|
1461
|
-
}
|
|
1462
|
-
__name(serializeArray, "serializeArray");
|
|
1463
|
-
function serializeLiteral(type) {
|
|
1464
|
-
if (typeof type.value === "string") return `literal("${type.value}")`;
|
|
1465
|
-
return `literal(${type.value})`;
|
|
1466
|
-
}
|
|
1467
|
-
__name(serializeLiteral, "serializeLiteral");
|
|
1468
|
-
function serializeInlineObject(type) {
|
|
1469
|
-
const modePrefix = type.mode ? `mode(${type.mode}) ` : "";
|
|
1470
|
-
if (type.fields.length === 0) return `${modePrefix}{}`;
|
|
1471
|
-
const lines = [
|
|
1472
|
-
`${modePrefix}{`
|
|
1473
|
-
];
|
|
1474
|
-
for (const field of type.fields) {
|
|
1475
|
-
lines.push(serializeField(field, 2, {
|
|
1476
|
-
includeComments: true
|
|
1477
|
-
}));
|
|
1478
|
-
}
|
|
1479
|
-
lines.push(`${INDENT}}`);
|
|
1480
|
-
return lines.join("\n");
|
|
1481
|
-
}
|
|
1482
|
-
__name(serializeInlineObject, "serializeInlineObject");
|
|
1483
|
-
function serializeRoute(route, ctx) {
|
|
1484
|
-
const lines = [];
|
|
1485
|
-
const modStr = serializeModifiers(route.modifiers);
|
|
1486
|
-
const comment = ctx.includeComments && route.description ? ` # ${singleLineComment(route.description)}` : "";
|
|
1487
|
-
lines.push(`operation${modStr} ${route.path}: {${comment}`);
|
|
1488
|
-
if (route.params) {
|
|
1489
|
-
serializeParamSource(lines, "params", route.params, route.paramsMode, 1, ctx);
|
|
1490
|
-
}
|
|
1491
|
-
if (route.security !== void 0) {
|
|
1492
|
-
serializeSecurityBlock(lines, route.security, 1, ctx);
|
|
1493
|
-
}
|
|
1494
|
-
for (const op of route.operations) {
|
|
1495
|
-
serializeOperation(lines, op, 1, ctx);
|
|
1496
|
-
}
|
|
1497
|
-
lines.push("}");
|
|
1498
|
-
return lines.join("\n");
|
|
1499
|
-
}
|
|
1500
|
-
__name(serializeRoute, "serializeRoute");
|
|
1501
|
-
function serializeOperation(lines, op, depth, ctx) {
|
|
1502
|
-
const indent = INDENT.repeat(depth);
|
|
1503
|
-
const modStr = serializeModifiers(op.modifiers);
|
|
1504
|
-
const comment = ctx.includeComments && op.description ? ` # ${singleLineComment(op.description)}` : "";
|
|
1505
|
-
lines.push(`${indent}${op.method}${modStr}: {${comment}`);
|
|
1506
|
-
const inner = INDENT.repeat(depth + 1);
|
|
1507
|
-
if (op.service) {
|
|
1508
|
-
lines.push(`${inner}service: ${op.service}`);
|
|
1509
|
-
}
|
|
1510
|
-
if (op.sdk) {
|
|
1511
|
-
lines.push(`${inner}sdk: ${op.sdk}`);
|
|
1512
|
-
}
|
|
1513
|
-
if (op.signature) {
|
|
1514
|
-
const sigComment = ctx.includeComments && op.signatureDescription ? ` # ${singleLineComment(op.signatureDescription)}` : "";
|
|
1515
|
-
if (op.signaturePolicy) {
|
|
1516
|
-
lines.push(`${inner}signature: {`);
|
|
1517
|
-
lines.push(`${inner} options: ${op.signature}${sigComment}`);
|
|
1518
|
-
lines.push(`${inner} policy: ${op.signaturePolicy}`);
|
|
1519
|
-
lines.push(`${inner}}`);
|
|
1520
|
-
} else {
|
|
1521
|
-
lines.push(`${inner}signature: ${op.signature}${sigComment}`);
|
|
1522
|
-
}
|
|
1523
|
-
}
|
|
1524
|
-
if (op.security !== void 0) {
|
|
1525
|
-
serializeSecurityBlock(lines, op.security, depth + 1, ctx);
|
|
1526
|
-
}
|
|
1527
|
-
if (op.query) {
|
|
1528
|
-
serializeParamSource(lines, "query", op.query, op.queryMode, depth + 1, ctx);
|
|
1529
|
-
}
|
|
1530
|
-
if (op.headers) {
|
|
1531
|
-
serializeParamSource(lines, "headers", op.headers, op.headersMode, depth + 1, ctx);
|
|
1532
|
-
}
|
|
1533
|
-
if (op.request) {
|
|
1534
|
-
serializeRequest(lines, op.request, depth + 1);
|
|
1535
|
-
}
|
|
1536
|
-
if (op.responses.length > 0) {
|
|
1537
|
-
serializeResponses(lines, op.responses, depth + 1);
|
|
1538
|
-
}
|
|
1539
|
-
lines.push(`${indent}}`);
|
|
1540
|
-
return lines;
|
|
1541
|
-
}
|
|
1542
|
-
__name(serializeOperation, "serializeOperation");
|
|
1543
|
-
function serializeModifiers(modifiers) {
|
|
1544
|
-
if (!modifiers || modifiers.length === 0) return "";
|
|
1545
|
-
return `(${modifiers.join(", ")})`;
|
|
1546
|
-
}
|
|
1547
|
-
__name(serializeModifiers, "serializeModifiers");
|
|
1548
|
-
function serializeParamSource(lines, keyword, source, mode, depth, ctx) {
|
|
1549
|
-
const indent = INDENT.repeat(depth);
|
|
1550
|
-
if (source.kind === "ref") {
|
|
1551
|
-
lines.push(`${indent}${keyword}: ${source.name}`);
|
|
1552
|
-
return;
|
|
1553
|
-
}
|
|
1554
|
-
if (source.kind === "type") {
|
|
1555
|
-
lines.push(`${indent}${keyword}: ${serializeType(source.node)}`);
|
|
1556
|
-
return;
|
|
1557
|
-
}
|
|
1558
|
-
const modeStr = mode ? `mode(${mode}) ` : "";
|
|
1559
|
-
lines.push(`${indent}${keyword}: ${modeStr}{`);
|
|
1560
|
-
for (const param of source.nodes) {
|
|
1561
|
-
const optional = param.optional ? "?" : "";
|
|
1562
|
-
let typeStr = serializeType(param.type);
|
|
1563
|
-
if (param.nullable && !typeContainsNull(param.type)) {
|
|
1564
|
-
typeStr = `${typeStr} | null`;
|
|
1565
|
-
}
|
|
1566
|
-
const defaultVal = param.default !== void 0 ? ` = ${serializeDefault(param.default)}` : "";
|
|
1567
|
-
const comment = ctx.includeComments && param.description ? ` # ${singleLineComment(param.description)}` : "";
|
|
1568
|
-
lines.push(`${INDENT.repeat(depth + 1)}${param.name}${optional}: ${typeStr}${defaultVal}${comment}`);
|
|
1569
|
-
}
|
|
1570
|
-
lines.push(`${indent}}`);
|
|
1571
|
-
}
|
|
1572
|
-
__name(serializeParamSource, "serializeParamSource");
|
|
1573
|
-
function serializeRequest(lines, request, depth) {
|
|
1574
|
-
const indent = INDENT.repeat(depth);
|
|
1575
|
-
lines.push(`${indent}request: {`);
|
|
1576
|
-
for (const body of request.bodies) {
|
|
1577
|
-
lines.push(`${INDENT.repeat(depth + 1)}${body.contentType}: ${serializeType(body.bodyType)}`);
|
|
1578
|
-
}
|
|
1579
|
-
lines.push(`${indent}}`);
|
|
1580
|
-
}
|
|
1581
|
-
__name(serializeRequest, "serializeRequest");
|
|
1582
|
-
function serializeResponses(lines, responses, depth) {
|
|
1583
|
-
const indent = INDENT.repeat(depth);
|
|
1584
|
-
lines.push(`${indent}response: {`);
|
|
1585
|
-
for (const resp of responses) {
|
|
1586
|
-
const bodies = resp.bodies;
|
|
1587
|
-
const hasHeaders = resp.headers && resp.headers.length > 0;
|
|
1588
|
-
if (bodies.length > 0 || hasHeaders) {
|
|
1589
|
-
lines.push(`${INDENT.repeat(depth + 1)}${resp.statusCode}: {`);
|
|
1590
|
-
for (const body of bodies) {
|
|
1591
|
-
lines.push(`${INDENT.repeat(depth + 2)}${body.contentType}: ${serializeType(body.bodyType)}`);
|
|
1592
|
-
}
|
|
1593
|
-
if (hasHeaders) {
|
|
1594
|
-
lines.push(`${INDENT.repeat(depth + 2)}headers: {`);
|
|
1595
|
-
for (const h of resp.headers) {
|
|
1596
|
-
const opt = h.optional ? "?" : "";
|
|
1597
|
-
const trail = h.description ? ` # ${singleLineComment(h.description)}` : "";
|
|
1598
|
-
lines.push(`${INDENT.repeat(depth + 3)}${h.name}${opt}: ${serializeType(h.type)}${trail}`);
|
|
1599
|
-
}
|
|
1600
|
-
lines.push(`${INDENT.repeat(depth + 2)}}`);
|
|
1601
|
-
}
|
|
1602
|
-
lines.push(`${INDENT.repeat(depth + 1)}}`);
|
|
1603
|
-
} else {
|
|
1604
|
-
lines.push(`${INDENT.repeat(depth + 1)}${resp.statusCode}:`);
|
|
1605
|
-
}
|
|
1606
|
-
}
|
|
1607
|
-
lines.push(`${indent}}`);
|
|
1608
|
-
}
|
|
1609
|
-
__name(serializeResponses, "serializeResponses");
|
|
1610
|
-
function serializeSecurityBlock(lines, security, depth, ctx) {
|
|
1611
|
-
const indent = INDENT.repeat(depth);
|
|
1612
|
-
if (security === "none") {
|
|
1613
|
-
lines.push(`${indent}security: none`);
|
|
1614
|
-
return;
|
|
1615
|
-
}
|
|
1616
|
-
const sec = security;
|
|
1617
|
-
if (sec.policy !== void 0) {
|
|
1618
|
-
const comment = ctx.includeComments && sec.policyDescription ? ` # ${singleLineComment(sec.policyDescription)}` : "";
|
|
1619
|
-
const value = sec.policy === false ? "none" : sec.policy;
|
|
1620
|
-
lines.push(`${indent}security: {`);
|
|
1621
|
-
lines.push(`${INDENT.repeat(depth + 1)}policy: ${value}${comment}`);
|
|
1622
|
-
lines.push(`${indent}}`);
|
|
1623
|
-
} else {
|
|
1624
|
-
lines.push(`${indent}security: {}`);
|
|
1625
|
-
}
|
|
1626
|
-
}
|
|
1627
|
-
__name(serializeSecurityBlock, "serializeSecurityBlock");
|
|
1431
|
+
var serializeType = printType;
|
|
1628
1432
|
|
|
1629
1433
|
// src/convert.ts
|
|
1630
1434
|
import { readFileSync } from "fs";
|
|
@@ -1661,8 +1465,9 @@ var WarningCollector = class {
|
|
|
1661
1465
|
};
|
|
1662
1466
|
|
|
1663
1467
|
// src/convert.ts
|
|
1468
|
+
import { parseCk, decomposeCk, validateRefs, DiagnosticCollector } from "@contractkit/core";
|
|
1664
1469
|
async function convertOpenApiToCk(options) {
|
|
1665
|
-
const { split = "by-tag", includeComments = true } = options;
|
|
1470
|
+
const { split = "by-tag", includeComments = true, errorResponses = "documented" } = options;
|
|
1666
1471
|
const warnings = new WarningCollector(options.onWarning);
|
|
1667
1472
|
const rawDoc = await parseInput(options.input);
|
|
1668
1473
|
const doc = normalize(rawDoc, warnings);
|
|
@@ -1676,7 +1481,8 @@ async function convertOpenApiToCk(options) {
|
|
|
1676
1481
|
includeComments,
|
|
1677
1482
|
namedSchemas: schemas,
|
|
1678
1483
|
extractedModels,
|
|
1679
|
-
inlineCounter: 0
|
|
1484
|
+
inlineCounter: 0,
|
|
1485
|
+
insideModel: true
|
|
1680
1486
|
};
|
|
1681
1487
|
const models = schemasToModels(schemas, schemaCtx);
|
|
1682
1488
|
const { routes, routeTags } = pathsToRoutes(doc, {
|
|
@@ -1685,28 +1491,50 @@ async function convertOpenApiToCk(options) {
|
|
|
1685
1491
|
includeComments,
|
|
1686
1492
|
namedSchemas: schemas,
|
|
1687
1493
|
extractedModels,
|
|
1688
|
-
globalSecurity: doc.security
|
|
1494
|
+
globalSecurity: doc.security,
|
|
1495
|
+
errorResponses
|
|
1689
1496
|
});
|
|
1497
|
+
const known = new Set(models.map((m) => m.name));
|
|
1498
|
+
for (const extracted of extractedModels) {
|
|
1499
|
+
if (known.has(extracted.name)) continue;
|
|
1500
|
+
known.add(extracted.name);
|
|
1501
|
+
models.push(extracted);
|
|
1502
|
+
}
|
|
1690
1503
|
const files = /* @__PURE__ */ new Map();
|
|
1691
1504
|
if (split === "by-tag") {
|
|
1692
1505
|
const ckRoots = splitByTag(models, routes, routeTags);
|
|
1693
1506
|
for (const [filename, root] of ckRoots) {
|
|
1694
|
-
files.set(filename, astToCk(root
|
|
1695
|
-
includeComments
|
|
1696
|
-
}));
|
|
1507
|
+
files.set(filename, astToCk(root));
|
|
1697
1508
|
}
|
|
1698
1509
|
} else {
|
|
1699
1510
|
const root = mergeIntoSingle(models, routes);
|
|
1700
|
-
files.set("api.ck", astToCk(root
|
|
1701
|
-
includeComments
|
|
1702
|
-
}));
|
|
1511
|
+
files.set("api.ck", astToCk(root));
|
|
1703
1512
|
}
|
|
1513
|
+
checkGeneratedFiles(files, warnings);
|
|
1704
1514
|
return {
|
|
1705
1515
|
files,
|
|
1706
1516
|
warnings: warnings.warnings
|
|
1707
1517
|
};
|
|
1708
1518
|
}
|
|
1709
1519
|
__name(convertOpenApiToCk, "convertOpenApiToCk");
|
|
1520
|
+
function checkGeneratedFiles(files, warnings) {
|
|
1521
|
+
const diag = new DiagnosticCollector();
|
|
1522
|
+
const roots = [];
|
|
1523
|
+
for (const [filename, text] of files) {
|
|
1524
|
+
const before = diag.getAll().length;
|
|
1525
|
+
const root = parseCk(text, filename, diag);
|
|
1526
|
+
if (diag.getAll().length === before) roots.push(root);
|
|
1527
|
+
}
|
|
1528
|
+
if (roots.length === files.size) {
|
|
1529
|
+
const decomposed = roots.map(decomposeCk);
|
|
1530
|
+
validateRefs(decomposed.map((d) => d.contract), decomposed.map((d) => d.op), diag);
|
|
1531
|
+
}
|
|
1532
|
+
for (const d of diag.getAll()) {
|
|
1533
|
+
if (d.severity !== "error") continue;
|
|
1534
|
+
warnings.warn(d.file, `generated .ck is not valid (line ${d.line}): ${d.message}`);
|
|
1535
|
+
}
|
|
1536
|
+
}
|
|
1537
|
+
__name(checkGeneratedFiles, "checkGeneratedFiles");
|
|
1710
1538
|
async function parseInput(input) {
|
|
1711
1539
|
if (typeof input === "object") {
|
|
1712
1540
|
return input;
|
|
@@ -1784,4 +1612,4 @@ export {
|
|
|
1784
1612
|
serializeType,
|
|
1785
1613
|
convertOpenApiToCk
|
|
1786
1614
|
};
|
|
1787
|
-
//# sourceMappingURL=chunk-
|
|
1615
|
+
//# sourceMappingURL=chunk-U7V7LFBV.js.map
|