@stackwright-pro/openapi 0.3.0-alpha.6 → 0.3.0-alpha.8
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.d.mts +7 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.js +105 -65
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +105 -65
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -470,8 +470,13 @@ declare class ActionGenerator {
|
|
|
470
470
|
private generateActionExports;
|
|
471
471
|
private generateRegistry;
|
|
472
472
|
private generateEmptyFile;
|
|
473
|
-
/** Derives the client method name from endpoint + method
|
|
473
|
+
/** Derives the client method name from endpoint + method.
|
|
474
|
+
* Prefers operationId (matching ClientGenerator.getMethodName()), falls back
|
|
475
|
+
* to path+method derivation for specs that omit operationIds. */
|
|
474
476
|
private endpointToClientMethod;
|
|
477
|
+
/** Convert any string to camelCase — handles operationId values like
|
|
478
|
+
* createOrder, list_items, get-by-id, etc. */
|
|
479
|
+
private camelCase;
|
|
475
480
|
private toCamelCase;
|
|
476
481
|
private toPascalCase;
|
|
477
482
|
private capitalize;
|
|
@@ -523,6 +528,7 @@ declare class ClientGenerator {
|
|
|
523
528
|
private schemaMapping?;
|
|
524
529
|
private requiredSchemas;
|
|
525
530
|
private generatedRequestSchemas;
|
|
531
|
+
private generatedRequestTypes;
|
|
526
532
|
constructor(document: OpenAPIDocument, schemaMapping?: SchemaMapping);
|
|
527
533
|
/**
|
|
528
534
|
* Generate typed API client code from OpenAPI document
|
package/dist/index.d.ts
CHANGED
|
@@ -470,8 +470,13 @@ declare class ActionGenerator {
|
|
|
470
470
|
private generateActionExports;
|
|
471
471
|
private generateRegistry;
|
|
472
472
|
private generateEmptyFile;
|
|
473
|
-
/** Derives the client method name from endpoint + method
|
|
473
|
+
/** Derives the client method name from endpoint + method.
|
|
474
|
+
* Prefers operationId (matching ClientGenerator.getMethodName()), falls back
|
|
475
|
+
* to path+method derivation for specs that omit operationIds. */
|
|
474
476
|
private endpointToClientMethod;
|
|
477
|
+
/** Convert any string to camelCase — handles operationId values like
|
|
478
|
+
* createOrder, list_items, get-by-id, etc. */
|
|
479
|
+
private camelCase;
|
|
475
480
|
private toCamelCase;
|
|
476
481
|
private toPascalCase;
|
|
477
482
|
private capitalize;
|
|
@@ -523,6 +528,7 @@ declare class ClientGenerator {
|
|
|
523
528
|
private schemaMapping?;
|
|
524
529
|
private requiredSchemas;
|
|
525
530
|
private generatedRequestSchemas;
|
|
531
|
+
private generatedRequestTypes;
|
|
526
532
|
constructor(document: OpenAPIDocument, schemaMapping?: SchemaMapping);
|
|
527
533
|
/**
|
|
528
534
|
* Generate typed API client code from OpenAPI document
|
package/dist/index.js
CHANGED
|
@@ -160,8 +160,14 @@ var SchemaResolver = class {
|
|
|
160
160
|
const response = responses[responseCode];
|
|
161
161
|
if (!response) return void 0;
|
|
162
162
|
for (const contentType of SUPPORTED_CONTENT_TYPES) {
|
|
163
|
-
const
|
|
164
|
-
if (!
|
|
163
|
+
const responseContent = response.content;
|
|
164
|
+
if (!responseContent) continue;
|
|
165
|
+
const exactContent = responseContent[contentType];
|
|
166
|
+
const normalizedEntry = exactContent ? void 0 : Object.entries(responseContent).find(
|
|
167
|
+
([k]) => k === contentType || k.startsWith(`${contentType};`)
|
|
168
|
+
);
|
|
169
|
+
const content = exactContent ?? normalizedEntry?.[1];
|
|
170
|
+
if (!content || !content.schema) continue;
|
|
165
171
|
const schema = content.schema;
|
|
166
172
|
if ("$ref" in schema) {
|
|
167
173
|
console.warn(
|
|
@@ -291,7 +297,7 @@ ${body}`;
|
|
|
291
297
|
const isNullable = "nullable" in schema && schema.nullable === true;
|
|
292
298
|
let baseSchema = this.schemaTypeToZod(schema);
|
|
293
299
|
if (schema.description) {
|
|
294
|
-
const escapedDesc = schema.description.replace(/'/g, "\\'");
|
|
300
|
+
const escapedDesc = schema.description.replace(/'/g, "\\'").replace(/\r?\n/g, " ");
|
|
295
301
|
baseSchema += `.describe('${escapedDesc}')`;
|
|
296
302
|
}
|
|
297
303
|
if (isNullable) {
|
|
@@ -429,7 +435,7 @@ ${body}`;
|
|
|
429
435
|
if (!schema.properties || Object.keys(schema.properties).length === 0) {
|
|
430
436
|
if (schema.additionalProperties) {
|
|
431
437
|
const valueSchema = typeof schema.additionalProperties === "object" ? this.schemaToZod(schema.additionalProperties) : "z.unknown()";
|
|
432
|
-
return `z.record(${valueSchema})`;
|
|
438
|
+
return `z.record(z.string(), ${valueSchema})`;
|
|
433
439
|
}
|
|
434
440
|
return "z.object({})";
|
|
435
441
|
}
|
|
@@ -626,10 +632,10 @@ var CollectionProviderGenerator = class {
|
|
|
626
632
|
const authHeader = this.generateAuthHeader(auth);
|
|
627
633
|
const arraySchemaName = `${schemaName.replace(/Schema$/, "")}ArraySchema`;
|
|
628
634
|
const validationSchema = unknownFallback ? "z.unknown()" : isArray ? arraySchemaName : schemaName;
|
|
629
|
-
const imports = bare ? "" : unknownFallback ? `import type { CollectionProvider,
|
|
635
|
+
const imports = bare ? "" : unknownFallback ? `import type { CollectionProvider, CollectionEntry, CollectionListOptions, CollectionListResult } from '@stackwright/collections';
|
|
630
636
|
import { z } from 'zod';
|
|
631
637
|
|
|
632
|
-
` : `import type { CollectionProvider,
|
|
638
|
+
` : `import type { CollectionProvider, CollectionEntry, CollectionListOptions, CollectionListResult } from '@stackwright/collections';
|
|
633
639
|
import { ${isArray ? arraySchemaName : schemaName} } from './schemas';
|
|
634
640
|
|
|
635
641
|
`;
|
|
@@ -648,29 +654,20 @@ export class ${providerName} implements CollectionProvider {
|
|
|
648
654
|
}
|
|
649
655
|
|
|
650
656
|
/**
|
|
651
|
-
*
|
|
657
|
+
* List available collection names.
|
|
652
658
|
*/
|
|
653
|
-
|
|
654
|
-
return '${collectionName}';
|
|
659
|
+
async collections(): Promise<string[]> {
|
|
660
|
+
return ['${collectionName}'];
|
|
655
661
|
}
|
|
656
662
|
|
|
657
663
|
/**
|
|
658
664
|
* List all items in the collection
|
|
659
665
|
*/
|
|
660
|
-
async list(
|
|
661
|
-
limit?: number;
|
|
662
|
-
offset?: number;
|
|
663
|
-
filters?: Record<string, unknown>;
|
|
664
|
-
}): Promise<CollectionItem[]> {
|
|
666
|
+
async list(_collection: string, opts?: CollectionListOptions): Promise<CollectionListResult> {
|
|
665
667
|
const url = new URL(\`\${this.baseUrl}${endpoint}\`);
|
|
666
668
|
|
|
667
|
-
|
|
668
|
-
if (
|
|
669
|
-
url.searchParams.set('limit', String(options.limit));
|
|
670
|
-
}
|
|
671
|
-
if (options?.offset) {
|
|
672
|
-
url.searchParams.set('offset', String(options.offset));
|
|
673
|
-
}
|
|
669
|
+
if (opts?.limit) url.searchParams.set('limit', String(opts.limit));
|
|
670
|
+
if (opts?.offset) url.searchParams.set('offset', String(opts.offset));
|
|
674
671
|
|
|
675
672
|
const response = await fetch(url.toString(), {
|
|
676
673
|
method: '${method.toUpperCase()}',
|
|
@@ -686,29 +683,24 @@ export class ${providerName} implements CollectionProvider {
|
|
|
686
683
|
// Validate response with Zod schema
|
|
687
684
|
const validated = ${validationSchema}.parse(data);
|
|
688
685
|
|
|
689
|
-
|
|
690
|
-
return
|
|
686
|
+
const entries = ${isArray ? "validated" : "[validated]"}.map((item: any) => this.toCollectionEntry(item));
|
|
687
|
+
return { entries, total: entries.length };
|
|
691
688
|
}
|
|
692
689
|
|
|
693
690
|
/**
|
|
694
691
|
* Get a single item by slug
|
|
695
692
|
*/
|
|
696
|
-
async get(slug: string): Promise<
|
|
693
|
+
async get(_collection: string, slug: string): Promise<CollectionEntry | null> {
|
|
697
694
|
${this.generateGetMethod(endpoint, slugField, isArray, collectionName, schemaName, unknownFallback)}
|
|
698
695
|
}
|
|
699
696
|
|
|
700
697
|
/**
|
|
701
|
-
* Convert API response to
|
|
698
|
+
* Convert API response to CollectionEntry
|
|
702
699
|
*/
|
|
703
|
-
private
|
|
700
|
+
private toCollectionEntry(item: any): CollectionEntry {
|
|
704
701
|
return {
|
|
705
702
|
slug: String(item.${slugField}),
|
|
706
|
-
|
|
707
|
-
data: item,
|
|
708
|
-
metadata: {
|
|
709
|
-
source: '${collectionName}',
|
|
710
|
-
_raw: item,
|
|
711
|
-
},
|
|
703
|
+
...item,
|
|
712
704
|
};
|
|
713
705
|
}
|
|
714
706
|
|
|
@@ -751,10 +743,10 @@ export class ${providerName} implements CollectionProvider {
|
|
|
751
743
|
const data = await response.json();
|
|
752
744
|
const validated = ${validationExpr}.parse(data);
|
|
753
745
|
|
|
754
|
-
return this.
|
|
746
|
+
return this.toCollectionEntry(validated);`;
|
|
755
747
|
} else {
|
|
756
|
-
return `const
|
|
757
|
-
return
|
|
748
|
+
return `const result = await this.list(_collection);
|
|
749
|
+
return result.entries.find((item) => item.slug === slug) ?? null;`;
|
|
758
750
|
}
|
|
759
751
|
}
|
|
760
752
|
/**
|
|
@@ -1002,13 +994,25 @@ import type { CollectionActionRegistry } from '@stackwright-pro/workflow';
|
|
|
1002
994
|
export const actions: CollectionActionRegistry = {};
|
|
1003
995
|
`;
|
|
1004
996
|
}
|
|
1005
|
-
/** Derives the client method name from endpoint + method
|
|
997
|
+
/** Derives the client method name from endpoint + method.
|
|
998
|
+
* Prefers operationId (matching ClientGenerator.getMethodName()), falls back
|
|
999
|
+
* to path+method derivation for specs that omit operationIds. */
|
|
1006
1000
|
endpointToClientMethod(endpoint, method) {
|
|
1001
|
+
const pathItem = this.document.paths?.[endpoint];
|
|
1002
|
+
const operation = pathItem?.[method.toLowerCase()];
|
|
1003
|
+
if (operation?.operationId) {
|
|
1004
|
+
return this.camelCase(operation.operationId);
|
|
1005
|
+
}
|
|
1007
1006
|
const parts = endpoint.split("/").filter(Boolean).filter((p) => !p.startsWith("{")).map((p) => p.replace(/-/g, "_"));
|
|
1008
1007
|
const prefix = method.toLowerCase();
|
|
1009
1008
|
const suffix = parts.map((p) => this.capitalize(p)).join("");
|
|
1010
1009
|
return `${prefix}${suffix}`;
|
|
1011
1010
|
}
|
|
1011
|
+
/** Convert any string to camelCase — handles operationId values like
|
|
1012
|
+
* createOrder, list_items, get-by-id, etc. */
|
|
1013
|
+
camelCase(str) {
|
|
1014
|
+
return str.replace(/[^a-zA-Z0-9]+(.)/g, (_, chr) => chr.toUpperCase()).replace(/^[A-Z]/, (chr) => chr.toLowerCase());
|
|
1015
|
+
}
|
|
1012
1016
|
toCamelCase(str) {
|
|
1013
1017
|
const parts = str.split(/[-_]/);
|
|
1014
1018
|
return parts.map((p, i) => i === 0 ? p.toLowerCase() : this.capitalize(p)).join("");
|
|
@@ -1029,11 +1033,13 @@ var ClientGenerator = class {
|
|
|
1029
1033
|
}
|
|
1030
1034
|
this.requiredSchemas = /* @__PURE__ */ new Set();
|
|
1031
1035
|
this.generatedRequestSchemas = /* @__PURE__ */ new Set();
|
|
1036
|
+
this.generatedRequestTypes = /* @__PURE__ */ new Set();
|
|
1032
1037
|
}
|
|
1033
1038
|
resolver;
|
|
1034
1039
|
schemaMapping;
|
|
1035
1040
|
requiredSchemas;
|
|
1036
1041
|
generatedRequestSchemas;
|
|
1042
|
+
generatedRequestTypes;
|
|
1037
1043
|
/**
|
|
1038
1044
|
* Generate typed API client code from OpenAPI document
|
|
1039
1045
|
*/
|
|
@@ -1052,12 +1058,11 @@ var ClientGenerator = class {
|
|
|
1052
1058
|
}
|
|
1053
1059
|
this.requiredSchemas.clear();
|
|
1054
1060
|
this.generatedRequestSchemas.clear();
|
|
1061
|
+
this.generatedRequestTypes.clear();
|
|
1055
1062
|
let code = this.generateImports(!!schemaMapping, validateResponses);
|
|
1056
1063
|
code += "\n";
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
code += "\n";
|
|
1060
|
-
}
|
|
1064
|
+
code += this.generateRequestSchemas(endpoints);
|
|
1065
|
+
code += "\n";
|
|
1061
1066
|
code += this.generateTypes(endpoints, schemaMapping);
|
|
1062
1067
|
code += "\n";
|
|
1063
1068
|
code += this.generateErrorClasses();
|
|
@@ -1088,9 +1093,9 @@ var ClientGenerator = class {
|
|
|
1088
1093
|
*/
|
|
1089
1094
|
|
|
1090
1095
|
`;
|
|
1091
|
-
|
|
1092
|
-
code += `import { z } from 'zod';
|
|
1096
|
+
code += `import { z } from 'zod';
|
|
1093
1097
|
`;
|
|
1098
|
+
if (useSchemas && validateResponses) {
|
|
1094
1099
|
code += `import * as schemas from './schemas';
|
|
1095
1100
|
`;
|
|
1096
1101
|
}
|
|
@@ -1123,6 +1128,9 @@ var ClientGenerator = class {
|
|
|
1123
1128
|
const schemaName = `${typeName}RequestSchema`;
|
|
1124
1129
|
const schemaCode = this.generateRequestSchemaForEndpoint(endpoint);
|
|
1125
1130
|
if (schemaCode) {
|
|
1131
|
+
if (this.generatedRequestSchemas.has(schemaName)) {
|
|
1132
|
+
continue;
|
|
1133
|
+
}
|
|
1126
1134
|
code += `export const ${schemaName} = ${schemaCode};
|
|
1127
1135
|
|
|
1128
1136
|
`;
|
|
@@ -1174,7 +1182,8 @@ ${parts.join(",\n")}
|
|
|
1174
1182
|
for (const param of params) {
|
|
1175
1183
|
const zodSchema = this.parameterSchemaToZod(param);
|
|
1176
1184
|
const desc = param.description ? `.describe('${this.escapeString(param.description)}')` : "";
|
|
1177
|
-
|
|
1185
|
+
const safeParamKey = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(param.name) ? param.name : `'${param.name}'`;
|
|
1186
|
+
paramSchemas.push(` ${safeParamKey}: ${zodSchema}${desc}`);
|
|
1178
1187
|
}
|
|
1179
1188
|
return `z.object({
|
|
1180
1189
|
${paramSchemas.join(",\n")}
|
|
@@ -1193,7 +1202,8 @@ ${paramSchemas.join(",\n")}
|
|
|
1193
1202
|
if (!param.required) {
|
|
1194
1203
|
zodSchema += ".optional()";
|
|
1195
1204
|
}
|
|
1196
|
-
|
|
1205
|
+
const safeParamKey = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(param.name) ? param.name : `'${param.name}'`;
|
|
1206
|
+
paramSchemas.push(` ${safeParamKey}: ${zodSchema}`);
|
|
1197
1207
|
}
|
|
1198
1208
|
return `z.object({
|
|
1199
1209
|
${paramSchemas.join(",\n")}
|
|
@@ -1266,7 +1276,7 @@ ${paramSchemas.join(",\n")}
|
|
|
1266
1276
|
}
|
|
1267
1277
|
if (schema.type === "object") {
|
|
1268
1278
|
if (!schema.properties) {
|
|
1269
|
-
return "z.record(z.unknown())";
|
|
1279
|
+
return "z.record(z.string(), z.unknown())";
|
|
1270
1280
|
}
|
|
1271
1281
|
const props = [];
|
|
1272
1282
|
const required = schema.required || [];
|
|
@@ -1276,7 +1286,8 @@ ${paramSchemas.join(",\n")}
|
|
|
1276
1286
|
if (!isRequired) {
|
|
1277
1287
|
propZod += ".optional()";
|
|
1278
1288
|
}
|
|
1279
|
-
|
|
1289
|
+
const safeKey = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(key) ? key : `'${key}'`;
|
|
1290
|
+
props.push(`${safeKey}: ${propZod}`);
|
|
1280
1291
|
}
|
|
1281
1292
|
return `z.object({ ${props.join(", ")} })`;
|
|
1282
1293
|
}
|
|
@@ -1434,13 +1445,24 @@ ${paramSchemas.join(",\n")}
|
|
|
1434
1445
|
const operationId = endpoint.operationId || this.getMethodName(endpoint);
|
|
1435
1446
|
const mapping = schemaMapping[operationId];
|
|
1436
1447
|
if (!mapping) {
|
|
1448
|
+
const typeName2 = this.getOperationTypeName(endpoint);
|
|
1449
|
+
const requestSchemaName2 = `${typeName2}RequestSchema`;
|
|
1450
|
+
if (this.generatedRequestSchemas.has(requestSchemaName2) && !this.generatedRequestTypes.has(requestSchemaName2)) {
|
|
1451
|
+
code += `export type ${typeName2}Request = z.infer<typeof ${requestSchemaName2}>;
|
|
1452
|
+
`;
|
|
1453
|
+
this.generatedRequestTypes.add(requestSchemaName2);
|
|
1454
|
+
}
|
|
1455
|
+
code += `export type ${typeName2}Response = unknown;
|
|
1456
|
+
|
|
1457
|
+
`;
|
|
1437
1458
|
continue;
|
|
1438
1459
|
}
|
|
1439
1460
|
const typeName = this.getOperationTypeName(endpoint);
|
|
1440
1461
|
const requestSchemaName = mapping.requestSchema || typeName + "RequestSchema";
|
|
1441
|
-
if (this.generatedRequestSchemas.has(requestSchemaName)) {
|
|
1462
|
+
if (this.generatedRequestSchemas.has(requestSchemaName) && !this.generatedRequestTypes.has(requestSchemaName)) {
|
|
1442
1463
|
this.addRequiredSchema(requestSchemaName);
|
|
1443
1464
|
code += "export type " + typeName + "Request = z.infer<typeof " + requestSchemaName + ">;\n";
|
|
1465
|
+
this.generatedRequestTypes.add(requestSchemaName);
|
|
1444
1466
|
}
|
|
1445
1467
|
this.addRequiredSchema(mapping.responseSchema);
|
|
1446
1468
|
code += `export type ${typeName}Response = z.infer<typeof schemas.${mapping.responseSchema}>;
|
|
@@ -1485,8 +1507,9 @@ ${paramSchemas.join(",\n")}
|
|
|
1485
1507
|
const type = this.getTypeFromSchemaLegacy(param.schema);
|
|
1486
1508
|
const desc = param.description ? ` /** ${param.description} */
|
|
1487
1509
|
` : "";
|
|
1510
|
+
const safeParamKey = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(param.name) ? param.name : `'${param.name}'`;
|
|
1488
1511
|
code += desc;
|
|
1489
|
-
code += ` ${
|
|
1512
|
+
code += ` ${safeParamKey}${required}: ${type};
|
|
1490
1513
|
`;
|
|
1491
1514
|
}
|
|
1492
1515
|
code += " };\n";
|
|
@@ -1499,8 +1522,9 @@ ${paramSchemas.join(",\n")}
|
|
|
1499
1522
|
const type = this.getTypeFromSchemaLegacy(param.schema);
|
|
1500
1523
|
const desc = param.description ? ` /** ${param.description} */
|
|
1501
1524
|
` : "";
|
|
1525
|
+
const safeParamKey = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(param.name) ? param.name : `'${param.name}'`;
|
|
1502
1526
|
code += desc;
|
|
1503
|
-
code += ` ${
|
|
1527
|
+
code += ` ${safeParamKey}${required}: ${type};
|
|
1504
1528
|
`;
|
|
1505
1529
|
}
|
|
1506
1530
|
code += " };\n";
|
|
@@ -1513,8 +1537,9 @@ ${paramSchemas.join(",\n")}
|
|
|
1513
1537
|
const type = this.getTypeFromSchemaLegacy(param.schema);
|
|
1514
1538
|
const desc = param.description ? ` /** ${param.description} */
|
|
1515
1539
|
` : "";
|
|
1540
|
+
const safeParamKey = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(param.name) ? param.name : `'${param.name}'`;
|
|
1516
1541
|
code += desc;
|
|
1517
|
-
code += ` ${
|
|
1542
|
+
code += ` ${safeParamKey}${required}: ${type};
|
|
1518
1543
|
`;
|
|
1519
1544
|
}
|
|
1520
1545
|
code += " };\n";
|
|
@@ -1861,9 +1886,10 @@ ${paramSchemas.join(",\n")}
|
|
|
1861
1886
|
code += ` if (request.query) {
|
|
1862
1887
|
`;
|
|
1863
1888
|
for (const param of queryParams) {
|
|
1864
|
-
|
|
1889
|
+
const safeAccess = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(param.name) ? `.${param.name}` : `['${param.name}']`;
|
|
1890
|
+
code += ` if (request.query${safeAccess} != null) {
|
|
1865
1891
|
`;
|
|
1866
|
-
code += ` searchParams.append('${param.name}', String(request.query
|
|
1892
|
+
code += ` searchParams.append('${param.name}', String(request.query${safeAccess}));
|
|
1867
1893
|
`;
|
|
1868
1894
|
code += ` }
|
|
1869
1895
|
`;
|
|
@@ -1887,7 +1913,7 @@ ${paramSchemas.join(",\n")}
|
|
|
1887
1913
|
code += ` method: '${method.toUpperCase()}',
|
|
1888
1914
|
`;
|
|
1889
1915
|
if (hasBody) {
|
|
1890
|
-
code += ` body: request.body ? JSON.stringify(request.body) :
|
|
1916
|
+
code += ` body: request.body ? JSON.stringify(request.body) : undefined,
|
|
1891
1917
|
`;
|
|
1892
1918
|
}
|
|
1893
1919
|
if (headerParams.length > 0) {
|
|
@@ -2004,9 +2030,10 @@ ${paramSchemas.join(",\n")}
|
|
|
2004
2030
|
code += ` if (request.query) {
|
|
2005
2031
|
`;
|
|
2006
2032
|
for (const param of queryParams) {
|
|
2007
|
-
|
|
2033
|
+
const safeAccess = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(param.name) ? `.${param.name}` : `['${param.name}']`;
|
|
2034
|
+
code += ` if (request.query${safeAccess} != null) {
|
|
2008
2035
|
`;
|
|
2009
|
-
code += ` searchParams.append('${param.name}', String(request.query
|
|
2036
|
+
code += ` searchParams.append('${param.name}', String(request.query${safeAccess}));
|
|
2010
2037
|
`;
|
|
2011
2038
|
code += ` }
|
|
2012
2039
|
`;
|
|
@@ -2028,7 +2055,7 @@ ${paramSchemas.join(",\n")}
|
|
|
2028
2055
|
code += ` method: '${method.toUpperCase()}',
|
|
2029
2056
|
`;
|
|
2030
2057
|
if (hasBody) {
|
|
2031
|
-
code += ` body: request.body ? JSON.stringify(request.body) :
|
|
2058
|
+
code += ` body: request.body ? JSON.stringify(request.body) : undefined,
|
|
2032
2059
|
`;
|
|
2033
2060
|
}
|
|
2034
2061
|
const headerParams = params.filter((p) => p.in === "header");
|
|
@@ -2194,7 +2221,7 @@ ${paramSchemas.join(",\n")}
|
|
|
2194
2221
|
if (endpoint.operationId) {
|
|
2195
2222
|
return this.camelCase(endpoint.operationId);
|
|
2196
2223
|
}
|
|
2197
|
-
const pathParts = endpoint.path.split("/").filter((p) => p
|
|
2224
|
+
const pathParts = endpoint.path.split("/").filter((p) => p).map((p) => p.startsWith("{") && p.endsWith("}") ? p.slice(1, -1) : p).join("-");
|
|
2198
2225
|
return this.camelCase(`${endpoint.method}-${pathParts}`);
|
|
2199
2226
|
}
|
|
2200
2227
|
/**
|
|
@@ -3000,6 +3027,7 @@ import { z } from 'zod';
|
|
|
3000
3027
|
if (!operation) continue;
|
|
3001
3028
|
const opId = operation.operationId || this.generateOperationId(pathStr, method);
|
|
3002
3029
|
const responseSchemaName = `${this.getOperationTypeName(opId)}ResponseSchema`;
|
|
3030
|
+
if (!generatedSchemas.has(responseSchemaName)) continue;
|
|
3003
3031
|
schemaMapping[opId] = {
|
|
3004
3032
|
requestSchema: null,
|
|
3005
3033
|
// ClientGenerator handles request schema generation + type inference
|
|
@@ -3024,7 +3052,13 @@ import type * as schemas from './schemas';
|
|
|
3024
3052
|
|
|
3025
3053
|
`;
|
|
3026
3054
|
if (collections) {
|
|
3055
|
+
const resolver = new SchemaResolver(document);
|
|
3027
3056
|
for (const collection of collections) {
|
|
3057
|
+
const schema = resolver.getResponseSchema(
|
|
3058
|
+
collection.endpoint,
|
|
3059
|
+
collection.method?.toLowerCase() ?? "get"
|
|
3060
|
+
);
|
|
3061
|
+
if (!schema) continue;
|
|
3028
3062
|
const collectionName = this.sanitizeName(collection.endpoint);
|
|
3029
3063
|
const typeName = this.capitalize(collectionName);
|
|
3030
3064
|
const schemaName = `${typeName}Schema`;
|
|
@@ -3070,13 +3104,20 @@ import type * as schemas from './schemas';
|
|
|
3070
3104
|
collection.endpoint,
|
|
3071
3105
|
collection.method?.toLowerCase() ?? "get"
|
|
3072
3106
|
);
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3107
|
+
if (schema) {
|
|
3108
|
+
const isArray = schema.type === "array";
|
|
3109
|
+
if (isArray) {
|
|
3110
|
+
schemaImports.add(`${this.capitalize(collectionName)}ArraySchema`);
|
|
3111
|
+
} else {
|
|
3112
|
+
schemaImports.add(`${this.capitalize(collectionName)}Schema`);
|
|
3113
|
+
}
|
|
3078
3114
|
}
|
|
3079
3115
|
}
|
|
3116
|
+
const needsZodImport = classBlocks.some((block) => block.includes("z.unknown()"));
|
|
3117
|
+
const zodImportLine = needsZodImport ? `import { z } from 'zod';
|
|
3118
|
+
` : "";
|
|
3119
|
+
const schemaImportLine = schemaImports.size > 0 ? `import { ${Array.from(schemaImports).join(", ")} } from './schemas';
|
|
3120
|
+
` : "";
|
|
3080
3121
|
let providerCode = `/**
|
|
3081
3122
|
* Generated CollectionProvider from OpenAPI spec
|
|
3082
3123
|
* Integration: ${integrationName}
|
|
@@ -3085,9 +3126,8 @@ import type * as schemas from './schemas';
|
|
|
3085
3126
|
* Regenerate by running: pnpm prebuild
|
|
3086
3127
|
*/
|
|
3087
3128
|
|
|
3088
|
-
import type { CollectionProvider,
|
|
3089
|
-
|
|
3090
|
-
|
|
3129
|
+
import type { CollectionProvider, CollectionEntry, CollectionListOptions, CollectionListResult } from '@stackwright/collections';
|
|
3130
|
+
${zodImportLine}${schemaImportLine}
|
|
3091
3131
|
`;
|
|
3092
3132
|
providerCode += classBlocks.join("\n");
|
|
3093
3133
|
fs2__default.default.writeFileSync(path2__default.default.join(outputDir, "provider.ts"), providerCode);
|