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.
@@ -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
- const schemes = resolveSecuritySchemes();
37640
- const spec = {
37641
- openapi: resolveOpenApiVersion(),
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
- const usedTags = [];
37664
- const seenIds = /* @__PURE__ */ new Set();
37665
- for (const route of routes) {
37666
- if (!isIncludedPath(route.pattern, includePrefixes, excludePrefixes)) continue;
37667
- const openApiPath = patternToOpenAPI(route.pattern);
37668
- const method = route.method.toLowerCase();
37669
- if (!spec.paths[openApiPath]) {
37670
- spec.paths[openApiPath] = {};
37671
- }
37672
- const tags = route.meta?.tags ?? inferTags(route.pattern);
37673
- for (const t of tags) {
37674
- if (!usedTags.includes(t)) usedTags.push(t);
37675
- }
37676
- const operation = {
37677
- operationId: uniqueOperationId(method, openApiPath, seenIds),
37678
- summary: route.meta?.summary ?? `${route.method} ${route.pattern}`,
37679
- tags,
37680
- responses: route.meta?.responses ?? {
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
- if (method === "get" && !route.pattern.includes("[id]") && !route.pattern.includes("[...")) {
37696
- const modelName = inferModelFromPath(route.pattern);
37697
- if (modelName && models.some((m) => m.tableName === modelName)) {
37698
- operation.parameters = [
37699
- ...operation.parameters ?? [],
37700
- { name: "page", in: "query", schema: { type: "integer", default: 1 } },
37701
- { name: "limit", in: "query", schema: { type: "integer", default: 20 } },
37702
- { name: "sort", in: "query", schema: { type: "string" }, description: "Sort fields (prefix with - for descending)" }
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
- const reqSchemaRef = parseRequestSchema(route.meta?.requestSchema);
37707
- if (reqSchemaRef && (method === "post" || method === "put" || method === "patch")) {
37708
- refSchemas.add(reqSchemaRef.name);
37709
- const media = {
37710
- schema: { $ref: `#/components/schemas/${reqSchemaRef.name}` }
37711
- };
37712
- if (route.meta?.example !== void 0) media.example = route.meta.example;
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
- content: { [reqSchemaRef.contentType]: media }
37753
+ required: true,
37754
+ content: { "application/json": mediaWithExample({ $ref: sref }, route.meta?.example) }
37715
37755
  };
37716
- } else if (method === "post" || method === "put") {
37717
- const modelName = inferModelFromPath(route.pattern);
37718
- const schemaKey = modelName ? tableToSchema.get(modelName) : void 0;
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
- const respSchemas = parseResponseSchemas(route.meta?.responseSchemas);
37739
- if (respSchemas.length > 0) {
37740
- const responses = operation.responses;
37741
- for (const { status: status2, name, isList } of respSchemas) {
37742
- refSchemas.add(name);
37743
- const sref = `#/components/schemas/${name}`;
37744
- const schema = isList ? { type: "array", items: { $ref: sref } } : { $ref: sref };
37745
- responses[status2] = {
37746
- description: status2.startsWith("2") ? "Successful response" : "Response",
37747
- content: { "application/json": { schema } }
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
- const hasExplicitSecurity = route.meta?.security !== void 0 || route.meta?.scopes !== void 0 && route.meta.scopes.length > 0;
37752
- if (hasExplicitSecurity) {
37753
- const normalized = normalizeSecurity(route.meta?.security, route.meta?.scopes);
37754
- operation.security = normalized.length > 0 ? sanitizeSecurity(normalized, schemes) : [];
37755
- if (normalized.length > 0) {
37756
- const responses = operation.responses;
37757
- if (!responses["401"]) responses["401"] = { description: "Unauthorized" };
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
- spec.paths[openApiPath][method] = operation;
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
- const schemes = resolveSecuritySchemes();
25914
- const spec = {
25915
- openapi: resolveOpenApiVersion(),
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
- const usedTags = [];
25938
- const seenIds = /* @__PURE__ */ new Set();
25939
- for (const route of routes) {
25940
- if (!isIncludedPath(route.pattern, includePrefixes, excludePrefixes)) continue;
25941
- const openApiPath = patternToOpenAPI(route.pattern);
25942
- const method = route.method.toLowerCase();
25943
- if (!spec.paths[openApiPath]) {
25944
- spec.paths[openApiPath] = {};
25945
- }
25946
- const tags = route.meta?.tags ?? inferTags(route.pattern);
25947
- for (const t of tags) {
25948
- if (!usedTags.includes(t)) usedTags.push(t);
25949
- }
25950
- const operation = {
25951
- operationId: uniqueOperationId(method, openApiPath, seenIds),
25952
- summary: route.meta?.summary ?? `${route.method} ${route.pattern}`,
25953
- tags,
25954
- responses: route.meta?.responses ?? {
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
- if (method === "get" && !route.pattern.includes("[id]") && !route.pattern.includes("[...")) {
25970
- const modelName = inferModelFromPath(route.pattern);
25971
- if (modelName && models.some((m) => m.tableName === modelName)) {
25972
- operation.parameters = [
25973
- ...operation.parameters ?? [],
25974
- { name: "page", in: "query", schema: { type: "integer", default: 1 } },
25975
- { name: "limit", in: "query", schema: { type: "integer", default: 20 } },
25976
- { name: "sort", in: "query", schema: { type: "string" }, description: "Sort fields (prefix with - for descending)" }
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
- const reqSchemaRef = parseRequestSchema(route.meta?.requestSchema);
25981
- if (reqSchemaRef && (method === "post" || method === "put" || method === "patch")) {
25982
- refSchemas.add(reqSchemaRef.name);
25983
- const media = {
25984
- schema: { $ref: `#/components/schemas/${reqSchemaRef.name}` }
25985
- };
25986
- if (route.meta?.example !== void 0) media.example = route.meta.example;
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
- content: { [reqSchemaRef.contentType]: media }
26027
+ required: true,
26028
+ content: { "application/json": mediaWithExample({ $ref: sref }, route.meta?.example) }
25989
26029
  };
25990
- } else if (method === "post" || method === "put") {
25991
- const modelName = inferModelFromPath(route.pattern);
25992
- const schemaKey = modelName ? tableToSchema.get(modelName) : void 0;
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
- const respSchemas = parseResponseSchemas(route.meta?.responseSchemas);
26013
- if (respSchemas.length > 0) {
26014
- const responses = operation.responses;
26015
- for (const { status: status2, name, isList } of respSchemas) {
26016
- refSchemas.add(name);
26017
- const sref = `#/components/schemas/${name}`;
26018
- const schema = isList ? { type: "array", items: { $ref: sref } } : { $ref: sref };
26019
- responses[status2] = {
26020
- description: status2.startsWith("2") ? "Successful response" : "Response",
26021
- content: { "application/json": { schema } }
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
- const hasExplicitSecurity = route.meta?.security !== void 0 || route.meta?.scopes !== void 0 && route.meta.scopes.length > 0;
26026
- if (hasExplicitSecurity) {
26027
- const normalized = normalizeSecurity(route.meta?.security, route.meta?.scopes);
26028
- operation.security = normalized.length > 0 ? sanitizeSecurity(normalized, schemes) : [];
26029
- if (normalized.length > 0) {
26030
- const responses = operation.responses;
26031
- if (!responses["401"]) responses["401"] = { description: "Unauthorized" };
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
- spec.paths[openApiPath][method] = operation;
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;