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/bin/cli.mjs +62 -9
- package/dist/index.cjs +286 -21
- package/dist/index.d.ts +62 -1
- package/dist/index.mjs +286 -21
- package/package.json +2 -2
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
|
|
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
|
|
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 {
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
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 {
|
|
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
|
|
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
|
-
|
|
2673
|
-
if (outputMode === "
|
|
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.
|
|
5
|
-
"description": "
|
|
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",
|