tina4-nodejs 3.13.132 → 3.13.133
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +3 -2
- package/package.json +1 -1
- package/packages/cli/dist/bin.js +151 -117
- package/packages/core/dist/index.js +151 -117
- package/packages/orm/dist/index.js +151 -117
- package/packages/swagger/dist/index.js +150 -116
- package/packages/swagger/src/generator.ts +261 -154
|
@@ -37614,12 +37614,46 @@ function sanitizeSecurity(reqs, schemes) {
|
|
|
37614
37614
|
});
|
|
37615
37615
|
}
|
|
37616
37616
|
function generate2(routes, models = []) {
|
|
37617
|
+
const schemes = resolveSecuritySchemes();
|
|
37618
|
+
const spec = {
|
|
37619
|
+
openapi: resolveOpenApiVersion(),
|
|
37620
|
+
info: buildInfo(),
|
|
37621
|
+
servers: resolveServers(),
|
|
37622
|
+
paths: {},
|
|
37623
|
+
components: {
|
|
37624
|
+
schemas: {},
|
|
37625
|
+
// Configurable security schemes (v3.13.42): bearerFormat via env, optional
|
|
37626
|
+
// apiKey scheme, plus any programmatically-registered schemes (which may
|
|
37627
|
+
// override bearerAuth — e.g. an oauth2 scheme with scopes).
|
|
37628
|
+
securitySchemes: schemes
|
|
37629
|
+
}
|
|
37630
|
+
};
|
|
37631
|
+
const tableToSchema = /* @__PURE__ */ new Map();
|
|
37632
|
+
buildComponentSchemas(models, spec, tableToSchema);
|
|
37633
|
+
const ctx = {
|
|
37634
|
+
models,
|
|
37635
|
+
tableToSchema,
|
|
37636
|
+
schemes,
|
|
37637
|
+
// Default scheme secured routes use when no explicit meta.security is set.
|
|
37638
|
+
defaultScheme: process.env.TINA4_SWAGGER_DEFAULT_SCHEME ?? "bearerAuth",
|
|
37639
|
+
// Path filters (comma-separated raw-path prefixes).
|
|
37640
|
+
includePrefixes: csv(process.env.TINA4_SWAGGER_INCLUDE),
|
|
37641
|
+
excludePrefixes: csv(process.env.TINA4_SWAGGER_EXCLUDE),
|
|
37642
|
+
// Reusable custom schemas referenced by routes via meta.requestSchema/responseSchemas.
|
|
37643
|
+
refSchemas: /* @__PURE__ */ new Set(),
|
|
37644
|
+
usedTags: [],
|
|
37645
|
+
seenIds: /* @__PURE__ */ new Set()
|
|
37646
|
+
};
|
|
37647
|
+
for (const route of routes) {
|
|
37648
|
+
buildOperation(route, spec, ctx);
|
|
37649
|
+
}
|
|
37650
|
+
buildRefSchemas(spec, ctx.refSchemas);
|
|
37651
|
+
buildTags(spec, ctx.usedTags);
|
|
37652
|
+
return spec;
|
|
37653
|
+
}
|
|
37654
|
+
function buildInfo() {
|
|
37617
37655
|
const info = {
|
|
37618
37656
|
title: process.env.TINA4_SWAGGER_TITLE ?? "Tina4 API",
|
|
37619
|
-
// The app's version, defaulting to 1.0.0 — NOT the framework's (Node shipped
|
|
37620
|
-
// 0.0.1). description defaults to the empty string, not a canned sentence.
|
|
37621
|
-
// Both are the settled cross-framework defaults (parity with the Python
|
|
37622
|
-
// master); TINA4_SWAGGER_VERSION / _DESCRIPTION still override.
|
|
37623
37657
|
version: process.env.TINA4_SWAGGER_VERSION ?? "1.0.0",
|
|
37624
37658
|
description: process.env.TINA4_SWAGGER_DESCRIPTION ?? ""
|
|
37625
37659
|
};
|
|
@@ -37636,135 +37670,134 @@ function generate2(routes, models = []) {
|
|
|
37636
37670
|
const [name, url] = licenseRaw.split("|").map((s) => s.trim());
|
|
37637
37671
|
info.license = url ? { name, url } : { name };
|
|
37638
37672
|
}
|
|
37639
|
-
|
|
37640
|
-
|
|
37641
|
-
|
|
37642
|
-
info,
|
|
37643
|
-
servers: resolveServers(),
|
|
37644
|
-
paths: {},
|
|
37645
|
-
components: {
|
|
37646
|
-
schemas: {},
|
|
37647
|
-
// Configurable security schemes (v3.13.42): bearerFormat via env, optional
|
|
37648
|
-
// apiKey scheme, plus any programmatically-registered schemes (which may
|
|
37649
|
-
// override bearerAuth — e.g. an oauth2 scheme with scopes).
|
|
37650
|
-
securitySchemes: schemes
|
|
37651
|
-
}
|
|
37652
|
-
};
|
|
37653
|
-
const defaultScheme = process.env.TINA4_SWAGGER_DEFAULT_SCHEME ?? "bearerAuth";
|
|
37654
|
-
const includePrefixes = csv(process.env.TINA4_SWAGGER_INCLUDE);
|
|
37655
|
-
const excludePrefixes = csv(process.env.TINA4_SWAGGER_EXCLUDE);
|
|
37656
|
-
const refSchemas = /* @__PURE__ */ new Set();
|
|
37657
|
-
const tableToSchema = /* @__PURE__ */ new Map();
|
|
37673
|
+
return info;
|
|
37674
|
+
}
|
|
37675
|
+
function buildComponentSchemas(models, spec, tableToSchema) {
|
|
37658
37676
|
for (const model of models) {
|
|
37659
37677
|
const schemaKey = schemaNameForModel(model);
|
|
37660
37678
|
tableToSchema.set(model.tableName, schemaKey);
|
|
37661
37679
|
spec.components.schemas[schemaKey] = modelToSchema(model);
|
|
37662
37680
|
}
|
|
37663
|
-
|
|
37664
|
-
|
|
37665
|
-
|
|
37666
|
-
|
|
37667
|
-
|
|
37668
|
-
|
|
37669
|
-
|
|
37670
|
-
|
|
37671
|
-
|
|
37672
|
-
|
|
37673
|
-
|
|
37674
|
-
|
|
37675
|
-
|
|
37676
|
-
|
|
37677
|
-
|
|
37678
|
-
|
|
37679
|
-
|
|
37680
|
-
|
|
37681
|
-
"200": { description: "Successful response" }
|
|
37682
|
-
}
|
|
37683
|
-
};
|
|
37684
|
-
if (route.meta?.description) operation.description = route.meta.description;
|
|
37685
|
-
if (route.meta?.deprecated) operation.deprecated = true;
|
|
37686
|
-
const pathParams = extractPathParams(route.pattern);
|
|
37687
|
-
if (pathParams.length > 0) {
|
|
37688
|
-
operation.parameters = pathParams.map(({ name, schema }) => ({
|
|
37689
|
-
name,
|
|
37690
|
-
in: "path",
|
|
37691
|
-
required: true,
|
|
37692
|
-
schema
|
|
37693
|
-
}));
|
|
37681
|
+
}
|
|
37682
|
+
function buildOperation(route, spec, ctx) {
|
|
37683
|
+
if (!isIncludedPath(route.pattern, ctx.includePrefixes, ctx.excludePrefixes)) return;
|
|
37684
|
+
const openApiPath = patternToOpenAPI(route.pattern);
|
|
37685
|
+
const method = route.method.toLowerCase();
|
|
37686
|
+
if (!spec.paths[openApiPath]) {
|
|
37687
|
+
spec.paths[openApiPath] = {};
|
|
37688
|
+
}
|
|
37689
|
+
const tags = route.meta?.tags ?? inferTags(route.pattern);
|
|
37690
|
+
for (const t of tags) {
|
|
37691
|
+
if (!ctx.usedTags.includes(t)) ctx.usedTags.push(t);
|
|
37692
|
+
}
|
|
37693
|
+
const operation = {
|
|
37694
|
+
operationId: uniqueOperationId(method, openApiPath, ctx.seenIds),
|
|
37695
|
+
summary: route.meta?.summary ?? `${route.method} ${route.pattern}`,
|
|
37696
|
+
tags,
|
|
37697
|
+
responses: route.meta?.responses ?? {
|
|
37698
|
+
"200": { description: "Successful response" }
|
|
37694
37699
|
}
|
|
37695
|
-
|
|
37696
|
-
|
|
37697
|
-
|
|
37698
|
-
|
|
37699
|
-
|
|
37700
|
-
|
|
37701
|
-
|
|
37702
|
-
|
|
37703
|
-
|
|
37704
|
-
|
|
37700
|
+
};
|
|
37701
|
+
if (route.meta?.description) operation.description = route.meta.description;
|
|
37702
|
+
if (route.meta?.deprecated) operation.deprecated = true;
|
|
37703
|
+
const parameters = operationParameters(route, method, ctx.models);
|
|
37704
|
+
if (parameters.length > 0) operation.parameters = parameters;
|
|
37705
|
+
operationRequestBody(route, method, operation, ctx);
|
|
37706
|
+
operationResponseSchemas(route, operation, ctx.refSchemas);
|
|
37707
|
+
operationSecurity(route, method, operation, ctx.schemes, ctx.defaultScheme);
|
|
37708
|
+
spec.paths[openApiPath][method] = operation;
|
|
37709
|
+
}
|
|
37710
|
+
function operationParameters(route, method, models) {
|
|
37711
|
+
let parameters = [];
|
|
37712
|
+
const pathParams = extractPathParams(route.pattern);
|
|
37713
|
+
if (pathParams.length > 0) {
|
|
37714
|
+
parameters = pathParams.map(({ name, schema }) => ({
|
|
37715
|
+
name,
|
|
37716
|
+
in: "path",
|
|
37717
|
+
required: true,
|
|
37718
|
+
schema
|
|
37719
|
+
}));
|
|
37720
|
+
}
|
|
37721
|
+
if (method === "get" && !route.pattern.includes("[id]") && !route.pattern.includes("[...")) {
|
|
37722
|
+
const modelName = inferModelFromPath(route.pattern);
|
|
37723
|
+
if (modelName && models.some((m) => m.tableName === modelName)) {
|
|
37724
|
+
parameters = [
|
|
37725
|
+
...parameters,
|
|
37726
|
+
{ name: "page", in: "query", schema: { type: "integer", default: 1 } },
|
|
37727
|
+
{ name: "limit", in: "query", schema: { type: "integer", default: 20 } },
|
|
37728
|
+
{ name: "sort", in: "query", schema: { type: "string" }, description: "Sort fields (prefix with - for descending)" }
|
|
37729
|
+
];
|
|
37705
37730
|
}
|
|
37706
|
-
|
|
37707
|
-
|
|
37708
|
-
|
|
37709
|
-
|
|
37710
|
-
|
|
37711
|
-
|
|
37712
|
-
|
|
37731
|
+
}
|
|
37732
|
+
return parameters;
|
|
37733
|
+
}
|
|
37734
|
+
function mediaWithExample(schema, example) {
|
|
37735
|
+
const media = { schema };
|
|
37736
|
+
if (example !== void 0) media.example = example;
|
|
37737
|
+
return media;
|
|
37738
|
+
}
|
|
37739
|
+
function operationRequestBody(route, method, operation, ctx) {
|
|
37740
|
+
const reqSchemaRef = parseRequestSchema(route.meta?.requestSchema);
|
|
37741
|
+
if (reqSchemaRef && (method === "post" || method === "put" || method === "patch")) {
|
|
37742
|
+
ctx.refSchemas.add(reqSchemaRef.name);
|
|
37743
|
+
const media = mediaWithExample({ $ref: `#/components/schemas/${reqSchemaRef.name}` }, route.meta?.example);
|
|
37744
|
+
operation.requestBody = {
|
|
37745
|
+
content: { [reqSchemaRef.contentType]: media }
|
|
37746
|
+
};
|
|
37747
|
+
} else if (method === "post" || method === "put") {
|
|
37748
|
+
const modelName = inferModelFromPath(route.pattern);
|
|
37749
|
+
const schemaKey = modelName ? ctx.tableToSchema.get(modelName) : void 0;
|
|
37750
|
+
if (schemaKey) {
|
|
37751
|
+
const sref = `#/components/schemas/${schemaKey}`;
|
|
37713
37752
|
operation.requestBody = {
|
|
37714
|
-
|
|
37753
|
+
required: true,
|
|
37754
|
+
content: { "application/json": mediaWithExample({ $ref: sref }, route.meta?.example) }
|
|
37715
37755
|
};
|
|
37716
|
-
|
|
37717
|
-
|
|
37718
|
-
|
|
37719
|
-
if (schemaKey) {
|
|
37720
|
-
const sref = `#/components/schemas/${schemaKey}`;
|
|
37721
|
-
const media = { schema: { $ref: sref } };
|
|
37722
|
-
if (route.meta?.example !== void 0) media.example = route.meta.example;
|
|
37723
|
-
operation.requestBody = {
|
|
37724
|
-
required: true,
|
|
37725
|
-
content: { "application/json": media }
|
|
37726
|
-
};
|
|
37727
|
-
if (route.meta?.responses === void 0) {
|
|
37728
|
-
operation.responses = {
|
|
37729
|
-
"200": { description: "Successful response", content: { "application/json": { schema: { $ref: sref } } } }
|
|
37730
|
-
};
|
|
37731
|
-
}
|
|
37732
|
-
} else if (route.meta?.example !== void 0) {
|
|
37733
|
-
operation.requestBody = {
|
|
37734
|
-
content: { "application/json": { schema: inferSchema(route.meta.example), example: route.meta.example } }
|
|
37756
|
+
if (route.meta?.responses === void 0) {
|
|
37757
|
+
operation.responses = {
|
|
37758
|
+
"200": { description: "Successful response", content: { "application/json": { schema: { $ref: sref } } } }
|
|
37735
37759
|
};
|
|
37736
37760
|
}
|
|
37761
|
+
} else if (route.meta?.example !== void 0) {
|
|
37762
|
+
operation.requestBody = {
|
|
37763
|
+
content: { "application/json": { schema: inferSchema(route.meta.example), example: route.meta.example } }
|
|
37764
|
+
};
|
|
37737
37765
|
}
|
|
37738
|
-
|
|
37739
|
-
|
|
37740
|
-
|
|
37741
|
-
|
|
37742
|
-
|
|
37743
|
-
|
|
37744
|
-
|
|
37745
|
-
|
|
37746
|
-
|
|
37747
|
-
|
|
37748
|
-
|
|
37749
|
-
|
|
37766
|
+
}
|
|
37767
|
+
}
|
|
37768
|
+
function operationResponseSchemas(route, operation, refSchemas) {
|
|
37769
|
+
const respSchemas = parseResponseSchemas(route.meta?.responseSchemas);
|
|
37770
|
+
if (respSchemas.length > 0) {
|
|
37771
|
+
const responses = operation.responses;
|
|
37772
|
+
for (const { status: status2, name, isList } of respSchemas) {
|
|
37773
|
+
refSchemas.add(name);
|
|
37774
|
+
const sref = `#/components/schemas/${name}`;
|
|
37775
|
+
const schema = isList ? { type: "array", items: { $ref: sref } } : { $ref: sref };
|
|
37776
|
+
responses[status2] = {
|
|
37777
|
+
description: status2.startsWith("2") ? "Successful response" : "Response",
|
|
37778
|
+
content: { "application/json": { schema } }
|
|
37779
|
+
};
|
|
37750
37780
|
}
|
|
37751
|
-
|
|
37752
|
-
|
|
37753
|
-
|
|
37754
|
-
|
|
37755
|
-
|
|
37756
|
-
|
|
37757
|
-
|
|
37758
|
-
|
|
37759
|
-
} else if (routeRequiresAuth(route, method)) {
|
|
37760
|
-
const requirements = [{ [defaultScheme]: [] }];
|
|
37761
|
-
if (defaultScheme === "bearerAuth" && schemes.ssoSession) requirements.push({ ssoSession: [] });
|
|
37762
|
-
operation.security = sanitizeSecurity(requirements, schemes);
|
|
37781
|
+
}
|
|
37782
|
+
}
|
|
37783
|
+
function operationSecurity(route, method, operation, schemes, defaultScheme) {
|
|
37784
|
+
const hasExplicitSecurity = route.meta?.security !== void 0 || route.meta?.scopes !== void 0 && route.meta.scopes.length > 0;
|
|
37785
|
+
if (hasExplicitSecurity) {
|
|
37786
|
+
const normalized = normalizeSecurity(route.meta?.security, route.meta?.scopes);
|
|
37787
|
+
operation.security = normalized.length > 0 ? sanitizeSecurity(normalized, schemes) : [];
|
|
37788
|
+
if (normalized.length > 0) {
|
|
37763
37789
|
const responses = operation.responses;
|
|
37764
37790
|
if (!responses["401"]) responses["401"] = { description: "Unauthorized" };
|
|
37765
37791
|
}
|
|
37766
|
-
|
|
37792
|
+
} else if (routeRequiresAuth(route, method)) {
|
|
37793
|
+
const requirements = [{ [defaultScheme]: [] }];
|
|
37794
|
+
if (defaultScheme === "bearerAuth" && schemes.ssoSession) requirements.push({ ssoSession: [] });
|
|
37795
|
+
operation.security = sanitizeSecurity(requirements, schemes);
|
|
37796
|
+
const responses = operation.responses;
|
|
37797
|
+
if (!responses["401"]) responses["401"] = { description: "Unauthorized" };
|
|
37767
37798
|
}
|
|
37799
|
+
}
|
|
37800
|
+
function buildRefSchemas(spec, refSchemas) {
|
|
37768
37801
|
if (refSchemas.size > 0) {
|
|
37769
37802
|
const schemas = spec.components.schemas;
|
|
37770
37803
|
for (const name of refSchemas) {
|
|
@@ -37773,10 +37806,11 @@ function generate2(routes, models = []) {
|
|
|
37773
37806
|
}
|
|
37774
37807
|
}
|
|
37775
37808
|
}
|
|
37809
|
+
}
|
|
37810
|
+
function buildTags(spec, usedTags) {
|
|
37776
37811
|
if (usedTags.length > 0) {
|
|
37777
37812
|
spec.tags = usedTags.map((name) => ({ name }));
|
|
37778
37813
|
}
|
|
37779
|
-
return spec;
|
|
37780
37814
|
}
|
|
37781
37815
|
function routeRequiresAuth(route, method) {
|
|
37782
37816
|
if (route.noAuth) return false;
|
|
@@ -25888,12 +25888,46 @@ function sanitizeSecurity(reqs, schemes) {
|
|
|
25888
25888
|
});
|
|
25889
25889
|
}
|
|
25890
25890
|
function generate2(routes, models = []) {
|
|
25891
|
+
const schemes = resolveSecuritySchemes();
|
|
25892
|
+
const spec = {
|
|
25893
|
+
openapi: resolveOpenApiVersion(),
|
|
25894
|
+
info: buildInfo(),
|
|
25895
|
+
servers: resolveServers(),
|
|
25896
|
+
paths: {},
|
|
25897
|
+
components: {
|
|
25898
|
+
schemas: {},
|
|
25899
|
+
// Configurable security schemes (v3.13.42): bearerFormat via env, optional
|
|
25900
|
+
// apiKey scheme, plus any programmatically-registered schemes (which may
|
|
25901
|
+
// override bearerAuth — e.g. an oauth2 scheme with scopes).
|
|
25902
|
+
securitySchemes: schemes
|
|
25903
|
+
}
|
|
25904
|
+
};
|
|
25905
|
+
const tableToSchema = /* @__PURE__ */ new Map();
|
|
25906
|
+
buildComponentSchemas(models, spec, tableToSchema);
|
|
25907
|
+
const ctx = {
|
|
25908
|
+
models,
|
|
25909
|
+
tableToSchema,
|
|
25910
|
+
schemes,
|
|
25911
|
+
// Default scheme secured routes use when no explicit meta.security is set.
|
|
25912
|
+
defaultScheme: process.env.TINA4_SWAGGER_DEFAULT_SCHEME ?? "bearerAuth",
|
|
25913
|
+
// Path filters (comma-separated raw-path prefixes).
|
|
25914
|
+
includePrefixes: csv(process.env.TINA4_SWAGGER_INCLUDE),
|
|
25915
|
+
excludePrefixes: csv(process.env.TINA4_SWAGGER_EXCLUDE),
|
|
25916
|
+
// Reusable custom schemas referenced by routes via meta.requestSchema/responseSchemas.
|
|
25917
|
+
refSchemas: /* @__PURE__ */ new Set(),
|
|
25918
|
+
usedTags: [],
|
|
25919
|
+
seenIds: /* @__PURE__ */ new Set()
|
|
25920
|
+
};
|
|
25921
|
+
for (const route of routes) {
|
|
25922
|
+
buildOperation(route, spec, ctx);
|
|
25923
|
+
}
|
|
25924
|
+
buildRefSchemas(spec, ctx.refSchemas);
|
|
25925
|
+
buildTags(spec, ctx.usedTags);
|
|
25926
|
+
return spec;
|
|
25927
|
+
}
|
|
25928
|
+
function buildInfo() {
|
|
25891
25929
|
const info = {
|
|
25892
25930
|
title: process.env.TINA4_SWAGGER_TITLE ?? "Tina4 API",
|
|
25893
|
-
// The app's version, defaulting to 1.0.0 — NOT the framework's (Node shipped
|
|
25894
|
-
// 0.0.1). description defaults to the empty string, not a canned sentence.
|
|
25895
|
-
// Both are the settled cross-framework defaults (parity with the Python
|
|
25896
|
-
// master); TINA4_SWAGGER_VERSION / _DESCRIPTION still override.
|
|
25897
25931
|
version: process.env.TINA4_SWAGGER_VERSION ?? "1.0.0",
|
|
25898
25932
|
description: process.env.TINA4_SWAGGER_DESCRIPTION ?? ""
|
|
25899
25933
|
};
|
|
@@ -25910,135 +25944,134 @@ function generate2(routes, models = []) {
|
|
|
25910
25944
|
const [name, url] = licenseRaw.split("|").map((s) => s.trim());
|
|
25911
25945
|
info.license = url ? { name, url } : { name };
|
|
25912
25946
|
}
|
|
25913
|
-
|
|
25914
|
-
|
|
25915
|
-
|
|
25916
|
-
info,
|
|
25917
|
-
servers: resolveServers(),
|
|
25918
|
-
paths: {},
|
|
25919
|
-
components: {
|
|
25920
|
-
schemas: {},
|
|
25921
|
-
// Configurable security schemes (v3.13.42): bearerFormat via env, optional
|
|
25922
|
-
// apiKey scheme, plus any programmatically-registered schemes (which may
|
|
25923
|
-
// override bearerAuth — e.g. an oauth2 scheme with scopes).
|
|
25924
|
-
securitySchemes: schemes
|
|
25925
|
-
}
|
|
25926
|
-
};
|
|
25927
|
-
const defaultScheme = process.env.TINA4_SWAGGER_DEFAULT_SCHEME ?? "bearerAuth";
|
|
25928
|
-
const includePrefixes = csv(process.env.TINA4_SWAGGER_INCLUDE);
|
|
25929
|
-
const excludePrefixes = csv(process.env.TINA4_SWAGGER_EXCLUDE);
|
|
25930
|
-
const refSchemas = /* @__PURE__ */ new Set();
|
|
25931
|
-
const tableToSchema = /* @__PURE__ */ new Map();
|
|
25947
|
+
return info;
|
|
25948
|
+
}
|
|
25949
|
+
function buildComponentSchemas(models, spec, tableToSchema) {
|
|
25932
25950
|
for (const model of models) {
|
|
25933
25951
|
const schemaKey = schemaNameForModel(model);
|
|
25934
25952
|
tableToSchema.set(model.tableName, schemaKey);
|
|
25935
25953
|
spec.components.schemas[schemaKey] = modelToSchema(model);
|
|
25936
25954
|
}
|
|
25937
|
-
|
|
25938
|
-
|
|
25939
|
-
|
|
25940
|
-
|
|
25941
|
-
|
|
25942
|
-
|
|
25943
|
-
|
|
25944
|
-
|
|
25945
|
-
|
|
25946
|
-
|
|
25947
|
-
|
|
25948
|
-
|
|
25949
|
-
|
|
25950
|
-
|
|
25951
|
-
|
|
25952
|
-
|
|
25953
|
-
|
|
25954
|
-
|
|
25955
|
-
"200": { description: "Successful response" }
|
|
25956
|
-
}
|
|
25957
|
-
};
|
|
25958
|
-
if (route.meta?.description) operation.description = route.meta.description;
|
|
25959
|
-
if (route.meta?.deprecated) operation.deprecated = true;
|
|
25960
|
-
const pathParams = extractPathParams(route.pattern);
|
|
25961
|
-
if (pathParams.length > 0) {
|
|
25962
|
-
operation.parameters = pathParams.map(({ name, schema }) => ({
|
|
25963
|
-
name,
|
|
25964
|
-
in: "path",
|
|
25965
|
-
required: true,
|
|
25966
|
-
schema
|
|
25967
|
-
}));
|
|
25955
|
+
}
|
|
25956
|
+
function buildOperation(route, spec, ctx) {
|
|
25957
|
+
if (!isIncludedPath(route.pattern, ctx.includePrefixes, ctx.excludePrefixes)) return;
|
|
25958
|
+
const openApiPath = patternToOpenAPI(route.pattern);
|
|
25959
|
+
const method = route.method.toLowerCase();
|
|
25960
|
+
if (!spec.paths[openApiPath]) {
|
|
25961
|
+
spec.paths[openApiPath] = {};
|
|
25962
|
+
}
|
|
25963
|
+
const tags = route.meta?.tags ?? inferTags(route.pattern);
|
|
25964
|
+
for (const t of tags) {
|
|
25965
|
+
if (!ctx.usedTags.includes(t)) ctx.usedTags.push(t);
|
|
25966
|
+
}
|
|
25967
|
+
const operation = {
|
|
25968
|
+
operationId: uniqueOperationId(method, openApiPath, ctx.seenIds),
|
|
25969
|
+
summary: route.meta?.summary ?? `${route.method} ${route.pattern}`,
|
|
25970
|
+
tags,
|
|
25971
|
+
responses: route.meta?.responses ?? {
|
|
25972
|
+
"200": { description: "Successful response" }
|
|
25968
25973
|
}
|
|
25969
|
-
|
|
25970
|
-
|
|
25971
|
-
|
|
25972
|
-
|
|
25973
|
-
|
|
25974
|
-
|
|
25975
|
-
|
|
25976
|
-
|
|
25977
|
-
|
|
25978
|
-
|
|
25974
|
+
};
|
|
25975
|
+
if (route.meta?.description) operation.description = route.meta.description;
|
|
25976
|
+
if (route.meta?.deprecated) operation.deprecated = true;
|
|
25977
|
+
const parameters = operationParameters(route, method, ctx.models);
|
|
25978
|
+
if (parameters.length > 0) operation.parameters = parameters;
|
|
25979
|
+
operationRequestBody(route, method, operation, ctx);
|
|
25980
|
+
operationResponseSchemas(route, operation, ctx.refSchemas);
|
|
25981
|
+
operationSecurity(route, method, operation, ctx.schemes, ctx.defaultScheme);
|
|
25982
|
+
spec.paths[openApiPath][method] = operation;
|
|
25983
|
+
}
|
|
25984
|
+
function operationParameters(route, method, models) {
|
|
25985
|
+
let parameters = [];
|
|
25986
|
+
const pathParams = extractPathParams(route.pattern);
|
|
25987
|
+
if (pathParams.length > 0) {
|
|
25988
|
+
parameters = pathParams.map(({ name, schema }) => ({
|
|
25989
|
+
name,
|
|
25990
|
+
in: "path",
|
|
25991
|
+
required: true,
|
|
25992
|
+
schema
|
|
25993
|
+
}));
|
|
25994
|
+
}
|
|
25995
|
+
if (method === "get" && !route.pattern.includes("[id]") && !route.pattern.includes("[...")) {
|
|
25996
|
+
const modelName = inferModelFromPath(route.pattern);
|
|
25997
|
+
if (modelName && models.some((m) => m.tableName === modelName)) {
|
|
25998
|
+
parameters = [
|
|
25999
|
+
...parameters,
|
|
26000
|
+
{ name: "page", in: "query", schema: { type: "integer", default: 1 } },
|
|
26001
|
+
{ name: "limit", in: "query", schema: { type: "integer", default: 20 } },
|
|
26002
|
+
{ name: "sort", in: "query", schema: { type: "string" }, description: "Sort fields (prefix with - for descending)" }
|
|
26003
|
+
];
|
|
25979
26004
|
}
|
|
25980
|
-
|
|
25981
|
-
|
|
25982
|
-
|
|
25983
|
-
|
|
25984
|
-
|
|
25985
|
-
|
|
25986
|
-
|
|
26005
|
+
}
|
|
26006
|
+
return parameters;
|
|
26007
|
+
}
|
|
26008
|
+
function mediaWithExample(schema, example) {
|
|
26009
|
+
const media = { schema };
|
|
26010
|
+
if (example !== void 0) media.example = example;
|
|
26011
|
+
return media;
|
|
26012
|
+
}
|
|
26013
|
+
function operationRequestBody(route, method, operation, ctx) {
|
|
26014
|
+
const reqSchemaRef = parseRequestSchema(route.meta?.requestSchema);
|
|
26015
|
+
if (reqSchemaRef && (method === "post" || method === "put" || method === "patch")) {
|
|
26016
|
+
ctx.refSchemas.add(reqSchemaRef.name);
|
|
26017
|
+
const media = mediaWithExample({ $ref: `#/components/schemas/${reqSchemaRef.name}` }, route.meta?.example);
|
|
26018
|
+
operation.requestBody = {
|
|
26019
|
+
content: { [reqSchemaRef.contentType]: media }
|
|
26020
|
+
};
|
|
26021
|
+
} else if (method === "post" || method === "put") {
|
|
26022
|
+
const modelName = inferModelFromPath(route.pattern);
|
|
26023
|
+
const schemaKey = modelName ? ctx.tableToSchema.get(modelName) : void 0;
|
|
26024
|
+
if (schemaKey) {
|
|
26025
|
+
const sref = `#/components/schemas/${schemaKey}`;
|
|
25987
26026
|
operation.requestBody = {
|
|
25988
|
-
|
|
26027
|
+
required: true,
|
|
26028
|
+
content: { "application/json": mediaWithExample({ $ref: sref }, route.meta?.example) }
|
|
25989
26029
|
};
|
|
25990
|
-
|
|
25991
|
-
|
|
25992
|
-
|
|
25993
|
-
if (schemaKey) {
|
|
25994
|
-
const sref = `#/components/schemas/${schemaKey}`;
|
|
25995
|
-
const media = { schema: { $ref: sref } };
|
|
25996
|
-
if (route.meta?.example !== void 0) media.example = route.meta.example;
|
|
25997
|
-
operation.requestBody = {
|
|
25998
|
-
required: true,
|
|
25999
|
-
content: { "application/json": media }
|
|
26000
|
-
};
|
|
26001
|
-
if (route.meta?.responses === void 0) {
|
|
26002
|
-
operation.responses = {
|
|
26003
|
-
"200": { description: "Successful response", content: { "application/json": { schema: { $ref: sref } } } }
|
|
26004
|
-
};
|
|
26005
|
-
}
|
|
26006
|
-
} else if (route.meta?.example !== void 0) {
|
|
26007
|
-
operation.requestBody = {
|
|
26008
|
-
content: { "application/json": { schema: inferSchema(route.meta.example), example: route.meta.example } }
|
|
26030
|
+
if (route.meta?.responses === void 0) {
|
|
26031
|
+
operation.responses = {
|
|
26032
|
+
"200": { description: "Successful response", content: { "application/json": { schema: { $ref: sref } } } }
|
|
26009
26033
|
};
|
|
26010
26034
|
}
|
|
26035
|
+
} else if (route.meta?.example !== void 0) {
|
|
26036
|
+
operation.requestBody = {
|
|
26037
|
+
content: { "application/json": { schema: inferSchema(route.meta.example), example: route.meta.example } }
|
|
26038
|
+
};
|
|
26011
26039
|
}
|
|
26012
|
-
|
|
26013
|
-
|
|
26014
|
-
|
|
26015
|
-
|
|
26016
|
-
|
|
26017
|
-
|
|
26018
|
-
|
|
26019
|
-
|
|
26020
|
-
|
|
26021
|
-
|
|
26022
|
-
|
|
26023
|
-
|
|
26040
|
+
}
|
|
26041
|
+
}
|
|
26042
|
+
function operationResponseSchemas(route, operation, refSchemas) {
|
|
26043
|
+
const respSchemas = parseResponseSchemas(route.meta?.responseSchemas);
|
|
26044
|
+
if (respSchemas.length > 0) {
|
|
26045
|
+
const responses = operation.responses;
|
|
26046
|
+
for (const { status: status2, name, isList } of respSchemas) {
|
|
26047
|
+
refSchemas.add(name);
|
|
26048
|
+
const sref = `#/components/schemas/${name}`;
|
|
26049
|
+
const schema = isList ? { type: "array", items: { $ref: sref } } : { $ref: sref };
|
|
26050
|
+
responses[status2] = {
|
|
26051
|
+
description: status2.startsWith("2") ? "Successful response" : "Response",
|
|
26052
|
+
content: { "application/json": { schema } }
|
|
26053
|
+
};
|
|
26024
26054
|
}
|
|
26025
|
-
|
|
26026
|
-
|
|
26027
|
-
|
|
26028
|
-
|
|
26029
|
-
|
|
26030
|
-
|
|
26031
|
-
|
|
26032
|
-
|
|
26033
|
-
} else if (routeRequiresAuth(route, method)) {
|
|
26034
|
-
const requirements = [{ [defaultScheme]: [] }];
|
|
26035
|
-
if (defaultScheme === "bearerAuth" && schemes.ssoSession) requirements.push({ ssoSession: [] });
|
|
26036
|
-
operation.security = sanitizeSecurity(requirements, schemes);
|
|
26055
|
+
}
|
|
26056
|
+
}
|
|
26057
|
+
function operationSecurity(route, method, operation, schemes, defaultScheme) {
|
|
26058
|
+
const hasExplicitSecurity = route.meta?.security !== void 0 || route.meta?.scopes !== void 0 && route.meta.scopes.length > 0;
|
|
26059
|
+
if (hasExplicitSecurity) {
|
|
26060
|
+
const normalized = normalizeSecurity(route.meta?.security, route.meta?.scopes);
|
|
26061
|
+
operation.security = normalized.length > 0 ? sanitizeSecurity(normalized, schemes) : [];
|
|
26062
|
+
if (normalized.length > 0) {
|
|
26037
26063
|
const responses = operation.responses;
|
|
26038
26064
|
if (!responses["401"]) responses["401"] = { description: "Unauthorized" };
|
|
26039
26065
|
}
|
|
26040
|
-
|
|
26066
|
+
} else if (routeRequiresAuth(route, method)) {
|
|
26067
|
+
const requirements = [{ [defaultScheme]: [] }];
|
|
26068
|
+
if (defaultScheme === "bearerAuth" && schemes.ssoSession) requirements.push({ ssoSession: [] });
|
|
26069
|
+
operation.security = sanitizeSecurity(requirements, schemes);
|
|
26070
|
+
const responses = operation.responses;
|
|
26071
|
+
if (!responses["401"]) responses["401"] = { description: "Unauthorized" };
|
|
26041
26072
|
}
|
|
26073
|
+
}
|
|
26074
|
+
function buildRefSchemas(spec, refSchemas) {
|
|
26042
26075
|
if (refSchemas.size > 0) {
|
|
26043
26076
|
const schemas = spec.components.schemas;
|
|
26044
26077
|
for (const name of refSchemas) {
|
|
@@ -26047,10 +26080,11 @@ function generate2(routes, models = []) {
|
|
|
26047
26080
|
}
|
|
26048
26081
|
}
|
|
26049
26082
|
}
|
|
26083
|
+
}
|
|
26084
|
+
function buildTags(spec, usedTags) {
|
|
26050
26085
|
if (usedTags.length > 0) {
|
|
26051
26086
|
spec.tags = usedTags.map((name) => ({ name }));
|
|
26052
26087
|
}
|
|
26053
|
-
return spec;
|
|
26054
26088
|
}
|
|
26055
26089
|
function routeRequiresAuth(route, method) {
|
|
26056
26090
|
if (route.noAuth) return false;
|