openapi-sync 5.0.7 → 6.0.0

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.
@@ -0,0 +1 @@
1
+ var m=Object.defineProperty,n=Object.defineProperties;var o=Object.getOwnPropertyDescriptors;var i=Object.getOwnPropertySymbols;var p=Object.prototype.hasOwnProperty,q=Object.prototype.propertyIsEnumerable;var j=(c,a,b)=>a in c?m(c,a,{enumerable:true,configurable:true,writable:true,value:b}):c[a]=b,r=(c,a)=>{for(var b in a||(a={}))p.call(a,b)&&j(c,b,a[b]);if(i)for(var b of i(a))q.call(a,b)&&j(c,b,a[b]);return c},s=(c,a)=>n(c,o(a));var t=(c=>typeof require!="undefined"?require:typeof Proxy!="undefined"?new Proxy(c,{get:(a,b)=>(typeof require!="undefined"?require:a)[b]}):c)(function(c){if(typeof require!="undefined")return require.apply(this,arguments);throw Error('Dynamic require of "'+c+'" is not supported')});var u=(c,a,b)=>()=>{if(b)throw b[0];try{return c&&(a=c(c=0)),a}catch(e){throw b=[e],e}};var v=(c,a)=>()=>{try{return a||c((a={exports:{}}).exports,a),a.exports}catch(b){throw a=0,b}};var w=(c,a,b)=>new Promise((e,h)=>{var k=d=>{try{f(b.next(d));}catch(g){h(g);}},l=d=>{try{f(b.throw(d));}catch(g){h(g);}},f=d=>d.done?e(d.value):Promise.resolve(d.value).then(k,l);f((b=b.apply(c,a)).next());});export{r as a,s as b,t as c,u as d,v as e,w as f};
package/dist/index.d.mts CHANGED
@@ -417,6 +417,84 @@ type IOpenApiSecuritySchemes = {
417
417
  name?: string;
418
418
  };
419
419
  };
420
+ /**
421
+ * Summary of a single endpoint returned by {@link ListEndpoints}.
422
+ *
423
+ * AI agents can use this to understand the API surface before deciding which
424
+ * client type to generate, or to filter endpoints by tag/method.
425
+ *
426
+ * @public
427
+ */
428
+ type EndpointSummary = {
429
+ /** Camel-case function name used in generated code (e.g. `getPetById`) */
430
+ name: string;
431
+ /** HTTP method in uppercase (e.g. `"GET"`) */
432
+ method: string;
433
+ /** OpenAPI path (e.g. `"/pet/{petId}"`) */
434
+ path: string;
435
+ /** OpenAPI operation ID if present */
436
+ operationId?: string;
437
+ /** OpenAPI tags assigned to this endpoint */
438
+ tags?: string[];
439
+ /** Short human-readable summary from the OpenAPI spec */
440
+ summary?: string;
441
+ };
442
+ /**
443
+ * Structured result returned by {@link Init} and {@link GenerateClient}.
444
+ *
445
+ * When the CLI is invoked with `--json`, this object is serialized to stdout.
446
+ * AI agents should parse this to determine success, discover written files,
447
+ * and surface errors without screen-scraping emoji log lines.
448
+ *
449
+ * @example
450
+ * ```ts
451
+ * const result = await Init();
452
+ * if (!result.success) {
453
+ * console.error(result.errors);
454
+ * } else {
455
+ * console.log(`Wrote ${result.filesWritten.length} files`);
456
+ * }
457
+ * ```
458
+ *
459
+ * @public
460
+ */
461
+ type SyncResult = {
462
+ /** Whether the operation completed without fatal errors */
463
+ success: boolean;
464
+ /** API name(s) that were processed */
465
+ apis: string[];
466
+ /** Absolute paths of all files written to disk */
467
+ filesWritten: string[];
468
+ /** Total number of endpoints discovered across all processed APIs */
469
+ endpointCount: number;
470
+ /** Non-fatal warnings (e.g. skipped endpoints) */
471
+ warnings: string[];
472
+ /** Fatal or per-file error messages; non-empty when `success` is false */
473
+ errors: string[];
474
+ };
475
+ /**
476
+ * Structured result returned by {@link ValidateConfig}.
477
+ *
478
+ * Lets agents check configuration and spec validity before running a full sync.
479
+ * No files are written to disk during validation.
480
+ *
481
+ * @public
482
+ */
483
+ type ValidationResult = {
484
+ /** Whether the config file and all specs are valid */
485
+ valid: boolean;
486
+ /** Per-API validation results */
487
+ apis: Record<string, {
488
+ /** Whether this specific API's spec is valid and reachable */
489
+ valid: boolean;
490
+ /** Number of endpoints found in the spec */
491
+ endpointCount: number;
492
+ /** Error message if invalid */
493
+ error?: string;
494
+ }>;
495
+ /** Config-level errors (missing fields, bad types, etc.) */
496
+ configErrors: string[];
497
+ };
420
498
 
421
499
  /**
422
500
  * Check if a value is a JSON object
@@ -584,19 +662,184 @@ declare const variableName: RegExp;
584
662
  declare const variableNameChar: RegExp;
585
663
 
586
664
  /**
587
- * Initialize and sync all OpenAPI specifications
665
+ * @fileoverview Typed error classes for openapi-sync.
588
666
  *
589
- * This is the main entry point for the OpenAPI sync process. It loads the configuration,
590
- * resets the state, and syncs all configured APIs to generate types, endpoints, and validations.
667
+ * Each error class exposes a stable `code` string so that AI agents and
668
+ * programmatic consumers can reliably branch on error type without parsing
669
+ * human-readable messages.
591
670
  *
592
- * @param {Object} [options] - Optional configuration
593
- * @param {number} [options.refetchInterval] - Interval in milliseconds to automatically refetch specifications
594
- * @returns {Promise<void>}
595
- * @throws {Error} When configuration is not found or sync fails
671
+ * When the CLI is invoked with `--json`, errors are serialized to:
672
+ * ```json
673
+ * { "success": false, "error": { "code": "SPEC_FETCH_FAILED", "message": "...", "url": "..." } }
674
+ * ```
675
+ *
676
+ * @public
677
+ */
678
+ /**
679
+ * Base class for all openapi-sync errors.
680
+ *
681
+ * @public
682
+ */
683
+ declare abstract class OpenApiSyncError extends Error {
684
+ abstract readonly code: string;
685
+ /**
686
+ * Serialize this error to a plain object suitable for JSON output.
687
+ * @returns Plain object with `code`, `message`, and any extra fields.
688
+ */
689
+ toJSON(): Record<string, unknown>;
690
+ }
691
+ /**
692
+ * Thrown when no configuration file (`openapi.sync.ts/js/json`) is found
693
+ * in the current working directory.
596
694
  *
597
695
  * @example
598
- * // Sync once
599
- * await Init();
696
+ * // Agent can catch and suggest a fix:
697
+ * catch (e) {
698
+ * if (e instanceof ConfigNotFoundError) {
699
+ * // run: npx openapi-sync init --no-interactive --api-name X --api-url Y
700
+ * }
701
+ * }
702
+ *
703
+ * @public
704
+ */
705
+ declare class ConfigNotFoundError extends OpenApiSyncError {
706
+ readonly code: "CONFIG_NOT_FOUND";
707
+ constructor(searchedPaths: string[]);
708
+ toJSON(): {
709
+ searchedPaths: string;
710
+ };
711
+ }
712
+ /**
713
+ * Thrown when the configuration file exists but fails to parse or export a
714
+ * valid config object.
715
+ *
716
+ * @public
717
+ */
718
+ declare class ConfigParseError extends OpenApiSyncError {
719
+ readonly code: "CONFIG_PARSE_FAILED";
720
+ readonly configPath: string;
721
+ constructor(configPath: string, cause: unknown);
722
+ toJSON(): {
723
+ configPath: string;
724
+ };
725
+ }
726
+ /**
727
+ * Thrown when the config object is missing required fields or contains
728
+ * invalid values (e.g., empty `api` map, unsupported client type).
729
+ *
730
+ * @public
731
+ */
732
+ declare class ConfigValidationError extends OpenApiSyncError {
733
+ readonly code: "CONFIG_INVALID";
734
+ readonly field: string;
735
+ constructor(field: string, reason: string);
736
+ toJSON(): {
737
+ field: string;
738
+ };
739
+ }
740
+ /**
741
+ * Thrown when fetching an OpenAPI spec from a URL fails (network error,
742
+ * timeout, non-2xx response, etc.).
743
+ *
744
+ * @public
745
+ */
746
+ declare class SpecFetchError extends OpenApiSyncError {
747
+ readonly code: "SPEC_FETCH_FAILED";
748
+ readonly url: string;
749
+ readonly statusCode?: number;
750
+ constructor(url: string, cause: unknown, statusCode?: number);
751
+ toJSON(): {
752
+ statusCode?: number | undefined;
753
+ url: string;
754
+ };
755
+ }
756
+ /**
757
+ * Thrown when reading a local OpenAPI spec file fails (file not found,
758
+ * permission denied, etc.).
759
+ *
760
+ * @public
761
+ */
762
+ declare class SpecReadError extends OpenApiSyncError {
763
+ readonly code: "SPEC_READ_FAILED";
764
+ readonly filePath: string;
765
+ constructor(filePath: string, cause: unknown);
766
+ toJSON(): {
767
+ filePath: string;
768
+ };
769
+ }
770
+ /**
771
+ * Thrown when the OpenAPI spec is fetched/read successfully but fails to
772
+ * parse or validate as a valid OpenAPI document.
773
+ *
774
+ * @public
775
+ */
776
+ declare class SpecParseError extends OpenApiSyncError {
777
+ readonly code: "SPEC_PARSE_FAILED";
778
+ readonly api: string;
779
+ constructor(api: string, cause: unknown);
780
+ toJSON(): {
781
+ api: string;
782
+ };
783
+ }
784
+ /**
785
+ * Thrown when writing a generated file to disk fails.
786
+ *
787
+ * @public
788
+ */
789
+ declare class GenerationError extends OpenApiSyncError {
790
+ readonly code: "GENERATION_FAILED";
791
+ readonly file: string;
792
+ readonly api: string;
793
+ constructor(file: string, api: string, cause: unknown);
794
+ toJSON(): {
795
+ file: string;
796
+ api: string;
797
+ };
798
+ }
799
+ /**
800
+ * Thrown when an API name specified by the user (e.g. via `--api`) does not
801
+ * exist in the config file.
802
+ *
803
+ * @public
804
+ */
805
+ declare class UnknownApiError extends OpenApiSyncError {
806
+ readonly code: "UNKNOWN_API";
807
+ readonly requestedApi: string;
808
+ readonly availableApis: string[];
809
+ constructor(requestedApi: string, availableApis: string[]);
810
+ toJSON(): {
811
+ requestedApi: string;
812
+ availableApis: string[];
813
+ };
814
+ }
815
+
816
+ /**
817
+ * Initialize and sync all OpenAPI specifications.
818
+ *
819
+ * Loads the configuration, resets state, and syncs all configured APIs to
820
+ * generate TypeScript types, endpoint functions, and validation schemas.
821
+ *
822
+ * **Agent-safe** — call programmatically without any interactive prompts.
823
+ * Returns a structured {@link SyncResult} that can be serialized to JSON.
824
+ *
825
+ * @param {Object} [options] - Optional configuration overrides
826
+ * @param {number} [options.refetchInterval] - Auto-refetch interval in milliseconds
827
+ * @param {boolean} [options.silent=false] - Suppress all console output (useful when the caller handles logging)
828
+ * @returns {Promise<SyncResult>} Structured result describing files written and any errors
829
+ * @throws {ConfigNotFoundError} When no config file is found in the working directory
830
+ * @throws {ConfigParseError} When the config file cannot be parsed
831
+ *
832
+ * @example
833
+ * // Sync once and check results
834
+ * const result = await Init();
835
+ * if (result.success) {
836
+ * console.log(`Wrote ${result.filesWritten.length} files for ${result.endpointCount} endpoints`);
837
+ * }
838
+ *
839
+ * @example
840
+ * // Sync silently and get JSON-serializable result
841
+ * const result = await Init({ silent: true });
842
+ * process.stdout.write(JSON.stringify(result));
600
843
  *
601
844
  * @example
602
845
  * // Sync with auto-refresh every 10 seconds
@@ -606,41 +849,47 @@ declare const variableNameChar: RegExp;
606
849
  */
607
850
  declare const Init: (options?: {
608
851
  refetchInterval?: number;
609
- }) => Promise<void>;
852
+ silent?: boolean;
853
+ }) => Promise<SyncResult>;
610
854
  /**
611
- * Generate API client from OpenAPI specification
855
+ * Generate a type-safe API client from synced OpenAPI specifications.
612
856
  *
613
- * Generates type-safe API clients based on OpenAPI specifications. Supports multiple
614
- * client types including Fetch, Axios, React Query, SWR, and RTK Query. Can filter
615
- * endpoints by tags or specific endpoint names.
857
+ * Supports Fetch, Axios, React Query, SWR, and RTK Query. Runs a sync
858
+ * first to collect up-to-date endpoint information, then generates client files.
859
+ *
860
+ * **Agent-safe** — fully non-interactive. Returns a structured {@link SyncResult}.
616
861
  *
617
862
  * @param {Object} options - Client generation options
618
- * @param {"fetch" | "axios" | "react-query" | "swr" | "rtk-query"} options.type - Type of client to generate
619
- * @param {string} [options.apiName] - Specific API name to generate client for (generates for all if not specified)
863
+ * @param {"fetch"|"axios"|"react-query"|"swr"|"rtk-query"} options.type - Client type to generate
864
+ * @param {string} [options.apiName] - Specific API from config (generates for all if omitted)
620
865
  * @param {string[]} [options.tags] - Filter endpoints by OpenAPI tags
621
866
  * @param {string[]} [options.endpoints] - Filter by specific endpoint operation IDs
622
- * @param {string} [options.outputDir] - Custom output directory for generated client
623
- * @param {string} [options.baseURL] - Base URL for API requests
624
- * @returns {Promise<void>}
625
- * @throws {Error} When API name is not found in configuration or sync fails
867
+ * @param {string} [options.outputDir] - Custom output directory for generated client files
868
+ * @param {string} [options.baseURL] - Base URL for API requests (baked into the generated client)
869
+ * @param {boolean} [options.silent=false] - Suppress all console output
870
+ * @returns {Promise<SyncResult>} Structured result describing files written and any errors
871
+ * @throws {ConfigNotFoundError} When no config file is found
872
+ * @throws {UnknownApiError} When the specified apiName is not in config
626
873
  *
627
874
  * @example
628
875
  * // Generate Axios client for all APIs
629
- * await GenerateClient({ type: "axios" });
876
+ * const result = await GenerateClient({ type: "axios" });
630
877
  *
631
878
  * @example
632
879
  * // Generate React Query hooks for a specific API
633
- * await GenerateClient({
880
+ * const result = await GenerateClient({
634
881
  * type: "react-query",
635
882
  * apiName: "petstore",
636
- * baseURL: "https://api.example.com"
883
+ * baseURL: "https://api.example.com",
884
+ * silent: true,
637
885
  * });
886
+ * console.log(JSON.stringify(result));
638
887
  *
639
888
  * @example
640
- * // Generate SWR hooks for specific endpoints
641
- * await GenerateClient({
889
+ * // Generate SWR hooks for specific endpoints only
890
+ * const result = await GenerateClient({
642
891
  * type: "swr",
643
- * endpoints: ["getPetById", "createPet"]
892
+ * endpoints: ["getPetById", "createPet"],
644
893
  * });
645
894
  *
646
895
  * @public
@@ -652,23 +901,93 @@ declare const GenerateClient: (options: {
652
901
  endpoints?: string[];
653
902
  outputDir?: string;
654
903
  baseURL?: string;
655
- }) => Promise<void>;
904
+ silent?: boolean;
905
+ }) => Promise<SyncResult>;
656
906
  /**
657
- * Interactive CLI wizard to create configuration
907
+ * Validate the config file and all configured API specs without writing any files.
908
+ *
909
+ * Use this as a pre-flight check before running {@link Init} or {@link GenerateClient}.
910
+ * Safe to run repeatedly — it has no side effects.
911
+ *
912
+ * **Agent-safe** — fully non-interactive. Returns a structured {@link ValidationResult}.
913
+ *
914
+ * @param {Object} [options]
915
+ * @param {boolean} [options.silent=false] - Suppress console output
916
+ * @returns {Promise<ValidationResult>} Structured validation report
917
+ *
918
+ * @example
919
+ * // Validate before syncing
920
+ * const validation = await ValidateConfig();
921
+ * if (!validation.valid) {
922
+ * console.error("Config errors:", validation.configErrors);
923
+ * for (const [api, info] of Object.entries(validation.apis)) {
924
+ * if (!info.valid) console.error(` ${api}: ${info.error}`);
925
+ * }
926
+ * process.exit(1);
927
+ * }
928
+ * await Init();
929
+ *
930
+ * @public
931
+ */
932
+ declare const ValidateConfig: (options?: {
933
+ silent?: boolean;
934
+ }) => Promise<ValidationResult>;
935
+ /**
936
+ * List all endpoints discovered in the configured API specs.
937
+ *
938
+ * Fetches and parses specs, then returns a flat array of endpoint summaries.
939
+ * No files are written. Use this to understand the API surface before
940
+ * deciding which client type or tag filters to apply.
941
+ *
942
+ * **Agent-safe** — fully non-interactive.
943
+ *
944
+ * @param {Object} [options]
945
+ * @param {string} [options.apiName] - Limit to a specific API (lists all if omitted)
946
+ * @param {string[]} [options.tags] - Filter endpoints by OpenAPI tags
947
+ * @param {boolean} [options.silent=false] - Suppress console output
948
+ * @returns {Promise<Record<string, EndpointSummary[]>>} Map of API name → endpoint summaries
949
+ *
950
+ * @example
951
+ * // List all endpoints across all APIs
952
+ * const endpoints = await ListEndpoints();
953
+ * for (const [api, list] of Object.entries(endpoints)) {
954
+ * console.log(`${api}: ${list.length} endpoints`);
955
+ * list.forEach(e => console.log(` ${e.method} ${e.path} — ${e.summary}`));
956
+ * }
957
+ *
958
+ * @example
959
+ * // List only GET endpoints for the petstore API
960
+ * const result = await ListEndpoints({ apiName: "petstore", tags: ["pet"] });
961
+ *
962
+ * @public
963
+ */
964
+ declare const ListEndpoints: (options?: {
965
+ apiName?: string;
966
+ tags?: string[];
967
+ silent?: boolean;
968
+ }) => Promise<Record<string, EndpointSummary[]>>;
969
+ /**
970
+ * Interactive CLI wizard to create configuration.
658
971
  *
659
972
  * Launches an interactive command-line wizard that guides users through
660
- * creating an openapi.sync configuration file. Prompts for API URLs,
661
- * output directories, client generation preferences, and more.
973
+ * creating an `openapi.sync` configuration file.
974
+ *
975
+ * > ⚠️ **Not agent-safe** — this function blocks on stdin prompts.
976
+ * > AI agents should use the CLI with `--no-interactive` instead:
977
+ * > ```bash
978
+ * > npx openapi-sync init --no-interactive --api-name myapi --api-url https://...
979
+ * > ```
662
980
  *
663
981
  * @returns {Promise<void>}
664
982
  * @throws {Error} When wizard is cancelled or configuration creation fails
665
983
  *
666
984
  * @example
667
- * // Run the interactive setup wizard
985
+ * // Human-interactive setup
668
986
  * await InteractiveInit();
669
987
  *
670
988
  * @public
989
+ * @interactiveOnly
671
990
  */
672
991
  declare const InteractiveInit: () => Promise<void>;
673
992
 
674
- export { type ExtractedCustomCode, GenerateClient, type IConfig, type IConfigClientGeneration, type IConfigCustomCode, type IConfigDoc, type IConfigExclude, type IConfigFolderSplit, type IConfigInclude, type IConfigReplaceWord, type IConfigValidations, type IOpenApSchemaSpec, type IOpenApiMediaTypeSpec, type IOpenApiParameterSpec, type IOpenApiRequestBodySpec, type IOpenApiResponseSpec, type IOpenApiSecuritySchemes, type IOpenApiSpec, Init, InteractiveInit, JSONStringify, capitalize, createCustomCodeMarker, extractCustomCode, getEndpointDetails, getNestedValue, isJson, isYamlString, mergeCustomCode, renderTypeRefMD, variableName, variableNameChar, yamlStringToJson };
993
+ export { ConfigNotFoundError, ConfigParseError, ConfigValidationError, type EndpointSummary, type ExtractedCustomCode, GenerateClient, GenerationError, type IConfig, type IConfigClientGeneration, type IConfigCustomCode, type IConfigDoc, type IConfigExclude, type IConfigFolderSplit, type IConfigInclude, type IConfigReplaceWord, type IConfigValidations, type IOpenApSchemaSpec, type IOpenApiMediaTypeSpec, type IOpenApiParameterSpec, type IOpenApiRequestBodySpec, type IOpenApiResponseSpec, type IOpenApiSecuritySchemes, type IOpenApiSpec, Init, InteractiveInit, JSONStringify, ListEndpoints, OpenApiSyncError, SpecFetchError, SpecParseError, SpecReadError, type SyncResult, UnknownApiError, ValidateConfig, type ValidationResult, capitalize, createCustomCodeMarker, extractCustomCode, getEndpointDetails, getNestedValue, isJson, isYamlString, mergeCustomCode, renderTypeRefMD, variableName, variableNameChar, yamlStringToJson };