@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 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
- document = await SwaggerParser__default.default.dereference(api);
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: dereference
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 content = response.content?.["application/json"];
140
- const schema = content?.schema;
141
- if (!schema) return void 0;
142
- if ("$ref" in schema) {
143
- console.warn(
144
- `[SchemaResolver] Unresolved $ref in schema for ${path3}. Ensure the document is fully dereferenced before passing to SchemaResolver.`
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 schema;
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
- throw new Error(`No response schema found for ${method.toUpperCase()} ${endpoint}`);
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 { ${isArray ? arraySchemaName : schemaName}} } from './schemas';
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 = ${schemaName}.parse(data);
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
- collections || [],
2889
+ httpCollections,
2838
2890
  outputDir,
2839
2891
  name,
2840
2892
  endpointFilter
2841
2893
  );
2842
- await this.generateTypes(document, collections || [], outputDir, name);
2843
- if (collections && collections.length > 0) {
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);