oapiex 0.2.2 → 0.3.1

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/index.mjs CHANGED
@@ -1110,11 +1110,18 @@ var TypeScriptModuleRenderer = class {
1110
1110
  "export interface OpenApiMediaTypeDefinition<TExample = unknown> {\n schema?: OpenApiSchemaDefinition\n example?: TExample\n}",
1111
1111
  "export interface OpenApiResponseDefinition<_TResponse = unknown, TExample = unknown> {\n description: string\n content?: Record<string, OpenApiMediaTypeDefinition<TExample>>\n}",
1112
1112
  "export interface OpenApiRequestBodyDefinition<TInput = unknown> {\n description?: string\n required: boolean\n content: Record<string, OpenApiMediaTypeDefinition<TInput>>\n}",
1113
- "export interface OpenApiOperationDefinition<_TResponse = unknown, TResponseExample = unknown, TInput = Record<string, never>, _TQuery = Record<string, never>, _THeader = Record<string, never>, _TParams = Record<string, never>> {\n summary?: string\n description?: string\n operationId?: string\n parameters?: OpenApiParameterDefinition[]\n requestBody?: OpenApiRequestBodyDefinition<TInput>\n responses: Record<string, OpenApiResponseDefinition<_TResponse, TResponseExample>>\n}",
1113
+ "export interface OpenApiOauthFlowDefinition {\n authorizationUrl?: string\n tokenUrl?: string\n refreshUrl?: string\n scopes?: Record<string, string>\n}",
1114
+ "export type OpenApiSecurityRequirementDefinition = Record<string, string[]>",
1115
+ "export type OpenApiSecuritySchemeDefinition =\n | {\n type: 'http'\n description?: string\n scheme: string\n bearerFormat?: string\n }\n | {\n type: 'apiKey'\n description?: string\n name: string\n in: 'query' | 'header' | 'cookie'\n }\n | {\n type: 'oauth2'\n description?: string\n flows?: Record<string, OpenApiOauthFlowDefinition>\n }\n | {\n type: 'openIdConnect'\n description?: string\n openIdConnectUrl: string\n }",
1116
+ "export interface OpenApiComponentsDefinition {\n securitySchemes?: Record<string, OpenApiSecuritySchemeDefinition>\n}",
1117
+ "export interface OpenApiOperationDefinition<_TResponse = unknown, TResponseExample = unknown, TInput = Record<string, never>, _TQuery = Record<string, never>, _THeader = Record<string, never>, _TParams = Record<string, never>> {\n summary?: string\n description?: string\n operationId?: string\n security?: OpenApiSecurityRequirementDefinition[]\n parameters?: OpenApiParameterDefinition[]\n requestBody?: OpenApiRequestBodyDefinition<TInput>\n responses: Record<string, OpenApiResponseDefinition<_TResponse, TResponseExample>>\n}",
1114
1118
  "export interface OpenApiSdkParameterManifest {\n name: string\n accessor: string\n in: 'query' | 'header' | 'path'\n required: boolean\n description?: string\n}",
1115
- "export interface OpenApiSdkOperationManifest {\n path: string\n method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'\n methodName: string\n summary?: string\n description?: string\n operationId?: string\n requestBodyDescription?: string\n responseDescription?: string\n responseType: string\n inputType: string\n queryType: string\n headerType: string\n paramsType: string\n hasBody: boolean\n bodyRequired: boolean\n pathParams: OpenApiSdkParameterManifest[]\n queryParams: OpenApiSdkParameterManifest[]\n headerParams: OpenApiSdkParameterManifest[]\n}",
1119
+ "export interface OpenApiSdkSecurityRequirementSchemeManifest {\n name: string\n scopes: string[]\n}",
1120
+ "export interface OpenApiSdkSecurityRequirementManifest {\n schemes: OpenApiSdkSecurityRequirementSchemeManifest[]\n}",
1121
+ "export interface OpenApiSdkSecuritySchemeManifest {\n name: string\n helperName: string\n description?: string\n type: 'http' | 'apiKey' | 'oauth2' | 'openIdConnect'\n authType: 'bearer' | 'basic' | 'apiKey' | 'oauth2'\n scheme?: string\n bearerFormat?: string\n in?: 'header' | 'query' | 'cookie'\n parameterName?: string\n openIdConnectUrl?: string\n scopes?: string[]\n}",
1122
+ "export interface OpenApiSdkOperationManifest {\n path: string\n method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'\n methodName: string\n summary?: string\n description?: string\n operationId?: string\n requestBodyDescription?: string\n responseDescription?: string\n responseType: string\n inputType: string\n queryType: string\n headerType: string\n paramsType: string\n hasBody: boolean\n bodyRequired: boolean\n pathParams: OpenApiSdkParameterManifest[]\n queryParams: OpenApiSdkParameterManifest[]\n headerParams: OpenApiSdkParameterManifest[]\n security?: OpenApiSdkSecurityRequirementManifest[]\n}",
1116
1123
  "export interface OpenApiSdkGroupManifest {\n className: string\n propertyName: string\n operations: OpenApiSdkOperationManifest[]\n}",
1117
- "export interface OpenApiSdkManifest {\n groups: OpenApiSdkGroupManifest[]\n}",
1124
+ "export interface OpenApiSdkManifest {\n groups: OpenApiSdkGroupManifest[]\n securitySchemes: OpenApiSdkSecuritySchemeManifest[]\n security?: OpenApiSdkSecurityRequirementManifest[]\n}",
1118
1125
  "export interface OpenApiRuntimeBundle<TApi = unknown> {\n document: unknown\n manifest: OpenApiSdkManifest\n __api?: TApi\n}",
1119
1126
  Object.entries(document.paths).map(([path, operations]) => {
1120
1127
  const pathTypeName = this.derivePathTypeName(path);
@@ -1132,7 +1139,7 @@ var TypeScriptModuleRenderer = class {
1132
1139
  }).join("\n\n"), `export interface ${pathTypeName} {\n${Object.keys(operations).map((method) => ` ${method}: ${this.deriveOperationInterfaceName(path, method)}`).join("\n")}\n}`].join("\n\n");
1133
1140
  }).join("\n\n"),
1134
1141
  `export interface Paths {\n${Object.keys(document.paths).map((path) => ` ${this.formatPropertyKey(path)}: ${this.derivePathTypeName(path)}`).join("\n")}\n}`,
1135
- `export interface ${rootTypeName} {\n openapi: '3.1.0'\n info: OpenApiInfo\n paths: Paths\n}`
1142
+ `export interface ${rootTypeName} {\n openapi: '3.1.0'\n info: OpenApiInfo\n components?: OpenApiComponentsDefinition\n security?: OpenApiSecurityRequirementDefinition[]\n paths: Paths\n}`
1136
1143
  ].join("\n\n");
1137
1144
  }
1138
1145
  renderSdkApiInterface(rootTypeName, manifest) {
@@ -1227,7 +1234,7 @@ var TypeScriptModuleRenderer = class {
1227
1234
  if (value.length === 0) return "[]";
1228
1235
  const nextIndent = this.indent(indentLevel + 1);
1229
1236
  const currentIndent = this.indent(indentLevel);
1230
- return `[\n${value.map((entry) => `${nextIndent}${this.renderLiteral(entry, indentLevel + 1)}`).join(",\n")}\n${currentIndent}]`;
1237
+ return `\n[`.startsWith("\n[") ? `[\n${value.map((entry) => `${nextIndent}${this.renderLiteral(entry, indentLevel + 1)}`).join(",\n")}\n${currentIndent}]` : "[]";
1231
1238
  }
1232
1239
  if (typeof value === "object") {
1233
1240
  const entries = Object.entries(value);
@@ -2010,9 +2017,22 @@ var TypeScriptShapeBuilder = class {
2010
2017
  var TypeScriptTypeBuilder = class {
2011
2018
  naming = new TypeScriptNamingSupport();
2012
2019
  shapes = new TypeScriptShapeBuilder(this.naming);
2020
+ /**
2021
+ * Creates a new generator context, which holds the state of the type generation process.
2022
+ *
2023
+ * @returns
2024
+ */
2013
2025
  createContext() {
2014
2026
  return this.shapes.createContext();
2015
2027
  }
2028
+ /**
2029
+ * Collects semantic models from the given OpenAPI document, which represent the various
2030
+ * shapes and types that need to be generated for the SDK, along with their associated
2031
+ * metadata such as paths, methods, and roles.
2032
+ *
2033
+ * @param document The OpenAPI document to extract semantic models from.
2034
+ * @returns An array of semantic models representing the shapes and types for the SDK.
2035
+ */
2016
2036
  collectSemanticModels(document) {
2017
2037
  const models = [];
2018
2038
  for (const [path, operations] of Object.entries(document.paths)) {
@@ -2075,9 +2095,20 @@ var TypeScriptTypeBuilder = class {
2075
2095
  }
2076
2096
  return models;
2077
2097
  }
2098
+ /**
2099
+ * Builds the SDK manifest from the given OpenAPI document, using the provided operation
2100
+ * type references and naming strategy options.
2101
+ *
2102
+ * @param document The OpenAPI document to build the SDK manifest from.
2103
+ * @param operationTypeRefs A map of operation type references.
2104
+ * @param options Naming strategy options for the SDK manifest.
2105
+ * @returns The generated SDK manifest.
2106
+ */
2078
2107
  buildSdkManifest(document, operationTypeRefs, options = {}) {
2079
2108
  const sdkGroupNamesBySignature = this.naming.deriveSdkGroupNamesBySignature(document, options.namespaceStrategy ?? "smart");
2080
2109
  const groups = /* @__PURE__ */ new Map();
2110
+ const securitySchemes = this.buildSecuritySchemes(document);
2111
+ const globalSecurity = this.buildSecurityRequirements(document.security);
2081
2112
  for (const [path, operations] of Object.entries(document.paths)) {
2082
2113
  const staticSignature = this.naming.getStaticPathSegments(path).join("/");
2083
2114
  const className = sdkGroupNamesBySignature.get(staticSignature) ?? "Resource";
@@ -2114,34 +2145,96 @@ var TypeScriptTypeBuilder = class {
2114
2145
  bodyRequired: operation.requestBody?.required ?? false,
2115
2146
  pathParams: this.naming.createSdkParameterManifest(operation.parameters, "path", path),
2116
2147
  queryParams: this.naming.createSdkParameterManifest(operation.parameters, "query", path),
2117
- headerParams: this.naming.createSdkParameterManifest(operation.parameters, "header", path)
2148
+ headerParams: this.naming.createSdkParameterManifest(operation.parameters, "header", path),
2149
+ security: this.buildSecurityRequirements(operation.security)
2118
2150
  });
2119
2151
  }
2120
2152
  groups.set(propertyName, group);
2121
2153
  }
2122
- return { groups: Array.from(groups.values()).map((group) => ({
2123
- ...group,
2124
- operations: this.naming.ensureUniqueSdkMethodNames(group.operations)
2125
- })).sort((left, right) => left.propertyName.localeCompare(right.propertyName)) };
2154
+ return {
2155
+ groups: Array.from(groups.values()).map((group) => ({
2156
+ ...group,
2157
+ operations: this.naming.ensureUniqueSdkMethodNames(group.operations)
2158
+ })).sort((left, right) => left.propertyName.localeCompare(right.propertyName)),
2159
+ securitySchemes,
2160
+ security: globalSecurity
2161
+ };
2126
2162
  }
2163
+ /**
2164
+ * Infers a shape node from a given example value, using the provided name hint for
2165
+ * type naming purposes.
2166
+ *
2167
+ * @param value The example value to infer the shape from.
2168
+ * @param nameHint A hint for naming the inferred type.
2169
+ * @returns The inferred shape node.
2170
+ */
2127
2171
  inferShapeFromExample(value, nameHint) {
2128
2172
  return this.shapes.inferShapeFromExample(value, nameHint);
2129
2173
  }
2174
+ /**
2175
+ * Sanitizes a given string to be used as a type name in TypeScript.
2176
+ *
2177
+ * @param value The string to sanitize.
2178
+ * @returns The sanitized type name.
2179
+ */
2130
2180
  sanitizeTypeName(value) {
2131
2181
  return this.naming.sanitizeTypeName(value);
2132
2182
  }
2183
+ /**
2184
+ * Registers a shape with a preferred name and returns the assigned type name, ensuring
2185
+ * that it is unique within the given context.
2186
+ *
2187
+ * @param shape The shape node to register.
2188
+ * @param preferredName The preferred name for the type.
2189
+ * @param context The generator context.
2190
+ * @param collisionSuffix The suffix to use in case of name collisions.
2191
+ * @returns The assigned type name.
2192
+ */
2133
2193
  registerNamedShape(shape, preferredName, context, collisionSuffix) {
2134
2194
  return this.shapes.registerNamedShape(shape, preferredName, context, collisionSuffix);
2135
2195
  }
2196
+ /**
2197
+ * Determines the top-level shape for a given shape and its semantic role, which can
2198
+ * be used for namespace grouping in the generated SDK.
2199
+ *
2200
+ * @param shape The shape node to evaluate.
2201
+ * @param role The semantic role of the shape.
2202
+ * @returns The top-level shape node for the given shape and role.
2203
+ */
2136
2204
  namespaceTopLevelShape(shape, role) {
2137
2205
  return this.shapes.namespaceTopLevelShape(shape, role);
2138
2206
  }
2207
+ /**
2208
+ * Registers the given object shape and returns the assigned type name.
2209
+ *
2210
+ * @param shape The object shape to register.
2211
+ * @param preferredName The preferred name for the type.
2212
+ * @param context The generator context.
2213
+ * @param collisionSuffix The suffix to use in case of name collisions.
2214
+ * @param emitAlias Whether to emit a type alias.
2215
+ * @returns The assigned type name.
2216
+ */
2139
2217
  registerObjectShape(shape, preferredName, context, collisionSuffix, emitAlias = false) {
2140
2218
  return this.shapes.registerObjectShape(shape, preferredName, context, collisionSuffix, emitAlias);
2141
2219
  }
2220
+ /**
2221
+ * Determines the priority of an operation based on its characteristics, such as the
2222
+ * presence of a request body, which can be used for sorting operations when generating the SDK.
2223
+ *
2224
+ * @param operation The operation object to evaluate.
2225
+ * @returns The priority of the operation.
2226
+ */
2142
2227
  getOperationPriority(operation) {
2143
2228
  return Number(Boolean(operation.requestBody)) * 10;
2144
2229
  }
2230
+ /**
2231
+ * Resolves the description for a successful response from the given responses
2232
+ * object, preferring common success status codes and falling back to any available
2233
+ * description if necessary.
2234
+ *
2235
+ * @param responses The responses object to evaluate.
2236
+ * @returns The description of the successful response, or undefined if none is found.
2237
+ */
2145
2238
  resolveSuccessResponseDescription(responses) {
2146
2239
  for (const statusCode of [
2147
2240
  "200",
@@ -2157,6 +2250,85 @@ var TypeScriptTypeBuilder = class {
2157
2250
  if (description) return description;
2158
2251
  }
2159
2252
  }
2253
+ /**
2254
+ * Builds the security schemes for the entire API, based on the provided OpenAPI document.
2255
+ *
2256
+ * @param document The OpenAPI document to build the security schemes from.
2257
+ * @returns The security scheme manifest objects.
2258
+ */
2259
+ buildSecuritySchemes(document) {
2260
+ return Object.entries(document.components?.securitySchemes ?? {}).map(([name, scheme]) => this.createSecuritySchemeManifest(name, scheme)).filter((entry) => entry !== null).sort((left, right) => left.name.localeCompare(right.name));
2261
+ }
2262
+ /**
2263
+ * Builds the security requirements for an operation or the entire API, based on the
2264
+ * provided OpenAPI security requirement objects.
2265
+ *
2266
+ * @param name The name of the security requirement (used for logging or error messages).
2267
+ * @param scheme The OpenAPI security scheme object to create the manifest from.
2268
+ * @returns The security scheme manifest object, or null if the scheme type is not supported.
2269
+ */
2270
+ createSecuritySchemeManifest(name, scheme) {
2271
+ const helperName = this.createSecurityHelperName(name);
2272
+ if (scheme.type === "apiKey") return {
2273
+ name,
2274
+ helperName,
2275
+ description: scheme.description,
2276
+ type: "apiKey",
2277
+ authType: "apiKey",
2278
+ in: scheme.in,
2279
+ parameterName: scheme.name
2280
+ };
2281
+ if (scheme.type === "oauth2") return {
2282
+ name,
2283
+ helperName,
2284
+ description: scheme.description,
2285
+ type: "oauth2",
2286
+ authType: "oauth2",
2287
+ scopes: this.collectSecurityScopes(scheme)
2288
+ };
2289
+ if (scheme.type === "openIdConnect") return {
2290
+ name,
2291
+ helperName,
2292
+ description: scheme.description,
2293
+ type: "openIdConnect",
2294
+ authType: "oauth2",
2295
+ openIdConnectUrl: scheme.openIdConnectUrl
2296
+ };
2297
+ const normalizedScheme = scheme.scheme.toLowerCase();
2298
+ return {
2299
+ name,
2300
+ helperName,
2301
+ description: scheme.description,
2302
+ type: "http",
2303
+ authType: normalizedScheme === "basic" ? "basic" : "bearer",
2304
+ scheme: scheme.scheme,
2305
+ bearerFormat: scheme.bearerFormat
2306
+ };
2307
+ }
2308
+ /**
2309
+ * Builds the security requirements for an operation or the entire API, based on the
2310
+ * provided OpenAPI security requirement objects.
2311
+ *
2312
+ * @param requirements The OpenAPI security requirement objects to build the manifest from.
2313
+ * @returns The security requirement manifest objects, or undefined if none are provided.
2314
+ */
2315
+ buildSecurityRequirements(requirements) {
2316
+ if (!requirements || requirements.length === 0) return;
2317
+ const normalized = requirements.map((requirement) => ({ schemes: Object.entries(requirement).map(([name, scopes]) => ({
2318
+ name,
2319
+ scopes: [...scopes].sort()
2320
+ })).sort((left, right) => left.name.localeCompare(right.name)) })).filter((requirement) => requirement.schemes.length > 0);
2321
+ return normalized.length > 0 ? normalized : void 0;
2322
+ }
2323
+ collectSecurityScopes(scheme) {
2324
+ const scopes = /* @__PURE__ */ new Set();
2325
+ for (const flow of Object.values(scheme.flows ?? {})) for (const scope of Object.keys(flow.scopes ?? {})) scopes.add(scope);
2326
+ return scopes.size > 0 ? Array.from(scopes).sort() : void 0;
2327
+ }
2328
+ createSecurityHelperName(name) {
2329
+ const sanitized = this.naming.sanitizeTypeName(name);
2330
+ return sanitized.endsWith("Auth") ? `create${sanitized}` : `create${sanitized}Auth`;
2331
+ }
2160
2332
  };
2161
2333
 
2162
2334
  //#endregion
@@ -2343,7 +2515,7 @@ var SdkPackageGenerator = class {
2343
2515
  "package.json": this.renderPackageJson(options),
2344
2516
  "README.md": this.renderReadme(manifest, options, outputMode, signatureStyle),
2345
2517
  "src/Schema.ts": schemaModule,
2346
- "src/index.ts": this.renderIndexFile(classNames, outputMode, rootTypeName),
2518
+ "src/index.ts": this.renderIndexFile(classNames, outputMode, rootTypeName, manifest),
2347
2519
  "tsconfig.json": this.renderTsconfig(),
2348
2520
  "tsdown.config.ts": this.renderTsdownConfig(),
2349
2521
  "vitest.config.ts": this.renderVitestConfig(),
@@ -2593,7 +2765,7 @@ var SdkPackageGenerator = class {
2593
2765
  const title = `# ${packageName}`;
2594
2766
  const description = this.renderReadmeDescription(outputMode);
2595
2767
  const usage = this.renderReadmeUsage(manifest, packageName, outputMode, signatureStyle);
2596
- const exports = this.renderReadmeExports(outputMode);
2768
+ const exports = this.renderReadmeExports(outputMode, manifest);
2597
2769
  return [
2598
2770
  title,
2599
2771
  "",
@@ -2635,8 +2807,13 @@ var SdkPackageGenerator = class {
2635
2807
  const classSnippet = this.renderReadmeClientSnippet(packageName, "classes", signatureStyle, exampleOperation);
2636
2808
  if (outputMode === "classes") return classSnippet;
2637
2809
  const typeImports = exampleOperation ? this.collectReadmeTypeImports(exampleOperation.operation) : [];
2810
+ const valueImports = [
2811
+ "Core",
2812
+ "createClient",
2813
+ ...exampleOperation ? this.collectReadmeAuthHelperImports(exampleOperation) : []
2814
+ ];
2638
2815
  return [
2639
- typeImports.length > 0 ? `import { Core, createClient, type ${typeImports.join(", type ")} } from '${packageName}'` : `import { Core, createClient } from '${packageName}'`,
2816
+ typeImports.length > 0 ? `import { ${valueImports.join(", ")}, type ${typeImports.join(", type ")} } from '${packageName}'` : `import { ${valueImports.join(", ")} } from '${packageName}'`,
2640
2817
  "",
2641
2818
  ...this.renderReadmeClientBody("sdk", "classes", signatureStyle, exampleOperation),
2642
2819
  "",
@@ -2648,7 +2825,9 @@ var SdkPackageGenerator = class {
2648
2825
  renderReadmeClientSnippet(packageName, mode, signatureStyle, exampleOperation) {
2649
2826
  const importNames = mode === "runtime" ? ["createClient"] : ["Core"];
2650
2827
  const typeImports = exampleOperation ? this.collectReadmeTypeImports(exampleOperation.operation) : [];
2651
- const importLine = typeImports.length > 0 ? `import { ${importNames.join(", ")}, type ${typeImports.join(", type ")} } from '${packageName}'` : `import { ${importNames.join(", ")} } from '${packageName}'`;
2828
+ const helperImports = exampleOperation ? this.collectReadmeAuthHelperImports(exampleOperation) : [];
2829
+ const valueImports = [...importNames, ...helperImports];
2830
+ const importLine = typeImports.length > 0 ? `import { ${valueImports.join(", ")}, type ${typeImports.join(", type ")} } from '${packageName}'` : `import { ${valueImports.join(", ")} } from '${packageName}'`;
2652
2831
  const sdkVariable = mode === "runtime" ? "runtimeSdk" : "sdk";
2653
2832
  return [
2654
2833
  importLine,
@@ -2664,16 +2843,27 @@ var SdkPackageGenerator = class {
2664
2843
  " clientId: process.env.CLIENT_ID!,",
2665
2844
  " clientSecret: process.env.CLIENT_SECRET!,",
2666
2845
  " environment: 'sandbox',",
2846
+ ...exampleOperation ? this.renderReadmeAuthLines(exampleOperation.operation.security ?? exampleOperation.groupSecurity ?? exampleOperation.globalSecurity) : [],
2667
2847
  "})",
2668
2848
  ...callLines.length > 0 ? ["", ...callLines] : []
2669
2849
  ];
2670
2850
  }
2671
- renderReadmeExports(outputMode) {
2672
- if (outputMode === "runtime") return ["`createClient()` for a typed runtime SDK instance", "`Schema` exports for request, response, params, query, and header types"];
2673
- if (outputMode === "classes") return ["`Core` as the class-based SDK entrypoint", "generated API classes plus `Schema` type exports"];
2851
+ renderReadmeExports(outputMode, manifest) {
2852
+ const authLine = manifest.securitySchemes.length > 0 ? ["generated auth helpers derived from OpenAPI security schemes"] : [];
2853
+ if (outputMode === "runtime") return [
2854
+ "`createClient()` for a typed runtime SDK instance",
2855
+ ...authLine,
2856
+ "`Schema` exports for request, response, params, query, and header types"
2857
+ ];
2858
+ if (outputMode === "classes") return [
2859
+ "`Core` as the class-based SDK entrypoint",
2860
+ ...authLine,
2861
+ "generated API classes plus `Schema` type exports"
2862
+ ];
2674
2863
  return [
2675
2864
  "`Core` for class-based usage",
2676
2865
  "`createClient()` for runtime-first usage",
2866
+ ...authLine,
2677
2867
  "`Schema` exports for generated request, response, params, query, and header types"
2678
2868
  ];
2679
2869
  }
@@ -2683,9 +2873,33 @@ var SdkPackageGenerator = class {
2683
2873
  if (!group || !operation) return null;
2684
2874
  return {
2685
2875
  group,
2686
- operation
2876
+ operation,
2877
+ groupSecurity: void 0,
2878
+ globalSecurity: manifest.security
2687
2879
  };
2688
2880
  }
2881
+ renderReadmeAuthLines(security) {
2882
+ if (!security || security.length === 0) return [];
2883
+ const selectedRequirement = security[0];
2884
+ if (!selectedRequirement || selectedRequirement.schemes.length === 0) return [];
2885
+ const value = selectedRequirement.schemes.length === 1 ? this.renderReadmeAuthFactoryCall(selectedRequirement.schemes[0].name) : `[
2886
+ ${selectedRequirement.schemes.map((scheme) => this.renderReadmeAuthFactoryCall(scheme.name)).join(",\n ")}
2887
+ ]`;
2888
+ const lines = security.length > 1 ? [" // Choose a generated auth helper that matches your API access setup."] : [];
2889
+ lines.push(` auth: ${value},`);
2890
+ return lines;
2891
+ }
2892
+ renderReadmeAuthFactoryCall(schemeName) {
2893
+ const helperName = this.createSecurityHelperName(schemeName);
2894
+ const envName = this.toConstantCase(schemeName);
2895
+ if (/basic/i.test(schemeName)) return `${helperName}(process.env.${envName}_USERNAME!, process.env.${envName}_PASSWORD!)`;
2896
+ return `${helperName}(process.env.${envName}_VALUE!)`;
2897
+ }
2898
+ collectReadmeAuthHelperImports(exampleOperation) {
2899
+ const selectedRequirement = (exampleOperation.operation.security ?? exampleOperation.groupSecurity ?? exampleOperation.globalSecurity)?.[0];
2900
+ if (!selectedRequirement) return [];
2901
+ return selectedRequirement.schemes.map((scheme) => this.createSecurityHelperName(scheme.name));
2902
+ }
2689
2903
  collectReadmeTypeImports(operation) {
2690
2904
  const types = /* @__PURE__ */ new Set();
2691
2905
  if (operation.pathParams.length > 0) types.add(operation.paramsType);
@@ -2792,13 +3006,13 @@ var SdkPackageGenerator = class {
2792
3006
  "})"
2793
3007
  ].join("\n");
2794
3008
  }
2795
- renderIndexFile(classNames, outputMode, rootTypeName) {
3009
+ renderIndexFile(classNames, outputMode, rootTypeName, manifest) {
2796
3010
  const rootExportName = `${rootTypeName.charAt(0).toLowerCase()}${rootTypeName.slice(1)}`;
2797
3011
  const lines = [
2798
3012
  `import type { ${rootTypeName}Api } from './Schema'`,
2799
- `import { ${rootExportName}Sdk } from './Schema'`,
3013
+ `import { ${rootExportName}Manifest, ${rootExportName}Sdk } from './Schema'`,
2800
3014
  "import { createSdk as createBoundSdk } from '@oapiex/sdk-kit'",
2801
- "import type { BaseApi as KitBaseApi, Core as KitCore, InitOptions } from '@oapiex/sdk-kit'",
3015
+ "import type { AuthConfig, BaseApi as KitBaseApi, Core as KitCore, InitOptions } from '@oapiex/sdk-kit'",
2802
3016
  "",
2803
3017
  "export * from './Schema'"
2804
3018
  ];
@@ -2809,6 +3023,15 @@ var SdkPackageGenerator = class {
2809
3023
  lines.push("export { Core } from './Core'");
2810
3024
  }
2811
3025
  lines.push("");
3026
+ lines.push(`export const securitySchemes = ${rootExportName}Manifest.securitySchemes`);
3027
+ lines.push(`export const security = ${rootExportName}Manifest.security`);
3028
+ if (manifest.securitySchemes.length > 0) {
3029
+ lines.push("");
3030
+ for (const scheme of manifest.securitySchemes) {
3031
+ lines.push(...this.renderSecurityHelper(scheme));
3032
+ lines.push("");
3033
+ }
3034
+ }
2812
3035
  lines.push("export const createClient = (");
2813
3036
  lines.push(" options: InitOptions");
2814
3037
  lines.push(`): KitCore & { api: KitBaseApi & ${rootTypeName}Api } =>`);
@@ -2826,6 +3049,7 @@ var SdkPackageGenerator = class {
2826
3049
  lines.push("} from '@oapiex/sdk-kit'");
2827
3050
  lines.push("");
2828
3051
  lines.push("export type {");
3052
+ lines.push(" AuthConfig,");
2829
3053
  lines.push(" InitOptions,");
2830
3054
  lines.push(" UnifiedResponse,");
2831
3055
  lines.push(" XGenericObject,");
@@ -2837,6 +3061,47 @@ var SdkPackageGenerator = class {
2837
3061
  return !["Record", "Promise"].includes(identifier);
2838
3062
  })));
2839
3063
  }
3064
+ renderSecurityHelper(scheme) {
3065
+ if (scheme.authType === "basic") return [
3066
+ `export const ${scheme.helperName} = (username: string, password: string): AuthConfig => ({`,
3067
+ " type: 'basic',",
3068
+ " username,",
3069
+ " password,",
3070
+ "})"
3071
+ ];
3072
+ if (scheme.authType === "apiKey") return [
3073
+ `export const ${scheme.helperName} = (value: string): AuthConfig => ({`,
3074
+ " type: 'apiKey',",
3075
+ ` name: ${JSON.stringify(scheme.parameterName ?? scheme.name)},`,
3076
+ " value,",
3077
+ ` in: ${JSON.stringify(scheme.in ?? "header")},`,
3078
+ "})"
3079
+ ];
3080
+ if (scheme.authType === "oauth2") return [
3081
+ `export const ${scheme.helperName} = (accessToken: string, tokenType = 'Bearer'): AuthConfig => ({`,
3082
+ " type: 'oauth2',",
3083
+ " accessToken,",
3084
+ " tokenType,",
3085
+ "})"
3086
+ ];
3087
+ return [
3088
+ `export const ${scheme.helperName} = (token: string): AuthConfig => ({`,
3089
+ " type: 'bearer',",
3090
+ " token,",
3091
+ ...scheme.scheme && scheme.scheme.toLowerCase() !== "bearer" ? [` prefix: ${JSON.stringify(this.normalizeHttpAuthPrefix(scheme.scheme))},`] : [],
3092
+ "})"
3093
+ ];
3094
+ }
3095
+ createSecurityHelperName(schemeName) {
3096
+ const sanitized = this.typeBuilder.sanitizeTypeName(schemeName);
3097
+ return sanitized.endsWith("Auth") ? `create${sanitized}` : `create${sanitized}Auth`;
3098
+ }
3099
+ normalizeHttpAuthPrefix(scheme) {
3100
+ return scheme.charAt(0).toUpperCase() + scheme.slice(1);
3101
+ }
3102
+ toConstantCase(value) {
3103
+ return value.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[^A-Za-z0-9]+/g, "_").replace(/^_+|_+$/g, "").toUpperCase() || "AUTH";
3104
+ }
2840
3105
  };
2841
3106
 
2842
3107
  //#endregion
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "oapiex",
3
3
  "type": "module",
4
- "version": "0.2.2",
5
- "description": "A CLI tool to extract OpenAPI specifications from documentations generated by readme.com and other similar platforms, and generate TypeScript SDKs for seamless API integration.",
4
+ "version": "0.3.1",
5
+ "description": "CLI and TypeScript toolkit for turning documentation pages into usable developer artifacts.",
6
6
  "main": "./dist/index.cjs",
7
7
  "private": false,
8
8
  "homepage": "https://toneflix.github.io/oapiex",