Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

@contractkit/openapi-to-ck

Package Overview
Dependencies
Maintainers
1
Versions
11
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@contractkit/openapi-to-ck - npm Package Compare versions

Comparing version
0.7.8
to
0.8.0
+1768
dist/chunk-YOTHJ2V4.js
var __defProp = Object.defineProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
// src/normalize.ts
function normalize(doc, warnings) {
const version = detectVersion(doc);
if (version === "2.0") {
return normalizeSwagger2(doc, warnings);
}
if (version === "3.0") {
return normalizeOas30(doc, warnings);
}
return doc;
}
__name(normalize, "normalize");
function detectVersion(doc) {
if (typeof doc.swagger === "string" && doc.swagger.startsWith("2")) return "2.0";
if (typeof doc.openapi === "string") {
if (doc.openapi.startsWith("3.0")) return "3.0";
}
return "3.1";
}
__name(detectVersion, "detectVersion");
function normalizeSwagger2(doc, warnings) {
const info = doc.info ?? {
title: "Untitled",
version: "0.0.0"
};
const basePath = doc.basePath ?? "";
const schemes = doc.schemes ?? [
"https"
];
const host = doc.host ?? "localhost";
const globalConsumes = doc.consumes ?? [
"application/json"
];
const globalProduces = doc.produces ?? [
"application/json"
];
const result = {
openapi: "3.1.0",
info: {
title: info.title ?? "Untitled",
version: info.version ?? "0.0.0",
description: info.description
},
servers: [
{
url: `${schemes[0]}://${host}${basePath}`
}
],
paths: {},
components: {
schemas: {},
securitySchemes: {}
},
tags: doc.tags ?? []
};
const definitions = doc.definitions ?? {};
for (const [name, schema] of Object.entries(definitions)) {
result.components.schemas[name] = normalizeNullable30(schema);
}
const secDefs = doc.securityDefinitions ?? {};
for (const [name, scheme] of Object.entries(secDefs)) {
result.components.securitySchemes[name] = convertSecurityScheme2(scheme);
}
const paths = doc.paths ?? {};
for (const [path, pathItem] of Object.entries(paths)) {
result.paths[path] = normalizePathItem2(pathItem, globalConsumes, globalProduces, warnings);
}
if (doc.security) {
result.security = doc.security;
}
return result;
}
__name(normalizeSwagger2, "normalizeSwagger2");
function normalizePathItem2(pathItem, globalConsumes, globalProduces, warnings) {
const methods = [
"get",
"post",
"put",
"patch",
"delete",
"head",
"options"
];
const normalized = {};
const pathParams = pathItem.parameters ?? [];
for (const method of methods) {
const op = pathItem[method];
if (!op) continue;
const opConsumes = op.consumes ?? globalConsumes;
const opProduces = op.produces ?? globalProduces;
const params = [
...pathParams,
...op.parameters ?? []
];
const nonBodyParams = [];
let requestBody;
for (const param of params) {
if (param.in === "body") {
const contentType = opConsumes[0] ?? "application/json";
requestBody = {
description: param.description,
required: param.required ?? true,
content: {
[contentType]: {
schema: normalizeNullable30(param.schema ?? {})
}
}
};
} else if (param.in === "formData") {
warnings.info(`#/paths/${encodePathSegment(method)}`, "formData parameters converted to multipart/form-data requestBody");
if (!requestBody) {
requestBody = {
content: {
"multipart/form-data": {
schema: {
type: "object",
properties: {},
required: []
}
}
}
};
}
const formSchema = requestBody.content["multipart/form-data"].schema;
const props = formSchema.properties;
props[param.name] = normalizeNullable30(param);
if (param.required) {
formSchema.required.push(param.name);
}
} else {
const normalizedParam = {
...param
};
if (param.type) {
normalizedParam.schema = normalizeNullable30({
type: param.type,
format: param.format,
enum: param.enum,
items: param.items,
default: param.default,
minimum: param.minimum,
maximum: param.maximum,
minLength: param.minLength,
maxLength: param.maxLength,
pattern: param.pattern
});
delete normalizedParam.type;
delete normalizedParam.format;
delete normalizedParam.enum;
delete normalizedParam.items;
}
nonBodyParams.push(normalizedParam);
}
}
const responses = {};
const opResponses = op.responses ?? {};
for (const [code, resp] of Object.entries(opResponses)) {
const contentType = opProduces[0] ?? "application/json";
const headers = convertResponseHeaders2(resp.headers);
const responseEntry = {
description: resp.description ?? ""
};
if (resp.schema) {
responseEntry.content = {
[contentType]: {
schema: normalizeNullable30(resp.schema)
}
};
}
if (headers) {
responseEntry.headers = headers;
}
responses[code] = responseEntry;
}
normalized[method] = {
operationId: op.operationId,
summary: op.summary,
description: op.description,
tags: op.tags,
parameters: nonBodyParams.length > 0 ? nonBodyParams : void 0,
requestBody,
responses,
security: op.security,
deprecated: op.deprecated
};
}
return normalized;
}
__name(normalizePathItem2, "normalizePathItem2");
function convertResponseHeaders2(headers) {
if (!headers) return void 0;
const out = {};
for (const [name, header] of Object.entries(headers)) {
if (!header || typeof header !== "object") continue;
const { description, type, format, items, ...rest } = header;
const schema = {
...rest
};
if (type !== void 0) schema.type = type;
if (format !== void 0) schema.format = format;
if (items !== void 0) schema.items = items;
const normalized = {};
if (description !== void 0) normalized.description = description;
if (Object.keys(schema).length > 0) normalized.schema = normalizeNullable30(schema);
out[name] = normalized;
}
return Object.keys(out).length > 0 ? out : void 0;
}
__name(convertResponseHeaders2, "convertResponseHeaders2");
function convertSecurityScheme2(scheme) {
const type = scheme.type;
if (type === "basic") {
return {
type: "http",
scheme: "basic"
};
}
if (type === "apiKey") {
return {
type: "apiKey",
name: scheme.name,
in: scheme.in
};
}
if (type === "oauth2") {
const flow = scheme.flow;
const flows = {};
if (flow === "implicit") {
flows.implicit = {
authorizationUrl: scheme.authorizationUrl,
scopes: scheme.scopes ?? {}
};
} else if (flow === "password") {
flows.password = {
tokenUrl: scheme.tokenUrl,
scopes: scheme.scopes ?? {}
};
} else if (flow === "application") {
flows.clientCredentials = {
tokenUrl: scheme.tokenUrl,
scopes: scheme.scopes ?? {}
};
} else if (flow === "accessCode") {
flows.authorizationCode = {
authorizationUrl: scheme.authorizationUrl,
tokenUrl: scheme.tokenUrl,
scopes: scheme.scopes ?? {}
};
}
return {
type: "oauth2",
flows
};
}
return scheme;
}
__name(convertSecurityScheme2, "convertSecurityScheme2");
function normalizeOas30(doc, _warnings) {
if (doc.components?.schemas) {
for (const [name, schema] of Object.entries(doc.components.schemas)) {
doc.components.schemas[name] = normalizeNullable30(schema);
}
}
if (doc.paths) {
for (const pathItem of Object.values(doc.paths)) {
normalizePathItemSchemas(pathItem);
}
}
doc.openapi = "3.1.0";
return doc;
}
__name(normalizeOas30, "normalizeOas30");
function normalizePathItemSchemas(pathItem) {
const methods = [
"get",
"post",
"put",
"patch",
"delete",
"head",
"options"
];
for (const method of methods) {
const op = pathItem[method];
if (!op) continue;
const params = op.parameters ?? [];
for (const param of params) {
if (param.schema) {
param.schema = normalizeNullable30(param.schema);
}
}
const reqBody = op.requestBody;
if (reqBody?.content) {
for (const mediaType of Object.values(reqBody.content)) {
if (mediaType.schema) {
mediaType.schema = normalizeNullable30(mediaType.schema);
}
}
}
const responses = op.responses ?? {};
for (const resp of Object.values(responses)) {
if (resp.content) {
for (const mediaType of Object.values(resp.content)) {
if (mediaType.schema) {
mediaType.schema = normalizeNullable30(mediaType.schema);
}
}
}
}
}
}
__name(normalizePathItemSchemas, "normalizePathItemSchemas");
function normalizeNullable30(schema) {
if (!schema || typeof schema !== "object") return schema;
const result = {
...schema
};
if (result.nullable === true && typeof result.type === "string") {
result.type = [
result.type,
"null"
];
delete result.nullable;
}
if (result.properties && typeof result.properties === "object") {
const props = result.properties;
for (const [key, val] of Object.entries(props)) {
props[key] = normalizeNullable30(val);
}
}
if (result.items && typeof result.items === "object" && !Array.isArray(result.items)) {
result.items = normalizeNullable30(result.items);
}
if (result.additionalProperties && typeof result.additionalProperties === "object") {
result.additionalProperties = normalizeNullable30(result.additionalProperties);
}
for (const combiner of [
"allOf",
"oneOf",
"anyOf"
]) {
if (Array.isArray(result[combiner])) {
result[combiner] = result[combiner].map(normalizeNullable30);
}
}
return result;
}
__name(normalizeNullable30, "normalizeNullable30");
function encodePathSegment(s) {
return s.replace(/~/g, "~0").replace(/\//g, "~1");
}
__name(encodePathSegment, "encodePathSegment");
// src/circular-refs.ts
function detectCircularRefs(schemas) {
const circular = /* @__PURE__ */ new Set();
const visiting = /* @__PURE__ */ new Set();
const visited = /* @__PURE__ */ new Set();
function visit(name) {
if (visited.has(name)) return;
if (visiting.has(name)) {
circular.add(name);
return;
}
visiting.add(name);
const schema = schemas[name];
if (schema && typeof schema === "object") {
for (const ref of collectRefs(schema)) {
const refName = extractRefName(ref);
if (refName && schemas[refName]) {
visit(refName);
}
}
}
visiting.delete(name);
visited.add(name);
}
__name(visit, "visit");
for (const name of Object.keys(schemas)) {
visit(name);
}
return circular;
}
__name(detectCircularRefs, "detectCircularRefs");
function collectRefs(obj) {
const refs = [];
function walk(val) {
if (!val || typeof val !== "object") return;
if (Array.isArray(val)) {
for (const item of val) walk(item);
return;
}
const record = val;
if (typeof record.$ref === "string") {
refs.push(record.$ref);
}
for (const v of Object.values(record)) {
walk(v);
}
}
__name(walk, "walk");
walk(obj);
return refs;
}
__name(collectRefs, "collectRefs");
function extractRefName(ref) {
const match = ref.match(/^#\/(?:components\/schemas|definitions)\/(.+)$/);
return match?.[1];
}
__name(extractRefName, "extractRefName");
// src/schema-to-ast.ts
var LOC = {
file: "",
line: 0
};
var FORMAT_TO_SCALAR = {
email: "email",
uri: "url",
url: "url",
uuid: "uuid",
date: "date",
"date-time": "datetime",
time: "time",
binary: "binary",
int64: "bigint"
};
function schemasToModels(schemas, ctx) {
const models = [];
for (const [name, schema] of Object.entries(schemas)) {
const modelCtx = {
...ctx,
path: `#/components/schemas/${name}`
};
const model = schemaToModel(name, schema, modelCtx);
if (model) models.push(model);
}
models.push(...ctx.extractedModels);
return models;
}
__name(schemasToModels, "schemasToModels");
function schemaToModel(name, schema, ctx) {
warnUnsupported(schema, ctx);
const description = ctx.includeComments ? schema.description : void 0;
if (schema.allOf && schema.allOf.length === 2) {
const [first, second] = schema.allOf;
const refMember = first?.$ref ? first : second?.$ref ? second : null;
const objectMember = first?.$ref ? second : first;
if (refMember?.$ref && objectMember?.properties) {
const baseName = extractRefName(refMember.$ref);
if (baseName) {
const fields = schemaPropertiesToFields(objectMember, ctx);
return {
kind: "model",
name,
bases: [
baseName
],
fields,
description,
loc: LOC
};
}
}
}
if (schema.properties || schema.type === "object" && !schema.additionalProperties) {
const fields = schemaPropertiesToFields(schema, ctx);
return {
kind: "model",
name,
fields,
description,
loc: LOC
};
}
const typeNode = schemaToTypeNode(schema, ctx);
return {
kind: "model",
name,
fields: [],
type: typeNode,
description,
loc: LOC
};
}
__name(schemaToModel, "schemaToModel");
function schemaToTypeNode(schema, ctx) {
if (schema.$ref) {
const refName = extractRefName(schema.$ref);
if (refName) {
if (ctx.circularRefs.has(refName)) {
return {
kind: "lazy",
inner: {
kind: "ref",
name: refName
}
};
}
return {
kind: "ref",
name: refName
};
}
ctx.warnings.warn(ctx.path, `Unresolvable $ref: ${schema.$ref}`);
return {
kind: "scalar",
name: "unknown"
};
}
if (schema.const !== void 0) {
return {
kind: "literal",
value: schema.const
};
}
if (schema.enum) {
return {
kind: "enum",
values: schema.enum.map(String)
};
}
if (schema.oneOf && schema.oneOf.length > 0) {
if (schema.discriminator?.propertyName) {
return toDiscriminatedUnion(schema.oneOf, schema.discriminator.propertyName, ctx);
}
return toUnion(schema.oneOf, ctx);
}
if (schema.anyOf && schema.anyOf.length > 0) {
if (schema.discriminator?.propertyName) {
return toDiscriminatedUnion(schema.anyOf, schema.discriminator.propertyName, ctx);
}
return toUnion(schema.anyOf, ctx);
}
if (schema.allOf && schema.allOf.length > 0) {
if (schema.allOf.length === 1) {
return schemaToTypeNode(schema.allOf[0], ctx);
}
return {
kind: "intersection",
members: schema.allOf.map((s) => schemaToTypeNode(s, ctx))
};
}
const types = normalizeTypeField(schema);
if (types === null) {
if (schema.properties) {
return schemaToInlineObject(schema, ctx);
}
return {
kind: "scalar",
name: "unknown"
};
}
const { baseType, nullable } = types;
let typeNode;
switch (baseType) {
case "string":
typeNode = stringSchemaToType(schema);
break;
case "integer":
typeNode = integerSchemaToType(schema);
break;
case "number":
typeNode = numberSchemaToType(schema);
break;
case "boolean":
typeNode = {
kind: "scalar",
name: "boolean"
};
break;
case "null":
typeNode = {
kind: "scalar",
name: "null"
};
break;
case "array":
typeNode = arraySchemaToType(schema, ctx);
break;
case "object":
typeNode = objectSchemaToType(schema, ctx);
break;
default:
ctx.warnings.warn(ctx.path, `Unknown type: ${baseType}`);
typeNode = {
kind: "scalar",
name: "unknown"
};
}
if (nullable) {
return {
kind: "union",
members: [
typeNode,
{
kind: "scalar",
name: "null"
}
]
};
}
return typeNode;
}
__name(schemaToTypeNode, "schemaToTypeNode");
function stringSchemaToType(schema) {
if (schema.format) {
const scalarName = FORMAT_TO_SCALAR[schema.format];
if (scalarName) {
return {
kind: "scalar",
name: scalarName
};
}
}
const mods = {};
if (schema.minLength !== void 0 && schema.maxLength !== void 0 && schema.minLength === schema.maxLength) {
mods.len = schema.minLength;
} else {
if (schema.minLength !== void 0) mods.min = schema.minLength;
if (schema.maxLength !== void 0) mods.max = schema.maxLength;
}
if (schema.pattern) mods.regex = `/${schema.pattern}/`;
if (schema.format && !FORMAT_TO_SCALAR[schema.format]) mods.format = schema.format;
return {
kind: "scalar",
name: "string",
...mods
};
}
__name(stringSchemaToType, "stringSchemaToType");
function integerSchemaToType(schema) {
const name = schema.format === "int64" ? "bigint" : "int";
const mods = {};
if (schema.minimum !== void 0) mods.min = schema.minimum;
if (schema.maximum !== void 0) mods.max = schema.maximum;
return {
kind: "scalar",
name,
...mods
};
}
__name(integerSchemaToType, "integerSchemaToType");
function numberSchemaToType(schema) {
const mods = {};
if (schema.minimum !== void 0) mods.min = schema.minimum;
if (schema.maximum !== void 0) mods.max = schema.maximum;
return {
kind: "scalar",
name: "number",
...mods
};
}
__name(numberSchemaToType, "numberSchemaToType");
function arraySchemaToType(schema, ctx) {
if (schema.prefixItems && schema.prefixItems.length > 0) {
return {
kind: "tuple",
items: schema.prefixItems.map((s) => schemaToTypeNode(s, ctx))
};
}
const item = schema.items ? schemaToTypeNode(schema.items, ctx) : {
kind: "scalar",
name: "unknown"
};
const mods = {};
if (schema.minItems !== void 0) mods.min = schema.minItems;
if (schema.maxItems !== void 0) mods.max = schema.maxItems;
return {
kind: "array",
item,
...mods
};
}
__name(arraySchemaToType, "arraySchemaToType");
function objectSchemaToType(schema, ctx) {
if (schema.additionalProperties && typeof schema.additionalProperties === "object" && !schema.properties) {
return {
kind: "record",
key: {
kind: "scalar",
name: "string"
},
value: schemaToTypeNode(schema.additionalProperties, ctx)
};
}
if (schema.properties) {
return schemaToInlineObject(schema, ctx);
}
return {
kind: "scalar",
name: "object"
};
}
__name(objectSchemaToType, "objectSchemaToType");
function schemaToInlineObject(schema, ctx) {
const fields = schemaPropertiesToFields(schema, ctx);
return {
kind: "inlineObject",
fields
};
}
__name(schemaToInlineObject, "schemaToInlineObject");
function schemaPropertiesToFields(schema, ctx) {
const properties = schema.properties ?? {};
const required = new Set(schema.required ?? []);
const fields = [];
for (const [name, propSchema] of Object.entries(properties)) {
const propCtx = {
...ctx,
path: `${ctx.path}/properties/${name}`
};
const fieldType = schemaToTypeNode(propSchema, propCtx);
let nullable = false;
let effectiveType = fieldType;
if (fieldType.kind === "union") {
const nonNull = fieldType.members.filter((m) => !(m.kind === "scalar" && m.name === "null"));
if (nonNull.length < fieldType.members.length) {
nullable = true;
effectiveType = nonNull.length === 1 ? nonNull[0] : {
kind: "union",
members: nonNull
};
}
}
const visibility = propSchema.readOnly ? "readonly" : propSchema.writeOnly ? "writeonly" : "normal";
fields.push({
name,
optional: !required.has(name),
nullable,
visibility,
type: effectiveType,
default: propSchema.default,
deprecated: propSchema.deprecated,
description: ctx.includeComments ? propSchema.description : void 0,
loc: LOC
});
}
return fields;
}
__name(schemaPropertiesToFields, "schemaPropertiesToFields");
function normalizeTypeField(schema) {
if (!schema.type) return null;
if (typeof schema.type === "string") {
return {
baseType: schema.type,
nullable: false
};
}
if (Array.isArray(schema.type)) {
const types = schema.type;
const nonNull = types.filter((t) => t !== "null");
const nullable = types.includes("null");
if (nonNull.length === 1) {
return {
baseType: nonNull[0],
nullable
};
}
if (nonNull.length === 0) {
return {
baseType: "null",
nullable: false
};
}
return {
baseType: nonNull[0],
nullable
};
}
return null;
}
__name(normalizeTypeField, "normalizeTypeField");
function toUnion(schemas, ctx) {
const members = schemas.map((s) => schemaToTypeNode(s, ctx));
if (members.length === 1) return members[0];
return {
kind: "union",
members
};
}
__name(toUnion, "toUnion");
function toDiscriminatedUnion(schemas, discriminator, ctx) {
const members = schemas.map((s) => schemaToTypeNode(s, ctx));
if (members.length === 1) return members[0];
return {
kind: "discriminatedUnion",
discriminator,
members
};
}
__name(toDiscriminatedUnion, "toDiscriminatedUnion");
function warnUnsupported(schema, ctx) {
if (schema.xml) ctx.warnings.warn(ctx.path, "xml metadata is not supported, skipping");
if (schema.externalDocs) ctx.warnings.info(ctx.path, "externalDocs is not supported, skipping");
if (schema.not) ctx.warnings.warn(ctx.path, "not keyword is not supported, skipping");
}
__name(warnUnsupported, "warnUnsupported");
function extractInlineModel(schema, suggestedName, ctx) {
if (schema.$ref) {
return {
typeNode: schemaToTypeNode(schema, ctx)
};
}
if (schema.properties || schema.type === "object" && schema.additionalProperties === void 0) {
const fields = schemaPropertiesToFields(schema, ctx);
const model = {
kind: "model",
name: suggestedName,
fields,
description: ctx.includeComments ? schema.description : void 0,
loc: LOC
};
return {
typeNode: {
kind: "ref",
name: suggestedName
},
model
};
}
return {
typeNode: schemaToTypeNode(schema, ctx)
};
}
__name(extractInlineModel, "extractInlineModel");
function sanitizeName(name, warnings) {
const cleaned = name.replace(/[^a-zA-Z0-9_$]/g, " ").split(/\s+/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
if (cleaned !== name) {
warnings.info(`#/components/schemas/${name}`, `Schema name sanitized: "${name}" \u2192 "${cleaned}"`);
}
return cleaned || "UnnamedSchema";
}
__name(sanitizeName, "sanitizeName");
// src/paths-to-ast.ts
var LOC2 = {
file: "",
line: 0
};
var HTTP_METHODS = [
"get",
"post",
"put",
"patch",
"delete"
];
function pathsToRoutes(doc, ctx) {
const routes = [];
const routeTags = /* @__PURE__ */ new Map();
const paths = doc.paths ?? {};
for (const [path, pathItem] of Object.entries(paths)) {
if (!pathItem) continue;
const result = pathItemToRoute(path, pathItem, ctx);
if (result) {
routes.push(result.route);
routeTags.set(result.route, result.tag);
}
}
return {
routes,
routeTags
};
}
__name(pathsToRoutes, "pathsToRoutes");
function pathItemToRoute(path, pathItem, ctx) {
const operations = [];
let primaryTag = "default";
const pathParams = (pathItem.parameters ?? []).filter((p) => p.in === "path");
for (const method of HTTP_METHODS) {
const op = pathItem[method];
if (!op) continue;
const opNode = operationToNode(method, op, path, ctx);
operations.push(opNode);
if (op.tags && op.tags.length > 0 && primaryTag === "default") {
primaryTag = op.tags[0];
}
}
if (operations.length === 0) return null;
const params = buildPathParams(path, pathParams, pathItem, ctx);
const route = {
path,
operations,
loc: LOC2
};
if (params.length > 0) {
route.params = {
kind: "params",
nodes: params
};
}
if (pathItem.description && ctx.includeComments) {
route.description = pathItem.description;
}
return {
route,
tag: primaryTag
};
}
__name(pathItemToRoute, "pathItemToRoute");
function operationToNode(method, op, path, ctx) {
const pathPrefix = `#/paths/${encodePathSegment2(path)}/${method}`;
const schemaCtx = makeSchemaCtx(ctx, pathPrefix);
const node = {
method,
responses: [],
loc: LOC2
};
if (op.operationId) {
node.sdk = op.operationId;
}
if (op.description && ctx.includeComments) {
node.description = op.description;
}
if (op.deprecated) {
node.modifiers = [
"deprecated"
];
}
const queryParams = [];
const headerParams = [];
for (const param of op.parameters ?? []) {
if (param.in === "query") {
queryParams.push(parameterToNode(param, schemaCtx));
} else if (param.in === "header") {
headerParams.push(parameterToNode(param, schemaCtx));
}
}
if (queryParams.length > 0) {
node.query = {
kind: "params",
nodes: queryParams
};
}
if (headerParams.length > 0) {
node.headers = {
kind: "params",
nodes: headerParams
};
}
if (op.requestBody) {
node.request = requestBodyToNode(op.requestBody, op.operationId ?? `${method}${toPascalCase(path)}`, schemaCtx, ctx);
}
const responses = op.responses ?? {};
for (const [code, resp] of Object.entries(responses)) {
const statusCode = parseInt(code, 10);
if (isNaN(statusCode)) continue;
const respNode = responseToNode(statusCode, resp, op.operationId ?? `${method}${toPascalCase(path)}`, schemaCtx, ctx);
node.responses.push(respNode);
}
if (op.security !== void 0) {
node.security = convertSecurity(op.security);
}
return node;
}
__name(operationToNode, "operationToNode");
function buildPathParams(path, pathLevelParams, pathItem, ctx) {
const schemaCtx = makeSchemaCtx(ctx, `#/paths/${encodePathSegment2(path)}`);
const paramMap = /* @__PURE__ */ new Map();
for (const p of pathLevelParams) {
paramMap.set(p.name, p);
}
for (const method of HTTP_METHODS) {
const op = pathItem[method];
if (!op?.parameters) continue;
for (const p of op.parameters) {
if (p.in === "path" && !paramMap.has(p.name)) {
paramMap.set(p.name, p);
}
}
}
const templateNames = [
...path.matchAll(/\{([^}]+)\}/g)
].map((m) => m[1]);
return templateNames.map((name) => {
const param = paramMap.get(name);
if (param) {
return parameterToNode(param, schemaCtx);
}
return {
name,
optional: false,
nullable: false,
type: {
kind: "scalar",
name: "string"
},
loc: LOC2
};
});
}
__name(buildPathParams, "buildPathParams");
function parameterToNode(param, ctx) {
const type = param.schema ? schemaToTypeNode(param.schema, ctx) : {
kind: "scalar",
name: "string"
};
return {
name: param.name,
optional: param.in !== "path" && !param.required,
nullable: false,
type,
description: ctx.includeComments ? param.description : void 0,
loc: LOC2
};
}
__name(parameterToNode, "parameterToNode");
function requestBodyToNode(reqBody, operationName, schemaCtx, ctx) {
const content = reqBody.content;
if (!content) return void 0;
const supported = /* @__PURE__ */ new Set([
"application/json",
"application/x-www-form-urlencoded",
"multipart/form-data"
]);
const bodies = [];
for (const [contentType, mediaType] of Object.entries(content)) {
if (!supported.has(contentType) || !mediaType?.schema) continue;
const { typeNode, model } = extractInlineModel(mediaType.schema, `${toPascalCase(operationName)}Request`, schemaCtx);
if (model) {
ctx.extractedModels.push(model);
}
bodies.push({
contentType,
bodyType: typeNode
});
}
if (bodies.length === 0) return void 0;
return {
bodies
};
}
__name(requestBodyToNode, "requestBodyToNode");
function responseToNode(statusCode, resp, operationName, schemaCtx, ctx) {
const headers = convertResponseHeaders(resp.headers, schemaCtx);
if (!resp.content) {
return headers ? {
statusCode,
headers
} : {
statusCode
};
}
const [contentType, mediaType] = Object.entries(resp.content)[0] ?? [];
if (!contentType || !mediaType?.schema) {
return headers ? {
statusCode,
headers
} : {
statusCode
};
}
const { typeNode, model } = extractInlineModel(mediaType.schema, `${toPascalCase(operationName)}Response${statusCode}`, schemaCtx);
if (model) {
ctx.extractedModels.push(model);
}
return {
statusCode,
contentType,
bodyType: typeNode,
...headers ? {
headers
} : {}
};
}
__name(responseToNode, "responseToNode");
function convertResponseHeaders(headers, schemaCtx) {
if (!headers) return void 0;
const out = [];
for (const [name, header] of Object.entries(headers)) {
if (!header) continue;
const type = header.schema ? schemaToTypeNode(header.schema, schemaCtx) : {
kind: "scalar",
name: "string"
};
out.push({
name,
optional: !header.required,
type,
description: schemaCtx.includeComments ? header.description : void 0
});
}
return out.length > 0 ? out : void 0;
}
__name(convertResponseHeaders, "convertResponseHeaders");
function convertSecurity(security) {
if (security.length === 0) {
return "none";
}
return {
loc: LOC2
};
}
__name(convertSecurity, "convertSecurity");
function makeSchemaCtx(ctx, path) {
return {
circularRefs: ctx.circularRefs,
warnings: ctx.warnings,
path,
includeComments: ctx.includeComments,
namedSchemas: ctx.namedSchemas,
extractedModels: ctx.extractedModels,
inlineCounter: 0
};
}
__name(makeSchemaCtx, "makeSchemaCtx");
function toPascalCase(input) {
return input.replace(/[^a-zA-Z0-9]/g, " ").split(/\s+/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
}
__name(toPascalCase, "toPascalCase");
function encodePathSegment2(s) {
return s.replace(/~/g, "~0").replace(/\//g, "~1");
}
__name(encodePathSegment2, "encodePathSegment");
// src/tag-splitter.ts
function splitByTag(models, routes, routeTags) {
const routesByTag = /* @__PURE__ */ new Map();
for (const route of routes) {
const tag = routeTags.get(route) ?? "default";
const group = routesByTag.get(tag) ?? [];
group.push(route);
routesByTag.set(tag, group);
}
const modelsByTag = /* @__PURE__ */ new Map();
for (const [tag, tagRoutes] of routesByTag) {
const refs = /* @__PURE__ */ new Set();
for (const route of tagRoutes) {
collectRouteRefs(route, refs);
}
modelsByTag.set(tag, refs);
}
const modelNameToModel = new Map(models.map((m) => [
m.name,
m
]));
const modelAssignment = /* @__PURE__ */ new Map();
for (const model of models) {
const tags = [];
for (const [tag, refs] of modelsByTag) {
if (refs.has(model.name)) {
tags.push(tag);
}
}
if (tags.length === 0) {
modelAssignment.set(model.name, "shared");
} else if (tags.length === 1) {
modelAssignment.set(model.name, tags[0]);
} else {
modelAssignment.set(model.name, "shared");
}
}
for (const model of models) {
if (modelAssignment.get(model.name) === "shared") {
const refs = /* @__PURE__ */ new Set();
collectModelRefs(model, refs);
for (const ref of refs) {
if (modelNameToModel.has(ref)) {
const currentTag = modelAssignment.get(ref);
if (currentTag && currentTag !== "shared") {
const otherTags = [
...modelsByTag.entries()
].filter(([t, r]) => t !== currentTag && r.has(ref)).map(([t]) => t);
if (otherTags.length > 0) {
modelAssignment.set(ref, "shared");
}
}
}
}
}
}
const result = /* @__PURE__ */ new Map();
const allTags = /* @__PURE__ */ new Set([
...routesByTag.keys(),
...new Set(modelAssignment.values())
]);
for (const tag of allTags) {
const tagModels = models.filter((m) => modelAssignment.get(m.name) === tag);
const tagRoutes = routesByTag.get(tag) ?? [];
if (tagModels.length === 0 && tagRoutes.length === 0) continue;
const filename = sanitizeFilename(tag);
result.set(`${filename}.ck`, {
kind: "ckRoot",
meta: tag !== "shared" ? {
area: tag
} : {},
services: {},
models: tagModels,
routes: tagRoutes,
file: `${filename}.ck`
});
}
return result;
}
__name(splitByTag, "splitByTag");
function mergeIntoSingle(models, routes, filename = "api") {
return {
kind: "ckRoot",
meta: {},
services: {},
models,
routes,
file: `${filename}.ck`
};
}
__name(mergeIntoSingle, "mergeIntoSingle");
function collectRouteRefs(route, refs) {
if (route.params) {
collectParamSourceRefs(route.params, refs);
}
for (const op of route.operations) {
if (op.query) collectParamSourceRefs(op.query, refs);
if (op.headers) collectParamSourceRefs(op.headers, refs);
if (op.request) {
for (const body of op.request.bodies) collectTypeRefs(body.bodyType, refs);
}
for (const resp of op.responses) {
if (resp.bodyType) collectTypeRefs(resp.bodyType, refs);
}
}
}
__name(collectRouteRefs, "collectRouteRefs");
function collectParamSourceRefs(source, refs) {
if (typeof source === "string") {
refs.add(source);
return;
}
if (Array.isArray(source)) {
for (const param of source) {
if (param && typeof param === "object" && "type" in param) {
collectTypeRefs(param.type, refs);
}
}
return;
}
if (source && typeof source === "object" && "kind" in source) {
collectTypeRefs(source, refs);
}
}
__name(collectParamSourceRefs, "collectParamSourceRefs");
function collectTypeRefs(type, refs) {
switch (type.kind) {
case "ref":
refs.add(type.name);
break;
case "array":
collectTypeRefs(type.item, refs);
break;
case "tuple":
for (const item of type.items) collectTypeRefs(item, refs);
break;
case "record":
collectTypeRefs(type.key, refs);
collectTypeRefs(type.value, refs);
break;
case "union":
case "discriminatedUnion":
case "intersection":
for (const member of type.members) collectTypeRefs(member, refs);
break;
case "inlineObject":
for (const field of type.fields) collectTypeRefs(field.type, refs);
break;
case "lazy":
collectTypeRefs(type.inner, refs);
break;
}
}
__name(collectTypeRefs, "collectTypeRefs");
function collectModelRefs(model, refs) {
if (model.bases) for (const b of model.bases) refs.add(b);
if (model.type) collectTypeRefs(model.type, refs);
for (const field of model.fields) {
collectTypeRefs(field.type, refs);
}
}
__name(collectModelRefs, "collectModelRefs");
function sanitizeFilename(tag) {
return tag.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "") || "default";
}
__name(sanitizeFilename, "sanitizeFilename");
// src/ast-to-ck.ts
var INDENT = " ";
function astToCk(root, options = {}) {
const { includeComments = true } = options;
const ctx = {
includeComments
};
const parts = [];
const optionsBlock = serializeOptions(root);
if (optionsBlock) parts.push(optionsBlock);
for (const model of root.models) {
parts.push(serializeModel(model, ctx));
}
for (const route of root.routes) {
parts.push(serializeRoute(route, ctx));
}
return parts.join("\n\n") + "\n";
}
__name(astToCk, "astToCk");
function serializeOptions(root) {
const hasKeys = Object.keys(root.meta).length > 0;
const hasServices = root.services && Object.keys(root.services).length > 0;
const hasSecurity = root.security !== void 0;
if (!hasKeys && !hasServices && !hasSecurity) return null;
const lines = [
"options {"
];
if (hasKeys) {
lines.push(`${INDENT}keys: {`);
for (const [key, value] of Object.entries(root.meta)) {
lines.push(`${INDENT}${INDENT}${key}: ${value}`);
}
lines.push(`${INDENT}}`);
}
if (hasServices) {
lines.push(`${INDENT}services: {`);
for (const [name, path] of Object.entries(root.services)) {
lines.push(`${INDENT}${INDENT}${name}: "${path}"`);
}
lines.push(`${INDENT}}`);
}
if (hasSecurity) {
lines.push(`${INDENT}security: {`);
if (root.security === "none") {
lines.push(`${INDENT}${INDENT}none`);
} else {
const sec = root.security;
if (sec.policy !== void 0) {
const value = sec.policy === false ? "none" : sec.policy;
lines.push(`${INDENT}${INDENT}policy: ${value}`);
}
}
lines.push(`${INDENT}}`);
}
lines.push("}");
return lines.join("\n");
}
__name(serializeOptions, "serializeOptions");
function serializeModel(model, ctx) {
const parts = [];
const prefixes = [];
if (model.inputCase && model.inputCase !== "camel") {
prefixes.push(`format(input=${model.inputCase})`);
}
if (model.mode && model.mode !== "strict") {
prefixes.push(`mode(${model.mode})`);
}
if (model.deprecated) {
prefixes.push("deprecated");
}
const prefix = prefixes.length > 0 ? prefixes.join(" ") + " " : "";
const comment = ctx.includeComments && model.description ? ` # ${model.description}` : "";
if (model.type) {
parts.push(`contract ${prefix}${model.name}: ${serializeType(model.type)}${comment}`);
return parts.join("");
}
if (model.bases && model.bases.length > 0) {
parts.push(`contract ${prefix}${model.name}: ${model.bases.join(" & ")} & {${comment}`);
} else {
parts.push(`contract ${prefix}${model.name}: {${comment}`);
}
for (const field of model.fields) {
parts.push(serializeField(field, 1, ctx));
}
parts.push("}");
return parts.join("\n");
}
__name(serializeModel, "serializeModel");
function serializeField(field, depth, ctx) {
const indent = INDENT.repeat(depth);
const optional = field.optional ? "?" : "";
const visibility = field.visibility !== "normal" ? `${field.visibility} ` : "";
const deprecated = field.deprecated ? "deprecated " : "";
let typeStr = serializeType(field.type);
if (field.nullable && !typeContainsNull(field.type)) {
typeStr = `${typeStr} | null`;
}
const defaultVal = field.default !== void 0 ? ` = ${serializeDefault(field.default)}` : "";
const comment = ctx.includeComments && field.description ? ` # ${field.description}` : "";
return `${indent}${field.name}${optional}: ${deprecated}${visibility}${typeStr}${defaultVal}${comment}`;
}
__name(serializeField, "serializeField");
function typeContainsNull(type) {
if (type.kind === "scalar" && type.name === "null") return true;
if (type.kind === "union") return type.members.some(typeContainsNull);
return false;
}
__name(typeContainsNull, "typeContainsNull");
function serializeDefault(value) {
if (typeof value === "string") {
if (/^[a-zA-Z_$][a-zA-Z0-9_$\-.]*$/.test(value)) return value;
return `"${value}"`;
}
return String(value);
}
__name(serializeDefault, "serializeDefault");
function serializeType(type) {
switch (type.kind) {
case "scalar":
return serializeScalar(type);
case "array":
return serializeArray(type);
case "tuple":
return `tuple(${type.items.map(serializeType).join(", ")})`;
case "record":
return `record(${serializeType(type.key)}, ${serializeType(type.value)})`;
case "enum":
return `enum(${type.values.join(", ")})`;
case "literal":
return serializeLiteral(type);
case "union":
return type.members.map(serializeType).join(" | ");
case "discriminatedUnion":
return `discriminated(by=${type.discriminator}, ${type.members.map(serializeType).join(" | ")})`;
case "intersection":
return type.members.map(serializeType).join(" & ");
case "ref":
return type.name;
case "inlineObject":
return serializeInlineObject(type);
case "lazy":
return `lazy(${serializeType(type.inner)})`;
}
}
__name(serializeType, "serializeType");
function serializeScalar(type) {
const args = [];
if (type.len !== void 0) args.push(`length=${type.len}`);
if (type.min !== void 0) args.push(typeof type.min === "string" ? `min="${type.min}"` : `min=${type.min}`);
if (type.max !== void 0) args.push(typeof type.max === "string" ? `max="${type.max}"` : `max=${type.max}`);
if (type.regex !== void 0) args.push(`regex=${type.regex}`);
if (type.format !== void 0) args.push(`format=${type.format}`);
if (args.length === 0) return type.name;
return `${type.name}(${args.join(", ")})`;
}
__name(serializeScalar, "serializeScalar");
function serializeArray(type) {
const args = [
serializeType(type.item)
];
if (type.min !== void 0) args.push(`min=${type.min}`);
if (type.max !== void 0) args.push(`max=${type.max}`);
return `array(${args.join(", ")})`;
}
__name(serializeArray, "serializeArray");
function serializeLiteral(type) {
if (typeof type.value === "string") return `literal("${type.value}")`;
return `literal(${type.value})`;
}
__name(serializeLiteral, "serializeLiteral");
function serializeInlineObject(type) {
const modePrefix = type.mode ? `mode(${type.mode}) ` : "";
if (type.fields.length === 0) return `${modePrefix}{}`;
const lines = [
`${modePrefix}{`
];
for (const field of type.fields) {
lines.push(serializeField(field, 2, {
includeComments: true
}));
}
lines.push(`${INDENT}}`);
return lines.join("\n");
}
__name(serializeInlineObject, "serializeInlineObject");
function serializeRoute(route, ctx) {
const lines = [];
const modStr = serializeModifiers(route.modifiers);
const comment = ctx.includeComments && route.description ? ` # ${route.description}` : "";
lines.push(`operation${modStr} ${route.path}: {${comment}`);
if (route.params) {
serializeParamSource(lines, "params", route.params, route.paramsMode, 1, ctx);
}
if (route.security !== void 0) {
serializeSecurityBlock(lines, route.security, 1, ctx);
}
for (const op of route.operations) {
serializeOperation(lines, op, 1, ctx);
}
lines.push("}");
return lines.join("\n");
}
__name(serializeRoute, "serializeRoute");
function serializeOperation(lines, op, depth, ctx) {
const indent = INDENT.repeat(depth);
const modStr = serializeModifiers(op.modifiers);
const comment = ctx.includeComments && op.description ? ` # ${op.description}` : "";
lines.push(`${indent}${op.method}${modStr}: {${comment}`);
const inner = INDENT.repeat(depth + 1);
if (op.service) {
lines.push(`${inner}service: ${op.service}`);
}
if (op.sdk) {
lines.push(`${inner}sdk: ${op.sdk}`);
}
if (op.signature) {
const sigComment = ctx.includeComments && op.signatureDescription ? ` # ${op.signatureDescription}` : "";
lines.push(`${inner}signature: ${op.signature}${sigComment}`);
}
if (op.security !== void 0) {
serializeSecurityBlock(lines, op.security, depth + 1, ctx);
}
if (op.query) {
serializeParamSource(lines, "query", op.query, op.queryMode, depth + 1, ctx);
}
if (op.headers) {
serializeParamSource(lines, "headers", op.headers, op.headersMode, depth + 1, ctx);
}
if (op.request) {
serializeRequest(lines, op.request, depth + 1);
}
if (op.responses.length > 0) {
serializeResponses(lines, op.responses, depth + 1);
}
lines.push(`${indent}}`);
return lines;
}
__name(serializeOperation, "serializeOperation");
function serializeModifiers(modifiers) {
if (!modifiers || modifiers.length === 0) return "";
return `(${modifiers.join(", ")})`;
}
__name(serializeModifiers, "serializeModifiers");
function serializeParamSource(lines, keyword, source, mode, depth, ctx) {
const indent = INDENT.repeat(depth);
if (source.kind === "ref") {
lines.push(`${indent}${keyword}: ${source.name}`);
return;
}
if (source.kind === "type") {
lines.push(`${indent}${keyword}: ${serializeType(source.node)}`);
return;
}
const modeStr = mode ? `mode(${mode}) ` : "";
lines.push(`${indent}${keyword}: ${modeStr}{`);
for (const param of source.nodes) {
const optional = param.optional ? "?" : "";
let typeStr = serializeType(param.type);
if (param.nullable && !typeContainsNull(param.type)) {
typeStr = `${typeStr} | null`;
}
const defaultVal = param.default !== void 0 ? ` = ${serializeDefault(param.default)}` : "";
const comment = ctx.includeComments && param.description ? ` # ${param.description}` : "";
lines.push(`${INDENT.repeat(depth + 1)}${param.name}${optional}: ${typeStr}${defaultVal}${comment}`);
}
lines.push(`${indent}}`);
}
__name(serializeParamSource, "serializeParamSource");
function serializeRequest(lines, request, depth) {
const indent = INDENT.repeat(depth);
lines.push(`${indent}request: {`);
for (const body of request.bodies) {
lines.push(`${INDENT.repeat(depth + 1)}${body.contentType}: ${serializeType(body.bodyType)}`);
}
lines.push(`${indent}}`);
}
__name(serializeRequest, "serializeRequest");
function serializeResponses(lines, responses, depth) {
const indent = INDENT.repeat(depth);
lines.push(`${indent}response: {`);
for (const resp of responses) {
const hasBody = resp.bodyType && resp.contentType;
const hasHeaders = resp.headers && resp.headers.length > 0;
if (hasBody || hasHeaders) {
lines.push(`${INDENT.repeat(depth + 1)}${resp.statusCode}: {`);
if (hasBody) {
lines.push(`${INDENT.repeat(depth + 2)}${resp.contentType}: ${serializeType(resp.bodyType)}`);
}
if (hasHeaders) {
lines.push(`${INDENT.repeat(depth + 2)}headers: {`);
for (const h of resp.headers) {
const opt = h.optional ? "?" : "";
const trail = h.description ? ` # ${h.description}` : "";
lines.push(`${INDENT.repeat(depth + 3)}${h.name}${opt}: ${serializeType(h.type)}${trail}`);
}
lines.push(`${INDENT.repeat(depth + 2)}}`);
}
lines.push(`${INDENT.repeat(depth + 1)}}`);
} else {
lines.push(`${INDENT.repeat(depth + 1)}${resp.statusCode}:`);
}
}
lines.push(`${indent}}`);
}
__name(serializeResponses, "serializeResponses");
function serializeSecurityBlock(lines, security, depth, ctx) {
const indent = INDENT.repeat(depth);
if (security === "none") {
lines.push(`${indent}security: none`);
return;
}
const sec = security;
if (sec.policy !== void 0) {
const comment = ctx.includeComments && sec.policyDescription ? ` # ${sec.policyDescription}` : "";
const value = sec.policy === false ? "none" : sec.policy;
lines.push(`${indent}security: {`);
lines.push(`${INDENT.repeat(depth + 1)}policy: ${value}${comment}`);
lines.push(`${indent}}`);
} else {
lines.push(`${indent}security: {}`);
}
}
__name(serializeSecurityBlock, "serializeSecurityBlock");
// src/convert.ts
import { readFileSync } from "fs";
import { parse as parseYaml } from "yaml";
// src/warnings.ts
var WarningCollector = class {
static {
__name(this, "WarningCollector");
}
warnings = [];
onWarning;
constructor(onWarning) {
this.onWarning = onWarning;
}
warn(path, message) {
this.add({
path,
message,
severity: "warn"
});
}
info(path, message) {
this.add({
path,
message,
severity: "info"
});
}
add(warning) {
this.warnings.push(warning);
this.onWarning?.(warning);
}
};
// src/convert.ts
async function convertOpenApiToCk(options) {
const { split = "by-tag", includeComments = true } = options;
const warnings = new WarningCollector(options.onWarning);
const rawDoc = await parseInput(options.input);
const doc = normalize(rawDoc, warnings);
const schemas = sanitizeSchemaNames(doc, warnings);
const circularRefs = detectCircularRefs(schemas);
const extractedModels = [];
const schemaCtx = {
circularRefs,
warnings,
path: "#/components/schemas",
includeComments,
namedSchemas: schemas,
extractedModels,
inlineCounter: 0
};
const models = schemasToModels(schemas, schemaCtx);
const { routes, routeTags } = pathsToRoutes(doc, {
circularRefs,
warnings,
includeComments,
namedSchemas: schemas,
extractedModels,
globalSecurity: doc.security
});
const files = /* @__PURE__ */ new Map();
if (split === "by-tag") {
const ckRoots = splitByTag(models, routes, routeTags);
for (const [filename, root] of ckRoots) {
files.set(filename, astToCk(root, {
includeComments
}));
}
} else {
const root = mergeIntoSingle(models, routes);
files.set("api.ck", astToCk(root, {
includeComments
}));
}
return {
files,
warnings: warnings.warnings
};
}
__name(convertOpenApiToCk, "convertOpenApiToCk");
async function parseInput(input) {
if (typeof input === "object") {
return input;
}
try {
const content = readFileSync(input, "utf-8");
return parseJsonOrYaml(content);
} catch {
return parseJsonOrYaml(input);
}
}
__name(parseInput, "parseInput");
function parseJsonOrYaml(content) {
try {
return JSON.parse(content);
} catch {
return parseYaml(content);
}
}
__name(parseJsonOrYaml, "parseJsonOrYaml");
function sanitizeSchemaNames(doc, warnings) {
const original = doc.components?.schemas ?? {};
const sanitized = {};
const nameMap = /* @__PURE__ */ new Map();
for (const name of Object.keys(original)) {
const clean = sanitizeName(name, warnings);
if (sanitized[clean]) {
warnings.warn(`#/components/schemas/${name}`, `Name collision after sanitization: "${name}" and another schema both map to "${clean}"`);
let i = 2;
while (sanitized[`${clean}${i}`]) i++;
nameMap.set(name, `${clean}${i}`);
sanitized[`${clean}${i}`] = original[name];
} else {
nameMap.set(name, clean);
sanitized[clean] = original[name];
}
}
if (nameMap.size > 0) {
updateRefs(doc, nameMap);
}
return sanitized;
}
__name(sanitizeSchemaNames, "sanitizeSchemaNames");
function updateRefs(obj, nameMap) {
if (!obj || typeof obj !== "object") return;
if (Array.isArray(obj)) {
for (const item of obj) updateRefs(item, nameMap);
return;
}
const record = obj;
if (typeof record.$ref === "string") {
const match = record.$ref.match(/^#\/components\/schemas\/(.+)$/);
if (match?.[1] && nameMap.has(match[1])) {
record.$ref = `#/components/schemas/${nameMap.get(match[1])}`;
}
}
for (const value of Object.values(record)) {
updateRefs(value, nameMap);
}
}
__name(updateRefs, "updateRefs");
export {
__name,
normalize,
detectCircularRefs,
extractRefName,
schemasToModels,
schemaToTypeNode,
sanitizeName,
pathsToRoutes,
splitByTag,
mergeIntoSingle,
astToCk,
serializeType,
convertOpenApiToCk
};
//# sourceMappingURL=chunk-YOTHJ2V4.js.map

Sorry, the diff of this file is too big to display

+6
-6
> @contractkit/openapi-to-ck@0.7.8 build:ci /home/runner/work/ContractKit/ContractKit/packages/openapi-to-ck
> @contractkit/openapi-to-ck@0.8.0 build:ci /home/runner/work/ContractKit/ContractKit/packages/openapi-to-ck
> eslint --max-warnings=0 && pnpm run build
> @contractkit/openapi-to-ck@0.7.8 build /home/runner/work/ContractKit/ContractKit/packages/openapi-to-ck
> @contractkit/openapi-to-ck@0.8.0 build /home/runner/work/ContractKit/ContractKit/packages/openapi-to-ck
> tsup src/index.ts src/plugin.ts --format esm --sourcemap --dts && tsc --emitDeclarationOnly --declaration

@@ -16,10 +16,10 @@

ESM dist/plugin.js 2.51 KB
ESM dist/chunk-T2G2WRZ2.js 50.36 KB
ESM dist/chunk-YOTHJ2V4.js 50.45 KB
ESM dist/index.js.map 71.00 B
ESM dist/plugin.js.map 4.95 KB
ESM dist/chunk-T2G2WRZ2.js.map 116.56 KB
ESM ⚡️ Build success in 191ms
ESM dist/chunk-YOTHJ2V4.js.map 116.74 KB
ESM ⚡️ Build success in 248ms
DTS Build start
DTS ⚡️ Build success in 6387ms
DTS ⚡️ Build success in 7869ms
DTS dist/index.d.ts 8.29 KB
DTS dist/plugin.d.ts 128.00 B
> @contractkit/openapi-to-ck@0.7.8 test:ci /home/runner/work/ContractKit/ContractKit/packages/openapi-to-ck
> @contractkit/openapi-to-ck@0.8.0 test:ci /home/runner/work/ContractKit/ContractKit/packages/openapi-to-ck
> vitest run --coverage

@@ -9,13 +9,13 @@

✓ tests/ast-to-ck.test.ts (35 tests) 79ms
✓ tests/schema-to-ast.test.ts (36 tests) 44ms
✓ tests/normalize.test.ts (6 tests) 91ms
✓ tests/tag-splitter.test.ts (5 tests) 94ms
✓ tests/circular-refs.test.ts (8 tests) 24ms
✓ tests/convert.test.ts (16 tests) 142ms
✓ tests/ast-to-ck.test.ts (35 tests) 61ms
✓ tests/schema-to-ast.test.ts (36 tests) 47ms
✓ tests/normalize.test.ts (6 tests) 20ms
✓ tests/tag-splitter.test.ts (5 tests) 79ms
✓ tests/convert.test.ts (16 tests) 196ms
✓ tests/circular-refs.test.ts (8 tests) 42ms
 Test Files  6 passed (6)
 Tests  106 passed (106)
 Start at  13:02:53
 Duration  4.97s (transform 2.41s, setup 0ms, import 5.49s, tests 474ms, environment 1ms)
 Start at  17:08:46
 Duration  4.23s (transform 2.17s, setup 0ms, import 4.69s, tests 444ms, environment 1ms)

@@ -26,5 +26,5 @@  % Coverage report from v8

-------------------|---------|----------|---------|---------|-------------------------------------------------------------
All files | 81.01 | 70.41 | 94.69 | 83.55 |
src | 80.79 | 70.7 | 93.47 | 83.18 |
ast-to-ck.ts | 90.65 | 82.7 | 100 | 91.91 | 82-91,168,263,295-296,311,344-345,355,415
All files | 80.95 | 70.17 | 94.69 | 83.47 |
src | 80.72 | 70.45 | 93.47 | 83.1 |
ast-to-ck.ts | 90.27 | 81.48 | 100 | 91.5 | 82-92,169,264,296-297,312,345-346,356,417
circular-refs.ts | 100 | 90 | 100 | 100 | 20-23

@@ -31,0 +31,0 @@ convert.ts | 77.19 | 87.5 | 80 | 77.77 | 83-98,112-117

# @contractkit/openapi-to-ck
## 0.8.0
### Minor Changes
- dd8197b: **Breaking:** Replace the `requireMfa: boolean` field in `security: { ... }` blocks with `policy: <ident|none>`, and switch the generated Koa router middleware from `requireSecurity` to ServerKit's new `requirePolicy`.
The `security` declaration on operations, routes, and the file-level `options { security: { ... } }` block no longer accepts a `requireMfa:` line. The new field is `policy:` and takes a bare identifier (the named policy) or the keyword `none` to explicitly bypass policy enforcement. Existing `.ck` files that use `requireMfa:` will fail to parse.
```ck
# Before
security: {
requireMfa: true
}
# After
security: {
policy: paymentsWrite
}
# Explicit bypass
security: {
policy: none
}
```
**`@contractkit/core`** — `SecurityFields` interface drops `requireMfa` / `requireMfaDescription` and adds `policy?: string | false` / `policyDescription?: string`. The grammar's `SecurityRequireMfaLine` is replaced by `SecurityPolicyLine` (`policyKw ":" (noneKw | identifier)`). `security: none` (the route-level public sentinel) is unchanged.
**`@contractkit/plugin-typescript`** — Generated Koa routers now import `requirePolicy` from `@maroonedsoftware/koa` (previously `requireSecurity`) and emit `requirePolicy({ policy: 'name' })`, `requirePolicy({ policy: false })`, or bare `requirePolicy()`. Consumers must upgrade ServerKit alongside.
**`@contractkit/prettier-plugin`** — Formats `policy: <name>` and `policy: none` lines inside security blocks. Files containing `requireMfa:` will no longer round-trip and will surface as parse errors.
**`@contractkit/plugin-markdown`** — The "Security: authenticated" admonition now shows `policy: <name|none>` instead of `requireMfa: <bool>`.
**`@contractkit/openapi-to-ck`** — Non-empty OpenAPI `security` requirements continue to collapse to an empty `security: {}` (authenticated, default policy); the serializer now emits `policy:` lines when the field is set.
**`contractkit-vscode-extension`** — TextMate grammar highlights `policy:` inside the security block; LSP completion offers `policy` instead of `requireMfa`. Re-run `pnpm run vscode:install` to pick up the change.
### Patch Changes
- Updated dependencies [dd8197b]
- @contractkit/core@0.18.0
## 0.7.8

@@ -4,0 +46,0 @@

<?xml version="1.0" encoding="UTF-8"?>
<coverage generated="1778245378830" clover="3.2.0">
<project timestamp="1778245378831" name="All files">
<metrics statements="833" coveredstatements="696" conditionals="747" coveredconditionals="526" methods="113" coveredmethods="107" elements="1693" coveredelements="1329" complexity="0" loc="833" ncloc="833" packages="2" files="9" classes="9"/>
<coverage generated="1778692131019" clover="3.2.0">
<project timestamp="1778692131020" name="All files">
<metrics statements="835" coveredstatements="697" conditionals="751" coveredconditionals="527" methods="113" coveredmethods="107" elements="1699" coveredelements="1331" complexity="0" loc="835" ncloc="835" packages="2" files="9" classes="9"/>
<package name="src">
<metrics statements="803" coveredstatements="668" conditionals="727" coveredconditionals="514" methods="92" coveredmethods="86"/>
<metrics statements="805" coveredstatements="669" conditionals="731" coveredconditionals="515" methods="92" coveredmethods="86"/>
<file name="ast-to-ck.ts" path="/home/runner/work/ContractKit/ContractKit/packages/openapi-to-ck/src/ast-to-ck.ts">
<metrics statements="198" coveredstatements="182" conditionals="185" coveredconditionals="153" methods="18" coveredmethods="18"/>
<metrics statements="200" coveredstatements="183" conditionals="189" coveredconditionals="154" methods="18" coveredmethods="18"/>
<line num="17" count="2" type="stmt"/>

@@ -41,168 +41,170 @@ <line num="27" count="47" type="cond" truecount="1" falsecount="0"/>

<line num="87" count="0" type="cond" truecount="0" falsecount="2"/>
<line num="88" count="0" type="stmt"/>
<line num="91" count="0" type="stmt"/>
<line num="94" count="11" type="stmt"/>
<line num="88" count="0" type="cond" truecount="0" falsecount="2"/>
<line num="89" count="0" type="stmt"/>
<line num="92" count="0" type="stmt"/>
<line num="95" count="11" type="stmt"/>
<line num="101" count="46" type="stmt"/>
<line num="104" count="46" type="stmt"/>
<line num="105" count="46" type="cond" truecount="4" falsecount="0"/>
<line num="106" count="1" type="stmt"/>
<line num="108" count="46" type="cond" truecount="4" falsecount="0"/>
<line num="109" count="1" type="stmt"/>
<line num="111" count="46" type="cond" truecount="2" falsecount="0"/>
<line num="112" count="1" type="stmt"/>
<line num="115" count="46" type="cond" truecount="2" falsecount="0"/>
<line num="116" count="46" type="cond" truecount="4" falsecount="0"/>
<line num="119" count="46" type="cond" truecount="2" falsecount="0"/>
<line num="120" count="2" type="stmt"/>
<line num="96" count="11" type="stmt"/>
<line num="102" count="46" type="stmt"/>
<line num="105" count="46" type="stmt"/>
<line num="106" count="46" type="cond" truecount="4" falsecount="0"/>
<line num="107" count="1" type="stmt"/>
<line num="109" count="46" type="cond" truecount="4" falsecount="0"/>
<line num="110" count="1" type="stmt"/>
<line num="112" count="46" type="cond" truecount="2" falsecount="0"/>
<line num="113" count="1" type="stmt"/>
<line num="116" count="46" type="cond" truecount="2" falsecount="0"/>
<line num="117" count="46" type="cond" truecount="4" falsecount="0"/>
<line num="120" count="46" type="cond" truecount="2" falsecount="0"/>
<line num="121" count="2" type="stmt"/>
<line num="125" count="44" type="cond" truecount="4" falsecount="0"/>
<line num="126" count="1" type="stmt"/>
<line num="128" count="43" type="stmt"/>
<line num="131" count="44" type="stmt"/>
<line num="132" count="122" type="stmt"/>
<line num="135" count="44" type="stmt"/>
<line num="122" count="2" type="stmt"/>
<line num="126" count="44" type="cond" truecount="4" falsecount="0"/>
<line num="127" count="1" type="stmt"/>
<line num="129" count="43" type="stmt"/>
<line num="132" count="44" type="stmt"/>
<line num="133" count="122" type="stmt"/>
<line num="136" count="44" type="stmt"/>
<line num="140" count="124" type="stmt"/>
<line num="141" count="124" type="cond" truecount="2" falsecount="0"/>
<line num="137" count="44" type="stmt"/>
<line num="141" count="124" type="stmt"/>
<line num="142" count="124" type="cond" truecount="2" falsecount="0"/>
<line num="143" count="124" type="cond" truecount="1" falsecount="1"/>
<line num="145" count="124" type="stmt"/>
<line num="148" count="124" type="cond" truecount="4" falsecount="0"/>
<line num="149" count="1" type="stmt"/>
<line num="152" count="124" type="cond" truecount="2" falsecount="0"/>
<line num="153" count="124" type="cond" truecount="4" falsecount="0"/>
<line num="155" count="124" type="stmt"/>
<line num="159" count="1" type="cond" truecount="3" falsecount="1"/>
<line num="160" count="1" type="cond" truecount="1" falsecount="1"/>
<line num="161" count="1" type="stmt"/>
<line num="165" count="13" type="cond" truecount="2" falsecount="0"/>
<line num="167" count="11" type="cond" truecount="1" falsecount="1"/>
<line num="168" count="0" type="stmt"/>
<line num="170" count="2" type="stmt"/>
<line num="176" count="294" type="cond" truecount="12" falsecount="0"/>
<line num="178" count="169" type="stmt"/>
<line num="180" count="18" type="stmt"/>
<line num="182" count="1" type="stmt"/>
<line num="184" count="11" type="stmt"/>
<line num="186" count="26" type="stmt"/>
<line num="188" count="3" type="stmt"/>
<line num="190" count="1" type="stmt"/>
<line num="192" count="1" type="stmt"/>
<line num="194" count="1" type="stmt"/>
<line num="196" count="61" type="stmt"/>
<line num="198" count="1" type="stmt"/>
<line num="200" count="1" type="stmt"/>
<line num="212" count="169" type="stmt"/>
<line num="213" count="169" type="cond" truecount="2" falsecount="0"/>
<line num="214" count="169" type="cond" truecount="3" falsecount="1"/>
<line num="143" count="124" type="cond" truecount="2" falsecount="0"/>
<line num="144" count="124" type="cond" truecount="1" falsecount="1"/>
<line num="146" count="124" type="stmt"/>
<line num="149" count="124" type="cond" truecount="4" falsecount="0"/>
<line num="150" count="1" type="stmt"/>
<line num="153" count="124" type="cond" truecount="2" falsecount="0"/>
<line num="154" count="124" type="cond" truecount="4" falsecount="0"/>
<line num="156" count="124" type="stmt"/>
<line num="160" count="1" type="cond" truecount="3" falsecount="1"/>
<line num="161" count="1" type="cond" truecount="1" falsecount="1"/>
<line num="162" count="1" type="stmt"/>
<line num="166" count="13" type="cond" truecount="2" falsecount="0"/>
<line num="168" count="11" type="cond" truecount="1" falsecount="1"/>
<line num="169" count="0" type="stmt"/>
<line num="171" count="2" type="stmt"/>
<line num="177" count="294" type="cond" truecount="12" falsecount="0"/>
<line num="179" count="169" type="stmt"/>
<line num="181" count="18" type="stmt"/>
<line num="183" count="1" type="stmt"/>
<line num="185" count="11" type="stmt"/>
<line num="187" count="26" type="stmt"/>
<line num="189" count="3" type="stmt"/>
<line num="191" count="1" type="stmt"/>
<line num="193" count="1" type="stmt"/>
<line num="195" count="1" type="stmt"/>
<line num="197" count="61" type="stmt"/>
<line num="199" count="1" type="stmt"/>
<line num="201" count="1" type="stmt"/>
<line num="213" count="169" type="stmt"/>
<line num="214" count="169" type="cond" truecount="2" falsecount="0"/>
<line num="215" count="169" type="cond" truecount="3" falsecount="1"/>
<line num="216" count="169" type="cond" truecount="2" falsecount="0"/>
<line num="217" count="169" type="cond" truecount="1" falsecount="1"/>
<line num="219" count="169" type="cond" truecount="2" falsecount="0"/>
<line num="220" count="39" type="stmt"/>
<line num="224" count="18" type="stmt"/>
<line num="225" count="18" type="cond" truecount="2" falsecount="0"/>
<line num="216" count="169" type="cond" truecount="3" falsecount="1"/>
<line num="217" count="169" type="cond" truecount="2" falsecount="0"/>
<line num="218" count="169" type="cond" truecount="1" falsecount="1"/>
<line num="220" count="169" type="cond" truecount="2" falsecount="0"/>
<line num="221" count="39" type="stmt"/>
<line num="225" count="18" type="stmt"/>
<line num="226" count="18" type="cond" truecount="2" falsecount="0"/>
<line num="227" count="18" type="stmt"/>
<line num="231" count="3" type="cond" truecount="2" falsecount="0"/>
<line num="232" count="2" type="stmt"/>
<line num="236" count="1" type="cond" truecount="1" falsecount="1"/>
<line num="227" count="18" type="cond" truecount="2" falsecount="0"/>
<line num="228" count="18" type="stmt"/>
<line num="232" count="3" type="cond" truecount="2" falsecount="0"/>
<line num="233" count="2" type="stmt"/>
<line num="237" count="1" type="cond" truecount="1" falsecount="1"/>
<line num="239" count="1" type="stmt"/>
<line num="238" count="1" type="cond" truecount="1" falsecount="1"/>
<line num="240" count="1" type="stmt"/>
<line num="241" count="2" type="stmt"/>
<line num="243" count="1" type="stmt"/>
<line num="241" count="1" type="stmt"/>
<line num="242" count="2" type="stmt"/>
<line num="244" count="1" type="stmt"/>
<line num="250" count="45" type="stmt"/>
<line num="252" count="45" type="stmt"/>
<line num="253" count="45" type="cond" truecount="3" falsecount="1"/>
<line num="254" count="45" type="stmt"/>
<line num="257" count="45" type="cond" truecount="2" falsecount="0"/>
<line num="258" count="13" type="stmt"/>
<line num="262" count="45" type="cond" truecount="1" falsecount="1"/>
<line num="263" count="0" type="stmt"/>
<line num="267" count="45" type="stmt"/>
<line num="268" count="66" type="stmt"/>
<line num="271" count="45" type="stmt"/>
<line num="245" count="1" type="stmt"/>
<line num="251" count="45" type="stmt"/>
<line num="253" count="45" type="stmt"/>
<line num="254" count="45" type="cond" truecount="3" falsecount="1"/>
<line num="255" count="45" type="stmt"/>
<line num="258" count="45" type="cond" truecount="2" falsecount="0"/>
<line num="259" count="13" type="stmt"/>
<line num="263" count="45" type="cond" truecount="1" falsecount="1"/>
<line num="264" count="0" type="stmt"/>
<line num="268" count="45" type="stmt"/>
<line num="269" count="66" type="stmt"/>
<line num="272" count="45" type="stmt"/>
<line num="276" count="66" type="stmt"/>
<line num="273" count="45" type="stmt"/>
<line num="277" count="66" type="stmt"/>
<line num="278" count="66" type="cond" truecount="3" falsecount="1"/>
<line num="279" count="66" type="stmt"/>
<line num="281" count="66" type="stmt"/>
<line num="284" count="66" type="cond" truecount="2" falsecount="0"/>
<line num="285" count="2" type="stmt"/>
<line num="289" count="66" type="cond" truecount="2" falsecount="0"/>
<line num="290" count="55" type="stmt"/>
<line num="294" count="66" type="cond" truecount="1" falsecount="1"/>
<line num="295" count="0" type="cond" truecount="0" falsecount="4"/>
<line num="296" count="0" type="stmt"/>
<line num="300" count="66" type="cond" truecount="2" falsecount="0"/>
<line num="301" count="12" type="stmt"/>
<line num="305" count="66" type="cond" truecount="2" falsecount="0"/>
<line num="306" count="11" type="stmt"/>
<line num="310" count="66" type="cond" truecount="1" falsecount="1"/>
<line num="311" count="0" type="stmt"/>
<line num="315" count="66" type="cond" truecount="2" falsecount="0"/>
<line num="316" count="12" type="stmt"/>
<line num="320" count="66" type="cond" truecount="1" falsecount="1"/>
<line num="321" count="66" type="stmt"/>
<line num="324" count="66" type="stmt"/>
<line num="278" count="66" type="stmt"/>
<line num="279" count="66" type="cond" truecount="3" falsecount="1"/>
<line num="280" count="66" type="stmt"/>
<line num="282" count="66" type="stmt"/>
<line num="285" count="66" type="cond" truecount="2" falsecount="0"/>
<line num="286" count="2" type="stmt"/>
<line num="290" count="66" type="cond" truecount="2" falsecount="0"/>
<line num="291" count="55" type="stmt"/>
<line num="295" count="66" type="cond" truecount="1" falsecount="1"/>
<line num="296" count="0" type="cond" truecount="0" falsecount="4"/>
<line num="297" count="0" type="stmt"/>
<line num="301" count="66" type="cond" truecount="2" falsecount="0"/>
<line num="302" count="12" type="stmt"/>
<line num="306" count="66" type="cond" truecount="2" falsecount="0"/>
<line num="307" count="11" type="stmt"/>
<line num="311" count="66" type="cond" truecount="1" falsecount="1"/>
<line num="312" count="0" type="stmt"/>
<line num="316" count="66" type="cond" truecount="2" falsecount="0"/>
<line num="317" count="12" type="stmt"/>
<line num="321" count="66" type="cond" truecount="1" falsecount="1"/>
<line num="322" count="66" type="stmt"/>
<line num="325" count="66" type="stmt"/>
<line num="329" count="111" type="cond" truecount="4" falsecount="0"/>
<line num="330" count="1" type="stmt"/>
<line num="334" count="24" type="stmt"/>
<line num="337" count="24" type="cond" truecount="2" falsecount="0"/>
<line num="338" count="1" type="stmt"/>
<line num="326" count="66" type="stmt"/>
<line num="330" count="111" type="cond" truecount="4" falsecount="0"/>
<line num="331" count="1" type="stmt"/>
<line num="335" count="24" type="stmt"/>
<line num="338" count="24" type="cond" truecount="2" falsecount="0"/>
<line num="339" count="1" type="stmt"/>
<line num="343" count="23" type="cond" truecount="1" falsecount="1"/>
<line num="344" count="0" type="stmt"/>
<line num="340" count="1" type="stmt"/>
<line num="344" count="23" type="cond" truecount="1" falsecount="1"/>
<line num="345" count="0" type="stmt"/>
<line num="349" count="23" type="cond" truecount="1" falsecount="1"/>
<line num="350" count="24" type="stmt"/>
<line num="346" count="0" type="stmt"/>
<line num="350" count="23" type="cond" truecount="1" falsecount="1"/>
<line num="351" count="24" type="stmt"/>
<line num="352" count="23" type="cond" truecount="2" falsecount="0"/>
<line num="353" count="23" type="stmt"/>
<line num="354" count="23" type="cond" truecount="2" falsecount="2"/>
<line num="355" count="0" type="stmt"/>
<line num="357" count="23" type="cond" truecount="1" falsecount="1"/>
<line num="358" count="23" type="cond" truecount="3" falsecount="1"/>
<line num="359" count="23" type="stmt"/>
<line num="361" count="23" type="stmt"/>
<line num="365" count="12" type="stmt"/>
<line num="352" count="24" type="stmt"/>
<line num="353" count="23" type="cond" truecount="2" falsecount="0"/>
<line num="354" count="23" type="stmt"/>
<line num="355" count="23" type="cond" truecount="2" falsecount="2"/>
<line num="356" count="0" type="stmt"/>
<line num="358" count="23" type="cond" truecount="1" falsecount="1"/>
<line num="359" count="23" type="cond" truecount="3" falsecount="1"/>
<line num="360" count="23" type="stmt"/>
<line num="362" count="23" type="stmt"/>
<line num="366" count="12" type="stmt"/>
<line num="367" count="12" type="stmt"/>
<line num="368" count="12" type="stmt"/>
<line num="370" count="12" type="stmt"/>
<line num="374" count="66" type="stmt"/>
<line num="369" count="12" type="stmt"/>
<line num="371" count="12" type="stmt"/>
<line num="375" count="66" type="stmt"/>
<line num="376" count="66" type="stmt"/>
<line num="377" count="76" type="cond" truecount="2" falsecount="0"/>
<line num="377" count="66" type="stmt"/>
<line num="378" count="76" type="cond" truecount="2" falsecount="0"/>
<line num="379" count="76" type="cond" truecount="4" falsecount="0"/>
<line num="380" count="54" type="stmt"/>
<line num="381" count="54" type="cond" truecount="2" falsecount="0"/>
<line num="382" count="53" type="stmt"/>
<line num="384" count="54" type="cond" truecount="2" falsecount="0"/>
<line num="385" count="3" type="stmt"/>
<line num="379" count="76" type="cond" truecount="2" falsecount="0"/>
<line num="380" count="76" type="cond" truecount="4" falsecount="0"/>
<line num="381" count="54" type="stmt"/>
<line num="382" count="54" type="cond" truecount="2" falsecount="0"/>
<line num="383" count="53" type="stmt"/>
<line num="385" count="54" type="cond" truecount="2" falsecount="0"/>
<line num="386" count="3" type="stmt"/>
<line num="387" count="4" type="cond" truecount="2" falsecount="0"/>
<line num="387" count="3" type="stmt"/>
<line num="388" count="4" type="cond" truecount="2" falsecount="0"/>
<line num="389" count="4" type="stmt"/>
<line num="391" count="3" type="stmt"/>
<line num="393" count="54" type="stmt"/>
<line num="395" count="22" type="stmt"/>
<line num="398" count="66" type="stmt"/>
<line num="402" count="12" type="stmt"/>
<line num="403" count="12" type="cond" truecount="2" falsecount="0"/>
<line num="404" count="11" type="stmt"/>
<line num="389" count="4" type="cond" truecount="2" falsecount="0"/>
<line num="390" count="4" type="stmt"/>
<line num="392" count="3" type="stmt"/>
<line num="394" count="54" type="stmt"/>
<line num="396" count="22" type="stmt"/>
<line num="399" count="66" type="stmt"/>
<line num="403" count="12" type="stmt"/>
<line num="404" count="12" type="cond" truecount="2" falsecount="0"/>
<line num="405" count="11" type="stmt"/>
<line num="408" count="1" type="stmt"/>
<line num="409" count="1" type="cond" truecount="1" falsecount="1"/>
<line num="410" count="1" type="cond" truecount="3" falsecount="1"/>
<line num="411" count="1" type="stmt"/>
<line num="412" count="1" type="stmt"/>
<line num="406" count="11" type="stmt"/>
<line num="409" count="1" type="stmt"/>
<line num="410" count="1" type="cond" truecount="1" falsecount="1"/>
<line num="411" count="1" type="cond" truecount="3" falsecount="1"/>
<line num="412" count="1" type="cond" truecount="1" falsecount="1"/>
<line num="413" count="1" type="stmt"/>
<line num="415" count="0" type="stmt"/>
<line num="414" count="1" type="stmt"/>
<line num="415" count="1" type="stmt"/>
<line num="417" count="0" type="stmt"/>
</file>

@@ -209,0 +211,0 @@ <file name="circular-refs.ts" path="/home/runner/work/ContractKit/ContractKit/packages/openapi-to-ck/src/circular-refs.ts">

@@ -26,5 +26,5 @@

<div class='fl pad1y space-right2'>
<span class="strong">81.01% </span>
<span class="strong">80.95% </span>
<span class="quiet">Statements</span>
<span class='fraction'>747/922</span>
<span class='fraction'>748/924</span>
</div>

@@ -34,5 +34,5 @@

<div class='fl pad1y space-right2'>
<span class="strong">70.41% </span>
<span class="strong">70.17% </span>
<span class="quiet">Branches</span>
<span class='fraction'>526/747</span>
<span class='fraction'>527/751</span>
</div>

@@ -49,5 +49,5 @@

<div class='fl pad1y space-right2'>
<span class="strong">83.55% </span>
<span class="strong">83.47% </span>
<span class="quiet">Lines</span>
<span class='fraction'>696/833</span>
<span class='fraction'>697/835</span>
</div>

@@ -86,13 +86,13 @@

<td class="file high" data-value="src"><a href="src/index.html">src</a></td>
<td data-value="80.79" class="pic high">
<td data-value="80.72" class="pic high">
<div class="chart"><div class="cover-fill" style="width: 80%"></div><div class="cover-empty" style="width: 20%"></div></div>
</td>
<td data-value="80.79" class="pct high">80.79%</td>
<td data-value="885" class="abs high">715/885</td>
<td data-value="70.7" class="pct medium">70.7%</td>
<td data-value="727" class="abs medium">514/727</td>
<td data-value="80.72" class="pct high">80.72%</td>
<td data-value="887" class="abs high">716/887</td>
<td data-value="70.45" class="pct medium">70.45%</td>
<td data-value="731" class="abs medium">515/731</td>
<td data-value="93.47" class="pct high">93.47%</td>
<td data-value="92" class="abs high">86/92</td>
<td data-value="83.18" class="pct high">83.18%</td>
<td data-value="803" class="abs high">668/803</td>
<td data-value="83.1" class="pct high">83.1%</td>
<td data-value="805" class="abs high">669/805</td>
</tr>

@@ -123,3 +123,3 @@

<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
at 2026-05-08T13:02:58.721Z
at 2026-05-13T17:08:50.965Z
</div>

@@ -126,0 +126,0 @@ <script src="prettify.js"></script>

@@ -26,5 +26,5 @@

<div class='fl pad1y space-right2'>
<span class="strong">90.65% </span>
<span class="strong">90.27% </span>
<span class="quiet">Statements</span>
<span class='fraction'>194/214</span>
<span class='fraction'>195/216</span>
</div>

@@ -34,5 +34,5 @@

<div class='fl pad1y space-right2'>
<span class="strong">82.7% </span>
<span class="strong">81.48% </span>
<span class="quiet">Branches</span>
<span class='fraction'>153/185</span>
<span class='fraction'>154/189</span>
</div>

@@ -49,5 +49,5 @@

<div class='fl pad1y space-right2'>
<span class="strong">91.91% </span>
<span class="strong">91.5% </span>
<span class="quiet">Lines</span>
<span class='fraction'>182/198</span>
<span class='fraction'>183/200</span>
</div>

@@ -486,3 +486,5 @@

<a name='L417'></a><a href='#L417'>417</a>
<a name='L418'></a><a href='#L418'>418</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
<a name='L418'></a><a href='#L418'>418</a>
<a name='L419'></a><a href='#L419'>419</a>
<a name='L420'></a><a href='#L420'>420</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>

@@ -575,2 +577,3 @@ <span class="cline-any cline-neutral">&nbsp;</span>

<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>

@@ -901,2 +904,3 @@ <span class="cline-any cline-neutral">&nbsp;</span>

<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-neutral">&nbsp;</span>

@@ -992,4 +996,5 @@ <span class="cline-any cline-no">&nbsp;</span>

const sec = <span class="cstat-no" title="statement not covered" >root.security as SecurityFields;</span>
<span class="cstat-no" title="statement not covered" > if (sec.requireMfa !== undefined) {</span>
<span class="cstat-no" title="statement not covered" > lines.push(`${INDENT}${INDENT}requireMfa: ${sec.requireMfa}`);</span>
<span class="cstat-no" title="statement not covered" > if (sec.policy !== undefined) {</span>
const value = <span class="cstat-no" title="statement not covered" >sec.policy === false ? 'none' : sec.policy;</span>
<span class="cstat-no" title="statement not covered" > lines.push(`${INDENT}${INDENT}policy: ${value}`);</span>
}

@@ -1315,6 +1320,7 @@ }

const sec = security as SecurityFields;
if (sec.requireMfa !== undefined) {
const comment = ctx.includeComments &amp;&amp; sec.requireMfaDescription ? <span class="branch-0 cbranch-no" title="branch not covered" >` # ${sec.requireMfaDescription}` : '</span>';
if (sec.policy !== undefined) {
const comment = ctx.includeComments &amp;&amp; sec.policyDescription ? <span class="branch-0 cbranch-no" title="branch not covered" >` # ${sec.policyDescription}` : '</span>';
const value = sec.policy === false ? <span class="branch-0 cbranch-no" title="branch not covered" >'none' : s</span>ec.policy;
lines.push(`${indent}security: {`);
lines.push(`${INDENT.repeat(depth + 1)}requireMfa: ${sec.requireMfa}${comment}`);
lines.push(`${INDENT.repeat(depth + 1)}policy: ${value}${comment}`);
lines.push(`${indent}}`);

@@ -1332,3 +1338,3 @@ } else <span class="missing-if-branch" title="else path not taken" >E</span>{

<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
at 2026-05-08T13:02:58.721Z
at 2026-05-13T17:08:50.965Z
</div>

@@ -1335,0 +1341,0 @@ <script src="../prettify.js"></script>

@@ -286,3 +286,3 @@

<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
at 2026-05-08T13:02:58.721Z
at 2026-05-13T17:08:50.965Z
</div>

@@ -289,0 +289,0 @@ <script src="../prettify.js"></script>

@@ -523,3 +523,3 @@

<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
at 2026-05-08T13:02:58.721Z
at 2026-05-13T17:08:50.965Z
</div>

@@ -526,0 +526,0 @@ <script src="../prettify.js"></script>

@@ -26,5 +26,5 @@

<div class='fl pad1y space-right2'>
<span class="strong">80.79% </span>
<span class="strong">80.72% </span>
<span class="quiet">Statements</span>
<span class='fraction'>715/885</span>
<span class='fraction'>716/887</span>
</div>

@@ -34,5 +34,5 @@

<div class='fl pad1y space-right2'>
<span class="strong">70.7% </span>
<span class="strong">70.45% </span>
<span class="quiet">Branches</span>
<span class='fraction'>514/727</span>
<span class='fraction'>515/731</span>
</div>

@@ -49,5 +49,5 @@

<div class='fl pad1y space-right2'>
<span class="strong">83.18% </span>
<span class="strong">83.1% </span>
<span class="quiet">Lines</span>
<span class='fraction'>668/803</span>
<span class='fraction'>669/805</span>
</div>

@@ -86,13 +86,13 @@

<td class="file high" data-value="ast-to-ck.ts"><a href="ast-to-ck.ts.html">ast-to-ck.ts</a></td>
<td data-value="90.65" class="pic high">
<td data-value="90.27" class="pic high">
<div class="chart"><div class="cover-fill" style="width: 90%"></div><div class="cover-empty" style="width: 10%"></div></div>
</td>
<td data-value="90.65" class="pct high">90.65%</td>
<td data-value="214" class="abs high">194/214</td>
<td data-value="82.7" class="pct high">82.7%</td>
<td data-value="185" class="abs high">153/185</td>
<td data-value="90.27" class="pct high">90.27%</td>
<td data-value="216" class="abs high">195/216</td>
<td data-value="81.48" class="pct high">81.48%</td>
<td data-value="189" class="abs high">154/189</td>
<td data-value="100" class="pct high">100%</td>
<td data-value="18" class="abs high">18/18</td>
<td data-value="91.91" class="pct high">91.91%</td>
<td data-value="198" class="abs high">182/198</td>
<td data-value="91.5" class="pct high">91.5%</td>
<td data-value="200" class="abs high">183/200</td>
</tr>

@@ -213,3 +213,3 @@

<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
at 2026-05-08T13:02:58.721Z
at 2026-05-13T17:08:50.965Z
</div>

@@ -216,0 +216,0 @@ <script src="../prettify.js"></script>

@@ -1150,3 +1150,3 @@

<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
at 2026-05-08T13:02:58.721Z
at 2026-05-13T17:08:50.965Z
</div>

@@ -1153,0 +1153,0 @@ <script src="../prettify.js"></script>

@@ -1079,4 +1079,4 @@

&nbsp;
// The DSL's security model is simpler — OpenAPI scopes/roles don't map onto requireMfa,
// so any non-empty security requirement is collapsed to "authenticated, no MFA".
// The DSL's security model is simpler — OpenAPI scopes/roles don't map onto named policies,
// so any non-empty security requirement is collapsed to "authenticated, default policy".
<span class="cstat-no" title="statement not covered" > return { loc: LOC };</span>

@@ -1118,3 +1118,3 @@ }

<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
at 2026-05-08T13:02:58.721Z
at 2026-05-13T17:08:50.965Z
</div>

@@ -1121,0 +1121,0 @@ <script src="../prettify.js"></script>

@@ -1366,3 +1366,3 @@

<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
at 2026-05-08T13:02:58.721Z
at 2026-05-13T17:08:50.965Z
</div>

@@ -1369,0 +1369,0 @@ <script src="../prettify.js"></script>

@@ -673,3 +673,3 @@

<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
at 2026-05-08T13:02:58.721Z
at 2026-05-13T17:08:50.965Z
</div>

@@ -676,0 +676,0 @@ <script src="../prettify.js"></script>

@@ -142,3 +142,3 @@

<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
at 2026-05-08T13:02:58.721Z
at 2026-05-13T17:08:50.965Z
</div>

@@ -145,0 +145,0 @@ <script src="../prettify.js"></script>

@@ -547,3 +547,3 @@

<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
at 2026-05-08T13:02:58.721Z
at 2026-05-13T17:08:50.965Z
</div>

@@ -550,0 +550,0 @@ <script src="../prettify.js"></script>

@@ -104,3 +104,3 @@

<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
at 2026-05-08T13:02:58.721Z
at 2026-05-13T17:08:50.965Z
</div>

@@ -107,0 +107,0 @@ <script src="../prettify.js"></script>

@@ -1,1 +0,1 @@

{"version":3,"file":"ast-to-ck.d.ts","sourceRoot":"","sources":["../src/ast-to-ck.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACR,UAAU,EAGV,gBAAgB,EAUnB,MAAM,mBAAmB,CAAC;AAM3B,MAAM,WAAW,gBAAgB;IAC7B,6DAA6D;IAC7D,eAAe,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED,wBAAgB,OAAO,CAAC,IAAI,EAAE,UAAU,EAAE,OAAO,GAAE,gBAAqB,GAAG,MAAM,CAoBhF;AAiID,wBAAgB,aAAa,CAAC,IAAI,EAAE,gBAAgB,GAAG,MAAM,CA2B5D"}
{"version":3,"file":"ast-to-ck.d.ts","sourceRoot":"","sources":["../src/ast-to-ck.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACR,UAAU,EAGV,gBAAgB,EAUnB,MAAM,mBAAmB,CAAC;AAM3B,MAAM,WAAW,gBAAgB;IAC7B,6DAA6D;IAC7D,eAAe,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED,wBAAgB,OAAO,CAAC,IAAI,EAAE,UAAU,EAAE,OAAO,GAAE,gBAAqB,GAAG,MAAM,CAoBhF;AAkID,wBAAgB,aAAa,CAAC,IAAI,EAAE,gBAAgB,GAAG,MAAM,CA2B5D"}

@@ -14,3 +14,3 @@ import {

splitByTag
} from "./chunk-T2G2WRZ2.js";
} from "./chunk-YOTHJ2V4.js";
export {

@@ -17,0 +17,0 @@ astToCk,

import {
__name,
convertOpenApiToCk
} from "./chunk-T2G2WRZ2.js";
} from "./chunk-YOTHJ2V4.js";

@@ -6,0 +6,0 @@ // src/plugin.ts

{
"name": "@contractkit/openapi-to-ck",
"version": "0.7.8",
"version": "0.8.0",
"description": "Convert OpenAPI specs (2.0/3.0/3.1) to Contract Kit .ck files",

@@ -32,3 +32,3 @@ "author": {

"yaml": "^2.8.3",
"@contractkit/core": "0.17.0"
"@contractkit/core": "0.18.0"
},

@@ -35,0 +35,0 @@ "devDependencies": {

@@ -87,4 +87,5 @@ import type {

const sec = root.security as SecurityFields;
if (sec.requireMfa !== undefined) {
lines.push(`${INDENT}${INDENT}requireMfa: ${sec.requireMfa}`);
if (sec.policy !== undefined) {
const value = sec.policy === false ? 'none' : sec.policy;
lines.push(`${INDENT}${INDENT}policy: ${value}`);
}

@@ -410,6 +411,7 @@ }

const sec = security as SecurityFields;
if (sec.requireMfa !== undefined) {
const comment = ctx.includeComments && sec.requireMfaDescription ? ` # ${sec.requireMfaDescription}` : '';
if (sec.policy !== undefined) {
const comment = ctx.includeComments && sec.policyDescription ? ` # ${sec.policyDescription}` : '';
const value = sec.policy === false ? 'none' : sec.policy;
lines.push(`${indent}security: {`);
lines.push(`${INDENT.repeat(depth + 1)}requireMfa: ${sec.requireMfa}${comment}`);
lines.push(`${INDENT.repeat(depth + 1)}policy: ${value}${comment}`);
lines.push(`${indent}}`);

@@ -416,0 +418,0 @@ } else {

@@ -318,4 +318,4 @@ import type {

// The DSL's security model is simpler — OpenAPI scopes/roles don't map onto requireMfa,
// so any non-empty security requirement is collapsed to "authenticated, no MFA".
// The DSL's security model is simpler — OpenAPI scopes/roles don't map onto named policies,
// so any non-empty security requirement is collapsed to "authenticated, default policy".
return { loc: LOC };

@@ -322,0 +322,0 @@ }

@@ -330,3 +330,3 @@ import { describe, it, expect } from 'vitest';

it('serializes security with requireMfa', () => {
it('serializes security with policy', () => {
const root = ckRoot({

@@ -336,3 +336,3 @@ routes: [

opOperation('get', {
security: { requireMfa: true, loc: { file: 'test.ck', line: 1 } },
security: { policy: 'adminWrite', loc: { file: 'test.ck', line: 1 } },
responses: [opResponse(200, refType('Data'), 'application/json')],

@@ -345,3 +345,3 @@ }),

expect(result).toContain(' security: {');
expect(result).toContain(' requireMfa: true');
expect(result).toContain(' policy: adminWrite');
});

@@ -348,0 +348,0 @@

var __defProp = Object.defineProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
// src/normalize.ts
function normalize(doc, warnings) {
const version = detectVersion(doc);
if (version === "2.0") {
return normalizeSwagger2(doc, warnings);
}
if (version === "3.0") {
return normalizeOas30(doc, warnings);
}
return doc;
}
__name(normalize, "normalize");
function detectVersion(doc) {
if (typeof doc.swagger === "string" && doc.swagger.startsWith("2")) return "2.0";
if (typeof doc.openapi === "string") {
if (doc.openapi.startsWith("3.0")) return "3.0";
}
return "3.1";
}
__name(detectVersion, "detectVersion");
function normalizeSwagger2(doc, warnings) {
const info = doc.info ?? {
title: "Untitled",
version: "0.0.0"
};
const basePath = doc.basePath ?? "";
const schemes = doc.schemes ?? [
"https"
];
const host = doc.host ?? "localhost";
const globalConsumes = doc.consumes ?? [
"application/json"
];
const globalProduces = doc.produces ?? [
"application/json"
];
const result = {
openapi: "3.1.0",
info: {
title: info.title ?? "Untitled",
version: info.version ?? "0.0.0",
description: info.description
},
servers: [
{
url: `${schemes[0]}://${host}${basePath}`
}
],
paths: {},
components: {
schemas: {},
securitySchemes: {}
},
tags: doc.tags ?? []
};
const definitions = doc.definitions ?? {};
for (const [name, schema] of Object.entries(definitions)) {
result.components.schemas[name] = normalizeNullable30(schema);
}
const secDefs = doc.securityDefinitions ?? {};
for (const [name, scheme] of Object.entries(secDefs)) {
result.components.securitySchemes[name] = convertSecurityScheme2(scheme);
}
const paths = doc.paths ?? {};
for (const [path, pathItem] of Object.entries(paths)) {
result.paths[path] = normalizePathItem2(pathItem, globalConsumes, globalProduces, warnings);
}
if (doc.security) {
result.security = doc.security;
}
return result;
}
__name(normalizeSwagger2, "normalizeSwagger2");
function normalizePathItem2(pathItem, globalConsumes, globalProduces, warnings) {
const methods = [
"get",
"post",
"put",
"patch",
"delete",
"head",
"options"
];
const normalized = {};
const pathParams = pathItem.parameters ?? [];
for (const method of methods) {
const op = pathItem[method];
if (!op) continue;
const opConsumes = op.consumes ?? globalConsumes;
const opProduces = op.produces ?? globalProduces;
const params = [
...pathParams,
...op.parameters ?? []
];
const nonBodyParams = [];
let requestBody;
for (const param of params) {
if (param.in === "body") {
const contentType = opConsumes[0] ?? "application/json";
requestBody = {
description: param.description,
required: param.required ?? true,
content: {
[contentType]: {
schema: normalizeNullable30(param.schema ?? {})
}
}
};
} else if (param.in === "formData") {
warnings.info(`#/paths/${encodePathSegment(method)}`, "formData parameters converted to multipart/form-data requestBody");
if (!requestBody) {
requestBody = {
content: {
"multipart/form-data": {
schema: {
type: "object",
properties: {},
required: []
}
}
}
};
}
const formSchema = requestBody.content["multipart/form-data"].schema;
const props = formSchema.properties;
props[param.name] = normalizeNullable30(param);
if (param.required) {
formSchema.required.push(param.name);
}
} else {
const normalizedParam = {
...param
};
if (param.type) {
normalizedParam.schema = normalizeNullable30({
type: param.type,
format: param.format,
enum: param.enum,
items: param.items,
default: param.default,
minimum: param.minimum,
maximum: param.maximum,
minLength: param.minLength,
maxLength: param.maxLength,
pattern: param.pattern
});
delete normalizedParam.type;
delete normalizedParam.format;
delete normalizedParam.enum;
delete normalizedParam.items;
}
nonBodyParams.push(normalizedParam);
}
}
const responses = {};
const opResponses = op.responses ?? {};
for (const [code, resp] of Object.entries(opResponses)) {
const contentType = opProduces[0] ?? "application/json";
const headers = convertResponseHeaders2(resp.headers);
const responseEntry = {
description: resp.description ?? ""
};
if (resp.schema) {
responseEntry.content = {
[contentType]: {
schema: normalizeNullable30(resp.schema)
}
};
}
if (headers) {
responseEntry.headers = headers;
}
responses[code] = responseEntry;
}
normalized[method] = {
operationId: op.operationId,
summary: op.summary,
description: op.description,
tags: op.tags,
parameters: nonBodyParams.length > 0 ? nonBodyParams : void 0,
requestBody,
responses,
security: op.security,
deprecated: op.deprecated
};
}
return normalized;
}
__name(normalizePathItem2, "normalizePathItem2");
function convertResponseHeaders2(headers) {
if (!headers) return void 0;
const out = {};
for (const [name, header] of Object.entries(headers)) {
if (!header || typeof header !== "object") continue;
const { description, type, format, items, ...rest } = header;
const schema = {
...rest
};
if (type !== void 0) schema.type = type;
if (format !== void 0) schema.format = format;
if (items !== void 0) schema.items = items;
const normalized = {};
if (description !== void 0) normalized.description = description;
if (Object.keys(schema).length > 0) normalized.schema = normalizeNullable30(schema);
out[name] = normalized;
}
return Object.keys(out).length > 0 ? out : void 0;
}
__name(convertResponseHeaders2, "convertResponseHeaders2");
function convertSecurityScheme2(scheme) {
const type = scheme.type;
if (type === "basic") {
return {
type: "http",
scheme: "basic"
};
}
if (type === "apiKey") {
return {
type: "apiKey",
name: scheme.name,
in: scheme.in
};
}
if (type === "oauth2") {
const flow = scheme.flow;
const flows = {};
if (flow === "implicit") {
flows.implicit = {
authorizationUrl: scheme.authorizationUrl,
scopes: scheme.scopes ?? {}
};
} else if (flow === "password") {
flows.password = {
tokenUrl: scheme.tokenUrl,
scopes: scheme.scopes ?? {}
};
} else if (flow === "application") {
flows.clientCredentials = {
tokenUrl: scheme.tokenUrl,
scopes: scheme.scopes ?? {}
};
} else if (flow === "accessCode") {
flows.authorizationCode = {
authorizationUrl: scheme.authorizationUrl,
tokenUrl: scheme.tokenUrl,
scopes: scheme.scopes ?? {}
};
}
return {
type: "oauth2",
flows
};
}
return scheme;
}
__name(convertSecurityScheme2, "convertSecurityScheme2");
function normalizeOas30(doc, _warnings) {
if (doc.components?.schemas) {
for (const [name, schema] of Object.entries(doc.components.schemas)) {
doc.components.schemas[name] = normalizeNullable30(schema);
}
}
if (doc.paths) {
for (const pathItem of Object.values(doc.paths)) {
normalizePathItemSchemas(pathItem);
}
}
doc.openapi = "3.1.0";
return doc;
}
__name(normalizeOas30, "normalizeOas30");
function normalizePathItemSchemas(pathItem) {
const methods = [
"get",
"post",
"put",
"patch",
"delete",
"head",
"options"
];
for (const method of methods) {
const op = pathItem[method];
if (!op) continue;
const params = op.parameters ?? [];
for (const param of params) {
if (param.schema) {
param.schema = normalizeNullable30(param.schema);
}
}
const reqBody = op.requestBody;
if (reqBody?.content) {
for (const mediaType of Object.values(reqBody.content)) {
if (mediaType.schema) {
mediaType.schema = normalizeNullable30(mediaType.schema);
}
}
}
const responses = op.responses ?? {};
for (const resp of Object.values(responses)) {
if (resp.content) {
for (const mediaType of Object.values(resp.content)) {
if (mediaType.schema) {
mediaType.schema = normalizeNullable30(mediaType.schema);
}
}
}
}
}
}
__name(normalizePathItemSchemas, "normalizePathItemSchemas");
function normalizeNullable30(schema) {
if (!schema || typeof schema !== "object") return schema;
const result = {
...schema
};
if (result.nullable === true && typeof result.type === "string") {
result.type = [
result.type,
"null"
];
delete result.nullable;
}
if (result.properties && typeof result.properties === "object") {
const props = result.properties;
for (const [key, val] of Object.entries(props)) {
props[key] = normalizeNullable30(val);
}
}
if (result.items && typeof result.items === "object" && !Array.isArray(result.items)) {
result.items = normalizeNullable30(result.items);
}
if (result.additionalProperties && typeof result.additionalProperties === "object") {
result.additionalProperties = normalizeNullable30(result.additionalProperties);
}
for (const combiner of [
"allOf",
"oneOf",
"anyOf"
]) {
if (Array.isArray(result[combiner])) {
result[combiner] = result[combiner].map(normalizeNullable30);
}
}
return result;
}
__name(normalizeNullable30, "normalizeNullable30");
function encodePathSegment(s) {
return s.replace(/~/g, "~0").replace(/\//g, "~1");
}
__name(encodePathSegment, "encodePathSegment");
// src/circular-refs.ts
function detectCircularRefs(schemas) {
const circular = /* @__PURE__ */ new Set();
const visiting = /* @__PURE__ */ new Set();
const visited = /* @__PURE__ */ new Set();
function visit(name) {
if (visited.has(name)) return;
if (visiting.has(name)) {
circular.add(name);
return;
}
visiting.add(name);
const schema = schemas[name];
if (schema && typeof schema === "object") {
for (const ref of collectRefs(schema)) {
const refName = extractRefName(ref);
if (refName && schemas[refName]) {
visit(refName);
}
}
}
visiting.delete(name);
visited.add(name);
}
__name(visit, "visit");
for (const name of Object.keys(schemas)) {
visit(name);
}
return circular;
}
__name(detectCircularRefs, "detectCircularRefs");
function collectRefs(obj) {
const refs = [];
function walk(val) {
if (!val || typeof val !== "object") return;
if (Array.isArray(val)) {
for (const item of val) walk(item);
return;
}
const record = val;
if (typeof record.$ref === "string") {
refs.push(record.$ref);
}
for (const v of Object.values(record)) {
walk(v);
}
}
__name(walk, "walk");
walk(obj);
return refs;
}
__name(collectRefs, "collectRefs");
function extractRefName(ref) {
const match = ref.match(/^#\/(?:components\/schemas|definitions)\/(.+)$/);
return match?.[1];
}
__name(extractRefName, "extractRefName");
// src/schema-to-ast.ts
var LOC = {
file: "",
line: 0
};
var FORMAT_TO_SCALAR = {
email: "email",
uri: "url",
url: "url",
uuid: "uuid",
date: "date",
"date-time": "datetime",
time: "time",
binary: "binary",
int64: "bigint"
};
function schemasToModels(schemas, ctx) {
const models = [];
for (const [name, schema] of Object.entries(schemas)) {
const modelCtx = {
...ctx,
path: `#/components/schemas/${name}`
};
const model = schemaToModel(name, schema, modelCtx);
if (model) models.push(model);
}
models.push(...ctx.extractedModels);
return models;
}
__name(schemasToModels, "schemasToModels");
function schemaToModel(name, schema, ctx) {
warnUnsupported(schema, ctx);
const description = ctx.includeComments ? schema.description : void 0;
if (schema.allOf && schema.allOf.length === 2) {
const [first, second] = schema.allOf;
const refMember = first?.$ref ? first : second?.$ref ? second : null;
const objectMember = first?.$ref ? second : first;
if (refMember?.$ref && objectMember?.properties) {
const baseName = extractRefName(refMember.$ref);
if (baseName) {
const fields = schemaPropertiesToFields(objectMember, ctx);
return {
kind: "model",
name,
bases: [
baseName
],
fields,
description,
loc: LOC
};
}
}
}
if (schema.properties || schema.type === "object" && !schema.additionalProperties) {
const fields = schemaPropertiesToFields(schema, ctx);
return {
kind: "model",
name,
fields,
description,
loc: LOC
};
}
const typeNode = schemaToTypeNode(schema, ctx);
return {
kind: "model",
name,
fields: [],
type: typeNode,
description,
loc: LOC
};
}
__name(schemaToModel, "schemaToModel");
function schemaToTypeNode(schema, ctx) {
if (schema.$ref) {
const refName = extractRefName(schema.$ref);
if (refName) {
if (ctx.circularRefs.has(refName)) {
return {
kind: "lazy",
inner: {
kind: "ref",
name: refName
}
};
}
return {
kind: "ref",
name: refName
};
}
ctx.warnings.warn(ctx.path, `Unresolvable $ref: ${schema.$ref}`);
return {
kind: "scalar",
name: "unknown"
};
}
if (schema.const !== void 0) {
return {
kind: "literal",
value: schema.const
};
}
if (schema.enum) {
return {
kind: "enum",
values: schema.enum.map(String)
};
}
if (schema.oneOf && schema.oneOf.length > 0) {
if (schema.discriminator?.propertyName) {
return toDiscriminatedUnion(schema.oneOf, schema.discriminator.propertyName, ctx);
}
return toUnion(schema.oneOf, ctx);
}
if (schema.anyOf && schema.anyOf.length > 0) {
if (schema.discriminator?.propertyName) {
return toDiscriminatedUnion(schema.anyOf, schema.discriminator.propertyName, ctx);
}
return toUnion(schema.anyOf, ctx);
}
if (schema.allOf && schema.allOf.length > 0) {
if (schema.allOf.length === 1) {
return schemaToTypeNode(schema.allOf[0], ctx);
}
return {
kind: "intersection",
members: schema.allOf.map((s) => schemaToTypeNode(s, ctx))
};
}
const types = normalizeTypeField(schema);
if (types === null) {
if (schema.properties) {
return schemaToInlineObject(schema, ctx);
}
return {
kind: "scalar",
name: "unknown"
};
}
const { baseType, nullable } = types;
let typeNode;
switch (baseType) {
case "string":
typeNode = stringSchemaToType(schema);
break;
case "integer":
typeNode = integerSchemaToType(schema);
break;
case "number":
typeNode = numberSchemaToType(schema);
break;
case "boolean":
typeNode = {
kind: "scalar",
name: "boolean"
};
break;
case "null":
typeNode = {
kind: "scalar",
name: "null"
};
break;
case "array":
typeNode = arraySchemaToType(schema, ctx);
break;
case "object":
typeNode = objectSchemaToType(schema, ctx);
break;
default:
ctx.warnings.warn(ctx.path, `Unknown type: ${baseType}`);
typeNode = {
kind: "scalar",
name: "unknown"
};
}
if (nullable) {
return {
kind: "union",
members: [
typeNode,
{
kind: "scalar",
name: "null"
}
]
};
}
return typeNode;
}
__name(schemaToTypeNode, "schemaToTypeNode");
function stringSchemaToType(schema) {
if (schema.format) {
const scalarName = FORMAT_TO_SCALAR[schema.format];
if (scalarName) {
return {
kind: "scalar",
name: scalarName
};
}
}
const mods = {};
if (schema.minLength !== void 0 && schema.maxLength !== void 0 && schema.minLength === schema.maxLength) {
mods.len = schema.minLength;
} else {
if (schema.minLength !== void 0) mods.min = schema.minLength;
if (schema.maxLength !== void 0) mods.max = schema.maxLength;
}
if (schema.pattern) mods.regex = `/${schema.pattern}/`;
if (schema.format && !FORMAT_TO_SCALAR[schema.format]) mods.format = schema.format;
return {
kind: "scalar",
name: "string",
...mods
};
}
__name(stringSchemaToType, "stringSchemaToType");
function integerSchemaToType(schema) {
const name = schema.format === "int64" ? "bigint" : "int";
const mods = {};
if (schema.minimum !== void 0) mods.min = schema.minimum;
if (schema.maximum !== void 0) mods.max = schema.maximum;
return {
kind: "scalar",
name,
...mods
};
}
__name(integerSchemaToType, "integerSchemaToType");
function numberSchemaToType(schema) {
const mods = {};
if (schema.minimum !== void 0) mods.min = schema.minimum;
if (schema.maximum !== void 0) mods.max = schema.maximum;
return {
kind: "scalar",
name: "number",
...mods
};
}
__name(numberSchemaToType, "numberSchemaToType");
function arraySchemaToType(schema, ctx) {
if (schema.prefixItems && schema.prefixItems.length > 0) {
return {
kind: "tuple",
items: schema.prefixItems.map((s) => schemaToTypeNode(s, ctx))
};
}
const item = schema.items ? schemaToTypeNode(schema.items, ctx) : {
kind: "scalar",
name: "unknown"
};
const mods = {};
if (schema.minItems !== void 0) mods.min = schema.minItems;
if (schema.maxItems !== void 0) mods.max = schema.maxItems;
return {
kind: "array",
item,
...mods
};
}
__name(arraySchemaToType, "arraySchemaToType");
function objectSchemaToType(schema, ctx) {
if (schema.additionalProperties && typeof schema.additionalProperties === "object" && !schema.properties) {
return {
kind: "record",
key: {
kind: "scalar",
name: "string"
},
value: schemaToTypeNode(schema.additionalProperties, ctx)
};
}
if (schema.properties) {
return schemaToInlineObject(schema, ctx);
}
return {
kind: "scalar",
name: "object"
};
}
__name(objectSchemaToType, "objectSchemaToType");
function schemaToInlineObject(schema, ctx) {
const fields = schemaPropertiesToFields(schema, ctx);
return {
kind: "inlineObject",
fields
};
}
__name(schemaToInlineObject, "schemaToInlineObject");
function schemaPropertiesToFields(schema, ctx) {
const properties = schema.properties ?? {};
const required = new Set(schema.required ?? []);
const fields = [];
for (const [name, propSchema] of Object.entries(properties)) {
const propCtx = {
...ctx,
path: `${ctx.path}/properties/${name}`
};
const fieldType = schemaToTypeNode(propSchema, propCtx);
let nullable = false;
let effectiveType = fieldType;
if (fieldType.kind === "union") {
const nonNull = fieldType.members.filter((m) => !(m.kind === "scalar" && m.name === "null"));
if (nonNull.length < fieldType.members.length) {
nullable = true;
effectiveType = nonNull.length === 1 ? nonNull[0] : {
kind: "union",
members: nonNull
};
}
}
const visibility = propSchema.readOnly ? "readonly" : propSchema.writeOnly ? "writeonly" : "normal";
fields.push({
name,
optional: !required.has(name),
nullable,
visibility,
type: effectiveType,
default: propSchema.default,
deprecated: propSchema.deprecated,
description: ctx.includeComments ? propSchema.description : void 0,
loc: LOC
});
}
return fields;
}
__name(schemaPropertiesToFields, "schemaPropertiesToFields");
function normalizeTypeField(schema) {
if (!schema.type) return null;
if (typeof schema.type === "string") {
return {
baseType: schema.type,
nullable: false
};
}
if (Array.isArray(schema.type)) {
const types = schema.type;
const nonNull = types.filter((t) => t !== "null");
const nullable = types.includes("null");
if (nonNull.length === 1) {
return {
baseType: nonNull[0],
nullable
};
}
if (nonNull.length === 0) {
return {
baseType: "null",
nullable: false
};
}
return {
baseType: nonNull[0],
nullable
};
}
return null;
}
__name(normalizeTypeField, "normalizeTypeField");
function toUnion(schemas, ctx) {
const members = schemas.map((s) => schemaToTypeNode(s, ctx));
if (members.length === 1) return members[0];
return {
kind: "union",
members
};
}
__name(toUnion, "toUnion");
function toDiscriminatedUnion(schemas, discriminator, ctx) {
const members = schemas.map((s) => schemaToTypeNode(s, ctx));
if (members.length === 1) return members[0];
return {
kind: "discriminatedUnion",
discriminator,
members
};
}
__name(toDiscriminatedUnion, "toDiscriminatedUnion");
function warnUnsupported(schema, ctx) {
if (schema.xml) ctx.warnings.warn(ctx.path, "xml metadata is not supported, skipping");
if (schema.externalDocs) ctx.warnings.info(ctx.path, "externalDocs is not supported, skipping");
if (schema.not) ctx.warnings.warn(ctx.path, "not keyword is not supported, skipping");
}
__name(warnUnsupported, "warnUnsupported");
function extractInlineModel(schema, suggestedName, ctx) {
if (schema.$ref) {
return {
typeNode: schemaToTypeNode(schema, ctx)
};
}
if (schema.properties || schema.type === "object" && schema.additionalProperties === void 0) {
const fields = schemaPropertiesToFields(schema, ctx);
const model = {
kind: "model",
name: suggestedName,
fields,
description: ctx.includeComments ? schema.description : void 0,
loc: LOC
};
return {
typeNode: {
kind: "ref",
name: suggestedName
},
model
};
}
return {
typeNode: schemaToTypeNode(schema, ctx)
};
}
__name(extractInlineModel, "extractInlineModel");
function sanitizeName(name, warnings) {
const cleaned = name.replace(/[^a-zA-Z0-9_$]/g, " ").split(/\s+/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
if (cleaned !== name) {
warnings.info(`#/components/schemas/${name}`, `Schema name sanitized: "${name}" \u2192 "${cleaned}"`);
}
return cleaned || "UnnamedSchema";
}
__name(sanitizeName, "sanitizeName");
// src/paths-to-ast.ts
var LOC2 = {
file: "",
line: 0
};
var HTTP_METHODS = [
"get",
"post",
"put",
"patch",
"delete"
];
function pathsToRoutes(doc, ctx) {
const routes = [];
const routeTags = /* @__PURE__ */ new Map();
const paths = doc.paths ?? {};
for (const [path, pathItem] of Object.entries(paths)) {
if (!pathItem) continue;
const result = pathItemToRoute(path, pathItem, ctx);
if (result) {
routes.push(result.route);
routeTags.set(result.route, result.tag);
}
}
return {
routes,
routeTags
};
}
__name(pathsToRoutes, "pathsToRoutes");
function pathItemToRoute(path, pathItem, ctx) {
const operations = [];
let primaryTag = "default";
const pathParams = (pathItem.parameters ?? []).filter((p) => p.in === "path");
for (const method of HTTP_METHODS) {
const op = pathItem[method];
if (!op) continue;
const opNode = operationToNode(method, op, path, ctx);
operations.push(opNode);
if (op.tags && op.tags.length > 0 && primaryTag === "default") {
primaryTag = op.tags[0];
}
}
if (operations.length === 0) return null;
const params = buildPathParams(path, pathParams, pathItem, ctx);
const route = {
path,
operations,
loc: LOC2
};
if (params.length > 0) {
route.params = {
kind: "params",
nodes: params
};
}
if (pathItem.description && ctx.includeComments) {
route.description = pathItem.description;
}
return {
route,
tag: primaryTag
};
}
__name(pathItemToRoute, "pathItemToRoute");
function operationToNode(method, op, path, ctx) {
const pathPrefix = `#/paths/${encodePathSegment2(path)}/${method}`;
const schemaCtx = makeSchemaCtx(ctx, pathPrefix);
const node = {
method,
responses: [],
loc: LOC2
};
if (op.operationId) {
node.sdk = op.operationId;
}
if (op.description && ctx.includeComments) {
node.description = op.description;
}
if (op.deprecated) {
node.modifiers = [
"deprecated"
];
}
const queryParams = [];
const headerParams = [];
for (const param of op.parameters ?? []) {
if (param.in === "query") {
queryParams.push(parameterToNode(param, schemaCtx));
} else if (param.in === "header") {
headerParams.push(parameterToNode(param, schemaCtx));
}
}
if (queryParams.length > 0) {
node.query = {
kind: "params",
nodes: queryParams
};
}
if (headerParams.length > 0) {
node.headers = {
kind: "params",
nodes: headerParams
};
}
if (op.requestBody) {
node.request = requestBodyToNode(op.requestBody, op.operationId ?? `${method}${toPascalCase(path)}`, schemaCtx, ctx);
}
const responses = op.responses ?? {};
for (const [code, resp] of Object.entries(responses)) {
const statusCode = parseInt(code, 10);
if (isNaN(statusCode)) continue;
const respNode = responseToNode(statusCode, resp, op.operationId ?? `${method}${toPascalCase(path)}`, schemaCtx, ctx);
node.responses.push(respNode);
}
if (op.security !== void 0) {
node.security = convertSecurity(op.security);
}
return node;
}
__name(operationToNode, "operationToNode");
function buildPathParams(path, pathLevelParams, pathItem, ctx) {
const schemaCtx = makeSchemaCtx(ctx, `#/paths/${encodePathSegment2(path)}`);
const paramMap = /* @__PURE__ */ new Map();
for (const p of pathLevelParams) {
paramMap.set(p.name, p);
}
for (const method of HTTP_METHODS) {
const op = pathItem[method];
if (!op?.parameters) continue;
for (const p of op.parameters) {
if (p.in === "path" && !paramMap.has(p.name)) {
paramMap.set(p.name, p);
}
}
}
const templateNames = [
...path.matchAll(/\{([^}]+)\}/g)
].map((m) => m[1]);
return templateNames.map((name) => {
const param = paramMap.get(name);
if (param) {
return parameterToNode(param, schemaCtx);
}
return {
name,
optional: false,
nullable: false,
type: {
kind: "scalar",
name: "string"
},
loc: LOC2
};
});
}
__name(buildPathParams, "buildPathParams");
function parameterToNode(param, ctx) {
const type = param.schema ? schemaToTypeNode(param.schema, ctx) : {
kind: "scalar",
name: "string"
};
return {
name: param.name,
optional: param.in !== "path" && !param.required,
nullable: false,
type,
description: ctx.includeComments ? param.description : void 0,
loc: LOC2
};
}
__name(parameterToNode, "parameterToNode");
function requestBodyToNode(reqBody, operationName, schemaCtx, ctx) {
const content = reqBody.content;
if (!content) return void 0;
const supported = /* @__PURE__ */ new Set([
"application/json",
"application/x-www-form-urlencoded",
"multipart/form-data"
]);
const bodies = [];
for (const [contentType, mediaType] of Object.entries(content)) {
if (!supported.has(contentType) || !mediaType?.schema) continue;
const { typeNode, model } = extractInlineModel(mediaType.schema, `${toPascalCase(operationName)}Request`, schemaCtx);
if (model) {
ctx.extractedModels.push(model);
}
bodies.push({
contentType,
bodyType: typeNode
});
}
if (bodies.length === 0) return void 0;
return {
bodies
};
}
__name(requestBodyToNode, "requestBodyToNode");
function responseToNode(statusCode, resp, operationName, schemaCtx, ctx) {
const headers = convertResponseHeaders(resp.headers, schemaCtx);
if (!resp.content) {
return headers ? {
statusCode,
headers
} : {
statusCode
};
}
const [contentType, mediaType] = Object.entries(resp.content)[0] ?? [];
if (!contentType || !mediaType?.schema) {
return headers ? {
statusCode,
headers
} : {
statusCode
};
}
const { typeNode, model } = extractInlineModel(mediaType.schema, `${toPascalCase(operationName)}Response${statusCode}`, schemaCtx);
if (model) {
ctx.extractedModels.push(model);
}
return {
statusCode,
contentType,
bodyType: typeNode,
...headers ? {
headers
} : {}
};
}
__name(responseToNode, "responseToNode");
function convertResponseHeaders(headers, schemaCtx) {
if (!headers) return void 0;
const out = [];
for (const [name, header] of Object.entries(headers)) {
if (!header) continue;
const type = header.schema ? schemaToTypeNode(header.schema, schemaCtx) : {
kind: "scalar",
name: "string"
};
out.push({
name,
optional: !header.required,
type,
description: schemaCtx.includeComments ? header.description : void 0
});
}
return out.length > 0 ? out : void 0;
}
__name(convertResponseHeaders, "convertResponseHeaders");
function convertSecurity(security) {
if (security.length === 0) {
return "none";
}
return {
loc: LOC2
};
}
__name(convertSecurity, "convertSecurity");
function makeSchemaCtx(ctx, path) {
return {
circularRefs: ctx.circularRefs,
warnings: ctx.warnings,
path,
includeComments: ctx.includeComments,
namedSchemas: ctx.namedSchemas,
extractedModels: ctx.extractedModels,
inlineCounter: 0
};
}
__name(makeSchemaCtx, "makeSchemaCtx");
function toPascalCase(input) {
return input.replace(/[^a-zA-Z0-9]/g, " ").split(/\s+/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
}
__name(toPascalCase, "toPascalCase");
function encodePathSegment2(s) {
return s.replace(/~/g, "~0").replace(/\//g, "~1");
}
__name(encodePathSegment2, "encodePathSegment");
// src/tag-splitter.ts
function splitByTag(models, routes, routeTags) {
const routesByTag = /* @__PURE__ */ new Map();
for (const route of routes) {
const tag = routeTags.get(route) ?? "default";
const group = routesByTag.get(tag) ?? [];
group.push(route);
routesByTag.set(tag, group);
}
const modelsByTag = /* @__PURE__ */ new Map();
for (const [tag, tagRoutes] of routesByTag) {
const refs = /* @__PURE__ */ new Set();
for (const route of tagRoutes) {
collectRouteRefs(route, refs);
}
modelsByTag.set(tag, refs);
}
const modelNameToModel = new Map(models.map((m) => [
m.name,
m
]));
const modelAssignment = /* @__PURE__ */ new Map();
for (const model of models) {
const tags = [];
for (const [tag, refs] of modelsByTag) {
if (refs.has(model.name)) {
tags.push(tag);
}
}
if (tags.length === 0) {
modelAssignment.set(model.name, "shared");
} else if (tags.length === 1) {
modelAssignment.set(model.name, tags[0]);
} else {
modelAssignment.set(model.name, "shared");
}
}
for (const model of models) {
if (modelAssignment.get(model.name) === "shared") {
const refs = /* @__PURE__ */ new Set();
collectModelRefs(model, refs);
for (const ref of refs) {
if (modelNameToModel.has(ref)) {
const currentTag = modelAssignment.get(ref);
if (currentTag && currentTag !== "shared") {
const otherTags = [
...modelsByTag.entries()
].filter(([t, r]) => t !== currentTag && r.has(ref)).map(([t]) => t);
if (otherTags.length > 0) {
modelAssignment.set(ref, "shared");
}
}
}
}
}
}
const result = /* @__PURE__ */ new Map();
const allTags = /* @__PURE__ */ new Set([
...routesByTag.keys(),
...new Set(modelAssignment.values())
]);
for (const tag of allTags) {
const tagModels = models.filter((m) => modelAssignment.get(m.name) === tag);
const tagRoutes = routesByTag.get(tag) ?? [];
if (tagModels.length === 0 && tagRoutes.length === 0) continue;
const filename = sanitizeFilename(tag);
result.set(`${filename}.ck`, {
kind: "ckRoot",
meta: tag !== "shared" ? {
area: tag
} : {},
services: {},
models: tagModels,
routes: tagRoutes,
file: `${filename}.ck`
});
}
return result;
}
__name(splitByTag, "splitByTag");
function mergeIntoSingle(models, routes, filename = "api") {
return {
kind: "ckRoot",
meta: {},
services: {},
models,
routes,
file: `${filename}.ck`
};
}
__name(mergeIntoSingle, "mergeIntoSingle");
function collectRouteRefs(route, refs) {
if (route.params) {
collectParamSourceRefs(route.params, refs);
}
for (const op of route.operations) {
if (op.query) collectParamSourceRefs(op.query, refs);
if (op.headers) collectParamSourceRefs(op.headers, refs);
if (op.request) {
for (const body of op.request.bodies) collectTypeRefs(body.bodyType, refs);
}
for (const resp of op.responses) {
if (resp.bodyType) collectTypeRefs(resp.bodyType, refs);
}
}
}
__name(collectRouteRefs, "collectRouteRefs");
function collectParamSourceRefs(source, refs) {
if (typeof source === "string") {
refs.add(source);
return;
}
if (Array.isArray(source)) {
for (const param of source) {
if (param && typeof param === "object" && "type" in param) {
collectTypeRefs(param.type, refs);
}
}
return;
}
if (source && typeof source === "object" && "kind" in source) {
collectTypeRefs(source, refs);
}
}
__name(collectParamSourceRefs, "collectParamSourceRefs");
function collectTypeRefs(type, refs) {
switch (type.kind) {
case "ref":
refs.add(type.name);
break;
case "array":
collectTypeRefs(type.item, refs);
break;
case "tuple":
for (const item of type.items) collectTypeRefs(item, refs);
break;
case "record":
collectTypeRefs(type.key, refs);
collectTypeRefs(type.value, refs);
break;
case "union":
case "discriminatedUnion":
case "intersection":
for (const member of type.members) collectTypeRefs(member, refs);
break;
case "inlineObject":
for (const field of type.fields) collectTypeRefs(field.type, refs);
break;
case "lazy":
collectTypeRefs(type.inner, refs);
break;
}
}
__name(collectTypeRefs, "collectTypeRefs");
function collectModelRefs(model, refs) {
if (model.bases) for (const b of model.bases) refs.add(b);
if (model.type) collectTypeRefs(model.type, refs);
for (const field of model.fields) {
collectTypeRefs(field.type, refs);
}
}
__name(collectModelRefs, "collectModelRefs");
function sanitizeFilename(tag) {
return tag.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "") || "default";
}
__name(sanitizeFilename, "sanitizeFilename");
// src/ast-to-ck.ts
var INDENT = " ";
function astToCk(root, options = {}) {
const { includeComments = true } = options;
const ctx = {
includeComments
};
const parts = [];
const optionsBlock = serializeOptions(root);
if (optionsBlock) parts.push(optionsBlock);
for (const model of root.models) {
parts.push(serializeModel(model, ctx));
}
for (const route of root.routes) {
parts.push(serializeRoute(route, ctx));
}
return parts.join("\n\n") + "\n";
}
__name(astToCk, "astToCk");
function serializeOptions(root) {
const hasKeys = Object.keys(root.meta).length > 0;
const hasServices = root.services && Object.keys(root.services).length > 0;
const hasSecurity = root.security !== void 0;
if (!hasKeys && !hasServices && !hasSecurity) return null;
const lines = [
"options {"
];
if (hasKeys) {
lines.push(`${INDENT}keys: {`);
for (const [key, value] of Object.entries(root.meta)) {
lines.push(`${INDENT}${INDENT}${key}: ${value}`);
}
lines.push(`${INDENT}}`);
}
if (hasServices) {
lines.push(`${INDENT}services: {`);
for (const [name, path] of Object.entries(root.services)) {
lines.push(`${INDENT}${INDENT}${name}: "${path}"`);
}
lines.push(`${INDENT}}`);
}
if (hasSecurity) {
lines.push(`${INDENT}security: {`);
if (root.security === "none") {
lines.push(`${INDENT}${INDENT}none`);
} else {
const sec = root.security;
if (sec.requireMfa !== void 0) {
lines.push(`${INDENT}${INDENT}requireMfa: ${sec.requireMfa}`);
}
}
lines.push(`${INDENT}}`);
}
lines.push("}");
return lines.join("\n");
}
__name(serializeOptions, "serializeOptions");
function serializeModel(model, ctx) {
const parts = [];
const prefixes = [];
if (model.inputCase && model.inputCase !== "camel") {
prefixes.push(`format(input=${model.inputCase})`);
}
if (model.mode && model.mode !== "strict") {
prefixes.push(`mode(${model.mode})`);
}
if (model.deprecated) {
prefixes.push("deprecated");
}
const prefix = prefixes.length > 0 ? prefixes.join(" ") + " " : "";
const comment = ctx.includeComments && model.description ? ` # ${model.description}` : "";
if (model.type) {
parts.push(`contract ${prefix}${model.name}: ${serializeType(model.type)}${comment}`);
return parts.join("");
}
if (model.bases && model.bases.length > 0) {
parts.push(`contract ${prefix}${model.name}: ${model.bases.join(" & ")} & {${comment}`);
} else {
parts.push(`contract ${prefix}${model.name}: {${comment}`);
}
for (const field of model.fields) {
parts.push(serializeField(field, 1, ctx));
}
parts.push("}");
return parts.join("\n");
}
__name(serializeModel, "serializeModel");
function serializeField(field, depth, ctx) {
const indent = INDENT.repeat(depth);
const optional = field.optional ? "?" : "";
const visibility = field.visibility !== "normal" ? `${field.visibility} ` : "";
const deprecated = field.deprecated ? "deprecated " : "";
let typeStr = serializeType(field.type);
if (field.nullable && !typeContainsNull(field.type)) {
typeStr = `${typeStr} | null`;
}
const defaultVal = field.default !== void 0 ? ` = ${serializeDefault(field.default)}` : "";
const comment = ctx.includeComments && field.description ? ` # ${field.description}` : "";
return `${indent}${field.name}${optional}: ${deprecated}${visibility}${typeStr}${defaultVal}${comment}`;
}
__name(serializeField, "serializeField");
function typeContainsNull(type) {
if (type.kind === "scalar" && type.name === "null") return true;
if (type.kind === "union") return type.members.some(typeContainsNull);
return false;
}
__name(typeContainsNull, "typeContainsNull");
function serializeDefault(value) {
if (typeof value === "string") {
if (/^[a-zA-Z_$][a-zA-Z0-9_$\-.]*$/.test(value)) return value;
return `"${value}"`;
}
return String(value);
}
__name(serializeDefault, "serializeDefault");
function serializeType(type) {
switch (type.kind) {
case "scalar":
return serializeScalar(type);
case "array":
return serializeArray(type);
case "tuple":
return `tuple(${type.items.map(serializeType).join(", ")})`;
case "record":
return `record(${serializeType(type.key)}, ${serializeType(type.value)})`;
case "enum":
return `enum(${type.values.join(", ")})`;
case "literal":
return serializeLiteral(type);
case "union":
return type.members.map(serializeType).join(" | ");
case "discriminatedUnion":
return `discriminated(by=${type.discriminator}, ${type.members.map(serializeType).join(" | ")})`;
case "intersection":
return type.members.map(serializeType).join(" & ");
case "ref":
return type.name;
case "inlineObject":
return serializeInlineObject(type);
case "lazy":
return `lazy(${serializeType(type.inner)})`;
}
}
__name(serializeType, "serializeType");
function serializeScalar(type) {
const args = [];
if (type.len !== void 0) args.push(`length=${type.len}`);
if (type.min !== void 0) args.push(typeof type.min === "string" ? `min="${type.min}"` : `min=${type.min}`);
if (type.max !== void 0) args.push(typeof type.max === "string" ? `max="${type.max}"` : `max=${type.max}`);
if (type.regex !== void 0) args.push(`regex=${type.regex}`);
if (type.format !== void 0) args.push(`format=${type.format}`);
if (args.length === 0) return type.name;
return `${type.name}(${args.join(", ")})`;
}
__name(serializeScalar, "serializeScalar");
function serializeArray(type) {
const args = [
serializeType(type.item)
];
if (type.min !== void 0) args.push(`min=${type.min}`);
if (type.max !== void 0) args.push(`max=${type.max}`);
return `array(${args.join(", ")})`;
}
__name(serializeArray, "serializeArray");
function serializeLiteral(type) {
if (typeof type.value === "string") return `literal("${type.value}")`;
return `literal(${type.value})`;
}
__name(serializeLiteral, "serializeLiteral");
function serializeInlineObject(type) {
const modePrefix = type.mode ? `mode(${type.mode}) ` : "";
if (type.fields.length === 0) return `${modePrefix}{}`;
const lines = [
`${modePrefix}{`
];
for (const field of type.fields) {
lines.push(serializeField(field, 2, {
includeComments: true
}));
}
lines.push(`${INDENT}}`);
return lines.join("\n");
}
__name(serializeInlineObject, "serializeInlineObject");
function serializeRoute(route, ctx) {
const lines = [];
const modStr = serializeModifiers(route.modifiers);
const comment = ctx.includeComments && route.description ? ` # ${route.description}` : "";
lines.push(`operation${modStr} ${route.path}: {${comment}`);
if (route.params) {
serializeParamSource(lines, "params", route.params, route.paramsMode, 1, ctx);
}
if (route.security !== void 0) {
serializeSecurityBlock(lines, route.security, 1, ctx);
}
for (const op of route.operations) {
serializeOperation(lines, op, 1, ctx);
}
lines.push("}");
return lines.join("\n");
}
__name(serializeRoute, "serializeRoute");
function serializeOperation(lines, op, depth, ctx) {
const indent = INDENT.repeat(depth);
const modStr = serializeModifiers(op.modifiers);
const comment = ctx.includeComments && op.description ? ` # ${op.description}` : "";
lines.push(`${indent}${op.method}${modStr}: {${comment}`);
const inner = INDENT.repeat(depth + 1);
if (op.service) {
lines.push(`${inner}service: ${op.service}`);
}
if (op.sdk) {
lines.push(`${inner}sdk: ${op.sdk}`);
}
if (op.signature) {
const sigComment = ctx.includeComments && op.signatureDescription ? ` # ${op.signatureDescription}` : "";
lines.push(`${inner}signature: ${op.signature}${sigComment}`);
}
if (op.security !== void 0) {
serializeSecurityBlock(lines, op.security, depth + 1, ctx);
}
if (op.query) {
serializeParamSource(lines, "query", op.query, op.queryMode, depth + 1, ctx);
}
if (op.headers) {
serializeParamSource(lines, "headers", op.headers, op.headersMode, depth + 1, ctx);
}
if (op.request) {
serializeRequest(lines, op.request, depth + 1);
}
if (op.responses.length > 0) {
serializeResponses(lines, op.responses, depth + 1);
}
lines.push(`${indent}}`);
return lines;
}
__name(serializeOperation, "serializeOperation");
function serializeModifiers(modifiers) {
if (!modifiers || modifiers.length === 0) return "";
return `(${modifiers.join(", ")})`;
}
__name(serializeModifiers, "serializeModifiers");
function serializeParamSource(lines, keyword, source, mode, depth, ctx) {
const indent = INDENT.repeat(depth);
if (source.kind === "ref") {
lines.push(`${indent}${keyword}: ${source.name}`);
return;
}
if (source.kind === "type") {
lines.push(`${indent}${keyword}: ${serializeType(source.node)}`);
return;
}
const modeStr = mode ? `mode(${mode}) ` : "";
lines.push(`${indent}${keyword}: ${modeStr}{`);
for (const param of source.nodes) {
const optional = param.optional ? "?" : "";
let typeStr = serializeType(param.type);
if (param.nullable && !typeContainsNull(param.type)) {
typeStr = `${typeStr} | null`;
}
const defaultVal = param.default !== void 0 ? ` = ${serializeDefault(param.default)}` : "";
const comment = ctx.includeComments && param.description ? ` # ${param.description}` : "";
lines.push(`${INDENT.repeat(depth + 1)}${param.name}${optional}: ${typeStr}${defaultVal}${comment}`);
}
lines.push(`${indent}}`);
}
__name(serializeParamSource, "serializeParamSource");
function serializeRequest(lines, request, depth) {
const indent = INDENT.repeat(depth);
lines.push(`${indent}request: {`);
for (const body of request.bodies) {
lines.push(`${INDENT.repeat(depth + 1)}${body.contentType}: ${serializeType(body.bodyType)}`);
}
lines.push(`${indent}}`);
}
__name(serializeRequest, "serializeRequest");
function serializeResponses(lines, responses, depth) {
const indent = INDENT.repeat(depth);
lines.push(`${indent}response: {`);
for (const resp of responses) {
const hasBody = resp.bodyType && resp.contentType;
const hasHeaders = resp.headers && resp.headers.length > 0;
if (hasBody || hasHeaders) {
lines.push(`${INDENT.repeat(depth + 1)}${resp.statusCode}: {`);
if (hasBody) {
lines.push(`${INDENT.repeat(depth + 2)}${resp.contentType}: ${serializeType(resp.bodyType)}`);
}
if (hasHeaders) {
lines.push(`${INDENT.repeat(depth + 2)}headers: {`);
for (const h of resp.headers) {
const opt = h.optional ? "?" : "";
const trail = h.description ? ` # ${h.description}` : "";
lines.push(`${INDENT.repeat(depth + 3)}${h.name}${opt}: ${serializeType(h.type)}${trail}`);
}
lines.push(`${INDENT.repeat(depth + 2)}}`);
}
lines.push(`${INDENT.repeat(depth + 1)}}`);
} else {
lines.push(`${INDENT.repeat(depth + 1)}${resp.statusCode}:`);
}
}
lines.push(`${indent}}`);
}
__name(serializeResponses, "serializeResponses");
function serializeSecurityBlock(lines, security, depth, ctx) {
const indent = INDENT.repeat(depth);
if (security === "none") {
lines.push(`${indent}security: none`);
return;
}
const sec = security;
if (sec.requireMfa !== void 0) {
const comment = ctx.includeComments && sec.requireMfaDescription ? ` # ${sec.requireMfaDescription}` : "";
lines.push(`${indent}security: {`);
lines.push(`${INDENT.repeat(depth + 1)}requireMfa: ${sec.requireMfa}${comment}`);
lines.push(`${indent}}`);
} else {
lines.push(`${indent}security: {}`);
}
}
__name(serializeSecurityBlock, "serializeSecurityBlock");
// src/convert.ts
import { readFileSync } from "fs";
import { parse as parseYaml } from "yaml";
// src/warnings.ts
var WarningCollector = class {
static {
__name(this, "WarningCollector");
}
warnings = [];
onWarning;
constructor(onWarning) {
this.onWarning = onWarning;
}
warn(path, message) {
this.add({
path,
message,
severity: "warn"
});
}
info(path, message) {
this.add({
path,
message,
severity: "info"
});
}
add(warning) {
this.warnings.push(warning);
this.onWarning?.(warning);
}
};
// src/convert.ts
async function convertOpenApiToCk(options) {
const { split = "by-tag", includeComments = true } = options;
const warnings = new WarningCollector(options.onWarning);
const rawDoc = await parseInput(options.input);
const doc = normalize(rawDoc, warnings);
const schemas = sanitizeSchemaNames(doc, warnings);
const circularRefs = detectCircularRefs(schemas);
const extractedModels = [];
const schemaCtx = {
circularRefs,
warnings,
path: "#/components/schemas",
includeComments,
namedSchemas: schemas,
extractedModels,
inlineCounter: 0
};
const models = schemasToModels(schemas, schemaCtx);
const { routes, routeTags } = pathsToRoutes(doc, {
circularRefs,
warnings,
includeComments,
namedSchemas: schemas,
extractedModels,
globalSecurity: doc.security
});
const files = /* @__PURE__ */ new Map();
if (split === "by-tag") {
const ckRoots = splitByTag(models, routes, routeTags);
for (const [filename, root] of ckRoots) {
files.set(filename, astToCk(root, {
includeComments
}));
}
} else {
const root = mergeIntoSingle(models, routes);
files.set("api.ck", astToCk(root, {
includeComments
}));
}
return {
files,
warnings: warnings.warnings
};
}
__name(convertOpenApiToCk, "convertOpenApiToCk");
async function parseInput(input) {
if (typeof input === "object") {
return input;
}
try {
const content = readFileSync(input, "utf-8");
return parseJsonOrYaml(content);
} catch {
return parseJsonOrYaml(input);
}
}
__name(parseInput, "parseInput");
function parseJsonOrYaml(content) {
try {
return JSON.parse(content);
} catch {
return parseYaml(content);
}
}
__name(parseJsonOrYaml, "parseJsonOrYaml");
function sanitizeSchemaNames(doc, warnings) {
const original = doc.components?.schemas ?? {};
const sanitized = {};
const nameMap = /* @__PURE__ */ new Map();
for (const name of Object.keys(original)) {
const clean = sanitizeName(name, warnings);
if (sanitized[clean]) {
warnings.warn(`#/components/schemas/${name}`, `Name collision after sanitization: "${name}" and another schema both map to "${clean}"`);
let i = 2;
while (sanitized[`${clean}${i}`]) i++;
nameMap.set(name, `${clean}${i}`);
sanitized[`${clean}${i}`] = original[name];
} else {
nameMap.set(name, clean);
sanitized[clean] = original[name];
}
}
if (nameMap.size > 0) {
updateRefs(doc, nameMap);
}
return sanitized;
}
__name(sanitizeSchemaNames, "sanitizeSchemaNames");
function updateRefs(obj, nameMap) {
if (!obj || typeof obj !== "object") return;
if (Array.isArray(obj)) {
for (const item of obj) updateRefs(item, nameMap);
return;
}
const record = obj;
if (typeof record.$ref === "string") {
const match = record.$ref.match(/^#\/components\/schemas\/(.+)$/);
if (match?.[1] && nameMap.has(match[1])) {
record.$ref = `#/components/schemas/${nameMap.get(match[1])}`;
}
}
for (const value of Object.values(record)) {
updateRefs(value, nameMap);
}
}
__name(updateRefs, "updateRefs");
export {
__name,
normalize,
detectCircularRefs,
extractRefName,
schemasToModels,
schemaToTypeNode,
sanitizeName,
pathsToRoutes,
splitByTag,
mergeIntoSingle,
astToCk,
serializeType,
convertOpenApiToCk
};
//# sourceMappingURL=chunk-T2G2WRZ2.js.map

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display