@kubb/ast 5.13.1 → 5.14.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.
package/dist/index.cjs CHANGED
@@ -30,11 +30,35 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
30
30
  enumerable: true
31
31
  }) : target, mod));
32
32
  //#endregion
33
+ let node_crypto = require("node:crypto");
33
34
  let node_path = require("node:path");
34
35
  node_path = __toESM(node_path, 1);
35
- let node_crypto = require("node:crypto");
36
36
  //#region src/constants.ts
37
37
  /**
38
+ * Node kind discriminators used by every AST node (for example `node.kind === nodeKinds.text`).
39
+ */
40
+ const nodeKinds = {
41
+ input: "Input",
42
+ operation: "Operation",
43
+ schema: "Schema",
44
+ property: "Property",
45
+ parameter: "Parameter",
46
+ response: "Response",
47
+ requestBody: "RequestBody",
48
+ content: "Content",
49
+ type: "Type",
50
+ file: "File",
51
+ import: "Import",
52
+ export: "Export",
53
+ source: "Source",
54
+ const: "Const",
55
+ function: "Function",
56
+ arrowFunction: "ArrowFunction",
57
+ text: "Text",
58
+ break: "Break",
59
+ jsx: "Jsx"
60
+ };
61
+ /**
38
62
  * Schema type discriminators used by all AST schema nodes.
39
63
  *
40
64
  * Each value is a stable discriminator across the AST (for example `schema.type === schemaTypes.object`).
@@ -243,7 +267,7 @@ function isNodeRecord(value) {
243
267
  * ```
244
268
  */
245
269
  function getChildren(node, recurse) {
246
- if (node.kind === "Schema" && !recurse) return [];
270
+ if (node.kind === nodeKinds.schema && !recurse) return [];
247
271
  const children = [];
248
272
  visitChildNodes(node, (child) => {
249
273
  children.push(child);
@@ -301,7 +325,7 @@ function visitChildNodes(node, visit) {
301
325
  */
302
326
  function forEachSchema(node, visit) {
303
327
  const walk = (current) => {
304
- if (current.kind === "Schema") visit(current);
328
+ if (current.kind === nodeKinds.schema) visit(current);
305
329
  forEachChild(current, walk);
306
330
  };
307
331
  walk(node);
@@ -319,7 +343,7 @@ function forEachSchema(node, visit) {
319
343
  function findSchema(node, { predicate, followRefs = false }) {
320
344
  const seen = followRefs ? /* @__PURE__ */ new WeakSet() : void 0;
321
345
  const walk = (current) => {
322
- if (current.kind === "Schema") {
346
+ if (current.kind === nodeKinds.schema) {
323
347
  if (predicate(current)) return current;
324
348
  if (seen) {
325
349
  if (seen.has(current)) return void 0;
@@ -395,7 +419,7 @@ function transformNode(node, visitor, recurse, parent) {
395
419
  * (e.g. `additionalProperties: true`) intact.
396
420
  */
397
421
  function transformChildren(node, visitor, recurse) {
398
- const keys = node.kind === "Schema" && !recurse ? void 0 : VISITOR_KEYS[node.kind];
422
+ const keys = node.kind === nodeKinds.schema && !recurse ? void 0 : VISITOR_KEYS[node.kind];
399
423
  if (!keys) return node;
400
424
  const record = node;
401
425
  let updates;
@@ -680,11 +704,11 @@ function extractStringsFromNodes(nodes) {
680
704
  if (node) collected.push(node);
681
705
  continue;
682
706
  }
683
- if (node.kind === "Text" || node.kind === "Jsx") {
707
+ if (node.kind === nodeKinds.text || node.kind === nodeKinds.jsx) {
684
708
  if (node.value) collected.push(node.value);
685
709
  continue;
686
710
  }
687
- if (node.kind === "Break") continue;
711
+ if (node.kind === nodeKinds.break) continue;
688
712
  const parts = [];
689
713
  if ("params" in node && node.params) parts.push(node.params);
690
714
  if ("generics" in node && node.generics) parts.push(Array.isArray(node.generics) ? node.generics.join(", ") : node.generics);
@@ -697,131 +721,539 @@ function extractStringsFromNodes(nodes) {
697
721
  return collected.join("\n");
698
722
  }
699
723
  //#endregion
700
- //#region src/utils/refs.ts
701
- /** Returns the last segment of a `$ref` pointer, for example `'Pet'` for `#/components/schemas/Pet`. */
702
- function refName(ref) {
703
- return ref.split("/").at(-1) ?? ref;
724
+ //#region src/utils/combineFileMembers.ts
725
+ const IDENTIFIER_RUN = /[\w$]+/g;
726
+ /**
727
+ * How many imports a file needs before indexing the source beats scanning it once per name.
728
+ */
729
+ const INDEX_ABOVE_IMPORTS = 128;
730
+ /**
731
+ * Returns the local binding an import name item introduces: the alias when there is one, else the
732
+ * imported name.
733
+ */
734
+ function importLocalName(item) {
735
+ return typeof item === "string" ? item : item.name ?? item.propertyName;
704
736
  }
705
737
  /**
706
- * Resolves the emitted name of the schema a ref node points at. Prefers `targetName` (set when
707
- * the referenced schema was renamed, e.g. to break a collision), then the last segment of `ref`,
708
- * then `name`, then the nested `schema.name`.
709
- *
710
- * Returns `null` for non-ref nodes or when no name resolves.
711
- *
712
- * @example
713
- * `resolveRefName({ kind: 'Schema', type: 'ref', ref: '#/components/schemas/Pet' }) // 'Pet'`
714
- *
715
- * @example Collision-renamed target
716
- * `resolveRefName({ kind: 'Schema', type: 'ref', ref: '#/components/schemas/Order', targetName: 'OrderSchema' }) // 'OrderSchema'`
738
+ * Computes a multi-level sort key for exports and imports:
739
+ * non-array names first (wildcards/namespace aliases). Type-only before value. Alphabetical path. Unnamed before named.
717
740
  */
718
- function resolveRefName(node) {
719
- if (!node || node.type !== "ref") return null;
720
- if (node.targetName) return node.targetName;
721
- if (node.ref) return refName(node.ref);
722
- return node.name ?? node.schema?.name ?? null;
741
+ function sortKey(node) {
742
+ const isArray = Array.isArray(node.name) ? "1" : "0";
743
+ const typeOnly = node.isTypeOnly ? "0" : "1";
744
+ const hasName = node.name != null ? "1" : "0";
745
+ const name = Array.isArray(node.name) ? node.name.toSorted().join("\0") : node.name ?? "";
746
+ return `${isArray}:${typeOnly}:${node.path}:${hasName}:${name}`;
723
747
  }
724
748
  /**
725
- * Follows a resolved ref chain to its target schema. Returns an unresolved or cyclic ref as is.
726
- *
727
- * @example
728
- * ```ts
729
- * const target = resolveRef(refSchema)
730
- * ```
749
+ * Sorts members by {@link sortKey}. Keys are computed once per node, not per comparison.
731
750
  */
732
- function resolveRef(node) {
733
- if (node.type !== "ref" || !node.schema) return node;
734
- const seen = /* @__PURE__ */ new WeakSet();
735
- let current = node;
736
- while (current.type === "ref" && current.schema) {
737
- if (seen.has(current)) return current;
738
- seen.add(current);
739
- current = current.schema;
740
- }
741
- return current;
751
+ function sortMembers(nodes) {
752
+ return nodes.map((node) => ({
753
+ node,
754
+ key: sortKey(node)
755
+ })).sort((a, b) => a.key < b.key ? -1 : a.key > b.key ? 1 : 0).map(({ node }) => node);
742
756
  }
743
- /** Fields of a ref node that describe the ref itself, so they never override the resolved schema. */
744
- const REF_OWN_KEYS = /* @__PURE__ */ new Set([
745
- "kind",
746
- "type",
747
- "name",
748
- "ref",
749
- "schema"
750
- ]);
751
757
  /**
752
- * Merges a ref node with its resolved schema, giving the fields set at the usage site precedence.
753
- *
754
- * Every field on the ref except `kind`, `type`, `name`, `ref`, and `schema` overrides the same field
755
- * of the resolved `schema` (for example `description`, `nullable`, `readOnly`, `deprecated`). Fields
756
- * left `undefined` on the ref do not shadow the resolved schema. A ref without a resolved `schema`
757
- * is returned as is.
758
+ * Returns a collector for array-named members. A member whose path and `isTypeOnly` were already
759
+ * seen folds its names into that entry, otherwise it joins `result` as a new entry.
760
+ */
761
+ function mergeByPath(result) {
762
+ const byPath = /* @__PURE__ */ new Map();
763
+ return ({ node, names }) => {
764
+ const key = `${node.path}:${node.isTypeOnly ?? false}`;
765
+ const existing = byPath.get(key);
766
+ if (existing) {
767
+ for (const name of names) {
768
+ if (existing.seen.has(name)) continue;
769
+ existing.seen.add(name);
770
+ existing.names.push(name);
771
+ }
772
+ return;
773
+ }
774
+ const item = {
775
+ ...node,
776
+ name: [...names]
777
+ };
778
+ result.push(item);
779
+ byPath.set(key, {
780
+ names: item.name,
781
+ seen: new Set(names)
782
+ });
783
+ };
784
+ }
785
+ /**
786
+ * Deduplicates `SourceNode` objects by `name + isExportable + isTypeOnly`, keeping the first of each
787
+ * key. An unnamed source only drops when the same node appears twice, so markdown can repeat a
788
+ * paragraph or a table. Returns the deduplicated array in original order.
789
+ */
790
+ function combineSources(sources) {
791
+ const seen = /* @__PURE__ */ new Set();
792
+ return sources.filter((source) => {
793
+ const key = source.name != null ? `${source.name}:${source.isExportable ?? false}:${source.isTypeOnly ?? false}` : source;
794
+ if (seen.has(key)) return false;
795
+ seen.add(key);
796
+ return true;
797
+ });
798
+ }
799
+ /**
800
+ * Deduplicates and merges `ExportNode` objects by path and type.
758
801
  *
759
- * @example
760
- * ```ts
761
- * const ref = createSchema({ type: 'ref', ref: '#/components/schemas/Pet', description: 'A pet', schema: petSchema })
762
- * mergeRefWithSchema(ref) // petSchema's fields with description 'A pet'
763
- * ```
802
+ * Named exports with the same path and `isTypeOnly` flag have their names merged into a single export.
803
+ * Non-array exports are deduplicated by exact identity. Returns a sorted, deduplicated array.
764
804
  */
765
- function mergeRefWithSchema(node) {
766
- if (!node.schema) return node;
767
- const merged = { ...node.schema };
768
- for (const key of Object.keys(node)) {
769
- const value = node[key];
770
- if (value !== void 0 && !REF_OWN_KEYS.has(key)) merged[key] = value;
805
+ function combineExports(exports) {
806
+ const result = [];
807
+ const merge = mergeByPath(result);
808
+ const seen = /* @__PURE__ */ new Set();
809
+ for (const curr of sortMembers(exports)) {
810
+ const { name, path, isTypeOnly, asAlias } = curr;
811
+ if (Array.isArray(name)) {
812
+ if (name.length) merge({
813
+ node: curr,
814
+ names: [...new Set(name)]
815
+ });
816
+ continue;
817
+ }
818
+ const key = `${path}:${name ?? ""}:${isTypeOnly ?? false}:${asAlias ?? ""}`;
819
+ if (!seen.has(key)) {
820
+ result.push(curr);
821
+ seen.add(key);
822
+ }
771
823
  }
772
- return merged;
824
+ return result;
773
825
  }
774
826
  /**
775
- * Returns `true` when a schema is a bare ref without modifiers, metadata, or omit keys.
827
+ * Deduplicates and merges `ImportNode` objects, filtering out unused imports.
776
828
  *
777
- * Modifiers inherited from the resolved schema are included in the check because a ref emits the
778
- * referenced schema variable with those modifiers already applied.
829
+ * Retains imports that are referenced in `source` or re-exported. Imports with the same path and
830
+ * `isTypeOnly` flag have their names merged. Returns a sorted, deduplicated, filtered array.
779
831
  */
780
- function isBareRef(node, keysToOmit) {
781
- if (!node || node.type !== "ref") return false;
782
- const meta = mergeRefWithSchema(node);
783
- return !meta.nullable && !meta.optional && !meta.nullish && meta.default === void 0 && !meta.description && !meta.examples?.length && !keysToOmit?.length;
832
+ function combineImports(imports, exports, source) {
833
+ const exportedNames = new Set(exports.flatMap((e) => Array.isArray(e.name) ? e.name : e.name ? [e.name] : []));
834
+ const identifiers = source && imports.length > INDEX_ABOVE_IMPORTS ? new Set(source.match(IDENTIFIER_RUN)) : null;
835
+ const isUsed = (importName) => !source || identifiers?.has(importName) || source.includes(importName) || exportedNames.has(importName);
836
+ const importNameMemo = /* @__PURE__ */ new Map();
837
+ const canonicalizeName = (n) => {
838
+ if (typeof n === "string") return n;
839
+ const key = `${n.propertyName}:${n.name ?? ""}`;
840
+ if (!importNameMemo.has(key)) importNameMemo.set(key, n);
841
+ return importNameMemo.get(key);
842
+ };
843
+ const pathsWithUsedNamedImport = /* @__PURE__ */ new Set();
844
+ for (const node of imports) if (Array.isArray(node.name) && node.name.some((item) => isUsed(importLocalName(item)))) pathsWithUsedNamedImport.add(node.path);
845
+ const result = [];
846
+ const merge = mergeByPath(result);
847
+ const seen = /* @__PURE__ */ new Set();
848
+ for (const curr of sortMembers(imports)) {
849
+ if (curr.path === curr.root) continue;
850
+ const { name, path, isTypeOnly } = curr;
851
+ if (Array.isArray(name)) {
852
+ const names = [...new Set(name.map(canonicalizeName))].filter((item) => isUsed(importLocalName(item)));
853
+ if (names.length) merge({
854
+ node: curr,
855
+ names
856
+ });
857
+ continue;
858
+ }
859
+ if (!isUsed(name) && !pathsWithUsedNamedImport.has(path)) continue;
860
+ const key = `${path}:${name}:${isTypeOnly ?? false}`;
861
+ if (!seen.has(key)) {
862
+ result.push(curr);
863
+ seen.add(key);
864
+ }
865
+ }
866
+ return result;
784
867
  }
785
868
  //#endregion
786
- //#region ../../internals/utils/src/promise.ts
869
+ //#region src/nodes/base.ts
870
+ /** Builds a `createX` factory for a node whose only computed field is `kind`. */
871
+ function nodeFactory(kind) {
872
+ return (input) => {
873
+ return {
874
+ ...input,
875
+ kind
876
+ };
877
+ };
878
+ }
879
+ //#endregion
880
+ //#region src/nodes/file.ts
787
881
  /**
788
- * Wraps `factory` with a keyed cache backed by the provided store.
789
- *
790
- * Pass a `WeakMap` for object keys (results are GC-eligible when the key is
791
- * collected) or a `Map` for primitive keys. For multi-argument functions,
792
- * nest two `memoize` calls — the outer keyed by the first argument, the
793
- * inner (created once per outer miss) keyed by the second.
794
- *
795
- * Because the cache is owned by the caller, it can be shared, inspected, or
796
- * cleared independently of the memoized function.
882
+ * Creates an `ImportNode` representing a language-agnostic import/dependency declaration.
797
883
  *
798
- * @example Single WeakMap key
884
+ * @example Named import
799
885
  * ```ts
800
- * const cache = new WeakMap<SchemaNode, Set<string>>()
801
- * const getRefs = memoize(cache, (node) => collectRefs(node))
886
+ * createImport({ name: ['useState'], path: 'react' })
887
+ * // import { useState } from 'react'
802
888
  * ```
889
+ */
890
+ const createImport = nodeFactory(nodeKinds.import);
891
+ /**
892
+ * Creates an `ExportNode` representing a language-agnostic export/public API declaration.
803
893
  *
804
- * @example Single Map key (primitive)
894
+ * @example Named export
805
895
  * ```ts
806
- * const cache = new Map<string, Resolver>()
807
- * const getResolver = memoize(cache, (name) => buildResolver(name))
896
+ * createExport({ name: ['Pet'], path: './Pet' })
897
+ * // export { Pet } from './Pet'
808
898
  * ```
899
+ */
900
+ const createExport = nodeFactory(nodeKinds.export);
901
+ /**
902
+ * Creates a `SourceNode` representing a fragment of source code within a file.
809
903
  *
810
- * @example Two-level (object + primitive)
904
+ * @example
811
905
  * ```ts
812
- * const outer = new WeakMap<Params[], Map<string, Params[]>>()
813
- * const fn = memoize(outer, (params) => memoize(new Map(), (key) => transform(params, key)))
814
- * fn(params)('camelcase')
906
+ * createSource({ name: 'Pet', nodes: [createText('export type Pet = { id: number }')], isExportable: true })
815
907
  * ```
816
908
  */
817
- function memoize(store, factory) {
818
- return (key) => {
819
- if (store.has(key)) return store.get(key);
820
- const value = factory(key);
821
- store.set(key, value);
822
- return value;
823
- };
824
- }
909
+ const createSource = nodeFactory(nodeKinds.source);
910
+ /**
911
+ * Creates a fully resolved `FileNode` from a file input descriptor.
912
+ *
913
+ * Computes:
914
+ * - `id` SHA256 hash of the file path
915
+ * - `name` `baseName` without extension
916
+ * - `extname` extension extracted from `baseName`
917
+ *
918
+ * Deduplicates:
919
+ * - `sources` via `combineSources`
920
+ * - `exports` via `combineExports`
921
+ * - `imports` via `combineImports` (also filters unused imports)
922
+ *
923
+ * @throws {Error} when `baseName` has no extension.
924
+ *
925
+ * @example
926
+ * ```ts
927
+ * const file = createFile({
928
+ * baseName: 'petStore.ts',
929
+ * path: 'src/models/petStore.ts',
930
+ * sources: [createSource({ name: 'Pet', nodes: [createText('export type Pet = { id: number }')] })],
931
+ * imports: [createImport({ name: ['z'], path: 'zod' })],
932
+ * exports: [createExport({ name: ['Pet'], path: './petStore' })],
933
+ * })
934
+ * // file.id = SHA256 hash of 'src/models/petStore.ts'
935
+ * // file.name = 'petStore'
936
+ * // file.extname = '.ts'
937
+ * ```
938
+ *
939
+ * @example Copy a real file into the output
940
+ * ```ts
941
+ * const file = createFile({
942
+ * baseName: 'client.ts',
943
+ * path: 'src/gen/client.ts',
944
+ * copy: '/abs/path/to/templates/client.ts',
945
+ * })
946
+ * ```
947
+ */
948
+ function createFile(input) {
949
+ const extname = node_path.default.extname(input.baseName);
950
+ if (!extname) throw new Error(`No extname found for ${input.baseName}`);
951
+ const resolvedExports = input.exports?.length ? combineExports(input.exports) : [];
952
+ const resolvedSources = input.sources?.length ? combineSources(input.sources) : [];
953
+ const resolvedImports = (() => {
954
+ if (!input.imports?.length) return [];
955
+ const sourceParts = [];
956
+ const localNames = /* @__PURE__ */ new Set();
957
+ for (const item of resolvedSources) {
958
+ const extracted = item.nodes && extractStringsFromNodes(item.nodes);
959
+ if (extracted) sourceParts.push(extracted);
960
+ if (item.name) localNames.add(item.name);
961
+ }
962
+ const source = sourceParts.join("\n") || void 0;
963
+ return combineImports(input.imports, resolvedExports, source).flatMap((imp) => {
964
+ if (imp.path === input.path) return [];
965
+ if (!Array.isArray(imp.name)) return typeof imp.name === "string" && localNames.has(imp.name) ? [] : [imp];
966
+ const kept = imp.name.filter((item) => !localNames.has(importLocalName(item)));
967
+ if (!kept.length) return [];
968
+ return [kept.length === imp.name.length ? imp : {
969
+ ...imp,
970
+ name: kept
971
+ }];
972
+ });
973
+ })();
974
+ return {
975
+ ...input,
976
+ kind: nodeKinds.file,
977
+ id: (0, node_crypto.hash)("sha256", input.path, "hex"),
978
+ name: node_path.default.basename(input.baseName, extname),
979
+ extname,
980
+ imports: resolvedImports,
981
+ exports: resolvedExports,
982
+ sources: resolvedSources,
983
+ meta: input.meta ?? {}
984
+ };
985
+ }
986
+ //#endregion
987
+ //#region src/utils/inline.ts
988
+ /**
989
+ * Whether every source of `file` is inline. Other files absorb such a file, so it is never written.
990
+ */
991
+ function isVirtualFile(file) {
992
+ return file.sources.length > 0 && file.sources.every((source) => source.inline);
993
+ }
994
+ /**
995
+ * Replaces every name a file imports from a virtual file (see {@link isVirtualFile}) with that name's
996
+ * `inline.expression`, until no inline names are left. The definitions' own imports move to the importing file.
997
+ */
998
+ function inlineFiles(files, { language }) {
999
+ const virtualFiles = new Map(files.filter(isVirtualFile).map((file) => [file.path, file]));
1000
+ const result = {
1001
+ files: [],
1002
+ virtualPaths: [...virtualFiles.keys()],
1003
+ problems: []
1004
+ };
1005
+ if (virtualFiles.size === 0) return result;
1006
+ const definitions = /* @__PURE__ */ new Map();
1007
+ function define(path, name, from, lang) {
1008
+ const key = `${path}\0${name}`;
1009
+ if (definitions.has(key)) {
1010
+ const definition = definitions.get(key);
1011
+ if (definition === null) result.problems.push({
1012
+ path: from,
1013
+ message: `"${name}" from "${path}" is defined in terms of itself.`
1014
+ });
1015
+ return definition ?? void 0;
1016
+ }
1017
+ const file = virtualFiles.get(path);
1018
+ const inline = file.sources.find((source) => source.name === name)?.inline;
1019
+ if (!inline) {
1020
+ result.problems.push({
1021
+ path: from,
1022
+ message: `"${name}" is imported from "${path}", which never writes it and has no inline definition for it.`
1023
+ });
1024
+ return;
1025
+ }
1026
+ definitions.set(key, null);
1027
+ const imports = [];
1028
+ const { text, used } = replaceNames(inline.expression, file, lang, imports);
1029
+ imports.push(...file.imports.flatMap((imp) => virtualFiles.has(imp.path) ? [] : pickNames(imp, used)));
1030
+ const definition = {
1031
+ expression: text,
1032
+ imports
1033
+ };
1034
+ definitions.set(key, definition);
1035
+ return definition;
1036
+ }
1037
+ function replaceNames(input, file, lang, imports) {
1038
+ const used = /* @__PURE__ */ new Set();
1039
+ return {
1040
+ text: lang.replaceIdentifiers(input, (local) => {
1041
+ const target = findTarget(file, local);
1042
+ const definition = target && define(target.path, target.name, file.path, lang);
1043
+ if (!definition) {
1044
+ used.add(local);
1045
+ return;
1046
+ }
1047
+ imports.push(...definition.imports);
1048
+ return lang.wrap(definition.expression);
1049
+ }),
1050
+ used
1051
+ };
1052
+ }
1053
+ function findTarget(file, local) {
1054
+ if (virtualFiles.has(file.path) && file.sources.some((source) => source.name === local)) return {
1055
+ path: file.path,
1056
+ name: local
1057
+ };
1058
+ for (const imp of file.imports) {
1059
+ const item = virtualFiles.has(imp.path) && [imp.name].flat().find((item) => importLocalName(item) === local);
1060
+ if (item) return {
1061
+ path: imp.path,
1062
+ name: typeof item === "string" ? item : item.propertyName
1063
+ };
1064
+ }
1065
+ }
1066
+ for (const file of files) {
1067
+ if (virtualFiles.has(file.path) || !file.imports.some((imp) => virtualFiles.has(imp.path))) continue;
1068
+ const lang = language(file);
1069
+ if (!lang) {
1070
+ result.problems.push({
1071
+ path: file.path,
1072
+ message: `No parser for "${file.extname}" files supports inline sources.`
1073
+ });
1074
+ continue;
1075
+ }
1076
+ const carried = [];
1077
+ const sources = file.sources.map((source) => ({
1078
+ ...source,
1079
+ nodes: rewriteNodes(source.nodes, (text) => replaceNames(text, file, lang, carried).text)
1080
+ }));
1081
+ const imports = [...file.imports.filter((imp) => !virtualFiles.has(imp.path)), ...carried.map((imp) => imp.root ? {
1082
+ ...imp,
1083
+ root: file.path
1084
+ } : imp)];
1085
+ result.files.push(createFile({
1086
+ ...file,
1087
+ sources,
1088
+ imports
1089
+ }));
1090
+ }
1091
+ return result;
1092
+ }
1093
+ function pickNames(imp, used) {
1094
+ if (!Array.isArray(imp.name)) return used.has(imp.name) ? [imp] : [];
1095
+ const name = imp.name.filter((item) => used.has(importLocalName(item)));
1096
+ return name.length > 0 ? [{
1097
+ ...imp,
1098
+ name
1099
+ }] : [];
1100
+ }
1101
+ function rewriteNodes(nodes, rewrite) {
1102
+ return nodes?.map((node) => {
1103
+ if (typeof node === "string") return rewrite(node);
1104
+ switch (node.kind) {
1105
+ case nodeKinds.break: return node;
1106
+ case nodeKinds.text:
1107
+ case nodeKinds.jsx: return {
1108
+ ...node,
1109
+ value: rewrite(node.value)
1110
+ };
1111
+ case nodeKinds.function:
1112
+ case nodeKinds.arrowFunction: return {
1113
+ ...node,
1114
+ params: node.params && rewrite(node.params),
1115
+ generics: Array.isArray(node.generics) ? node.generics.map(rewrite) : node.generics && rewrite(node.generics),
1116
+ returnType: node.returnType && rewrite(node.returnType),
1117
+ nodes: rewriteNodes(node.nodes, rewrite)
1118
+ };
1119
+ case nodeKinds.const: return {
1120
+ ...node,
1121
+ type: node.type && rewrite(node.type),
1122
+ nodes: rewriteNodes(node.nodes, rewrite)
1123
+ };
1124
+ default: return {
1125
+ ...node,
1126
+ nodes: rewriteNodes(node.nodes, rewrite)
1127
+ };
1128
+ }
1129
+ });
1130
+ }
1131
+ //#endregion
1132
+ //#region src/utils/refs.ts
1133
+ /** Returns the last segment of a `$ref` pointer, for example `'Pet'` for `#/components/schemas/Pet`. */
1134
+ function refName(ref) {
1135
+ return ref.split("/").at(-1) ?? ref;
1136
+ }
1137
+ /**
1138
+ * Resolves the emitted name of the schema a ref node points at. Prefers `targetName` (set when
1139
+ * the referenced schema was renamed, e.g. to break a collision), then the last segment of `ref`,
1140
+ * then `name`, then the nested `schema.name`.
1141
+ *
1142
+ * Returns `null` for non-ref nodes or when no name resolves.
1143
+ *
1144
+ * @example
1145
+ * `resolveRefName({ kind: 'Schema', type: 'ref', ref: '#/components/schemas/Pet' }) // 'Pet'`
1146
+ *
1147
+ * @example Collision-renamed target
1148
+ * `resolveRefName({ kind: 'Schema', type: 'ref', ref: '#/components/schemas/Order', targetName: 'OrderSchema' }) // 'OrderSchema'`
1149
+ */
1150
+ function resolveRefName(node) {
1151
+ if (!node || node.type !== "ref") return null;
1152
+ if (node.targetName) return node.targetName;
1153
+ if (node.ref) return refName(node.ref);
1154
+ return node.name ?? node.schema?.name ?? null;
1155
+ }
1156
+ /**
1157
+ * Follows a resolved ref chain to its target schema. Returns an unresolved or cyclic ref as is.
1158
+ *
1159
+ * @example
1160
+ * ```ts
1161
+ * const target = resolveRef(refSchema)
1162
+ * ```
1163
+ */
1164
+ function resolveRef(node) {
1165
+ if (node.type !== "ref" || !node.schema) return node;
1166
+ const seen = /* @__PURE__ */ new WeakSet();
1167
+ let current = node;
1168
+ while (current.type === "ref" && current.schema) {
1169
+ if (seen.has(current)) return current;
1170
+ seen.add(current);
1171
+ current = current.schema;
1172
+ }
1173
+ return current;
1174
+ }
1175
+ /** Fields of a ref node that describe the ref itself, so they never override the resolved schema. */
1176
+ const REF_OWN_KEYS = /* @__PURE__ */ new Set([
1177
+ "kind",
1178
+ "type",
1179
+ "name",
1180
+ "ref",
1181
+ "schema"
1182
+ ]);
1183
+ /**
1184
+ * Merges a ref node with its resolved schema, giving the fields set at the usage site precedence.
1185
+ *
1186
+ * Every field on the ref except `kind`, `type`, `name`, `ref`, and `schema` overrides the same field
1187
+ * of the resolved `schema` (for example `description`, `nullable`, `readOnly`, `deprecated`). Fields
1188
+ * left `undefined` on the ref do not shadow the resolved schema. A ref without a resolved `schema`
1189
+ * is returned as is.
1190
+ *
1191
+ * @example
1192
+ * ```ts
1193
+ * const ref = createSchema({ type: 'ref', ref: '#/components/schemas/Pet', description: 'A pet', schema: petSchema })
1194
+ * mergeRefWithSchema(ref) // petSchema's fields with description 'A pet'
1195
+ * ```
1196
+ */
1197
+ function mergeRefWithSchema(node) {
1198
+ if (!node.schema) return node;
1199
+ const merged = { ...node.schema };
1200
+ for (const key of Object.keys(node)) {
1201
+ const value = node[key];
1202
+ if (value !== void 0 && !REF_OWN_KEYS.has(key)) merged[key] = value;
1203
+ }
1204
+ return merged;
1205
+ }
1206
+ /**
1207
+ * Returns `true` when a schema is a bare ref without modifiers, metadata, or omit keys.
1208
+ *
1209
+ * Modifiers inherited from the resolved schema are included in the check because a ref emits the
1210
+ * referenced schema variable with those modifiers already applied.
1211
+ */
1212
+ function isBareRef(node, keysToOmit) {
1213
+ if (!node || node.type !== "ref") return false;
1214
+ const meta = mergeRefWithSchema(node);
1215
+ return !meta.nullable && !meta.optional && !meta.nullish && meta.default === void 0 && !meta.description && !meta.examples?.length && !keysToOmit?.length;
1216
+ }
1217
+ //#endregion
1218
+ //#region ../../internals/utils/src/promise.ts
1219
+ /**
1220
+ * Wraps `factory` with a keyed cache backed by the provided store.
1221
+ *
1222
+ * Pass a `WeakMap` for object keys (results are GC-eligible when the key is
1223
+ * collected) or a `Map` for primitive keys. For multi-argument functions,
1224
+ * nest two `memoize` calls — the outer keyed by the first argument, the
1225
+ * inner (created once per outer miss) keyed by the second.
1226
+ *
1227
+ * Because the cache is owned by the caller, it can be shared, inspected, or
1228
+ * cleared independently of the memoized function.
1229
+ *
1230
+ * @example Single WeakMap key
1231
+ * ```ts
1232
+ * const cache = new WeakMap<SchemaNode, Set<string>>()
1233
+ * const getRefs = memoize(cache, (node) => collectRefs(node))
1234
+ * ```
1235
+ *
1236
+ * @example Single Map key (primitive)
1237
+ * ```ts
1238
+ * const cache = new Map<string, Resolver>()
1239
+ * const getResolver = memoize(cache, (name) => buildResolver(name))
1240
+ * ```
1241
+ *
1242
+ * @example Two-level (object + primitive)
1243
+ * ```ts
1244
+ * const outer = new WeakMap<Params[], Map<string, Params[]>>()
1245
+ * const fn = memoize(outer, (params) => memoize(new Map(), (key) => transform(params, key)))
1246
+ * fn(params)('camelcase')
1247
+ * ```
1248
+ */
1249
+ function memoize(store, factory) {
1250
+ return (key) => {
1251
+ if (store.has(key)) return store.get(key);
1252
+ const value = factory(key);
1253
+ store.set(key, value);
1254
+ return value;
1255
+ };
1256
+ }
825
1257
  //#endregion
826
1258
  //#region src/utils/schemaGraph.ts
827
1259
  /**
@@ -1080,17 +1512,6 @@ function inferDiscriminatorPropertyName(members) {
1080
1512
  }
1081
1513
  }
1082
1514
  //#endregion
1083
- //#region src/nodes/base.ts
1084
- /** Builds a `createX` factory for a node whose only computed field is `kind`. */
1085
- function nodeFactory(kind) {
1086
- return (input) => {
1087
- return {
1088
- ...input,
1089
- kind
1090
- };
1091
- };
1092
- }
1093
- //#endregion
1094
1515
  //#region src/nodes/code.ts
1095
1516
  /**
1096
1517
  * Creates a `ConstNode` representing a TypeScript `const` declaration.
@@ -1101,7 +1522,7 @@ function nodeFactory(kind) {
1101
1522
  * // export const pets: Pet[] = ... as const
1102
1523
  * ```
1103
1524
  */
1104
- const createConst = nodeFactory("Const");
1525
+ const createConst = nodeFactory(nodeKinds.const);
1105
1526
  /**
1106
1527
  * Creates a `TypeNode` representing a TypeScript `type` alias declaration.
1107
1528
  *
@@ -1111,7 +1532,7 @@ const createConst = nodeFactory("Const");
1111
1532
  * // export type Pet = ...
1112
1533
  * ```
1113
1534
  */
1114
- const createType = nodeFactory("Type");
1535
+ const createType = nodeFactory(nodeKinds.type);
1115
1536
  /**
1116
1537
  * Creates a `FunctionNode` representing a TypeScript `function` declaration.
1117
1538
  *
@@ -1121,7 +1542,7 @@ const createType = nodeFactory("Type");
1121
1542
  * // export async function fetchPet(): Promise<Pet> { ... }
1122
1543
  * ```
1123
1544
  */
1124
- const createFunction = nodeFactory("Function");
1545
+ const createFunction = nodeFactory(nodeKinds.function);
1125
1546
  /**
1126
1547
  * Creates an `ArrowFunctionNode` representing a TypeScript arrow function.
1127
1548
  *
@@ -1131,7 +1552,7 @@ const createFunction = nodeFactory("Function");
1131
1552
  * // export const double = (n: number) => ...
1132
1553
  * ```
1133
1554
  */
1134
- const createArrowFunction = nodeFactory("ArrowFunction");
1555
+ const createArrowFunction = nodeFactory(nodeKinds.arrowFunction);
1135
1556
  /**
1136
1557
  * Creates a {@link TextNode} representing a raw string fragment in the source output.
1137
1558
  *
@@ -1143,7 +1564,7 @@ const createArrowFunction = nodeFactory("ArrowFunction");
1143
1564
  */
1144
1565
  function createText(value) {
1145
1566
  return {
1146
- kind: "Text",
1567
+ kind: nodeKinds.text,
1147
1568
  value
1148
1569
  };
1149
1570
  }
@@ -1157,7 +1578,7 @@ function createText(value) {
1157
1578
  * ```
1158
1579
  */
1159
1580
  function createBreak() {
1160
- return { kind: "Break" };
1581
+ return { kind: nodeKinds.break };
1161
1582
  }
1162
1583
  /**
1163
1584
  * Creates a {@link JsxNode} representing a raw JSX fragment in the source output.
@@ -1170,7 +1591,7 @@ function createBreak() {
1170
1591
  */
1171
1592
  function createJsx(value) {
1172
1593
  return {
1173
- kind: "Jsx",
1594
+ kind: nodeKinds.jsx,
1174
1595
  value
1175
1596
  };
1176
1597
  }
@@ -1179,259 +1600,7 @@ function createJsx(value) {
1179
1600
  /**
1180
1601
  * Creates a `ContentNode` for a single request-body or response content type.
1181
1602
  */
1182
- const createContent = nodeFactory("Content");
1183
- //#endregion
1184
- //#region src/utils/combineFileMembers.ts
1185
- const IDENTIFIER_RUN = /[\w$]+/g;
1186
- /**
1187
- * How many imports a file needs before indexing the source beats scanning it once per name.
1188
- */
1189
- const INDEX_ABOVE_IMPORTS = 128;
1190
- /**
1191
- * Returns the local binding an import name item introduces: the alias when there is one, else the
1192
- * imported name.
1193
- */
1194
- function importLocalName(item) {
1195
- return typeof item === "string" ? item : item.name ?? item.propertyName;
1196
- }
1197
- /**
1198
- * Computes a multi-level sort key for exports and imports:
1199
- * non-array names first (wildcards/namespace aliases). Type-only before value. Alphabetical path. Unnamed before named.
1200
- */
1201
- function sortKey(node) {
1202
- const isArray = Array.isArray(node.name) ? "1" : "0";
1203
- const typeOnly = node.isTypeOnly ? "0" : "1";
1204
- const hasName = node.name != null ? "1" : "0";
1205
- const name = Array.isArray(node.name) ? node.name.toSorted().join("\0") : node.name ?? "";
1206
- return `${isArray}:${typeOnly}:${node.path}:${hasName}:${name}`;
1207
- }
1208
- /**
1209
- * Sorts members by {@link sortKey}. Keys are computed once per node, not per comparison.
1210
- */
1211
- function sortMembers(nodes) {
1212
- return nodes.map((node) => ({
1213
- node,
1214
- key: sortKey(node)
1215
- })).sort((a, b) => a.key < b.key ? -1 : a.key > b.key ? 1 : 0).map(({ node }) => node);
1216
- }
1217
- /**
1218
- * Returns a collector for array-named members. A member whose path and `isTypeOnly` were already
1219
- * seen folds its names into that entry, otherwise it joins `result` as a new entry.
1220
- */
1221
- function mergeByPath(result) {
1222
- const byPath = /* @__PURE__ */ new Map();
1223
- return ({ node, names }) => {
1224
- const key = `${node.path}:${node.isTypeOnly ?? false}`;
1225
- const existing = byPath.get(key);
1226
- if (existing) {
1227
- for (const name of names) {
1228
- if (existing.seen.has(name)) continue;
1229
- existing.seen.add(name);
1230
- existing.names.push(name);
1231
- }
1232
- return;
1233
- }
1234
- const item = {
1235
- ...node,
1236
- name: [...names]
1237
- };
1238
- result.push(item);
1239
- byPath.set(key, {
1240
- names: item.name,
1241
- seen: new Set(names)
1242
- });
1243
- };
1244
- }
1245
- /**
1246
- * Deduplicates `SourceNode` objects by `name + isExportable + isTypeOnly`, keeping the first of each
1247
- * key. An unnamed source only drops when the same node appears twice, so markdown can repeat a
1248
- * paragraph or a table. Returns the deduplicated array in original order.
1249
- */
1250
- function combineSources(sources) {
1251
- const seen = /* @__PURE__ */ new Set();
1252
- return sources.filter((source) => {
1253
- const key = source.name != null ? `${source.name}:${source.isExportable ?? false}:${source.isTypeOnly ?? false}` : source;
1254
- if (seen.has(key)) return false;
1255
- seen.add(key);
1256
- return true;
1257
- });
1258
- }
1259
- /**
1260
- * Deduplicates and merges `ExportNode` objects by path and type.
1261
- *
1262
- * Named exports with the same path and `isTypeOnly` flag have their names merged into a single export.
1263
- * Non-array exports are deduplicated by exact identity. Returns a sorted, deduplicated array.
1264
- */
1265
- function combineExports(exports) {
1266
- const result = [];
1267
- const merge = mergeByPath(result);
1268
- const seen = /* @__PURE__ */ new Set();
1269
- for (const curr of sortMembers(exports)) {
1270
- const { name, path, isTypeOnly, asAlias } = curr;
1271
- if (Array.isArray(name)) {
1272
- if (name.length) merge({
1273
- node: curr,
1274
- names: [...new Set(name)]
1275
- });
1276
- continue;
1277
- }
1278
- const key = `${path}:${name ?? ""}:${isTypeOnly ?? false}:${asAlias ?? ""}`;
1279
- if (!seen.has(key)) {
1280
- result.push(curr);
1281
- seen.add(key);
1282
- }
1283
- }
1284
- return result;
1285
- }
1286
- /**
1287
- * Deduplicates and merges `ImportNode` objects, filtering out unused imports.
1288
- *
1289
- * Retains imports that are referenced in `source` or re-exported. Imports with the same path and
1290
- * `isTypeOnly` flag have their names merged. Returns a sorted, deduplicated, filtered array.
1291
- */
1292
- function combineImports(imports, exports, source) {
1293
- const exportedNames = new Set(exports.flatMap((e) => Array.isArray(e.name) ? e.name : e.name ? [e.name] : []));
1294
- const identifiers = source && imports.length > INDEX_ABOVE_IMPORTS ? new Set(source.match(IDENTIFIER_RUN)) : null;
1295
- const isUsed = (importName) => !source || identifiers?.has(importName) || source.includes(importName) || exportedNames.has(importName);
1296
- const importNameMemo = /* @__PURE__ */ new Map();
1297
- const canonicalizeName = (n) => {
1298
- if (typeof n === "string") return n;
1299
- const key = `${n.propertyName}:${n.name ?? ""}`;
1300
- if (!importNameMemo.has(key)) importNameMemo.set(key, n);
1301
- return importNameMemo.get(key);
1302
- };
1303
- const pathsWithUsedNamedImport = /* @__PURE__ */ new Set();
1304
- for (const node of imports) if (Array.isArray(node.name) && node.name.some((item) => isUsed(importLocalName(item)))) pathsWithUsedNamedImport.add(node.path);
1305
- const result = [];
1306
- const merge = mergeByPath(result);
1307
- const seen = /* @__PURE__ */ new Set();
1308
- for (const curr of sortMembers(imports)) {
1309
- if (curr.path === curr.root) continue;
1310
- const { name, path, isTypeOnly } = curr;
1311
- if (Array.isArray(name)) {
1312
- const names = [...new Set(name.map(canonicalizeName))].filter((item) => isUsed(importLocalName(item)));
1313
- if (names.length) merge({
1314
- node: curr,
1315
- names
1316
- });
1317
- continue;
1318
- }
1319
- if (!isUsed(name) && !pathsWithUsedNamedImport.has(path)) continue;
1320
- const key = `${path}:${name}:${isTypeOnly ?? false}`;
1321
- if (!seen.has(key)) {
1322
- result.push(curr);
1323
- seen.add(key);
1324
- }
1325
- }
1326
- return result;
1327
- }
1328
- //#endregion
1329
- //#region src/nodes/file.ts
1330
- /**
1331
- * Creates an `ImportNode` representing a language-agnostic import/dependency declaration.
1332
- *
1333
- * @example Named import
1334
- * ```ts
1335
- * createImport({ name: ['useState'], path: 'react' })
1336
- * // import { useState } from 'react'
1337
- * ```
1338
- */
1339
- const createImport = nodeFactory("Import");
1340
- /**
1341
- * Creates an `ExportNode` representing a language-agnostic export/public API declaration.
1342
- *
1343
- * @example Named export
1344
- * ```ts
1345
- * createExport({ name: ['Pet'], path: './Pet' })
1346
- * // export { Pet } from './Pet'
1347
- * ```
1348
- */
1349
- const createExport = nodeFactory("Export");
1350
- /**
1351
- * Creates a `SourceNode` representing a fragment of source code within a file.
1352
- *
1353
- * @example
1354
- * ```ts
1355
- * createSource({ name: 'Pet', nodes: [createText('export type Pet = { id: number }')], isExportable: true })
1356
- * ```
1357
- */
1358
- const createSource = nodeFactory("Source");
1359
- /**
1360
- * Creates a fully resolved `FileNode` from a file input descriptor.
1361
- *
1362
- * Computes:
1363
- * - `id` SHA256 hash of the file path
1364
- * - `name` `baseName` without extension
1365
- * - `extname` extension extracted from `baseName`
1366
- *
1367
- * Deduplicates:
1368
- * - `sources` via `combineSources`
1369
- * - `exports` via `combineExports`
1370
- * - `imports` via `combineImports` (also filters unused imports)
1371
- *
1372
- * @throws {Error} when `baseName` has no extension.
1373
- *
1374
- * @example
1375
- * ```ts
1376
- * const file = createFile({
1377
- * baseName: 'petStore.ts',
1378
- * path: 'src/models/petStore.ts',
1379
- * sources: [createSource({ name: 'Pet', nodes: [createText('export type Pet = { id: number }')] })],
1380
- * imports: [createImport({ name: ['z'], path: 'zod' })],
1381
- * exports: [createExport({ name: ['Pet'], path: './petStore' })],
1382
- * })
1383
- * // file.id = SHA256 hash of 'src/models/petStore.ts'
1384
- * // file.name = 'petStore'
1385
- * // file.extname = '.ts'
1386
- * ```
1387
- *
1388
- * @example Copy a real file into the output
1389
- * ```ts
1390
- * const file = createFile({
1391
- * baseName: 'client.ts',
1392
- * path: 'src/gen/client.ts',
1393
- * copy: '/abs/path/to/templates/client.ts',
1394
- * })
1395
- * ```
1396
- */
1397
- function createFile(input) {
1398
- const extname = node_path.default.extname(input.baseName);
1399
- if (!extname) throw new Error(`No extname found for ${input.baseName}`);
1400
- const resolvedExports = input.exports?.length ? combineExports(input.exports) : [];
1401
- const resolvedSources = input.sources?.length ? combineSources(input.sources) : [];
1402
- const resolvedImports = (() => {
1403
- if (!input.imports?.length) return [];
1404
- const sourceParts = [];
1405
- const localNames = /* @__PURE__ */ new Set();
1406
- for (const item of resolvedSources) {
1407
- const extracted = item.nodes && extractStringsFromNodes(item.nodes);
1408
- if (extracted) sourceParts.push(extracted);
1409
- if (item.name) localNames.add(item.name);
1410
- }
1411
- const source = sourceParts.join("\n") || void 0;
1412
- return combineImports(input.imports, resolvedExports, source).flatMap((imp) => {
1413
- if (imp.path === input.path) return [];
1414
- if (!Array.isArray(imp.name)) return typeof imp.name === "string" && localNames.has(imp.name) ? [] : [imp];
1415
- const kept = imp.name.filter((item) => !localNames.has(importLocalName(item)));
1416
- if (!kept.length) return [];
1417
- return [kept.length === imp.name.length ? imp : {
1418
- ...imp,
1419
- name: kept
1420
- }];
1421
- });
1422
- })();
1423
- return {
1424
- ...input,
1425
- kind: "File",
1426
- id: (0, node_crypto.hash)("sha256", input.path, "hex"),
1427
- name: node_path.default.basename(input.baseName, extname),
1428
- extname,
1429
- imports: resolvedImports,
1430
- exports: resolvedExports,
1431
- sources: resolvedSources,
1432
- meta: input.meta ?? {}
1433
- };
1434
- }
1603
+ const createContent = nodeFactory(nodeKinds.content);
1435
1604
  //#endregion
1436
1605
  //#region src/nodes/input.ts
1437
1606
  /**
@@ -1453,7 +1622,7 @@ function createInput(overrides = {}) {
1453
1622
  enumNames: []
1454
1623
  },
1455
1624
  ...overrides,
1456
- kind: "Input"
1625
+ kind: nodeKinds.input
1457
1626
  };
1458
1627
  }
1459
1628
  //#endregion
@@ -1461,7 +1630,7 @@ function createInput(overrides = {}) {
1461
1630
  /**
1462
1631
  * Creates a `RequestBodyNode`.
1463
1632
  */
1464
- const createRequestBody = nodeFactory("RequestBody");
1633
+ const createRequestBody = nodeFactory(nodeKinds.requestBody);
1465
1634
  //#endregion
1466
1635
  //#region src/nodes/operation.ts
1467
1636
  function createOperation(props) {
@@ -1472,7 +1641,7 @@ function createOperation(props) {
1472
1641
  parameters: [],
1473
1642
  responses: [],
1474
1643
  ...rest,
1475
- kind: "Operation",
1644
+ kind: nodeKinds.operation,
1476
1645
  ...isHttp ? { protocol: "http" } : {},
1477
1646
  requestBody: requestBody ? createRequestBody(requestBody) : void 0
1478
1647
  };
@@ -1499,7 +1668,7 @@ function createParameter(input) {
1499
1668
  ...input,
1500
1669
  required,
1501
1670
  schema: optionality(input.schema, required),
1502
- kind: "Parameter"
1671
+ kind: nodeKinds.parameter
1503
1672
  };
1504
1673
  }
1505
1674
  //#endregion
@@ -1524,7 +1693,7 @@ function createProperty(input) {
1524
1693
  ...input,
1525
1694
  required,
1526
1695
  schema: optionality(input.schema, required),
1527
- kind: "Property"
1696
+ kind: nodeKinds.property
1528
1697
  };
1529
1698
  }
1530
1699
  //#endregion
@@ -1551,7 +1720,7 @@ function createResponse(props) {
1551
1720
  return {
1552
1721
  ...rest,
1553
1722
  content: entries,
1554
- kind: "Response"
1723
+ kind: nodeKinds.response
1555
1724
  };
1556
1725
  }
1557
1726
  //#endregion
@@ -1586,12 +1755,12 @@ function createSchema(props) {
1586
1755
  properties: [],
1587
1756
  primitive: "object",
1588
1757
  ...props,
1589
- kind: "Schema"
1758
+ kind: nodeKinds.schema
1590
1759
  };
1591
1760
  return {
1592
1761
  primitive: TYPE_TO_PRIMITIVE[props.type],
1593
1762
  ...props,
1594
- kind: "Schema"
1763
+ kind: nodeKinds.schema
1595
1764
  };
1596
1765
  }
1597
1766
  //#endregion
@@ -1639,10 +1808,13 @@ var src_exports = /* @__PURE__ */ __exportAll({
1639
1808
  forEachSchema: () => forEachSchema,
1640
1809
  getSchemaLiteralValues: () => getSchemaLiteralValues,
1641
1810
  inferDiscriminatorPropertyName: () => inferDiscriminatorPropertyName,
1811
+ inlineFiles: () => inlineFiles,
1642
1812
  isBareRef: () => isBareRef,
1643
1813
  isHttpOperationNode: () => isHttpOperationNode,
1814
+ isVirtualFile: () => isVirtualFile,
1644
1815
  mergeRefWithSchema: () => mergeRefWithSchema,
1645
1816
  narrowSchema: () => narrowSchema,
1817
+ nodeKinds: () => nodeKinds,
1646
1818
  optionality: () => optionality,
1647
1819
  refName: () => refName,
1648
1820
  resolveRef: () => resolveRef,
@@ -1681,10 +1853,13 @@ exports.forEachChild = forEachChild;
1681
1853
  exports.forEachSchema = forEachSchema;
1682
1854
  exports.getSchemaLiteralValues = getSchemaLiteralValues;
1683
1855
  exports.inferDiscriminatorPropertyName = inferDiscriminatorPropertyName;
1856
+ exports.inlineFiles = inlineFiles;
1684
1857
  exports.isBareRef = isBareRef;
1685
1858
  exports.isHttpOperationNode = isHttpOperationNode;
1859
+ exports.isVirtualFile = isVirtualFile;
1686
1860
  exports.mergeRefWithSchema = mergeRefWithSchema;
1687
1861
  exports.narrowSchema = narrowSchema;
1862
+ exports.nodeKinds = nodeKinds;
1688
1863
  exports.optionality = optionality;
1689
1864
  exports.refName = refName;
1690
1865
  exports.resolveRef = resolveRef;