ng-openapi 0.3.3 → 0.4.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.
Files changed (4) hide show
  1. package/cli.cjs +1412 -340
  2. package/index.d.ts +368 -28
  3. package/index.js +1491 -330
  4. package/package.json +1 -1
package/index.d.ts CHANGED
@@ -277,7 +277,11 @@ interface GeneratorConfig {
277
277
  output: string;
278
278
  /** Distinguishes tokens/providers when several clients coexist in one app. */
279
279
  clientName?: string;
280
- /** Custom acceptance check run on the parsed spec; returning false aborts generation. */
280
+ /**
281
+ * Custom acceptance check run on the parsed spec; returning false aborts
282
+ * generation. Receives the spec as authored — before deep-pointer `$ref`s
283
+ * are inlined (`core/inline-nested-refs.ts`).
284
+ */
281
285
  validateInput?: (spec: SwaggerSpec) => boolean;
282
286
  options: {
283
287
  /** How date/date-time formats are typed in generated models. */
@@ -446,15 +450,20 @@ interface GetMethodGenerationContext {
446
450
  declare class SwaggerParser {
447
451
  private readonly spec;
448
452
  private normalized?;
453
+ /** Non-fatal problems found while normalizing — a parameter with no usable name, for one. */
454
+ private readonly onWarning?;
449
455
  private constructor();
450
456
  /**
451
457
  * Loads, parses and wraps a spec.
452
458
  *
459
+ * @param onWarning receives non-fatal spec problems found while parsing or
460
+ * normalizing — a deep-pointer `$ref` that cannot be inlined, a parameter
461
+ * with no usable name.
453
462
  * @throws SpecLoadError when the file/URL cannot be read.
454
- * @throws SpecParseError when the content cannot be parsed or the
455
- * config's `validateInput` hook rejects the spec.
463
+ * @throws SpecParseError when the content cannot be parsed, `$ref` inlining
464
+ * fails, or the config's `validateInput` hook rejects the spec.
456
465
  */
457
- static create(swaggerPathOrUrl: string, config: GeneratorConfig): Promise<SwaggerParser>;
466
+ static create(swaggerPathOrUrl: string, config: GeneratorConfig, onWarning?: (message: string) => void): Promise<SwaggerParser>;
458
467
  /**
459
468
  * The version-free model generators consume. Computed once and cached —
460
469
  * all generators share the same NormalizedOperation instances, so they
@@ -486,7 +495,7 @@ declare class SwaggerParser {
486
495
  * http-resource body, overloads) — computing it once keeps the derivations
487
496
  * identical by construction.
488
497
  */
489
- declare function normalizeSpec(spec: SwaggerSpec): NormalizedSpec;
498
+ declare function normalizeSpec(spec: SwaggerSpec, onWarning?: (message: string) => void): NormalizedSpec;
490
499
  /**
491
500
  * Normalizes the JSON-Schema constructs OpenAPI 3.1 introduced so generators
492
501
  * never see them:
@@ -511,7 +520,29 @@ declare function normalizeSchema(schema: SwaggerDefinition): SwaggerDefinition;
511
520
  declare class NgOpenApiError extends Error {
512
521
  /** The underlying error that caused this one, when there is one. */
513
522
  readonly cause?: unknown;
514
- constructor(message: string, cause?: unknown);
523
+ protected constructor(message: string, cause?: unknown);
524
+ /**
525
+ * `message` and `name` are non-enumerable on Error, so the default
526
+ * JSON.stringify dropped both — the least useful possible serialization for
527
+ * something that exists to be logged.
528
+ *
529
+ * The payload is spread rather than enumerated: `source`, `issues`,
530
+ * `operation`, `names` and `placeholders` are why these classes are typed
531
+ * in the first place, and listing fields by hand silently drops whichever
532
+ * ones a later subclass adds.
533
+ */
534
+ toJSON(): Record<string, unknown>;
535
+ /**
536
+ * Recognizes branded errors from another bundled copy of this module, so
537
+ * `error instanceof SpecLoadError` works for a plugin-thrown error too.
538
+ * The prototype chain is checked first, so a caller's own subclass of these
539
+ * classes still matches even though it carries no lineage entry.
540
+ *
541
+ * Never throws and never runs foreign code: both reflective reads sit
542
+ * inside the try, because a Proxy traps `getPrototypeOf` just as readily as
543
+ * `getOwnPropertyDescriptor`.
544
+ */
545
+ static [Symbol.hasInstance](value: unknown): boolean;
515
546
  }
516
547
  /**
517
548
  * The spec input could not be read at all: missing/unreadable file,
@@ -526,14 +557,152 @@ declare class SpecLoadError extends NgOpenApiError {
526
557
  }
527
558
  /**
528
559
  * The spec content was read but could not be used: malformed JSON/YAML,
529
- * undeterminable format, an unsupported spec version, or a spec rejected
530
- * by the user's `validateInput` hook.
560
+ * undeterminable format, an unsupported spec version, a spec rejected by the
561
+ * user's `validateInput` hook, or a document pathological enough to break
562
+ * `$ref` inlining (`core/inline-nested-refs.ts`) — `source` is the spec path
563
+ * or URL in that last case.
531
564
  */
532
565
  declare class SpecParseError extends NgOpenApiError {
533
566
  /** The file path or URL the content came from, when known. */
534
567
  readonly source?: string;
535
568
  constructor(message: string, source?: string, cause?: unknown);
536
569
  }
570
+ /** Identifies the operation an emission-time error came from. */
571
+ interface OperationRef {
572
+ /** The operationId, when the spec declares one. */
573
+ operationId?: string;
574
+ method: string;
575
+ path: string;
576
+ }
577
+ /** `(GET) /pets/{id}` — or `getPet ((GET) /pets/{id})` when the spec names it. */
578
+ declare function describeOperation(operation: OperationRef): string;
579
+ /**
580
+ * A name destined for generated code is not a usable TypeScript identifier.
581
+ * The built-in conversions cannot produce one (see `string.utils.ts`), so this
582
+ * only ever reports a name that came from a user hook — today
583
+ * `customizeMethodName` — or an operation missing the `operationId` that hook
584
+ * needs. Raised instead of emitting the name, because a broken identifier
585
+ * surfaces downstream as an opaque ts-morph manipulation error that says
586
+ * nothing about which operation caused it.
587
+ */
588
+ declare class InvalidIdentifierError extends NgOpenApiError {
589
+ /** The rejected name, verbatim; absent when no name could be derived. */
590
+ readonly identifier?: string;
591
+ /** The operation whose name was being derived. */
592
+ readonly operation: OperationRef;
593
+ constructor(message: string, operation: OperationRef, identifier?: string);
594
+ }
595
+ /**
596
+ * Two operations produced the same generated name, which would emit colliding
597
+ * declarations. Distinct from InvalidIdentifierError: each name is valid on its
598
+ * own, they just cannot coexist.
599
+ */
600
+ declare class DuplicateGeneratedNameError extends NgOpenApiError {
601
+ /** The colliding generated names. */
602
+ readonly names: readonly string[];
603
+ /** The operations that produced them, when known. */
604
+ readonly operations: readonly OperationRef[];
605
+ constructor(message: string, names: readonly string[], operations?: readonly OperationRef[]);
606
+ }
607
+ /**
608
+ * A path template contains a `{placeholder}` with no matching parameter. Raised
609
+ * rather than emitted: the unsubstituted placeholder used to ship as literal
610
+ * text in every request URL, which compiles and so escapes every compile-time
611
+ * check the suite has.
612
+ */
613
+ declare class UnresolvedPathTemplateError extends NgOpenApiError {
614
+ /** The path as written in the spec. */
615
+ readonly path: string;
616
+ /** Placeholder names with no declared parameter. */
617
+ readonly placeholders: readonly string[];
618
+ constructor(message: string, path: string, placeholders: readonly string[]);
619
+ }
620
+ /**
621
+ * The user-supplied config is structurally invalid. Collects every issue
622
+ * instead of failing on the first, so a config file can be fixed in one pass.
623
+ */
624
+ declare class ConfigValidationError extends NgOpenApiError {
625
+ readonly issues: readonly string[];
626
+ constructor(issues: readonly string[]);
627
+ }
628
+ /**
629
+ * The config file itself could not be loaded or evaluated — distinct from
630
+ * SpecParseError, which says the *specification* failed to parse.
631
+ */
632
+ declare class ConfigLoadError extends NgOpenApiError {
633
+ /** Path of the config file that failed to load. */
634
+ readonly source: string;
635
+ constructor(message: string, source: string, cause?: unknown);
636
+ }
637
+
638
+ /**
639
+ * Wire names and header values are free-form spec text that ends up inside
640
+ * emitted *string literals*, not just identifiers. Hardening the identifier
641
+ * path left these unguarded:
642
+ *
643
+ * - a quote closes the literal early, which is a syntax error;
644
+ * - a backslash is worse because it is silent: the emitted literal means a
645
+ * different string, so the wrong name goes on the wire and still compiles;
646
+ * - a backtick or an interpolation opener inside a template literal (the
647
+ * request URL) ends the literal or starts an interpolation, failing with the
648
+ * same opaque ts-morph error as #125.
649
+ *
650
+ * Every emitted literal built from spec text must go through one of these.
651
+ */
652
+ /** A single-quoted TypeScript string literal holding exactly `value`. */
653
+ declare function quoteLiteral(value: string): string;
654
+ /** The inside of a single-quoted literal — use when the quotes are already there. */
655
+ declare function escapeSingleQuoted(value: string): string;
656
+ /**
657
+ * Escapes `value` for a position inside a template literal, where a backtick
658
+ * ends the literal and `${` opens an interpolation.
659
+ */
660
+ declare function escapeTemplateLiteral(value: string): string;
661
+ /**
662
+ * An object-literal property key holding exactly `name`.
663
+ *
664
+ * `__proto__` is the one key whose *literal* form carries semantics: as a plain
665
+ * or quoted property key it invokes the prototype setter and creates no own
666
+ * property, so the field silently disappears from the emitted object. A
667
+ * computed key is an ordinary key — used only where it is needed, so normal
668
+ * generated output keeps the more readable quoted form.
669
+ */
670
+ declare function emitObjectKey(name: string, quoteStyle?: "single" | "double"): string;
671
+ /** The inside of a double-quoted literal. */
672
+ declare function escapeDoubleQuoted(value: string): string;
673
+ /**
674
+ * A property name in a *declaration* position (an interface member, a type
675
+ * literal): the bare identifier when it is one, otherwise a quoted and escaped
676
+ * key.
677
+ *
678
+ * The unescaped `"${name}"` this replaces emitted `"say"hi"` for the legal
679
+ * property name `say"hi` — three syntax errors in one model file from a valid
680
+ * spec, with generation reporting success. `__proto__` needs no special
681
+ * treatment here: a declaration has no prototype setter to invoke, unlike the
682
+ * object-literal position that `emitObjectKey` serves.
683
+ */
684
+ declare function emitPropertyName(name: string): string;
685
+ /**
686
+ * Escapes text for a JSDoc block.
687
+ *
688
+ * A `*` followed by `/` closes the comment, so a description containing one
689
+ * ends the block early and everything after it is emitted as code. From a
690
+ * remote spec — ng-openapi accepts a URL as input — that writes arbitrary
691
+ * declarations into the consumer's source tree, and at definition level the
692
+ * result is valid TypeScript, so it compiles and no compile assertion sees it.
693
+ *
694
+ * `*\/` neutralizes it: the backslash has no meaning inside a comment, so it
695
+ * renders as written and cannot terminate the block. (TypeScript's own emitter
696
+ * uses `*_/` for the same job; either works, and this one keeps the text readable.)
697
+ */
698
+ declare function escapeJsDoc(text: string): string;
699
+ /**
700
+ * The `docs` array for a ts-morph structure, or undefined when there is no
701
+ * description. Every generator goes through this rather than building
702
+ * `[description]` inline, so the escape cannot be forgotten at a call site — every
703
+ * count of them written here so far has been wrong within a round.
704
+ */
705
+ declare function emitDocs(description: unknown): string[] | undefined;
537
706
 
538
707
  interface HeadersEmitOptions {
539
708
  /** Identifier of the per-request options parameter in the generated method ("options", "requestOptions", …). */
@@ -564,16 +733,78 @@ declare function emitHeaders(options: HeadersEmitOptions): string;
564
733
  */
565
734
  declare function emitDefaultHeadersMerge(optionsExpression: string, customHeaders: Record<string, string>): string;
566
735
 
736
+ /** What a generator binds besides its arguments, and whether it takes a body. */
737
+ interface ArgumentNameProfile {
738
+ /** Identifiers the emitted method already binds. */
739
+ readonly reserved: readonly string[];
740
+ /** Whether the emitted method takes the JSON request body as a parameter. */
741
+ readonly bindsRequestBody: boolean;
742
+ }
743
+ /**
744
+ * The core service method: `observe`/`options` are its trailing parameters,
745
+ * the rest are locals its body declares (see `emit/url.emit.ts`,
746
+ * `emit/query-params.emit.ts`, `emit/headers.emit.ts` and the form-data blocks
747
+ * of `service-method-body.generator.ts`).
748
+ */
749
+ declare const SERVICE_ARGUMENT_PROFILE: ArgumentNameProfile;
750
+ /**
751
+ * The httpResource plugin's equivalent. Deliberately a different set, not a
752
+ * copy: its trailing parameters are `resourceOptions`/`requestOptions` and it
753
+ * emits no `url`/`formData`/`formBody` locals. Reserving the core's names here
754
+ * would rename plugin parameters for no reason; reserving only the core's would
755
+ * let a `requestOptions` query parameter capture the plugin's own. It also
756
+ * wraps GETs only and never binds a request body, so reserving a body name
757
+ * would burn an identifier no emitted parameter uses.
758
+ */
759
+ declare const RESOURCE_ARGUMENT_PROFILE: ArgumentNameProfile;
760
+ /** A wire name that had to be renamed to stay distinct — surfaced as a warning. */
761
+ interface RenamedArgument {
762
+ /** The wire name, or the type-derived name of the request body. */
763
+ source: string;
764
+ identifier: string;
765
+ }
766
+ interface ArgumentNames {
767
+ /** The identifier bound to `wireName`. */
768
+ of(wireName: string): string;
769
+ /** The identifier of the JSON request body, when the operation has one. */
770
+ readonly body?: string;
771
+ /** Every identifier assigned, for emitters that derive locals from them. */
772
+ readonly all: readonly string[];
773
+ readonly renamed: readonly RenamedArgument[];
774
+ /**
775
+ * Wire names declared in more than one location (a path *and* a query
776
+ * `id`). They collapse to a single parameter, so the later declaration's
777
+ * type is discarded and one value is sent to both — worth a warning.
778
+ */
779
+ readonly merged: readonly string[];
780
+ }
781
+ /**
782
+ * Assigns every argument of one operation a distinct TypeScript identifier.
783
+ *
784
+ * Resolution is per-generator, not per-spec: the set of names already taken is
785
+ * a property of the code being emitted (the core service and the httpResource
786
+ * plugin bind different ones), so this is deliberately NOT precomputed on
787
+ * `NormalizedOperation`. It is a pure function of its inputs, so every call
788
+ * site of one generator gets the same answer without threading it through.
789
+ *
790
+ * Doing it one name at a time — as `camelCase(param.name)` per call site did —
791
+ * cannot see collisions: wire names are free-form, so `filter[name]` and
792
+ * `filter.name` both camelCase to `filterName`, and `options[]` lands on the
793
+ * method's own `options` parameter. Order is significant and must stay stable;
794
+ * it decides which argument keeps the unsuffixed name.
795
+ */
796
+ declare function resolveArgumentNames(operation: NormalizedOperation, config: MethodGenOptions, profile: ArgumentNameProfile): ArgumentNames;
797
+
567
798
  /**
568
799
  * Emits the `HttpParams` accumulation block for the core service method body.
569
800
  * Returns "" when the operation has no query parameters.
570
801
  */
571
- declare function emitQueryParams(queryParams: Parameter[]): string;
802
+ declare function emitQueryParams(queryParams: Parameter[], argumentNames: ArgumentNames): string;
572
803
  /**
573
804
  * Signal-aware variant for the http-resource plugin: each parameter may be a
574
805
  * signal, so its value is read once before the null check.
575
806
  */
576
- declare function emitSignalAwareQueryParams(queryParams: Parameter[]): string;
807
+ declare function emitSignalAwareQueryParams(queryParams: Parameter[], argumentNames: ArgumentNames): string;
577
808
 
578
809
  interface ServiceDecoratorEmitOptions {
579
810
  /** Class decorator flavor from GeneratorConfig; "service" requires Angular 22+. */
@@ -615,23 +846,34 @@ declare function plainParamValue(identifier: string): string;
615
846
  declare function signalAwareParamValue(identifier: string): string;
616
847
  /**
617
848
  * Builds the request-URL template literal, substituting `{param}` placeholders
618
- * with the (camelCased) method parameter identifiers.
849
+ * with the method parameter identifiers `argumentNames` assigned.
619
850
  */
620
- declare function emitUrlExpression(path: string, pathParams: Parameter[], paramValue?: (identifier: string) => string): string;
851
+ declare function emitUrlExpression(path: string, pathParams: Parameter[], argumentNames: ArgumentNames, paramValue?: (identifier: string) => string): string;
621
852
  /** `const url = …;` statement used by the core service method body. */
622
- declare function emitUrlConstruction(path: string, pathParams: Parameter[]): string;
853
+ declare function emitUrlConstruction(path: string, pathParams: Parameter[], argumentNames: ArgumentNames): string;
623
854
 
855
+ /**
856
+ * Whether `name` can be emitted as-is as a TypeScript identifier. Only needed
857
+ * for names ng-openapi did not derive itself — everything out of `camelCase`
858
+ * and `pascalCase` already satisfies this.
859
+ */
860
+ declare function isValidIdentifier(name: string): boolean;
624
861
  /**
625
862
  * Converts a string to camelCase. Dots, dashes, underscores and whitespace are
626
863
  * treated as word separators and removed (`"pet_id"` → `"petId"`,
627
- * `"filter.name"` → `"filterName"`).
864
+ * `"filter.name"` → `"filterName"`), as is every other character illegal in a
865
+ * TypeScript identifier (`"groups_{group_id}_delete"` →
866
+ * `"groupsGroupIdDelete"`), so the result is always a valid identifier.
628
867
  */
629
868
  declare function camelCase(str: string): string;
630
869
  /** Converts a string to kebab-case (`"PetStore"` → `"pet-store"`). */
631
870
  declare function kebabCase(str: string): string;
632
871
  /**
633
872
  * Converts a string to PascalCase. Dots, dashes, underscores and whitespace
634
- * are treated as word separators and removed (`"pet_store"` → `"PetStore"`).
873
+ * are treated as word separators and removed (`"pet_store"` → `"PetStore"`), as
874
+ * is every other character illegal in a TypeScript identifier
875
+ * (`"Groups (yes)"` → `"GroupsYes"`), so the result is always a valid
876
+ * identifier — service and resource class names are built from it.
635
877
  */
636
878
  declare function pascalCase(str: string): string;
637
879
  /** Converts a string to SCREAMING_SNAKE_CASE (`"PetStore"` → `"PET_STORE"`) — used for token names. */
@@ -642,6 +884,8 @@ declare function screamingSnakeCase(str: string): string;
642
884
  * prefixed with `_` so the result is always a valid TS identifier.
643
885
  */
644
886
  declare function pascalCaseForEnums(str: string): string;
887
+ /** Uppercases the first character and leaves the rest exactly as given. */
888
+ declare function capitalizeFirst(str: string): string;
645
889
 
646
890
  /**
647
891
  * Convert OpenAPI/Swagger types to TypeScript types
@@ -654,8 +898,6 @@ declare function pascalCaseForEnums(str: string): string;
654
898
  declare function getTypeScriptType(schemaOrType: TypeSchema | SwaggerDefinition | string | undefined, config: TypeMappingConfig, formatOrNullable?: string | boolean, isNullable?: boolean, context?: "type" | "service"): string;
655
899
  /** Appends `| null` to a type expression when the schema is nullable. */
656
900
  declare function nullableType(type: string, isNullable?: boolean): string;
657
- /** Escapes backslashes and single quotes for embedding in a single-quoted generated literal. */
658
- declare function escapeString(str: string): string;
659
901
 
660
902
  /** The content types the generators special-case when emitting method bodies. */
661
903
  declare const CONTENT_TYPES: {
@@ -690,6 +932,25 @@ declare function listGeneratedBarrelDirs(project: Project, rootPath: string): st
690
932
  * and suffixed, so multiple clients can coexist in one application
691
933
  * (`"PetsApi"` → `BASE_PATH_PETSAPI`); the default client uses `_DEFAULT`.
692
934
  */
935
+ /**
936
+ * The client name generation actually uses. `""` counts as unset: one site
937
+ * used `|| "default"` and two used a default parameter (which fires only on
938
+ * undefined), so an empty clientName produced `BASE_PATH_` in the tokens file
939
+ * and an import of `BASE_PATH_DEFAULT` in the providers — unresolvable.
940
+ */
941
+ declare function effectiveClientName(clientName?: string): string;
942
+ /**
943
+ * clientName as the stem of a generated identifier (`provide<Stem>Client`,
944
+ * `<Stem>BaseInterceptor`, `<Stem>Config`).
945
+ *
946
+ * A name that already is an identifier is capitalized and otherwise kept
947
+ * verbatim — `my_client` gives `provideMy_clientClient`, as it always has.
948
+ * Sending every name through pascalCase would have renamed that to
949
+ * `provideMyClientClient`, the function every consumer imports, for a
950
+ * config that was valid all along. Only a name that could not have compiled
951
+ * before (`my-client`, `my client`) is sanitized.
952
+ */
953
+ declare function clientNameIdentifier(clientName: string): string;
693
954
  /** Token identifying which client a request belongs to (read by interceptors). */
694
955
  declare function getClientContextTokenName(clientName?: string): string;
695
956
  /** Token providing the API base path for the client. */
@@ -709,9 +970,88 @@ declare function getResourceClassName(controllerName: string, naming?: NameDecor
709
970
  */
710
971
  declare function getModelTypeName(rawName: string, naming?: NameDecoration): string;
711
972
 
712
- /** Whether two or more declarations share a name — generation aborts on colliding method names. */
973
+ /**
974
+ * Groups operations into the per-controller buckets each client generator
975
+ * emits one file from: the first tag when there is one, otherwise the second
976
+ * path segment, otherwise "Default".
977
+ *
978
+ * Shared because the core service generator, the httpResource plugin and the
979
+ * zod plugin must agree on the grouping — a controller name decides a class
980
+ * name AND a file name, and the barrel generators re-derive the class name
981
+ * from the file name.
982
+ *
983
+ * `onWarning` fires when two distinct *tags* normalize onto one controller
984
+ * ("Groups (yes)" and "Groups-yes" both become "GroupsYes"). They are merged
985
+ * into a single file rather than dropped, but silently merging two documented
986
+ * tags is worth saying out loud. Path-derived names are excluded: a tagged
987
+ * `Users` operation next to an untagged `/users/...` one is the ordinary
988
+ * partially-tagged spec, and merging them is the intended behaviour.
989
+ */
990
+ declare function groupOperationsByController(operations: NormalizedOperation[], onWarning?: (message: string) => void): Record<string, NormalizedOperation[]>;
991
+
992
+ /**
993
+ * Valid identifiers that still cannot name a generated method.
994
+ *
995
+ * `constructor` declares the class constructor, so ts-morph rejects a method
996
+ * by that name. The rest are members the generated classes bind themselves
997
+ * (`httpClient` in the service; `basePath`, `clientContextToken` and the context helper in
998
+ * both): an
999
+ * operationId of `basePath` emitted a method next to the property of the same
1000
+ * name — TS2300 ten times over, reported as success. The same insight
1001
+ * ArgumentNameProfile.reserved encodes for parameters, applied to methods.
1002
+ * Other reserved words are fine — `class() {}` is a legal member.
1003
+ */
1004
+ declare const RESERVED_MEMBER_NAMES: readonly string[];
1005
+ /**
1006
+ * The derived method name that collided with a reserved member and was
1007
+ * renamed, or undefined when it did not. For the generators to warn on: a
1008
+ * rename is part of the public signature and must not be silent.
1009
+ */
1010
+ declare function reservedMemberCollision(operation: NormalizedOperation, config: MethodGenOptions): {
1011
+ from: string;
1012
+ to: string;
1013
+ } | undefined;
1014
+ /**
1015
+ * Single source of truth for the method name of an operation, shared by the
1016
+ * service generator and the httpResource plugin — the two emit different
1017
+ * clients over the same operations, and a user switching between them (or
1018
+ * running both) must get the same method names.
1019
+ */
1020
+ declare function getOperationMethodName(operation: NormalizedOperation, config: MethodGenOptions): string;
1021
+
1022
+ /** What the assertion reads off a class: ts-morph's ClassDeclaration satisfies it structurally. */
1023
+ interface ClassMembers {
1024
+ getMethods(): readonly {
1025
+ getName(): string;
1026
+ }[];
1027
+ getProperties(): readonly {
1028
+ getName(): string;
1029
+ }[];
1030
+ }
1031
+ /**
1032
+ * Throws when two operations produced the same method name, or a method name
1033
+ * landed on a property the class binds itself. One implementation for the
1034
+ * service and resource generators — the two copies had already begun to
1035
+ * drift, and the property check was missing from both.
1036
+ */
1037
+ declare function assertDistinctMemberNames(serviceClass: ClassMembers, className: string, operations: NormalizedOperation[], methodNameOf: (operation: NormalizedOperation) => string): void;
1038
+
1039
+ /**
1040
+ * Whether two or more declarations share a name.
1041
+ *
1042
+ * @deprecated The generators use `assertDistinctMemberNames`, which also
1043
+ * catches a method landing on a property and reports the colliding operations.
1044
+ * Kept because it was a public export; it will be removed in the next major.
1045
+ */
713
1046
  declare function hasDuplicateFunctionNames<T extends MethodDeclaration | FunctionDeclaration>(arr: T[]): boolean;
714
1047
 
1048
+ /** Outcome of resolving a `$ref` parameter: the component, or why not. */
1049
+ type ParameterResolution = {
1050
+ parameter: Parameter;
1051
+ } | {
1052
+ problem: string;
1053
+ };
1054
+ type ResolveParameter = (ref: string) => ParameterResolution;
715
1055
  /**
716
1056
  * Flattens the spec's `paths` object into one PathInfo per (path, method)
717
1057
  * pair, merging path-level parameters into each operation. Supports both
@@ -720,7 +1060,7 @@ declare function hasDuplicateFunctionNames<T extends MethodDeclaration | Functio
720
1060
  */
721
1061
  declare function extractPaths(swaggerPaths?: {
722
1062
  [p: string]: Path;
723
- }, methods?: string[]): PathInfo[];
1063
+ }, methods?: string[], onWarning?: (message: string) => void, resolveParameter?: ResolveParameter): PathInfo[];
724
1064
 
725
1065
  /** Result of analyzing a response's content types once for both concerns. */
726
1066
  interface ResponseTypeInfo {
@@ -870,15 +1210,15 @@ declare function validateInput(inputPath: string): void;
870
1210
  */
871
1211
  declare function generateFromConfig(config: GeneratorConfig, reporter?: Reporter): Promise<GenerationResult>;
872
1212
 
1213
+ declare function validateGeneratorConfig(config: unknown): asserts config is GeneratorConfig;
1214
+
873
1215
  /**
874
- * Thrown when the user-supplied config is structurally invalid. Collects every
875
- * issue instead of failing on the first one, so a config file can be fixed in
876
- * one pass.
1216
+ * Loads and normalizes a config file.
1217
+ *
1218
+ * Its own module rather than part of cli.ts: importing cli.ts runs the CLI
1219
+ * (commander parses argv at import time), so the config-load contract — which
1220
+ * hosts branch on via ConfigLoadError — could not otherwise be tested.
877
1221
  */
878
- declare class ConfigValidationError extends Error {
879
- readonly issues: string[];
880
- constructor(issues: string[]);
881
- }
882
- declare function validateGeneratorConfig(config: unknown): asserts config is GeneratorConfig;
1222
+ declare function loadConfigFile(configPath: string): Promise<GeneratorConfig>;
883
1223
 
884
- export { BASE_INTERCEPTOR_HEADER_COMMENT, CONTENT_TYPES, ConfigValidationError, type EnumValueObject, type GenerationPhase, type GenerationResult, type GeneratorConfig, type GetMethodGenerationContext, HTTP_RESOURCE_GENERATOR_HEADER_COMMENT, type HeadersEmitOptions, type IPluginGenerator, type IPluginGeneratorClass, MAIN_INDEX_GENERATOR_HEADER_COMMENT, type MethodGenOptions, type MethodGenerationContext, type NameDecoration, type NamingOptions, NgOpenApiError, type NgOpenapiClientConfig, type NormalizedOperation, type NormalizedSpec, type OpenApiSecurityScheme, PROVIDER_GENERATOR_HEADER_COMMENT, type Parameter, type PathInfo, type PluginGeneratorContext, REQUEST_PARAMS_GENERATOR_HEADER_COMMENT, type Reporter, type RequestBody, type ResponseKind, type ResponseTypeInfo, SERVICE_GENERATOR_HEADER_COMMENT, SERVICE_INDEX_GENERATOR_HEADER_COMMENT, type ServiceDecoratorEmit, type ServiceDecoratorEmitOptions, SpecLoadError, SpecParseError, type SpecVersion, type SwaggerDefinition, SwaggerParser, type SwaggerResponse, type SwaggerSpec, TYPE_GENERATOR_HEADER_COMMENT, type TypeGenOptions, type TypeMappingConfig, type TypeSchema, ZOD_PLUGIN_GENERATOR_HEADER_COMMENT, ZOD_PLUGIN_INDEX_GENERATOR_HEADER_COMMENT, camelCase, defineConfig, emitDefaultHeadersMerge, emitHeaders, emitQueryParams, emitResponseTypeOption, emitServiceDecorator, emitSignalAwareQueryParams, emitUrlConstruction, emitUrlExpression, escapeString, extractPaths, generateFromConfig, generateParseRequestTypeParams, getBasePathTokenName, getClientContextTokenName, getInterceptorsTokenName, getModelTypeName, getRequestBodyType, getResourceClassName, getResponseInfoFromResponse, getResponseType, getResponseTypeFromResponse, getServiceClassName, getTypeScriptType, hasDuplicateFunctionNames, inferResponseTypeFromContentType, isDataTypeInterface, isPrimitiveType, isUrl, joinRequestOptionEntries, kebabCase, listGeneratedBarrelDirs, listGeneratedFileNames, normalizeSchema, normalizeSpec, nullableType, pascalCase, pascalCaseForEnums, plainParamValue, screamingSnakeCase, signalAwareParamValue, validateGeneratorConfig, validateInput };
1224
+ export { type ArgumentNameProfile, type ArgumentNames, BASE_INTERCEPTOR_HEADER_COMMENT, CONTENT_TYPES, ConfigLoadError, ConfigValidationError, DuplicateGeneratedNameError, type EnumValueObject, type GenerationPhase, type GenerationResult, type GeneratorConfig, type GetMethodGenerationContext, HTTP_RESOURCE_GENERATOR_HEADER_COMMENT, type HeadersEmitOptions, type IPluginGenerator, type IPluginGeneratorClass, InvalidIdentifierError, MAIN_INDEX_GENERATOR_HEADER_COMMENT, type MethodGenOptions, type MethodGenerationContext, type NameDecoration, type NamingOptions, NgOpenApiError, type NgOpenapiClientConfig, type NormalizedOperation, type NormalizedSpec, type OpenApiSecurityScheme, type OperationRef, PROVIDER_GENERATOR_HEADER_COMMENT, type Parameter, type PathInfo, type PluginGeneratorContext, REQUEST_PARAMS_GENERATOR_HEADER_COMMENT, RESERVED_MEMBER_NAMES, RESOURCE_ARGUMENT_PROFILE, type RenamedArgument, type Reporter, type RequestBody, type ResponseKind, type ResponseTypeInfo, SERVICE_ARGUMENT_PROFILE, SERVICE_GENERATOR_HEADER_COMMENT, SERVICE_INDEX_GENERATOR_HEADER_COMMENT, type ServiceDecoratorEmit, type ServiceDecoratorEmitOptions, SpecLoadError, SpecParseError, type SpecVersion, type SwaggerDefinition, SwaggerParser, type SwaggerResponse, type SwaggerSpec, TYPE_GENERATOR_HEADER_COMMENT, type TypeGenOptions, type TypeMappingConfig, type TypeSchema, UnresolvedPathTemplateError, ZOD_PLUGIN_GENERATOR_HEADER_COMMENT, ZOD_PLUGIN_INDEX_GENERATOR_HEADER_COMMENT, assertDistinctMemberNames, camelCase, capitalizeFirst, clientNameIdentifier, defineConfig, describeOperation, effectiveClientName, emitDefaultHeadersMerge, emitDocs, emitHeaders, emitObjectKey, emitPropertyName, emitQueryParams, emitResponseTypeOption, emitServiceDecorator, emitSignalAwareQueryParams, emitUrlConstruction, emitUrlExpression, escapeDoubleQuoted, escapeJsDoc, escapeSingleQuoted, escapeSingleQuoted as escapeString, escapeTemplateLiteral, extractPaths, generateFromConfig, generateParseRequestTypeParams, getBasePathTokenName, getClientContextTokenName, getInterceptorsTokenName, getModelTypeName, getOperationMethodName, getRequestBodyType, getResourceClassName, getResponseInfoFromResponse, getResponseType, getResponseTypeFromResponse, getServiceClassName, getTypeScriptType, groupOperationsByController, hasDuplicateFunctionNames, inferResponseTypeFromContentType, isDataTypeInterface, isPrimitiveType, isUrl, isValidIdentifier, joinRequestOptionEntries, kebabCase, listGeneratedBarrelDirs, listGeneratedFileNames, loadConfigFile, normalizeSchema, normalizeSpec, nullableType, pascalCase, pascalCaseForEnums, plainParamValue, quoteLiteral, reservedMemberCollision, resolveArgumentNames, screamingSnakeCase, signalAwareParamValue, validateGeneratorConfig, validateInput };