@stackwright-pro/openapi 0.3.0-alpha.4 → 0.3.0-alpha.6
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 +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +73 -21
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +73 -21
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -110,6 +110,8 @@ interface OpenAPIConfig {
|
|
|
110
110
|
slug_field: string;
|
|
111
111
|
/** HTTP method for this collection endpoint — defaults to 'GET' */
|
|
112
112
|
method?: string;
|
|
113
|
+
/** Transport protocol — 'websocket' collections are skipped during prebuild */
|
|
114
|
+
transport?: string;
|
|
113
115
|
/** Optional filters */
|
|
114
116
|
filters?: Record<string, unknown>;
|
|
115
117
|
}>;
|
package/dist/index.d.ts
CHANGED
|
@@ -110,6 +110,8 @@ interface OpenAPIConfig {
|
|
|
110
110
|
slug_field: string;
|
|
111
111
|
/** HTTP method for this collection endpoint — defaults to 'GET' */
|
|
112
112
|
method?: string;
|
|
113
|
+
/** Transport protocol — 'websocket' collections are skipped during prebuild */
|
|
114
|
+
transport?: string;
|
|
113
115
|
/** Optional filters */
|
|
114
116
|
filters?: Record<string, unknown>;
|
|
115
117
|
}>;
|
package/dist/index.js
CHANGED
|
@@ -45,8 +45,18 @@ var OpenAPIParser = class {
|
|
|
45
45
|
await SwaggerParser__default.default.validate(api);
|
|
46
46
|
}
|
|
47
47
|
let document;
|
|
48
|
+
let dereferenced = false;
|
|
48
49
|
if (dereference) {
|
|
49
|
-
|
|
50
|
+
try {
|
|
51
|
+
document = await SwaggerParser__default.default.dereference(api);
|
|
52
|
+
dereferenced = true;
|
|
53
|
+
} catch {
|
|
54
|
+
console.warn(
|
|
55
|
+
`[OpenAPIParser] Warning: Could not fully dereference "${specPath}" (dangling $ref or circular ref). Proceeding without full dereferencing \u2014 some schema references may not resolve.`
|
|
56
|
+
);
|
|
57
|
+
document = api;
|
|
58
|
+
dereferenced = false;
|
|
59
|
+
}
|
|
50
60
|
} else {
|
|
51
61
|
document = api;
|
|
52
62
|
}
|
|
@@ -54,7 +64,7 @@ var OpenAPIParser = class {
|
|
|
54
64
|
return {
|
|
55
65
|
document,
|
|
56
66
|
version,
|
|
57
|
-
dereferenced
|
|
67
|
+
dereferenced
|
|
58
68
|
};
|
|
59
69
|
} catch (error) {
|
|
60
70
|
throw this.enhanceError(error, specPath);
|
|
@@ -104,6 +114,19 @@ var OpenAPIParser = class {
|
|
|
104
114
|
};
|
|
105
115
|
|
|
106
116
|
// src/parser/SchemaResolver.ts
|
|
117
|
+
var SUPPORTED_CONTENT_TYPES = [
|
|
118
|
+
"application/json",
|
|
119
|
+
"application/geo+json",
|
|
120
|
+
// GeoJSON (ORS, mapping APIs)
|
|
121
|
+
"application/vnd.api+json",
|
|
122
|
+
// JSON:API
|
|
123
|
+
"application/ld+json",
|
|
124
|
+
// JSON-LD
|
|
125
|
+
"application/problem+json",
|
|
126
|
+
// RFC 7807 problem details
|
|
127
|
+
"*/*"
|
|
128
|
+
// wildcard fallback
|
|
129
|
+
];
|
|
107
130
|
var SchemaResolver = class {
|
|
108
131
|
/**
|
|
109
132
|
* @param document - Fully dereferenced OpenAPI document. Pass the result of
|
|
@@ -136,16 +159,19 @@ var SchemaResolver = class {
|
|
|
136
159
|
const extractSchema = (responseCode) => {
|
|
137
160
|
const response = responses[responseCode];
|
|
138
161
|
if (!response) return void 0;
|
|
139
|
-
const
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
162
|
+
for (const contentType of SUPPORTED_CONTENT_TYPES) {
|
|
163
|
+
const content = response.content?.[contentType];
|
|
164
|
+
if (!content?.schema) continue;
|
|
165
|
+
const schema = content.schema;
|
|
166
|
+
if ("$ref" in schema) {
|
|
167
|
+
console.warn(
|
|
168
|
+
`[SchemaResolver] Unresolved $ref in schema for ${path3}. Ensure the document is fully dereferenced before passing to SchemaResolver.`
|
|
169
|
+
);
|
|
170
|
+
return schema;
|
|
171
|
+
}
|
|
146
172
|
return schema;
|
|
147
173
|
}
|
|
148
|
-
return
|
|
174
|
+
return void 0;
|
|
149
175
|
};
|
|
150
176
|
const preferred = extractSchema(preferredResponseCode);
|
|
151
177
|
if (preferred) return preferred;
|
|
@@ -556,9 +582,12 @@ var CollectionProviderGenerator = class {
|
|
|
556
582
|
const resolver = new SchemaResolver(this.document);
|
|
557
583
|
const schema = resolver.getResponseSchema(endpoint, method);
|
|
558
584
|
if (!schema) {
|
|
559
|
-
|
|
585
|
+
console.warn(
|
|
586
|
+
` > No response schema found for ${method.toUpperCase()} ${endpoint} \u2014 falling back to z.unknown()`
|
|
587
|
+
);
|
|
560
588
|
}
|
|
561
|
-
const isArray = schema.type === "array";
|
|
589
|
+
const isArray = schema != null ? schema.type === "array" : false;
|
|
590
|
+
const unknownFallback = schema == null;
|
|
562
591
|
const schemaName = `${this.capitalize(collectionName)}Schema`;
|
|
563
592
|
const params = {
|
|
564
593
|
providerName,
|
|
@@ -569,6 +598,7 @@ var CollectionProviderGenerator = class {
|
|
|
569
598
|
baseUrl,
|
|
570
599
|
schemaName,
|
|
571
600
|
isArray,
|
|
601
|
+
unknownFallback,
|
|
572
602
|
bare
|
|
573
603
|
};
|
|
574
604
|
if (auth !== void 0) {
|
|
@@ -590,13 +620,17 @@ var CollectionProviderGenerator = class {
|
|
|
590
620
|
auth,
|
|
591
621
|
schemaName,
|
|
592
622
|
isArray,
|
|
623
|
+
unknownFallback,
|
|
593
624
|
bare
|
|
594
625
|
} = params;
|
|
595
626
|
const authHeader = this.generateAuthHeader(auth);
|
|
596
627
|
const arraySchemaName = `${schemaName.replace(/Schema$/, "")}ArraySchema`;
|
|
597
|
-
const validationSchema = isArray ? arraySchemaName : schemaName;
|
|
598
|
-
const imports = bare ? "" : `import type { CollectionProvider, CollectionItem } from '@stackwright/collections';
|
|
599
|
-
import {
|
|
628
|
+
const validationSchema = unknownFallback ? "z.unknown()" : isArray ? arraySchemaName : schemaName;
|
|
629
|
+
const imports = bare ? "" : unknownFallback ? `import type { CollectionProvider, CollectionItem } from '@stackwright/collections';
|
|
630
|
+
import { z } from 'zod';
|
|
631
|
+
|
|
632
|
+
` : `import type { CollectionProvider, CollectionItem } from '@stackwright/collections';
|
|
633
|
+
import { ${isArray ? arraySchemaName : schemaName} } from './schemas';
|
|
600
634
|
|
|
601
635
|
`;
|
|
602
636
|
return `${imports}/**
|
|
@@ -660,7 +694,7 @@ export class ${providerName} implements CollectionProvider {
|
|
|
660
694
|
* Get a single item by slug
|
|
661
695
|
*/
|
|
662
696
|
async get(slug: string): Promise<CollectionItem | null> {
|
|
663
|
-
${this.generateGetMethod(endpoint, slugField, isArray, collectionName, schemaName)}
|
|
697
|
+
${this.generateGetMethod(endpoint, slugField, isArray, collectionName, schemaName, unknownFallback)}
|
|
664
698
|
}
|
|
665
699
|
|
|
666
700
|
/**
|
|
@@ -696,7 +730,8 @@ export class ${providerName} implements CollectionProvider {
|
|
|
696
730
|
/**
|
|
697
731
|
* Generate get method implementation
|
|
698
732
|
*/
|
|
699
|
-
generateGetMethod(endpoint, slugField, isArray, collectionName, schemaName) {
|
|
733
|
+
generateGetMethod(endpoint, slugField, isArray, collectionName, schemaName, unknownFallback = false) {
|
|
734
|
+
const validationExpr = unknownFallback ? "z.unknown()" : schemaName;
|
|
700
735
|
if (endpoint.includes("{id}") || endpoint.includes(":id")) {
|
|
701
736
|
const detailEndpoint = endpoint.replace("{id}", "${slug}").replace(":id", "${slug}");
|
|
702
737
|
return `const url = \`\${this.baseUrl}${detailEndpoint}\`;
|
|
@@ -714,7 +749,7 @@ export class ${providerName} implements CollectionProvider {
|
|
|
714
749
|
}
|
|
715
750
|
|
|
716
751
|
const data = await response.json();
|
|
717
|
-
const validated = ${
|
|
752
|
+
const validated = ${validationExpr}.parse(data);
|
|
718
753
|
|
|
719
754
|
return this.toCollectionItem(validated);`;
|
|
720
755
|
} else {
|
|
@@ -2816,6 +2851,23 @@ var OpenAPIPlugin = class {
|
|
|
2816
2851
|
async processIntegration(config, projectRoot) {
|
|
2817
2852
|
const { name, spec, auth, mockUrl, collections, endpoints, actions } = config;
|
|
2818
2853
|
console.log(` - Processing integration: ${name}`);
|
|
2854
|
+
const httpCollections = (collections || []).filter((c) => {
|
|
2855
|
+
if (c.transport === "websocket") {
|
|
2856
|
+
console.warn(
|
|
2857
|
+
` > Skipping collection "${c.endpoint}" (transport: websocket \u2014 not yet supported)`
|
|
2858
|
+
);
|
|
2859
|
+
return false;
|
|
2860
|
+
}
|
|
2861
|
+
return true;
|
|
2862
|
+
});
|
|
2863
|
+
const hasEndpoints = endpoints && (endpoints.include?.length || endpoints.exclude?.length);
|
|
2864
|
+
const hasActions = actions && actions.length > 0;
|
|
2865
|
+
if (httpCollections.length === 0 && !hasEndpoints && !hasActions) {
|
|
2866
|
+
console.log(
|
|
2867
|
+
" > No HTTP endpoints or REST collections found \u2014 skipping (WebSocket-only integration not yet supported)"
|
|
2868
|
+
);
|
|
2869
|
+
return;
|
|
2870
|
+
}
|
|
2819
2871
|
const specPath = spec.startsWith("http") ? spec : path2__default.default.resolve(projectRoot, spec);
|
|
2820
2872
|
const parser = new OpenAPIParser();
|
|
2821
2873
|
const { document } = await parser.parse(specPath);
|
|
@@ -2834,13 +2886,13 @@ var OpenAPIPlugin = class {
|
|
|
2834
2886
|
fs2__default.default.mkdirSync(outputDir, { recursive: true });
|
|
2835
2887
|
const schemaMapping = await this.generateSchemas(
|
|
2836
2888
|
document,
|
|
2837
|
-
|
|
2889
|
+
httpCollections,
|
|
2838
2890
|
outputDir,
|
|
2839
2891
|
name,
|
|
2840
2892
|
endpointFilter
|
|
2841
2893
|
);
|
|
2842
|
-
await this.generateTypes(document,
|
|
2843
|
-
if (
|
|
2894
|
+
await this.generateTypes(document, httpCollections, outputDir, name);
|
|
2895
|
+
if (httpCollections.length > 0) {
|
|
2844
2896
|
await this.generateProvider(document, config, outputDir, name);
|
|
2845
2897
|
}
|
|
2846
2898
|
await this.generateClient(document, outputDir, name, schemaMapping, endpointFilter);
|