vite-plugin-openapi-codegen 3.0.0 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.mjs CHANGED
@@ -647,15 +647,9 @@ function createClientRenderModel(model, useTypeAliases) {
647
647
  }))
648
648
  };
649
649
  }
650
- const ACCESS_POLICY_KINDS = new Set([
651
- "authenticated",
652
- "internal",
653
- "public",
654
- "role"
655
- ]);
656
- function createAccessPolicyEntries(operations) {
650
+ function createAccessPolicyEntries(operations, securitySchemes, topLevelSecurity) {
657
651
  return operations.flatMap((entry) => {
658
- const accessPolicy = readAccessPolicyExtension(entry);
652
+ const accessPolicy = readSecurityRequirement(entry, securitySchemes, topLevelSecurity);
659
653
  if (!accessPolicy) return [];
660
654
  return [{
661
655
  apiPath: entry.apiPath,
@@ -668,23 +662,25 @@ function createAccessPolicyEntries(operations) {
668
662
  }];
669
663
  });
670
664
  }
671
- function readAccessPolicyExtension(entry) {
672
- const accessPolicy = entry.operation["x-access"];
673
- if (accessPolicy == null) return null;
674
- if (!isRecord(accessPolicy)) throw new Error(`Operation "${entry.operationId}" has invalid x-access extension`);
675
- const kind = accessPolicy.kind;
676
- if (typeof kind !== "string" || !ACCESS_POLICY_KINDS.has(kind)) throw new Error(`Operation "${entry.operationId}" has invalid x-access kind`);
677
- const rolesValue = accessPolicy.roles;
678
- if (rolesValue == null) {
679
- if (kind === "role") throw new Error(`Operation "${entry.operationId}" role access requires x-access.roles`);
680
- return { kind };
665
+ function readSecurityRequirement(entry, securitySchemes, topLevelSecurity) {
666
+ const security = entry.operation.security ?? topLevelSecurity;
667
+ if (security === void 0) return null;
668
+ if (!Array.isArray(security)) throw new Error(`Operation "${entry.operationId}" has invalid security`);
669
+ if (security.length === 0) return { kind: "public" };
670
+ const requirement = security[0];
671
+ if (!isRecord(requirement)) throw new Error(`Operation "${entry.operationId}" has an invalid security requirement`);
672
+ for (const [schemeName, scopes] of Object.entries(requirement)) {
673
+ const scheme = securitySchemes?.[schemeName];
674
+ if (scheme?.type === "apiKey" && scheme.in === "header") return { kind: "internal" };
675
+ if (scheme?.type === "http" || scheme?.type === "apiKey" && scheme.in === "cookie") {
676
+ const roles = Array.isArray(scopes) ? scopes.filter((scope) => typeof scope === "string") : [];
677
+ return roles.length === 0 ? { kind: "authenticated" } : {
678
+ kind: "role",
679
+ roles
680
+ };
681
+ }
681
682
  }
682
- if (!Array.isArray(rolesValue) || rolesValue.some((role) => typeof role !== "string")) throw new Error(`Operation "${entry.operationId}" has invalid x-access.roles`);
683
- if (kind !== "role") throw new Error(`Operation "${entry.operationId}" non-role access must not include roles`);
684
- return {
685
- kind,
686
- roles: rolesValue
687
- };
683
+ throw new Error(`Operation "${entry.operationId}" has an unrecognized security scheme`);
688
684
  }
689
685
  function isRecord(value) {
690
686
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -710,7 +706,7 @@ function renderGeneratedArtifacts(spec, options, preCollectedOperations) {
710
706
  void: httpClient.voidFunction
711
707
  });
712
708
  if (clientModel.operations.length === 0) throw new Error(`No paths matching prefix "${pathPrefix}" found in openapi.json`);
713
- const accessPolicies = renderAccessPoliciesSource(createAccessPolicyEntries(operations), GENERATED_HEADER);
709
+ const accessPolicies = renderAccessPoliciesSource(createAccessPolicyEntries(operations, spec.components?.securitySchemes, spec.security), GENERATED_HEADER);
714
710
  return {
715
711
  api: renderApiSource(createApiEntries(clientModel.operations, useTypeAliases), GENERATED_HEADER),
716
712
  client: renderClientSource(createClientRenderModel(clientModel, useTypeAliases), GENERATED_HEADER, httpClient),
package/dist/index.d.mts CHANGED
@@ -26,10 +26,12 @@ interface OpenAPIResponse {
26
26
  content?: Record<string, OpenAPIContent>;
27
27
  description?: string;
28
28
  }
29
- type OpenAPIAccessKind = "authenticated" | "internal" | "public" | "role";
30
- interface OpenAPIAccessExtension {
31
- kind: OpenAPIAccessKind;
32
- roles?: string[];
29
+ type OpenAPISecurityRequirement = Record<string, string[]>;
30
+ interface OpenAPISecurityScheme {
31
+ type?: string;
32
+ scheme?: string;
33
+ in?: string;
34
+ name?: string;
33
35
  }
34
36
  interface OpenAPIOperation {
35
37
  operationId?: string;
@@ -37,7 +39,7 @@ interface OpenAPIOperation {
37
39
  requestBody?: OpenAPIRequestBody;
38
40
  responses?: Record<string, OpenAPIResponse>;
39
41
  tags?: string[];
40
- "x-access"?: OpenAPIAccessExtension;
42
+ security?: OpenAPISecurityRequirement[];
41
43
  }
42
44
  type OpenAPIPathItem = Partial<Record<HttpMethod, OpenAPIOperation>>;
43
45
  interface OpenAPISchema {
@@ -48,8 +50,10 @@ interface OpenAPISchema {
48
50
  }
49
51
  interface OpenAPISpec {
50
52
  paths?: Record<string, OpenAPIPathItem>;
53
+ security?: OpenAPISecurityRequirement[];
51
54
  components?: {
52
55
  schemas?: Record<string, OpenAPISchema>;
56
+ securitySchemes?: Record<string, OpenAPISecurityScheme>;
53
57
  };
54
58
  }
55
59
  interface OperationEntry {
package/dist/index.mjs CHANGED
@@ -645,15 +645,9 @@ function createClientRenderModel(model, useTypeAliases) {
645
645
  }))
646
646
  };
647
647
  }
648
- const ACCESS_POLICY_KINDS = new Set([
649
- "authenticated",
650
- "internal",
651
- "public",
652
- "role"
653
- ]);
654
- function createAccessPolicyEntries(operations) {
648
+ function createAccessPolicyEntries(operations, securitySchemes, topLevelSecurity) {
655
649
  return operations.flatMap((entry) => {
656
- const accessPolicy = readAccessPolicyExtension(entry);
650
+ const accessPolicy = readSecurityRequirement(entry, securitySchemes, topLevelSecurity);
657
651
  if (!accessPolicy) return [];
658
652
  return [{
659
653
  apiPath: entry.apiPath,
@@ -666,23 +660,25 @@ function createAccessPolicyEntries(operations) {
666
660
  }];
667
661
  });
668
662
  }
669
- function readAccessPolicyExtension(entry) {
670
- const accessPolicy = entry.operation["x-access"];
671
- if (accessPolicy == null) return null;
672
- if (!isRecord(accessPolicy)) throw new Error(`Operation "${entry.operationId}" has invalid x-access extension`);
673
- const kind = accessPolicy.kind;
674
- if (typeof kind !== "string" || !ACCESS_POLICY_KINDS.has(kind)) throw new Error(`Operation "${entry.operationId}" has invalid x-access kind`);
675
- const rolesValue = accessPolicy.roles;
676
- if (rolesValue == null) {
677
- if (kind === "role") throw new Error(`Operation "${entry.operationId}" role access requires x-access.roles`);
678
- return { kind };
663
+ function readSecurityRequirement(entry, securitySchemes, topLevelSecurity) {
664
+ const security = entry.operation.security ?? topLevelSecurity;
665
+ if (security === void 0) return null;
666
+ if (!Array.isArray(security)) throw new Error(`Operation "${entry.operationId}" has invalid security`);
667
+ if (security.length === 0) return { kind: "public" };
668
+ const requirement = security[0];
669
+ if (!isRecord(requirement)) throw new Error(`Operation "${entry.operationId}" has an invalid security requirement`);
670
+ for (const [schemeName, scopes] of Object.entries(requirement)) {
671
+ const scheme = securitySchemes?.[schemeName];
672
+ if (scheme?.type === "apiKey" && scheme.in === "header") return { kind: "internal" };
673
+ if (scheme?.type === "http" || scheme?.type === "apiKey" && scheme.in === "cookie") {
674
+ const roles = Array.isArray(scopes) ? scopes.filter((scope) => typeof scope === "string") : [];
675
+ return roles.length === 0 ? { kind: "authenticated" } : {
676
+ kind: "role",
677
+ roles
678
+ };
679
+ }
679
680
  }
680
- if (!Array.isArray(rolesValue) || rolesValue.some((role) => typeof role !== "string")) throw new Error(`Operation "${entry.operationId}" has invalid x-access.roles`);
681
- if (kind !== "role") throw new Error(`Operation "${entry.operationId}" non-role access must not include roles`);
682
- return {
683
- kind,
684
- roles: rolesValue
685
- };
681
+ throw new Error(`Operation "${entry.operationId}" has an unrecognized security scheme`);
686
682
  }
687
683
  function isRecord(value) {
688
684
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -708,7 +704,7 @@ function renderGeneratedArtifacts(spec, options, preCollectedOperations) {
708
704
  void: httpClient.voidFunction
709
705
  });
710
706
  if (clientModel.operations.length === 0) throw new Error(`No paths matching prefix "${pathPrefix}" found in openapi.json`);
711
- const accessPolicies = renderAccessPoliciesSource(createAccessPolicyEntries(operations), GENERATED_HEADER);
707
+ const accessPolicies = renderAccessPoliciesSource(createAccessPolicyEntries(operations, spec.components?.securitySchemes, spec.security), GENERATED_HEADER);
712
708
  return {
713
709
  api: renderApiSource(createApiEntries(clientModel.operations, useTypeAliases), GENERATED_HEADER),
714
710
  client: renderClientSource(createClientRenderModel(clientModel, useTypeAliases), GENERATED_HEADER, httpClient),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vite-plugin-openapi-codegen",
3
- "version": "3.0.0",
3
+ "version": "3.1.0",
4
4
  "description": "Vite plugin that generates typed API clients and route builders from OpenAPI specs",
5
5
  "keywords": [
6
6
  "api-client",