@stackwright-pro/openapi 0.1.0 → 0.1.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/dist/index.d.mts CHANGED
@@ -1,10 +1,65 @@
1
1
  import { OpenAPIV3, OpenAPIV3_1 } from 'openapi-types';
2
- import { z } from 'zod';
2
+ import { z, ZodSchema } from 'zod';
3
3
 
4
4
  /**
5
5
  * Supported OpenAPI versions
6
6
  */
7
7
  type OpenAPIDocument = OpenAPIV3.Document | OpenAPIV3_1.Document;
8
+ /**
9
+ * An approved API specification.
10
+ * Used by enterprise customers to whitelist specific OpenAPI specs.
11
+ */
12
+ interface ApprovedSpec {
13
+ /** Human-readable name for the spec */
14
+ name: string;
15
+ /** URL or file path to the approved spec */
16
+ url: string;
17
+ /** Expected SHA-256 hash of the spec content */
18
+ sha256: string;
19
+ }
20
+ /**
21
+ * Security configuration for approved specs enforcement.
22
+ * Configured in stackwright.yml under `prebuild.security`.
23
+ */
24
+ interface PrebuildSecurityConfig {
25
+ /** Enable approved-specs enforcement */
26
+ enabled: boolean;
27
+ /** List of approved specifications */
28
+ allowlist: ApprovedSpec[];
29
+ }
30
+ /**
31
+ * Result of validating a spec against the approved list.
32
+ */
33
+ interface ValidationResult {
34
+ /** Whether the spec is approved */
35
+ valid: boolean;
36
+ /** Error code if not valid */
37
+ errorCode?: 'SPEC_NOT_ON_ALLOWLIST' | 'SPEC_MODIFIED' | 'DOWNLOAD_FAILED';
38
+ /** Human-readable error message */
39
+ error?: string;
40
+ /** URL of the spec that was validated */
41
+ specUrl?: string;
42
+ }
43
+ /**
44
+ * Extended site config with prebuild security.
45
+ * Users can add this to stackwright.yml.
46
+ */
47
+ interface SiteConfig {
48
+ /** Prebuild configuration */
49
+ prebuild?: {
50
+ /** Security settings for approved-specs enforcement */
51
+ security?: PrebuildSecurityConfig;
52
+ };
53
+ }
54
+ /**
55
+ * Endpoint filter configuration
56
+ */
57
+ interface EndpointFilter$1 {
58
+ /** Endpoints/patterns to include (default: all) */
59
+ include?: string[];
60
+ /** Endpoints/patterns to exclude */
61
+ exclude?: string[];
62
+ }
8
63
  /**
9
64
  * Configuration for OpenAPI integration in stackwright.yml
10
65
  */
@@ -13,6 +68,8 @@ interface OpenAPIConfig {
13
68
  name: string;
14
69
  /** OpenAPI spec (URL or local file path) */
15
70
  spec: string;
71
+ /** Mock server URL for development (e.g., http://localhost:4010) */
72
+ mockUrl?: string;
16
73
  /** Authentication configuration */
17
74
  auth?: {
18
75
  type: 'bearer' | 'apiKey' | 'oauth2';
@@ -28,6 +85,8 @@ interface OpenAPIConfig {
28
85
  /** Optional filters */
29
86
  filters?: Record<string, unknown>;
30
87
  }>;
88
+ /** Endpoint filter - only generate code for matching endpoints */
89
+ endpoints?: EndpointFilter$1;
31
90
  }
32
91
  /**
33
92
  * Result of OpenAPI compilation
@@ -141,6 +200,8 @@ interface ZodGenerationOptions {
141
200
  optionalByDefault?: boolean;
142
201
  /** Custom name for the generated schema */
143
202
  schemaName?: string;
203
+ /** If true, emit only the schema + type — no import statements */
204
+ bare?: boolean;
144
205
  }
145
206
  /**
146
207
  * Generates Zod schemas from OpenAPI schema objects
@@ -263,6 +324,8 @@ interface ProviderGenerationOptions {
263
324
  type: 'bearer' | 'apiKey';
264
325
  headerName?: string;
265
326
  };
327
+ /** If true, emit only the class code without import statements */
328
+ bare?: boolean;
266
329
  }
267
330
  /**
268
331
  * Generates CollectionProvider implementations from OpenAPI specs
@@ -359,6 +422,7 @@ declare class ClientGenerator {
359
422
  private resolver;
360
423
  private schemaMapping?;
361
424
  private requiredSchemas;
425
+ private generatedRequestSchemas;
362
426
  constructor(document: OpenAPIDocument, schemaMapping?: SchemaMapping);
363
427
  /**
364
428
  * Generate typed API client code from OpenAPI document
@@ -510,6 +574,19 @@ declare class ClientGenerator {
510
574
  * Get all endpoints from the document
511
575
  */
512
576
  private getAllEndpoints;
577
+ /**
578
+ * Deduplicate colliding operationIds by appending a path-based suffix.
579
+ *
580
+ * Real-world specs (e.g. SAM.gov) reuse the same operationId across
581
+ * versioned paths. We make each one unique so the generated client
582
+ * has no duplicate method names.
583
+ */
584
+ private deduplicateOperationIds;
585
+ /**
586
+ * Turn an API path into a valid, readable identifier suffix.
587
+ * e.g. '/entity-information/v4/download-entities' -> 'entityInformationV4DownloadEntities'
588
+ */
589
+ private sanitizePath;
513
590
  /**
514
591
  * Get parameters for an operation
515
592
  */
@@ -604,10 +681,30 @@ interface PrebuildPlugin {
604
681
  * - Typed API client functions
605
682
  *
606
683
  * All generated code is written to src/generated/{integrationName}/
684
+ *
685
+ * === Phase 2: Approved-Specs Enforcement ===
686
+ *
687
+ * Enterprise customers can enable security enforcement via stackwright.yml:
688
+ *
689
+ * ```yaml
690
+ * prebuild:
691
+ * security:
692
+ * enabled: true
693
+ * allowlist:
694
+ * - name: logistics-api
695
+ * url: https://api.gov.mil/logistics/v1/openapi.yaml
696
+ * sha256: a1b2c3d4e5f6...
697
+ * ```
698
+ *
699
+ * This ensures only approved API specs can generate code.
607
700
  */
608
701
  declare class OpenAPIPlugin implements PrebuildPlugin {
609
702
  name: string;
610
703
  beforeBuild(context: PrebuildPluginContext): Promise<void>;
704
+ /**
705
+ * Print a security rejection error in a formatted box
706
+ */
707
+ private printSecurityRejection;
611
708
  private processIntegration;
612
709
  private generateSchemas;
613
710
  private generateTypes;
@@ -631,4 +728,236 @@ declare class OpenAPIPlugin implements PrebuildPlugin {
631
728
  */
632
729
  declare function createOpenAPIPlugin(): PrebuildPlugin;
633
730
 
634
- export { type ClientGenerationOptions, ClientGenerator, type CollectionConfig, CollectionProviderGenerator, OpenAPICollectionProvider, type OpenAPICompilationResult, type OpenAPIConfig, type OpenAPIDocument, OpenAPIParser, OpenAPIPlugin, type ParseOptions, type ParseResult, type ProviderGenerationOptions, type SchemaMapping, SchemaResolver, type TypeGenerationOptions, TypeGenerator, type ZodGenerationOptions, ZodSchemaGenerator, createOpenAPIPlugin };
731
+ /**
732
+ * Configuration for OpenAPI source adapter
733
+ */
734
+ interface OpenAPISourceConfig<T = unknown> {
735
+ /** Base URL of the API */
736
+ baseUrl: string;
737
+ /** API endpoint path */
738
+ endpoint: string;
739
+ /** HTTP method (default: 'get') */
740
+ method?: 'get' | 'post' | 'put' | 'patch' | 'delete';
741
+ /** Optional query parameters */
742
+ params?: Record<string, string | number | boolean>;
743
+ /** Optional request body */
744
+ body?: unknown;
745
+ /** Optional headers */
746
+ headers?: Record<string, string>;
747
+ /** Optional Zod schema for validation */
748
+ schema?: ZodSchema<T>;
749
+ /** Authentication config */
750
+ auth?: {
751
+ type: 'bearer' | 'apiKey';
752
+ token?: string;
753
+ apiKey?: string;
754
+ headerName?: string;
755
+ };
756
+ }
757
+ /**
758
+ * Creates a fetcher function from OpenAPI configuration
759
+ *
760
+ * @example
761
+ * ```typescript
762
+ * const fetcher = createOpenAPIFetcher({
763
+ * baseUrl: 'https://api.example.com',
764
+ * endpoint: '/equipment',
765
+ * auth: { type: 'bearer', token: process.env.API_TOKEN }
766
+ * });
767
+ *
768
+ * // Use with Pulse
769
+ * <Pulse fetcher={fetcher} interval={5000} />
770
+ * ```
771
+ */
772
+ declare function createOpenAPIFetcher<T = unknown>(config: OpenAPISourceConfig<T>): () => Promise<T>;
773
+
774
+ /**
775
+ * Filters endpoints based on include/exclude patterns.
776
+ *
777
+ * Supports:
778
+ * - Exact match: /equipment
779
+ * - Path prefix (includes subpaths): /equipment matches /equipment and /equipment/123
780
+ * - Path with params: /equipment/{id}
781
+ * - Single wildcard: /admin/* matches /admin/users (not /admin itself)
782
+ * - Double wildcard: /admin/** matches /admin/users and /admin/nested/deep
783
+ * - Root path: / matches all paths
784
+ */
785
+ declare class EndpointFilter {
786
+ private include;
787
+ private exclude;
788
+ constructor(filter?: EndpointFilter$1);
789
+ /**
790
+ * Check if a path should be included in code generation.
791
+ *
792
+ * Logic:
793
+ * 1. If path matches any exclude pattern → excluded
794
+ * 2. If path matches any include pattern → included
795
+ * 3. Otherwise → excluded (default deny)
796
+ */
797
+ matches(path: string): boolean;
798
+ /**
799
+ * Match path against a pattern.
800
+ */
801
+ private matchPattern;
802
+ /**
803
+ * Convert a glob-like pattern to a RegExp.
804
+ *
805
+ * Pattern semantics:
806
+ * - `*` = single path segment (no slashes), minimum 1 character
807
+ * - `**` = multiple path segments (includes slashes), zero or more segments
808
+ * - `{param}` = single path segment parameter
809
+ * - Exact match segments are literal strings
810
+ */
811
+ private globToRegex;
812
+ }
813
+
814
+ /**
815
+ * Validates that OpenAPI specs are on the approved list
816
+ * and haven't been modified since approval.
817
+ *
818
+ * @example
819
+ * ```typescript
820
+ * const config: PrebuildSecurityConfig = {
821
+ * enabled: true,
822
+ * allowlist: [
823
+ * {
824
+ * name: 'Government Logistics API',
825
+ * url: 'https://api.gov.mil/logistics/v1/openapi.yaml',
826
+ * sha256: 'a1b2c3d4e5f6...'
827
+ * }
828
+ * ]
829
+ * };
830
+ *
831
+ * const validator = new ApprovedSpecsValidator(config);
832
+ * const result = await validator.validate('https://api.gov.mil/logistics/v1/openapi.yaml');
833
+ *
834
+ * if (!result.valid) {
835
+ * console.error('Security rejection:', result.error);
836
+ * process.exit(1);
837
+ * }
838
+ * ```
839
+ */
840
+ declare class ApprovedSpecsValidator {
841
+ private allowlist;
842
+ private cache;
843
+ private skipHashVerification;
844
+ private readonly ALLOWED_DIRS;
845
+ private readonly MAX_RESPONSE_SIZE;
846
+ /**
847
+ * Create a new ApprovedSpecsValidator
848
+ *
849
+ * @param config - Security configuration from stackwright.yml
850
+ * @param skipHashVerification - Skip hash check (for testing/development)
851
+ */
852
+ constructor(config: PrebuildSecurityConfig, skipHashVerification?: boolean);
853
+ /**
854
+ * Build list of allowed directories for path traversal prevention.
855
+ * Defaults to cwd, specs subdir, and .stackwright cache dir.
856
+ */
857
+ private buildAllowedDirs;
858
+ /**
859
+ * Validate that a file path is within allowed directories (path traversal prevention).
860
+ * Uses realpathSync to resolve symlinks and prevent symlink traversal attacks.
861
+ *
862
+ * @param filePath - File path to validate
863
+ * @returns true if path is allowed, false otherwise
864
+ */
865
+ private isPathAllowed;
866
+ /**
867
+ * Check if a URL/path is a path traversal attempt.
868
+ * This is checked BEFORE the allowlist to prevent bypassing path security.
869
+ *
870
+ * @param specUrl - URL or path to check
871
+ * @returns true if this is a path traversal attempt
872
+ */
873
+ private isPathTraversalAttempt;
874
+ /**
875
+ * Validate that a hex string is a valid SHA-256 hash (64 hex characters).
876
+ *
877
+ * @param hash - String to validate
878
+ * @returns true if valid SHA-256 hex format
879
+ */
880
+ private isValidHex;
881
+ /**
882
+ * Validate that a redirect URL is safe (SSRF protection).
883
+ * Blocks:
884
+ * - HTTPS → HTTP downgrades
885
+ * - Private IP ranges (10.x.x.x, 172.16-31.x.x, 192.168.x.x, 127.x.x.x)
886
+ * - Localhost and loopback addresses
887
+ * - Cloud metadata endpoints
888
+ * - IPv6 private/link-local addresses
889
+ *
890
+ * @param location - Redirect location URL
891
+ * @param originalProtocol - Protocol of the original request
892
+ * @returns true if redirect is safe, false if it should be blocked
893
+ */
894
+ private isRedirectSafe;
895
+ /**
896
+ * Atomically check if path is allowed and read content if so.
897
+ * Prevents TOCTOU race conditions by combining existence check,
898
+ * symlink resolution, path validation, and file read in a single operation.
899
+ *
900
+ * @param filePath - File path to check and read
901
+ * @returns Object with content if successful, or error message
902
+ */
903
+ private readAllowedFile;
904
+ /**
905
+ * Check if security enforcement is enabled
906
+ */
907
+ isEnabled(): boolean;
908
+ /**
909
+ * Get the allowlist count
910
+ */
911
+ getAllowlistCount(): number;
912
+ /**
913
+ * Validate that a spec is on the approved list and matches expected hash.
914
+ *
915
+ * @param specUrl - URL or file path to the spec to validate
916
+ * @returns ValidationResult indicating if the spec is approved
917
+ */
918
+ validate(specUrl: string): Promise<ValidationResult>;
919
+ /**
920
+ * Validate multiple specs at once (batch validation).
921
+ * Fails fast on first error.
922
+ *
923
+ * @param specUrls - Array of URLs/paths to validate
924
+ * @returns Map of URL to ValidationResult
925
+ */
926
+ validateAll(specUrls: string[]): Promise<Map<string, ValidationResult>>;
927
+ /**
928
+ * Validate all specs and return all results (non-fail-fast).
929
+ *
930
+ * @param specUrls - Array of URLs/paths to validate
931
+ * @returns Map of URL to ValidationResult
932
+ */
933
+ validateAllComplete(specUrls: string[]): Promise<Map<string, ValidationResult>>;
934
+ /**
935
+ * Get content hash from cache or download.
936
+ */
937
+ private getHash;
938
+ /**
939
+ * Download content from URL or file.
940
+ * Includes path traversal and SSRF protection.
941
+ */
942
+ private download;
943
+ /**
944
+ * Check if two URLs match (handles trailing slashes, case sensitivity, etc.).
945
+ * Strips credentials and hash to prevent bypass via @ symbol.
946
+ */
947
+ private urlsMatch;
948
+ /**
949
+ * Format error message for spec not on allowlist
950
+ */
951
+ private formatAllowlistError;
952
+ /**
953
+ * Format error message for hash mismatch
954
+ */
955
+ private formatHashMismatchError;
956
+ /**
957
+ * Clear the download cache.
958
+ * Useful for long-running processes.
959
+ */
960
+ clearCache(): void;
961
+ }
962
+
963
+ export { type ApprovedSpec, ApprovedSpecsValidator, type ClientGenerationOptions, ClientGenerator, type CollectionConfig, CollectionProviderGenerator, EndpointFilter, OpenAPICollectionProvider, type OpenAPICompilationResult, type OpenAPIConfig, type OpenAPIDocument, OpenAPIParser, OpenAPIPlugin, type OpenAPISourceConfig, type ParseOptions, type ParseResult, type PrebuildSecurityConfig, type ProviderGenerationOptions, type SchemaMapping, SchemaResolver, type SiteConfig, type TypeGenerationOptions, TypeGenerator, type ValidationResult, type ZodGenerationOptions, ZodSchemaGenerator, createOpenAPIFetcher, createOpenAPIPlugin };