@stackwright-pro/openapi 0.3.0-alpha.5 → 0.3.0-alpha.7
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 +9 -1
- package/dist/index.d.ts +9 -1
- package/dist/index.js +137 -70
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +137 -70
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -33,8 +33,18 @@ var OpenAPIParser = class {
|
|
|
33
33
|
await SwaggerParser.validate(api);
|
|
34
34
|
}
|
|
35
35
|
let document;
|
|
36
|
+
let dereferenced = false;
|
|
36
37
|
if (dereference) {
|
|
37
|
-
|
|
38
|
+
try {
|
|
39
|
+
document = await SwaggerParser.dereference(api);
|
|
40
|
+
dereferenced = true;
|
|
41
|
+
} catch {
|
|
42
|
+
console.warn(
|
|
43
|
+
`[OpenAPIParser] Warning: Could not fully dereference "${specPath}" (dangling $ref or circular ref). Proceeding without full dereferencing \u2014 some schema references may not resolve.`
|
|
44
|
+
);
|
|
45
|
+
document = api;
|
|
46
|
+
dereferenced = false;
|
|
47
|
+
}
|
|
38
48
|
} else {
|
|
39
49
|
document = api;
|
|
40
50
|
}
|
|
@@ -42,7 +52,7 @@ var OpenAPIParser = class {
|
|
|
42
52
|
return {
|
|
43
53
|
document,
|
|
44
54
|
version,
|
|
45
|
-
dereferenced
|
|
55
|
+
dereferenced
|
|
46
56
|
};
|
|
47
57
|
} catch (error) {
|
|
48
58
|
throw this.enhanceError(error, specPath);
|
|
@@ -138,8 +148,14 @@ var SchemaResolver = class {
|
|
|
138
148
|
const response = responses[responseCode];
|
|
139
149
|
if (!response) return void 0;
|
|
140
150
|
for (const contentType of SUPPORTED_CONTENT_TYPES) {
|
|
141
|
-
const
|
|
142
|
-
if (!
|
|
151
|
+
const responseContent = response.content;
|
|
152
|
+
if (!responseContent) continue;
|
|
153
|
+
const exactContent = responseContent[contentType];
|
|
154
|
+
const normalizedEntry = exactContent ? void 0 : Object.entries(responseContent).find(
|
|
155
|
+
([k]) => k === contentType || k.startsWith(`${contentType};`)
|
|
156
|
+
);
|
|
157
|
+
const content = exactContent ?? normalizedEntry?.[1];
|
|
158
|
+
if (!content || !content.schema) continue;
|
|
143
159
|
const schema = content.schema;
|
|
144
160
|
if ("$ref" in schema) {
|
|
145
161
|
console.warn(
|
|
@@ -269,7 +285,7 @@ ${body}`;
|
|
|
269
285
|
const isNullable = "nullable" in schema && schema.nullable === true;
|
|
270
286
|
let baseSchema = this.schemaTypeToZod(schema);
|
|
271
287
|
if (schema.description) {
|
|
272
|
-
const escapedDesc = schema.description.replace(/'/g, "\\'");
|
|
288
|
+
const escapedDesc = schema.description.replace(/'/g, "\\'").replace(/\r?\n/g, " ");
|
|
273
289
|
baseSchema += `.describe('${escapedDesc}')`;
|
|
274
290
|
}
|
|
275
291
|
if (isNullable) {
|
|
@@ -407,7 +423,7 @@ ${body}`;
|
|
|
407
423
|
if (!schema.properties || Object.keys(schema.properties).length === 0) {
|
|
408
424
|
if (schema.additionalProperties) {
|
|
409
425
|
const valueSchema = typeof schema.additionalProperties === "object" ? this.schemaToZod(schema.additionalProperties) : "z.unknown()";
|
|
410
|
-
return `z.record(${valueSchema})`;
|
|
426
|
+
return `z.record(z.string(), ${valueSchema})`;
|
|
411
427
|
}
|
|
412
428
|
return "z.object({})";
|
|
413
429
|
}
|
|
@@ -604,10 +620,10 @@ var CollectionProviderGenerator = class {
|
|
|
604
620
|
const authHeader = this.generateAuthHeader(auth);
|
|
605
621
|
const arraySchemaName = `${schemaName.replace(/Schema$/, "")}ArraySchema`;
|
|
606
622
|
const validationSchema = unknownFallback ? "z.unknown()" : isArray ? arraySchemaName : schemaName;
|
|
607
|
-
const imports = bare ? "" : unknownFallback ? `import type { CollectionProvider,
|
|
623
|
+
const imports = bare ? "" : unknownFallback ? `import type { CollectionProvider, CollectionEntry, CollectionListOptions, CollectionListResult } from '@stackwright/collections';
|
|
608
624
|
import { z } from 'zod';
|
|
609
625
|
|
|
610
|
-
` : `import type { CollectionProvider,
|
|
626
|
+
` : `import type { CollectionProvider, CollectionEntry, CollectionListOptions, CollectionListResult } from '@stackwright/collections';
|
|
611
627
|
import { ${isArray ? arraySchemaName : schemaName} } from './schemas';
|
|
612
628
|
|
|
613
629
|
`;
|
|
@@ -626,29 +642,20 @@ export class ${providerName} implements CollectionProvider {
|
|
|
626
642
|
}
|
|
627
643
|
|
|
628
644
|
/**
|
|
629
|
-
*
|
|
645
|
+
* List available collection names.
|
|
630
646
|
*/
|
|
631
|
-
|
|
632
|
-
return '${collectionName}';
|
|
647
|
+
async collections(): Promise<string[]> {
|
|
648
|
+
return ['${collectionName}'];
|
|
633
649
|
}
|
|
634
650
|
|
|
635
651
|
/**
|
|
636
652
|
* List all items in the collection
|
|
637
653
|
*/
|
|
638
|
-
async list(
|
|
639
|
-
limit?: number;
|
|
640
|
-
offset?: number;
|
|
641
|
-
filters?: Record<string, unknown>;
|
|
642
|
-
}): Promise<CollectionItem[]> {
|
|
654
|
+
async list(_collection: string, opts?: CollectionListOptions): Promise<CollectionListResult> {
|
|
643
655
|
const url = new URL(\`\${this.baseUrl}${endpoint}\`);
|
|
644
656
|
|
|
645
|
-
|
|
646
|
-
if (
|
|
647
|
-
url.searchParams.set('limit', String(options.limit));
|
|
648
|
-
}
|
|
649
|
-
if (options?.offset) {
|
|
650
|
-
url.searchParams.set('offset', String(options.offset));
|
|
651
|
-
}
|
|
657
|
+
if (opts?.limit) url.searchParams.set('limit', String(opts.limit));
|
|
658
|
+
if (opts?.offset) url.searchParams.set('offset', String(opts.offset));
|
|
652
659
|
|
|
653
660
|
const response = await fetch(url.toString(), {
|
|
654
661
|
method: '${method.toUpperCase()}',
|
|
@@ -664,29 +671,24 @@ export class ${providerName} implements CollectionProvider {
|
|
|
664
671
|
// Validate response with Zod schema
|
|
665
672
|
const validated = ${validationSchema}.parse(data);
|
|
666
673
|
|
|
667
|
-
|
|
668
|
-
return
|
|
674
|
+
const entries = ${isArray ? "validated" : "[validated]"}.map((item: any) => this.toCollectionEntry(item));
|
|
675
|
+
return { entries, total: entries.length };
|
|
669
676
|
}
|
|
670
677
|
|
|
671
678
|
/**
|
|
672
679
|
* Get a single item by slug
|
|
673
680
|
*/
|
|
674
|
-
async get(slug: string): Promise<
|
|
681
|
+
async get(_collection: string, slug: string): Promise<CollectionEntry | null> {
|
|
675
682
|
${this.generateGetMethod(endpoint, slugField, isArray, collectionName, schemaName, unknownFallback)}
|
|
676
683
|
}
|
|
677
684
|
|
|
678
685
|
/**
|
|
679
|
-
* Convert API response to
|
|
686
|
+
* Convert API response to CollectionEntry
|
|
680
687
|
*/
|
|
681
|
-
private
|
|
688
|
+
private toCollectionEntry(item: any): CollectionEntry {
|
|
682
689
|
return {
|
|
683
690
|
slug: String(item.${slugField}),
|
|
684
|
-
|
|
685
|
-
data: item,
|
|
686
|
-
metadata: {
|
|
687
|
-
source: '${collectionName}',
|
|
688
|
-
_raw: item,
|
|
689
|
-
},
|
|
691
|
+
...item,
|
|
690
692
|
};
|
|
691
693
|
}
|
|
692
694
|
|
|
@@ -729,10 +731,10 @@ export class ${providerName} implements CollectionProvider {
|
|
|
729
731
|
const data = await response.json();
|
|
730
732
|
const validated = ${validationExpr}.parse(data);
|
|
731
733
|
|
|
732
|
-
return this.
|
|
734
|
+
return this.toCollectionEntry(validated);`;
|
|
733
735
|
} else {
|
|
734
|
-
return `const
|
|
735
|
-
return
|
|
736
|
+
return `const result = await this.list(_collection);
|
|
737
|
+
return result.entries.find((item) => item.slug === slug) ?? null;`;
|
|
736
738
|
}
|
|
737
739
|
}
|
|
738
740
|
/**
|
|
@@ -980,13 +982,25 @@ import type { CollectionActionRegistry } from '@stackwright-pro/workflow';
|
|
|
980
982
|
export const actions: CollectionActionRegistry = {};
|
|
981
983
|
`;
|
|
982
984
|
}
|
|
983
|
-
/** Derives the client method name from endpoint + method
|
|
985
|
+
/** Derives the client method name from endpoint + method.
|
|
986
|
+
* Prefers operationId (matching ClientGenerator.getMethodName()), falls back
|
|
987
|
+
* to path+method derivation for specs that omit operationIds. */
|
|
984
988
|
endpointToClientMethod(endpoint, method) {
|
|
989
|
+
const pathItem = this.document.paths?.[endpoint];
|
|
990
|
+
const operation = pathItem?.[method.toLowerCase()];
|
|
991
|
+
if (operation?.operationId) {
|
|
992
|
+
return this.camelCase(operation.operationId);
|
|
993
|
+
}
|
|
985
994
|
const parts = endpoint.split("/").filter(Boolean).filter((p) => !p.startsWith("{")).map((p) => p.replace(/-/g, "_"));
|
|
986
995
|
const prefix = method.toLowerCase();
|
|
987
996
|
const suffix = parts.map((p) => this.capitalize(p)).join("");
|
|
988
997
|
return `${prefix}${suffix}`;
|
|
989
998
|
}
|
|
999
|
+
/** Convert any string to camelCase — handles operationId values like
|
|
1000
|
+
* createOrder, list_items, get-by-id, etc. */
|
|
1001
|
+
camelCase(str) {
|
|
1002
|
+
return str.replace(/[^a-zA-Z0-9]+(.)/g, (_, chr) => chr.toUpperCase()).replace(/^[A-Z]/, (chr) => chr.toLowerCase());
|
|
1003
|
+
}
|
|
990
1004
|
toCamelCase(str) {
|
|
991
1005
|
const parts = str.split(/[-_]/);
|
|
992
1006
|
return parts.map((p, i) => i === 0 ? p.toLowerCase() : this.capitalize(p)).join("");
|
|
@@ -1007,11 +1021,13 @@ var ClientGenerator = class {
|
|
|
1007
1021
|
}
|
|
1008
1022
|
this.requiredSchemas = /* @__PURE__ */ new Set();
|
|
1009
1023
|
this.generatedRequestSchemas = /* @__PURE__ */ new Set();
|
|
1024
|
+
this.generatedRequestTypes = /* @__PURE__ */ new Set();
|
|
1010
1025
|
}
|
|
1011
1026
|
resolver;
|
|
1012
1027
|
schemaMapping;
|
|
1013
1028
|
requiredSchemas;
|
|
1014
1029
|
generatedRequestSchemas;
|
|
1030
|
+
generatedRequestTypes;
|
|
1015
1031
|
/**
|
|
1016
1032
|
* Generate typed API client code from OpenAPI document
|
|
1017
1033
|
*/
|
|
@@ -1030,12 +1046,11 @@ var ClientGenerator = class {
|
|
|
1030
1046
|
}
|
|
1031
1047
|
this.requiredSchemas.clear();
|
|
1032
1048
|
this.generatedRequestSchemas.clear();
|
|
1049
|
+
this.generatedRequestTypes.clear();
|
|
1033
1050
|
let code = this.generateImports(!!schemaMapping, validateResponses);
|
|
1034
1051
|
code += "\n";
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
code += "\n";
|
|
1038
|
-
}
|
|
1052
|
+
code += this.generateRequestSchemas(endpoints);
|
|
1053
|
+
code += "\n";
|
|
1039
1054
|
code += this.generateTypes(endpoints, schemaMapping);
|
|
1040
1055
|
code += "\n";
|
|
1041
1056
|
code += this.generateErrorClasses();
|
|
@@ -1066,9 +1081,9 @@ var ClientGenerator = class {
|
|
|
1066
1081
|
*/
|
|
1067
1082
|
|
|
1068
1083
|
`;
|
|
1069
|
-
|
|
1070
|
-
code += `import { z } from 'zod';
|
|
1084
|
+
code += `import { z } from 'zod';
|
|
1071
1085
|
`;
|
|
1086
|
+
if (useSchemas && validateResponses) {
|
|
1072
1087
|
code += `import * as schemas from './schemas';
|
|
1073
1088
|
`;
|
|
1074
1089
|
}
|
|
@@ -1101,6 +1116,9 @@ var ClientGenerator = class {
|
|
|
1101
1116
|
const schemaName = `${typeName}RequestSchema`;
|
|
1102
1117
|
const schemaCode = this.generateRequestSchemaForEndpoint(endpoint);
|
|
1103
1118
|
if (schemaCode) {
|
|
1119
|
+
if (this.generatedRequestSchemas.has(schemaName)) {
|
|
1120
|
+
continue;
|
|
1121
|
+
}
|
|
1104
1122
|
code += `export const ${schemaName} = ${schemaCode};
|
|
1105
1123
|
|
|
1106
1124
|
`;
|
|
@@ -1152,7 +1170,8 @@ ${parts.join(",\n")}
|
|
|
1152
1170
|
for (const param of params) {
|
|
1153
1171
|
const zodSchema = this.parameterSchemaToZod(param);
|
|
1154
1172
|
const desc = param.description ? `.describe('${this.escapeString(param.description)}')` : "";
|
|
1155
|
-
|
|
1173
|
+
const safeParamKey = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(param.name) ? param.name : `'${param.name}'`;
|
|
1174
|
+
paramSchemas.push(` ${safeParamKey}: ${zodSchema}${desc}`);
|
|
1156
1175
|
}
|
|
1157
1176
|
return `z.object({
|
|
1158
1177
|
${paramSchemas.join(",\n")}
|
|
@@ -1171,7 +1190,8 @@ ${paramSchemas.join(",\n")}
|
|
|
1171
1190
|
if (!param.required) {
|
|
1172
1191
|
zodSchema += ".optional()";
|
|
1173
1192
|
}
|
|
1174
|
-
|
|
1193
|
+
const safeParamKey = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(param.name) ? param.name : `'${param.name}'`;
|
|
1194
|
+
paramSchemas.push(` ${safeParamKey}: ${zodSchema}`);
|
|
1175
1195
|
}
|
|
1176
1196
|
return `z.object({
|
|
1177
1197
|
${paramSchemas.join(",\n")}
|
|
@@ -1244,7 +1264,7 @@ ${paramSchemas.join(",\n")}
|
|
|
1244
1264
|
}
|
|
1245
1265
|
if (schema.type === "object") {
|
|
1246
1266
|
if (!schema.properties) {
|
|
1247
|
-
return "z.record(z.unknown())";
|
|
1267
|
+
return "z.record(z.string(), z.unknown())";
|
|
1248
1268
|
}
|
|
1249
1269
|
const props = [];
|
|
1250
1270
|
const required = schema.required || [];
|
|
@@ -1254,7 +1274,8 @@ ${paramSchemas.join(",\n")}
|
|
|
1254
1274
|
if (!isRequired) {
|
|
1255
1275
|
propZod += ".optional()";
|
|
1256
1276
|
}
|
|
1257
|
-
|
|
1277
|
+
const safeKey = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(key) ? key : `'${key}'`;
|
|
1278
|
+
props.push(`${safeKey}: ${propZod}`);
|
|
1258
1279
|
}
|
|
1259
1280
|
return `z.object({ ${props.join(", ")} })`;
|
|
1260
1281
|
}
|
|
@@ -1412,13 +1433,24 @@ ${paramSchemas.join(",\n")}
|
|
|
1412
1433
|
const operationId = endpoint.operationId || this.getMethodName(endpoint);
|
|
1413
1434
|
const mapping = schemaMapping[operationId];
|
|
1414
1435
|
if (!mapping) {
|
|
1436
|
+
const typeName2 = this.getOperationTypeName(endpoint);
|
|
1437
|
+
const requestSchemaName2 = `${typeName2}RequestSchema`;
|
|
1438
|
+
if (this.generatedRequestSchemas.has(requestSchemaName2) && !this.generatedRequestTypes.has(requestSchemaName2)) {
|
|
1439
|
+
code += `export type ${typeName2}Request = z.infer<typeof ${requestSchemaName2}>;
|
|
1440
|
+
`;
|
|
1441
|
+
this.generatedRequestTypes.add(requestSchemaName2);
|
|
1442
|
+
}
|
|
1443
|
+
code += `export type ${typeName2}Response = unknown;
|
|
1444
|
+
|
|
1445
|
+
`;
|
|
1415
1446
|
continue;
|
|
1416
1447
|
}
|
|
1417
1448
|
const typeName = this.getOperationTypeName(endpoint);
|
|
1418
1449
|
const requestSchemaName = mapping.requestSchema || typeName + "RequestSchema";
|
|
1419
|
-
if (this.generatedRequestSchemas.has(requestSchemaName)) {
|
|
1450
|
+
if (this.generatedRequestSchemas.has(requestSchemaName) && !this.generatedRequestTypes.has(requestSchemaName)) {
|
|
1420
1451
|
this.addRequiredSchema(requestSchemaName);
|
|
1421
1452
|
code += "export type " + typeName + "Request = z.infer<typeof " + requestSchemaName + ">;\n";
|
|
1453
|
+
this.generatedRequestTypes.add(requestSchemaName);
|
|
1422
1454
|
}
|
|
1423
1455
|
this.addRequiredSchema(mapping.responseSchema);
|
|
1424
1456
|
code += `export type ${typeName}Response = z.infer<typeof schemas.${mapping.responseSchema}>;
|
|
@@ -1463,8 +1495,9 @@ ${paramSchemas.join(",\n")}
|
|
|
1463
1495
|
const type = this.getTypeFromSchemaLegacy(param.schema);
|
|
1464
1496
|
const desc = param.description ? ` /** ${param.description} */
|
|
1465
1497
|
` : "";
|
|
1498
|
+
const safeParamKey = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(param.name) ? param.name : `'${param.name}'`;
|
|
1466
1499
|
code += desc;
|
|
1467
|
-
code += ` ${
|
|
1500
|
+
code += ` ${safeParamKey}${required}: ${type};
|
|
1468
1501
|
`;
|
|
1469
1502
|
}
|
|
1470
1503
|
code += " };\n";
|
|
@@ -1477,8 +1510,9 @@ ${paramSchemas.join(",\n")}
|
|
|
1477
1510
|
const type = this.getTypeFromSchemaLegacy(param.schema);
|
|
1478
1511
|
const desc = param.description ? ` /** ${param.description} */
|
|
1479
1512
|
` : "";
|
|
1513
|
+
const safeParamKey = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(param.name) ? param.name : `'${param.name}'`;
|
|
1480
1514
|
code += desc;
|
|
1481
|
-
code += ` ${
|
|
1515
|
+
code += ` ${safeParamKey}${required}: ${type};
|
|
1482
1516
|
`;
|
|
1483
1517
|
}
|
|
1484
1518
|
code += " };\n";
|
|
@@ -1491,8 +1525,9 @@ ${paramSchemas.join(",\n")}
|
|
|
1491
1525
|
const type = this.getTypeFromSchemaLegacy(param.schema);
|
|
1492
1526
|
const desc = param.description ? ` /** ${param.description} */
|
|
1493
1527
|
` : "";
|
|
1528
|
+
const safeParamKey = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(param.name) ? param.name : `'${param.name}'`;
|
|
1494
1529
|
code += desc;
|
|
1495
|
-
code += ` ${
|
|
1530
|
+
code += ` ${safeParamKey}${required}: ${type};
|
|
1496
1531
|
`;
|
|
1497
1532
|
}
|
|
1498
1533
|
code += " };\n";
|
|
@@ -1839,9 +1874,10 @@ ${paramSchemas.join(",\n")}
|
|
|
1839
1874
|
code += ` if (request.query) {
|
|
1840
1875
|
`;
|
|
1841
1876
|
for (const param of queryParams) {
|
|
1842
|
-
|
|
1877
|
+
const safeAccess = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(param.name) ? `.${param.name}` : `['${param.name}']`;
|
|
1878
|
+
code += ` if (request.query${safeAccess} != null) {
|
|
1843
1879
|
`;
|
|
1844
|
-
code += ` searchParams.append('${param.name}', String(request.query
|
|
1880
|
+
code += ` searchParams.append('${param.name}', String(request.query${safeAccess}));
|
|
1845
1881
|
`;
|
|
1846
1882
|
code += ` }
|
|
1847
1883
|
`;
|
|
@@ -1865,7 +1901,7 @@ ${paramSchemas.join(",\n")}
|
|
|
1865
1901
|
code += ` method: '${method.toUpperCase()}',
|
|
1866
1902
|
`;
|
|
1867
1903
|
if (hasBody) {
|
|
1868
|
-
code += ` body: request.body ? JSON.stringify(request.body) :
|
|
1904
|
+
code += ` body: request.body ? JSON.stringify(request.body) : undefined,
|
|
1869
1905
|
`;
|
|
1870
1906
|
}
|
|
1871
1907
|
if (headerParams.length > 0) {
|
|
@@ -1982,9 +2018,10 @@ ${paramSchemas.join(",\n")}
|
|
|
1982
2018
|
code += ` if (request.query) {
|
|
1983
2019
|
`;
|
|
1984
2020
|
for (const param of queryParams) {
|
|
1985
|
-
|
|
2021
|
+
const safeAccess = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(param.name) ? `.${param.name}` : `['${param.name}']`;
|
|
2022
|
+
code += ` if (request.query${safeAccess} != null) {
|
|
1986
2023
|
`;
|
|
1987
|
-
code += ` searchParams.append('${param.name}', String(request.query
|
|
2024
|
+
code += ` searchParams.append('${param.name}', String(request.query${safeAccess}));
|
|
1988
2025
|
`;
|
|
1989
2026
|
code += ` }
|
|
1990
2027
|
`;
|
|
@@ -2006,7 +2043,7 @@ ${paramSchemas.join(",\n")}
|
|
|
2006
2043
|
code += ` method: '${method.toUpperCase()}',
|
|
2007
2044
|
`;
|
|
2008
2045
|
if (hasBody) {
|
|
2009
|
-
code += ` body: request.body ? JSON.stringify(request.body) :
|
|
2046
|
+
code += ` body: request.body ? JSON.stringify(request.body) : undefined,
|
|
2010
2047
|
`;
|
|
2011
2048
|
}
|
|
2012
2049
|
const headerParams = params.filter((p) => p.in === "header");
|
|
@@ -2172,7 +2209,7 @@ ${paramSchemas.join(",\n")}
|
|
|
2172
2209
|
if (endpoint.operationId) {
|
|
2173
2210
|
return this.camelCase(endpoint.operationId);
|
|
2174
2211
|
}
|
|
2175
|
-
const pathParts = endpoint.path.split("/").filter((p) => p
|
|
2212
|
+
const pathParts = endpoint.path.split("/").filter((p) => p).map((p) => p.startsWith("{") && p.endsWith("}") ? p.slice(1, -1) : p).join("-");
|
|
2176
2213
|
return this.camelCase(`${endpoint.method}-${pathParts}`);
|
|
2177
2214
|
}
|
|
2178
2215
|
/**
|
|
@@ -2829,6 +2866,23 @@ var OpenAPIPlugin = class {
|
|
|
2829
2866
|
async processIntegration(config, projectRoot) {
|
|
2830
2867
|
const { name, spec, auth, mockUrl, collections, endpoints, actions } = config;
|
|
2831
2868
|
console.log(` - Processing integration: ${name}`);
|
|
2869
|
+
const httpCollections = (collections || []).filter((c) => {
|
|
2870
|
+
if (c.transport === "websocket") {
|
|
2871
|
+
console.warn(
|
|
2872
|
+
` > Skipping collection "${c.endpoint}" (transport: websocket \u2014 not yet supported)`
|
|
2873
|
+
);
|
|
2874
|
+
return false;
|
|
2875
|
+
}
|
|
2876
|
+
return true;
|
|
2877
|
+
});
|
|
2878
|
+
const hasEndpoints = endpoints && (endpoints.include?.length || endpoints.exclude?.length);
|
|
2879
|
+
const hasActions = actions && actions.length > 0;
|
|
2880
|
+
if (httpCollections.length === 0 && !hasEndpoints && !hasActions) {
|
|
2881
|
+
console.log(
|
|
2882
|
+
" > No HTTP endpoints or REST collections found \u2014 skipping (WebSocket-only integration not yet supported)"
|
|
2883
|
+
);
|
|
2884
|
+
return;
|
|
2885
|
+
}
|
|
2832
2886
|
const specPath = spec.startsWith("http") ? spec : path2.resolve(projectRoot, spec);
|
|
2833
2887
|
const parser = new OpenAPIParser();
|
|
2834
2888
|
const { document } = await parser.parse(specPath);
|
|
@@ -2847,13 +2901,13 @@ var OpenAPIPlugin = class {
|
|
|
2847
2901
|
fs2.mkdirSync(outputDir, { recursive: true });
|
|
2848
2902
|
const schemaMapping = await this.generateSchemas(
|
|
2849
2903
|
document,
|
|
2850
|
-
|
|
2904
|
+
httpCollections,
|
|
2851
2905
|
outputDir,
|
|
2852
2906
|
name,
|
|
2853
2907
|
endpointFilter
|
|
2854
2908
|
);
|
|
2855
|
-
await this.generateTypes(document,
|
|
2856
|
-
if (
|
|
2909
|
+
await this.generateTypes(document, httpCollections, outputDir, name);
|
|
2910
|
+
if (httpCollections.length > 0) {
|
|
2857
2911
|
await this.generateProvider(document, config, outputDir, name);
|
|
2858
2912
|
}
|
|
2859
2913
|
await this.generateClient(document, outputDir, name, schemaMapping, endpointFilter);
|
|
@@ -2961,6 +3015,7 @@ import { z } from 'zod';
|
|
|
2961
3015
|
if (!operation) continue;
|
|
2962
3016
|
const opId = operation.operationId || this.generateOperationId(pathStr, method);
|
|
2963
3017
|
const responseSchemaName = `${this.getOperationTypeName(opId)}ResponseSchema`;
|
|
3018
|
+
if (!generatedSchemas.has(responseSchemaName)) continue;
|
|
2964
3019
|
schemaMapping[opId] = {
|
|
2965
3020
|
requestSchema: null,
|
|
2966
3021
|
// ClientGenerator handles request schema generation + type inference
|
|
@@ -2985,7 +3040,13 @@ import type * as schemas from './schemas';
|
|
|
2985
3040
|
|
|
2986
3041
|
`;
|
|
2987
3042
|
if (collections) {
|
|
3043
|
+
const resolver = new SchemaResolver(document);
|
|
2988
3044
|
for (const collection of collections) {
|
|
3045
|
+
const schema = resolver.getResponseSchema(
|
|
3046
|
+
collection.endpoint,
|
|
3047
|
+
collection.method?.toLowerCase() ?? "get"
|
|
3048
|
+
);
|
|
3049
|
+
if (!schema) continue;
|
|
2989
3050
|
const collectionName = this.sanitizeName(collection.endpoint);
|
|
2990
3051
|
const typeName = this.capitalize(collectionName);
|
|
2991
3052
|
const schemaName = `${typeName}Schema`;
|
|
@@ -3031,13 +3092,20 @@ import type * as schemas from './schemas';
|
|
|
3031
3092
|
collection.endpoint,
|
|
3032
3093
|
collection.method?.toLowerCase() ?? "get"
|
|
3033
3094
|
);
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3095
|
+
if (schema) {
|
|
3096
|
+
const isArray = schema.type === "array";
|
|
3097
|
+
if (isArray) {
|
|
3098
|
+
schemaImports.add(`${this.capitalize(collectionName)}ArraySchema`);
|
|
3099
|
+
} else {
|
|
3100
|
+
schemaImports.add(`${this.capitalize(collectionName)}Schema`);
|
|
3101
|
+
}
|
|
3039
3102
|
}
|
|
3040
3103
|
}
|
|
3104
|
+
const needsZodImport = classBlocks.some((block) => block.includes("z.unknown()"));
|
|
3105
|
+
const zodImportLine = needsZodImport ? `import { z } from 'zod';
|
|
3106
|
+
` : "";
|
|
3107
|
+
const schemaImportLine = schemaImports.size > 0 ? `import { ${Array.from(schemaImports).join(", ")} } from './schemas';
|
|
3108
|
+
` : "";
|
|
3041
3109
|
let providerCode = `/**
|
|
3042
3110
|
* Generated CollectionProvider from OpenAPI spec
|
|
3043
3111
|
* Integration: ${integrationName}
|
|
@@ -3046,9 +3114,8 @@ import type * as schemas from './schemas';
|
|
|
3046
3114
|
* Regenerate by running: pnpm prebuild
|
|
3047
3115
|
*/
|
|
3048
3116
|
|
|
3049
|
-
import type { CollectionProvider,
|
|
3050
|
-
|
|
3051
|
-
|
|
3117
|
+
import type { CollectionProvider, CollectionEntry, CollectionListOptions, CollectionListResult } from '@stackwright/collections';
|
|
3118
|
+
${zodImportLine}${schemaImportLine}
|
|
3052
3119
|
`;
|
|
3053
3120
|
providerCode += classBlocks.join("\n");
|
|
3054
3121
|
fs2.writeFileSync(path2.join(outputDir, "provider.ts"), providerCode);
|