zod-nest 1.5.0 → 1.6.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/README.md CHANGED
@@ -431,7 +431,7 @@ A compact, link-out index. Type signatures and detailed semantics live in the co
431
431
  - `ZodNestModule.forRoot(options?)`, `ZodNestModuleOptions`, `DEFAULT_REDACT_KEYS`, `DEFAULT_MAX_LOGGED_VALUE_BYTES`, `ZOD_NEST_OPTIONS`
432
432
 
433
433
  **Schema engine** — single-schema mode and extension points
434
- - `toOpenApi(schema, opts)`, `createRegistry()`, `defaultRegistry`, `ZodNestRegistry`, `Override`, `OverrideContext`, `overrideJSONSchema(schema, fragment | { input?, output? })`, `OverrideJSONSchemaArg`, `ZodNestError`, `ZodNestUnrepresentableError`, `extend`, `getLineage`, `LineageEntry`
434
+ - `toOpenApi(schema, opts)`, `createRegistry()`, `defaultRegistry`, `registerSchema(schema, registry?, options?)`, `ZodNestRegistry`, `RegisterSchemaOptions`, `Override`, `OverrideContext`, `overrideJSONSchema(schema, fragment | { input?, output? })`, `OverrideJSONSchemaArg`, `ZodNestError`, `ZodNestUnrepresentableError`, `extend`, `getLineage`, `LineageEntry`
435
435
 
436
436
  **Helpers** (subpath: `zod-nest/helpers`) — common JSON Schema fragments + presets for assembling overrides
437
437
  - **Fragment catalog** (frozen consts): `dateTimeFragment`, `dateFragment`, `timeFragment`, `uuidFragment`, `emailFragment`, `uriFragment`, `hostnameFragment`, `ipv4Fragment`, `ipv6Fragment`, `binaryFragment`, `byteFragment`, `int32Fragment`, `int64Fragment`, `floatFragment`, `doubleFragment`, `opaqueFragment`
@@ -87,10 +87,16 @@ var opaque = /* @__PURE__ */ __name((opts) => ({
87
87
  var customOverrideMap = /* @__PURE__ */ new WeakMap();
88
88
  var isWrapper = /* @__PURE__ */ __name((arg) => "input" in arg || "output" in arg, "isWrapper");
89
89
  var overrideJSONSchema = /* @__PURE__ */ __name((schema, arg) => {
90
- const overrideSchema = isWrapper(arg) ? arg : {
90
+ const overrideSchema = isWrapper(arg) ? {
91
+ ...arg
92
+ } : {
91
93
  input: arg,
92
94
  output: arg
93
95
  };
96
+ const schemaDescription = schema.description;
97
+ if (typeof schemaDescription === "string") {
98
+ overrideSchema.description = schemaDescription;
99
+ }
94
100
  customOverrideMap.set(schema, overrideSchema);
95
101
  return schema;
96
102
  }, "overrideJSONSchema");
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/helpers/fragments.ts","../../src/schema/custom-override.ts","../../src/helpers/presets.ts"],"names":["dateTimeFragment","type","format","dateFragment","timeFragment","uuidFragment","emailFragment","uriFragment","hostnameFragment","ipv4Fragment","ipv6Fragment","binaryFragment","byteFragment","int32Fragment","int64Fragment","floatFragment","doubleFragment","opaqueFragment","additionalProperties","enrich","base","extras","binary","opts","opaque","customOverrideMap","WeakMap","isWrapper","arg","overrideJSONSchema","schema","overrideSchema","input","output","set","FileSchema","z","instanceof","File","BlobSchema","Blob","BufferSchema","Buffer"],"mappings":"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","file":"index.js","sourcesContent":["import type { SchemaObject } from '../schema/openapi.types.js';\n\n/**\n * Common JSON Schema fragment catalog — reusable building blocks for assembling\n * fragments by hand: alongside `z.custom<T>()`, in `overrideJSONSchema` calls,\n * in custom override callbacks, in tests that build expected fragments.\n *\n * The catalog overlaps with what Zod constructs already emit (`z.uuid()` →\n * `uuidFragment` shape, `z.email()` → `emailFragment` shape, etc.) — the\n * helpers don't *replace* those Zod constructs; they're a parallel catalog\n * for programmatic fragment assembly.\n *\n * Each fragment is `as const satisfies SchemaObject` so its literal type\n * powers the dispatch in {@link enrich} — passing wrong-family options\n * fails at compile time.\n */\n\n// ---------------------------------------------------------------------------\n// Layer 1 — Fragment catalog\n// ---------------------------------------------------------------------------\n\n// String formats (RFC-defined)\nexport const dateTimeFragment = {\n type: 'string',\n format: 'date-time',\n} as const satisfies SchemaObject;\nexport const dateFragment = { type: 'string', format: 'date' } as const satisfies SchemaObject;\nexport const timeFragment = { type: 'string', format: 'time' } as const satisfies SchemaObject;\nexport const uuidFragment = { type: 'string', format: 'uuid' } as const satisfies SchemaObject;\nexport const emailFragment = { type: 'string', format: 'email' } as const satisfies SchemaObject;\nexport const uriFragment = { type: 'string', format: 'uri' } as const satisfies SchemaObject;\nexport const hostnameFragment = {\n type: 'string',\n format: 'hostname',\n} as const satisfies SchemaObject;\nexport const ipv4Fragment = { type: 'string', format: 'ipv4' } as const satisfies SchemaObject;\nexport const ipv6Fragment = { type: 'string', format: 'ipv6' } as const satisfies SchemaObject;\n\n// Binary / encoded payloads\nexport const binaryFragment = { type: 'string', format: 'binary' } as const satisfies SchemaObject;\nexport const byteFragment = { type: 'string', format: 'byte' } as const satisfies SchemaObject;\n\n// Numeric formats (OpenAPI 3.1)\nexport const int32Fragment = { type: 'integer', format: 'int32' } as const satisfies SchemaObject;\nexport const int64Fragment = { type: 'integer', format: 'int64' } as const satisfies SchemaObject;\nexport const floatFragment = { type: 'number', format: 'float' } as const satisfies SchemaObject;\nexport const doubleFragment = { type: 'number', format: 'double' } as const satisfies SchemaObject;\n\n// Object passthrough\nexport const opaqueFragment = {\n type: 'object',\n additionalProperties: true,\n} as const satisfies SchemaObject;\n\n// ---------------------------------------------------------------------------\n// Layer 2 — Per-family option types\n// ---------------------------------------------------------------------------\n\nexport interface StringFormatOptions {\n description?: string;\n examples?: string[];\n minLength?: number;\n maxLength?: number;\n pattern?: string;\n}\n\nexport interface BinaryFragmentOptions {\n description?: string;\n contentMediaType?: string;\n contentEncoding?: string;\n}\n\nexport interface NumberFormatOptions {\n description?: string;\n examples?: number[];\n minimum?: number;\n maximum?: number;\n exclusiveMinimum?: number;\n exclusiveMaximum?: number;\n multipleOf?: number;\n}\n\nexport interface OpaqueFragmentOptions {\n description?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Layer 3 — Type-strict generic enrich\n// ---------------------------------------------------------------------------\n\n// `binary` discriminates before the generic string-format branch because its\n// option set diverges (contentMediaType / contentEncoding instead of length\n// constraints). Order matters here.\ntype OptionsFor<T> = T extends { readonly format: 'binary' }\n ? BinaryFragmentOptions\n : T extends {\n readonly format:\n | 'date-time'\n | 'date'\n | 'time'\n | 'uuid'\n | 'email'\n | 'uri'\n | 'hostname'\n | 'ipv4'\n | 'ipv6'\n | 'byte';\n }\n ? StringFormatOptions\n : T extends { readonly format: 'int32' | 'int64' | 'float' | 'double' }\n ? NumberFormatOptions\n : T extends { readonly type: 'object'; readonly additionalProperties: true }\n ? OpaqueFragmentOptions\n : never;\n\n/**\n * Merge a catalog fragment with extras whose shape is dictated by the base\n * fragment's family. Passing wrong-family extras (e.g. a `contentMediaType`\n * onto `uuidFragment`) is a compile-time error.\n *\n * Returns a fresh `SchemaObject` — the original fragment is never mutated.\n */\nexport const enrich = <T extends SchemaObject>(base: T, extras: OptionsFor<T>): SchemaObject => ({\n ...base,\n ...extras,\n});\n\n// ---------------------------------------------------------------------------\n// Layer 3b — Sugar functions\n// ---------------------------------------------------------------------------\n\n/**\n * Binary-content fragment with typed enrichment. Sugar for\n * `enrich(binaryFragment, opts)` — exists because the `binary` option set\n * (`contentMediaType` / `contentEncoding`) is nuanced enough to deserve a\n * dedicated entry point and discoverable via auto-complete.\n *\n * @example\n * overrideJSONSchema(z.instanceof(File), binary());\n * overrideJSONSchema(z.instanceof(File), binary({ contentMediaType: 'application/pdf' }));\n */\nexport const binary = (opts?: BinaryFragmentOptions): SchemaObject => ({\n ...binaryFragment,\n ...opts,\n});\n\n/**\n * Opaque-object fragment with typed enrichment. Sugar for\n * `enrich(opaqueFragment, opts)` — exists for parity with {@link binary}\n * and because opaque passthrough payloads commonly carry a description\n * explaining why the API doesn't introspect them.\n *\n * @example\n * overrideJSONSchema(z.unknown(), opaque({ description: 'JWT passthrough' }));\n */\nexport const opaque = (opts?: OpaqueFragmentOptions): SchemaObject => ({\n ...opaqueFragment,\n ...opts,\n});\n","import type { z } from 'zod';\nimport type { $ZodType } from 'zod/v4/core';\nimport type { SchemaObject } from './openapi.types.js';\nimport type { Override } from './override.js';\n\n/**\n * Argument shape for {@link overrideJSONSchema}. Two forms:\n *\n * - A raw `SchemaObject` — applied to both input and output emission.\n * - A `{ input?, output? }` wrapper — distinct fragments per emission side,\n * for coercion shapes where input and output diverge (e.g.\n * `z.union([z.array(item), item.transform((v) => [v])])` accepts\n * `T | T[]` on input but always emits `T[]` on output).\n *\n * The wrapper form is detected by the presence of an `input` or `output`\n * key. Neither is a JSON Schema / OpenAPI 3.1 keyword, so the discriminator\n * is unambiguous in practice.\n */\nexport type OverrideJSONSchemaArg = SchemaObject | { input?: SchemaObject; output?: SchemaObject };\n\nexport interface StoredFragments {\n input?: SchemaObject;\n output?: SchemaObject;\n}\n\n/**\n * Per-instance JSON Schema registration store. Keyed by the Zod core type\n * (`$ZodType`) because that's what the override sees on `ctx.zodSchema`;\n * `z.ZodType` widens to `$ZodType` without a cast at the public API boundary.\n * Same WeakMap pattern as `composition.ts`'s `lineageMap`\n * (CLAUDE.md: \"transient, per-instance → WeakMap\").\n *\n * Value holds the per-direction fragments separately so the override factory\n * can pick the right one without surfacing `io` on `OverrideContext`.\n */\nconst customOverrideMap = new WeakMap<$ZodType, StoredFragments>();\n\nconst isWrapper = (\n arg: OverrideJSONSchemaArg,\n): arg is { input?: SchemaObject; output?: SchemaObject } => 'input' in arg || 'output' in arg;\n\n/**\n * Register a fixed JSON Schema fragment for a specific Zod schema instance.\n *\n * Designed for shapes JSON Schema can't model directly — `z.custom<T>()` and\n * `z.instanceof(...)` (e.g. multipart `File` fields) — which Zod emits as `{}`,\n * tripping `ZodNestUnrepresentableError` in strict mode. Also useful for\n * coercion shapes where input and output diverge (`singleOrArray`-style\n * helpers).\n *\n * Two call shapes:\n *\n * ```ts\n * // 1. Single fragment — applied verbatim to both input and output emission.\n * overrideJSONSchema(FileSchema, { type: 'string', format: 'binary' });\n *\n * // 2. Divergent fragments — separate fragments per emission side. Omit a\n * // side to leave Zod's default emission untouched on that side.\n * overrideJSONSchema(arrayOrItem, {\n * input: { anyOf: [arrFrag, itemFrag] },\n * output: arrFrag,\n * });\n * ```\n *\n * Idempotent: subsequent calls for the same schema overwrite the prior\n * registration (last-write-wins).\n */\nexport const overrideJSONSchema = <T extends z.ZodType>(\n schema: T,\n arg: OverrideJSONSchemaArg,\n): T => {\n const overrideSchema = isWrapper(arg) ? arg : { input: arg, output: arg };\n\n customOverrideMap.set(schema, overrideSchema);\n\n return schema;\n};\n\n/**\n * Read-only lookup into the registration store. Used by the engine to detect\n * when a pipe-typed schema covers its inner descent target with a relevant-io\n * fragment — see `buildToJsonSchemaOptions` in `engine.ts`. Returning a\n * possibly-`undefined` `StoredFragments` keeps the engine ignorant of the\n * underlying WeakMap.\n */\nexport const peekRegistration = (schema: $ZodType): StoredFragments | undefined =>\n customOverrideMap.get(schema);\n\n/**\n * Internal override factory consulted by the engine. Closes over the current\n * emission direction so the lookup can pick the right registered fragment\n * without surfacing `io` on the `OverrideContext` public contract. Mirrors\n * `createCompositionOverride` at `engine.ts:98`.\n *\n * Mutates `ctx.jsonSchema` in-place (clears existing keys, then assigns the\n * fragment) because Zod's override pipeline doesn't propagate\n * `ctx.jsonSchema = newObj` reassignments — only mutations on the existing\n * object reach the caller (see `composition.ts` for the same constraint).\n *\n * No-ops if the schema isn't registered, or if the registered record has no\n * fragment for the current `io` direction.\n */\nexport const createCustomOverride = (io: 'input' | 'output'): Override => {\n return ({ zodSchema, jsonSchema }) => {\n const record = customOverrideMap.get(zodSchema);\n if (record === undefined) {\n return;\n }\n const fragment = record[io];\n if (fragment === undefined) {\n return;\n }\n for (const key of Object.keys(jsonSchema)) {\n Reflect.deleteProperty(jsonSchema, key);\n }\n Object.assign(jsonSchema, fragment);\n };\n};\n","import { z } from 'zod';\n\nimport { overrideJSONSchema } from '../schema/custom-override.js';\nimport { binaryFragment } from './fragments.js';\n\n/**\n * Pre-registered Zod schemas for the common binary runtime types. Each\n * schema is wired through `overrideJSONSchema` so it emits `binaryFragment`\n * verbatim in OpenAPI — drop into a DTO without further setup.\n *\n * Node 22+ provides `File`, `Blob`, and `Buffer` as globals (zod-nest's\n * `engines.node >=22`), so `z.instanceof(...)` at module load is safe.\n *\n * @example\n * import { FileSchema } from 'zod-nest/helpers';\n *\n * class UploadDto extends createZodDto(\n * z.object({ file: FileSchema }),\n * ) {}\n */\n\nexport const FileSchema = overrideJSONSchema(z.instanceof(File), binaryFragment);\n\nexport const BlobSchema = overrideJSONSchema(z.instanceof(Blob), binaryFragment);\n\nexport const BufferSchema = overrideJSONSchema(z.instanceof(Buffer), binaryFragment);\n"]}
1
+ {"version":3,"sources":["../../src/helpers/fragments.ts","../../src/schema/custom-override.ts","../../src/helpers/presets.ts"],"names":["dateTimeFragment","type","format","dateFragment","timeFragment","uuidFragment","emailFragment","uriFragment","hostnameFragment","ipv4Fragment","ipv6Fragment","binaryFragment","byteFragment","int32Fragment","int64Fragment","floatFragment","doubleFragment","opaqueFragment","additionalProperties","enrich","base","extras","binary","opts","opaque","customOverrideMap","WeakMap","isWrapper","arg","overrideJSONSchema","schema","overrideSchema","input","output","schemaDescription","description","set","FileSchema","z","instanceof","File","BlobSchema","Blob","BufferSchema","Buffer"],"mappings":"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","file":"index.js","sourcesContent":["import type { SchemaObject } from '../schema/openapi.types.js';\n\n/**\n * Common JSON Schema fragment catalog — reusable building blocks for assembling\n * fragments by hand: alongside `z.custom<T>()`, in `overrideJSONSchema` calls,\n * in custom override callbacks, in tests that build expected fragments.\n *\n * The catalog overlaps with what Zod constructs already emit (`z.uuid()` →\n * `uuidFragment` shape, `z.email()` → `emailFragment` shape, etc.) — the\n * helpers don't *replace* those Zod constructs; they're a parallel catalog\n * for programmatic fragment assembly.\n *\n * Each fragment is `as const satisfies SchemaObject` so its literal type\n * powers the dispatch in {@link enrich} — passing wrong-family options\n * fails at compile time.\n */\n\n// ---------------------------------------------------------------------------\n// Layer 1 — Fragment catalog\n// ---------------------------------------------------------------------------\n\n// String formats (RFC-defined)\nexport const dateTimeFragment = {\n type: 'string',\n format: 'date-time',\n} as const satisfies SchemaObject;\nexport const dateFragment = { type: 'string', format: 'date' } as const satisfies SchemaObject;\nexport const timeFragment = { type: 'string', format: 'time' } as const satisfies SchemaObject;\nexport const uuidFragment = { type: 'string', format: 'uuid' } as const satisfies SchemaObject;\nexport const emailFragment = { type: 'string', format: 'email' } as const satisfies SchemaObject;\nexport const uriFragment = { type: 'string', format: 'uri' } as const satisfies SchemaObject;\nexport const hostnameFragment = {\n type: 'string',\n format: 'hostname',\n} as const satisfies SchemaObject;\nexport const ipv4Fragment = { type: 'string', format: 'ipv4' } as const satisfies SchemaObject;\nexport const ipv6Fragment = { type: 'string', format: 'ipv6' } as const satisfies SchemaObject;\n\n// Binary / encoded payloads\nexport const binaryFragment = { type: 'string', format: 'binary' } as const satisfies SchemaObject;\nexport const byteFragment = { type: 'string', format: 'byte' } as const satisfies SchemaObject;\n\n// Numeric formats (OpenAPI 3.1)\nexport const int32Fragment = { type: 'integer', format: 'int32' } as const satisfies SchemaObject;\nexport const int64Fragment = { type: 'integer', format: 'int64' } as const satisfies SchemaObject;\nexport const floatFragment = { type: 'number', format: 'float' } as const satisfies SchemaObject;\nexport const doubleFragment = { type: 'number', format: 'double' } as const satisfies SchemaObject;\n\n// Object passthrough\nexport const opaqueFragment = {\n type: 'object',\n additionalProperties: true,\n} as const satisfies SchemaObject;\n\n// ---------------------------------------------------------------------------\n// Layer 2 — Per-family option types\n// ---------------------------------------------------------------------------\n\nexport interface StringFormatOptions {\n description?: string;\n examples?: string[];\n minLength?: number;\n maxLength?: number;\n pattern?: string;\n}\n\nexport interface BinaryFragmentOptions {\n description?: string;\n contentMediaType?: string;\n contentEncoding?: string;\n}\n\nexport interface NumberFormatOptions {\n description?: string;\n examples?: number[];\n minimum?: number;\n maximum?: number;\n exclusiveMinimum?: number;\n exclusiveMaximum?: number;\n multipleOf?: number;\n}\n\nexport interface OpaqueFragmentOptions {\n description?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Layer 3 — Type-strict generic enrich\n// ---------------------------------------------------------------------------\n\n// `binary` discriminates before the generic string-format branch because its\n// option set diverges (contentMediaType / contentEncoding instead of length\n// constraints). Order matters here.\ntype OptionsFor<T> = T extends { readonly format: 'binary' }\n ? BinaryFragmentOptions\n : T extends {\n readonly format:\n | 'date-time'\n | 'date'\n | 'time'\n | 'uuid'\n | 'email'\n | 'uri'\n | 'hostname'\n | 'ipv4'\n | 'ipv6'\n | 'byte';\n }\n ? StringFormatOptions\n : T extends { readonly format: 'int32' | 'int64' | 'float' | 'double' }\n ? NumberFormatOptions\n : T extends { readonly type: 'object'; readonly additionalProperties: true }\n ? OpaqueFragmentOptions\n : never;\n\n/**\n * Merge a catalog fragment with extras whose shape is dictated by the base\n * fragment's family. Passing wrong-family extras (e.g. a `contentMediaType`\n * onto `uuidFragment`) is a compile-time error.\n *\n * Returns a fresh `SchemaObject` — the original fragment is never mutated.\n */\nexport const enrich = <T extends SchemaObject>(base: T, extras: OptionsFor<T>): SchemaObject => ({\n ...base,\n ...extras,\n});\n\n// ---------------------------------------------------------------------------\n// Layer 3b — Sugar functions\n// ---------------------------------------------------------------------------\n\n/**\n * Binary-content fragment with typed enrichment. Sugar for\n * `enrich(binaryFragment, opts)` — exists because the `binary` option set\n * (`contentMediaType` / `contentEncoding`) is nuanced enough to deserve a\n * dedicated entry point and discoverable via auto-complete.\n *\n * @example\n * overrideJSONSchema(z.instanceof(File), binary());\n * overrideJSONSchema(z.instanceof(File), binary({ contentMediaType: 'application/pdf' }));\n */\nexport const binary = (opts?: BinaryFragmentOptions): SchemaObject => ({\n ...binaryFragment,\n ...opts,\n});\n\n/**\n * Opaque-object fragment with typed enrichment. Sugar for\n * `enrich(opaqueFragment, opts)` — exists for parity with {@link binary}\n * and because opaque passthrough payloads commonly carry a description\n * explaining why the API doesn't introspect them.\n *\n * @example\n * overrideJSONSchema(z.unknown(), opaque({ description: 'JWT passthrough' }));\n */\nexport const opaque = (opts?: OpaqueFragmentOptions): SchemaObject => ({\n ...opaqueFragment,\n ...opts,\n});\n","import type { z } from 'zod';\nimport type { $ZodType } from 'zod/v4/core';\nimport type { SchemaObject } from './openapi.types.js';\nimport type { Override } from './override.js';\n\n/**\n * Argument shape for {@link overrideJSONSchema}. Two forms:\n *\n * - A raw `SchemaObject` — applied to both input and output emission.\n * - A `{ input?, output? }` wrapper — distinct fragments per emission side,\n * for coercion shapes where input and output diverge (e.g.\n * `z.union([z.array(item), item.transform((v) => [v])])` accepts\n * `T | T[]` on input but always emits `T[]` on output).\n *\n * The wrapper form is detected by the presence of an `input` or `output`\n * key. Neither is a JSON Schema / OpenAPI 3.1 keyword, so the discriminator\n * is unambiguous in practice.\n */\nexport type OverrideJSONSchemaArg = SchemaObject | { input?: SchemaObject; output?: SchemaObject };\n\nexport interface StoredFragments {\n input?: SchemaObject;\n output?: SchemaObject;\n /**\n * Description captured from the Zod schema at `overrideJSONSchema(...)` call\n * time (`.describe(...)` / `.meta({ description })` both write to\n * `z.globalRegistry`). Applied as a fallback at emission time when the\n * per-direction fragment doesn't supply its own `description`. Fragment-\n * supplied descriptions still win.\n */\n description?: string;\n}\n\n/**\n * Per-instance JSON Schema registration store. Keyed by the Zod core type\n * (`$ZodType`) because that's what the override sees on `ctx.zodSchema`;\n * `z.ZodType` widens to `$ZodType` without a cast at the public API boundary.\n * Same WeakMap pattern as `composition.ts`'s `lineageMap`\n * (CLAUDE.md: \"transient, per-instance → WeakMap\").\n *\n * Value holds the per-direction fragments separately so the override factory\n * can pick the right one without surfacing `io` on `OverrideContext`.\n */\nconst customOverrideMap = new WeakMap<$ZodType, StoredFragments>();\n\nconst isWrapper = (\n arg: OverrideJSONSchemaArg,\n): arg is { input?: SchemaObject; output?: SchemaObject } => 'input' in arg || 'output' in arg;\n\n/**\n * Register a fixed JSON Schema fragment for a specific Zod schema instance.\n *\n * Designed for shapes JSON Schema can't model directly — `z.custom<T>()` and\n * `z.instanceof(...)` (e.g. multipart `File` fields) — which Zod emits as `{}`,\n * tripping `ZodNestUnrepresentableError` in strict mode. Also useful for\n * coercion shapes where input and output diverge (`singleOrArray`-style\n * helpers).\n *\n * Two call shapes:\n *\n * ```ts\n * // 1. Single fragment — applied verbatim to both input and output emission.\n * overrideJSONSchema(FileSchema, { type: 'string', format: 'binary' });\n *\n * // 2. Divergent fragments — separate fragments per emission side. Omit a\n * // side to leave Zod's default emission untouched on that side.\n * overrideJSONSchema(arrayOrItem, {\n * input: { anyOf: [arrFrag, itemFrag] },\n * output: arrFrag,\n * });\n * ```\n *\n * Idempotent: subsequent calls for the same schema overwrite the prior\n * registration (last-write-wins).\n */\nexport const overrideJSONSchema = <T extends z.ZodType>(\n schema: T,\n arg: OverrideJSONSchemaArg,\n): T => {\n const overrideSchema: StoredFragments = isWrapper(arg) ? { ...arg } : { input: arg, output: arg };\n\n const schemaDescription = schema.description;\n if (typeof schemaDescription === 'string') {\n overrideSchema.description = schemaDescription;\n }\n\n customOverrideMap.set(schema, overrideSchema);\n\n return schema;\n};\n\n/**\n * Read-only lookup into the registration store. Used by the engine to detect\n * when a pipe-typed schema covers its inner descent target with a relevant-io\n * fragment — see `buildToJsonSchemaOptions` in `engine.ts`. Returning a\n * possibly-`undefined` `StoredFragments` keeps the engine ignorant of the\n * underlying WeakMap.\n */\nexport const peekRegistration = (schema: $ZodType): StoredFragments | undefined =>\n customOverrideMap.get(schema);\n\n/**\n * Internal override factory consulted by the engine. Closes over the current\n * emission direction so the lookup can pick the right registered fragment\n * without surfacing `io` on the `OverrideContext` public contract. Mirrors\n * `createCompositionOverride` at `engine.ts:98`.\n *\n * Mutates `ctx.jsonSchema` in-place (clears existing keys, then assigns the\n * fragment) because Zod's override pipeline doesn't propagate\n * `ctx.jsonSchema = newObj` reassignments — only mutations on the existing\n * object reach the caller (see `composition.ts` for the same constraint).\n *\n * Falls back to the schema's captured `description` (from `.describe(...)` /\n * `.meta({ description })`) when the per-direction fragment doesn't supply\n * one. Captured at `overrideJSONSchema(...)` call time so the inheritance\n * doesn't depend on Zod's emission-time metadata pass. `title` is\n * deliberately not inherited.\n *\n * No-ops if the schema isn't registered, or if the registered record has no\n * fragment for the current `io` direction.\n */\nexport const createCustomOverride = (io: 'input' | 'output'): Override => {\n return ({ zodSchema, jsonSchema }) => {\n const record = customOverrideMap.get(zodSchema);\n if (record === undefined) {\n return;\n }\n const fragment = record[io];\n if (fragment === undefined) {\n return;\n }\n for (const key of Object.keys(jsonSchema)) {\n Reflect.deleteProperty(jsonSchema, key);\n }\n Object.assign(jsonSchema, fragment);\n if (fragment.description === undefined && record.description !== undefined) {\n jsonSchema.description = record.description;\n }\n };\n};\n","import { z } from 'zod';\n\nimport { overrideJSONSchema } from '../schema/custom-override.js';\nimport { binaryFragment } from './fragments.js';\n\n/**\n * Pre-registered Zod schemas for the common binary runtime types. Each\n * schema is wired through `overrideJSONSchema` so it emits `binaryFragment`\n * verbatim in OpenAPI — drop into a DTO without further setup.\n *\n * Node 22+ provides `File`, `Blob`, and `Buffer` as globals (zod-nest's\n * `engines.node >=22`), so `z.instanceof(...)` at module load is safe.\n *\n * @example\n * import { FileSchema } from 'zod-nest/helpers';\n *\n * class UploadDto extends createZodDto(\n * z.object({ file: FileSchema }),\n * ) {}\n */\n\nexport const FileSchema = overrideJSONSchema(z.instanceof(File), binaryFragment);\n\nexport const BlobSchema = overrideJSONSchema(z.instanceof(Blob), binaryFragment);\n\nexport const BufferSchema = overrideJSONSchema(z.instanceof(Buffer), binaryFragment);\n"]}
@@ -85,10 +85,16 @@ var opaque = /* @__PURE__ */ __name((opts) => ({
85
85
  var customOverrideMap = /* @__PURE__ */ new WeakMap();
86
86
  var isWrapper = /* @__PURE__ */ __name((arg) => "input" in arg || "output" in arg, "isWrapper");
87
87
  var overrideJSONSchema = /* @__PURE__ */ __name((schema, arg) => {
88
- const overrideSchema = isWrapper(arg) ? arg : {
88
+ const overrideSchema = isWrapper(arg) ? {
89
+ ...arg
90
+ } : {
89
91
  input: arg,
90
92
  output: arg
91
93
  };
94
+ const schemaDescription = schema.description;
95
+ if (typeof schemaDescription === "string") {
96
+ overrideSchema.description = schemaDescription;
97
+ }
92
98
  customOverrideMap.set(schema, overrideSchema);
93
99
  return schema;
94
100
  }, "overrideJSONSchema");
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/helpers/fragments.ts","../../src/schema/custom-override.ts","../../src/helpers/presets.ts"],"names":["dateTimeFragment","type","format","dateFragment","timeFragment","uuidFragment","emailFragment","uriFragment","hostnameFragment","ipv4Fragment","ipv6Fragment","binaryFragment","byteFragment","int32Fragment","int64Fragment","floatFragment","doubleFragment","opaqueFragment","additionalProperties","enrich","base","extras","binary","opts","opaque","customOverrideMap","WeakMap","isWrapper","arg","overrideJSONSchema","schema","overrideSchema","input","output","set","FileSchema","z","instanceof","File","BlobSchema","Blob","BufferSchema","Buffer"],"mappings":"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","file":"index.mjs","sourcesContent":["import type { SchemaObject } from '../schema/openapi.types.js';\n\n/**\n * Common JSON Schema fragment catalog — reusable building blocks for assembling\n * fragments by hand: alongside `z.custom<T>()`, in `overrideJSONSchema` calls,\n * in custom override callbacks, in tests that build expected fragments.\n *\n * The catalog overlaps with what Zod constructs already emit (`z.uuid()` →\n * `uuidFragment` shape, `z.email()` → `emailFragment` shape, etc.) — the\n * helpers don't *replace* those Zod constructs; they're a parallel catalog\n * for programmatic fragment assembly.\n *\n * Each fragment is `as const satisfies SchemaObject` so its literal type\n * powers the dispatch in {@link enrich} — passing wrong-family options\n * fails at compile time.\n */\n\n// ---------------------------------------------------------------------------\n// Layer 1 — Fragment catalog\n// ---------------------------------------------------------------------------\n\n// String formats (RFC-defined)\nexport const dateTimeFragment = {\n type: 'string',\n format: 'date-time',\n} as const satisfies SchemaObject;\nexport const dateFragment = { type: 'string', format: 'date' } as const satisfies SchemaObject;\nexport const timeFragment = { type: 'string', format: 'time' } as const satisfies SchemaObject;\nexport const uuidFragment = { type: 'string', format: 'uuid' } as const satisfies SchemaObject;\nexport const emailFragment = { type: 'string', format: 'email' } as const satisfies SchemaObject;\nexport const uriFragment = { type: 'string', format: 'uri' } as const satisfies SchemaObject;\nexport const hostnameFragment = {\n type: 'string',\n format: 'hostname',\n} as const satisfies SchemaObject;\nexport const ipv4Fragment = { type: 'string', format: 'ipv4' } as const satisfies SchemaObject;\nexport const ipv6Fragment = { type: 'string', format: 'ipv6' } as const satisfies SchemaObject;\n\n// Binary / encoded payloads\nexport const binaryFragment = { type: 'string', format: 'binary' } as const satisfies SchemaObject;\nexport const byteFragment = { type: 'string', format: 'byte' } as const satisfies SchemaObject;\n\n// Numeric formats (OpenAPI 3.1)\nexport const int32Fragment = { type: 'integer', format: 'int32' } as const satisfies SchemaObject;\nexport const int64Fragment = { type: 'integer', format: 'int64' } as const satisfies SchemaObject;\nexport const floatFragment = { type: 'number', format: 'float' } as const satisfies SchemaObject;\nexport const doubleFragment = { type: 'number', format: 'double' } as const satisfies SchemaObject;\n\n// Object passthrough\nexport const opaqueFragment = {\n type: 'object',\n additionalProperties: true,\n} as const satisfies SchemaObject;\n\n// ---------------------------------------------------------------------------\n// Layer 2 — Per-family option types\n// ---------------------------------------------------------------------------\n\nexport interface StringFormatOptions {\n description?: string;\n examples?: string[];\n minLength?: number;\n maxLength?: number;\n pattern?: string;\n}\n\nexport interface BinaryFragmentOptions {\n description?: string;\n contentMediaType?: string;\n contentEncoding?: string;\n}\n\nexport interface NumberFormatOptions {\n description?: string;\n examples?: number[];\n minimum?: number;\n maximum?: number;\n exclusiveMinimum?: number;\n exclusiveMaximum?: number;\n multipleOf?: number;\n}\n\nexport interface OpaqueFragmentOptions {\n description?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Layer 3 — Type-strict generic enrich\n// ---------------------------------------------------------------------------\n\n// `binary` discriminates before the generic string-format branch because its\n// option set diverges (contentMediaType / contentEncoding instead of length\n// constraints). Order matters here.\ntype OptionsFor<T> = T extends { readonly format: 'binary' }\n ? BinaryFragmentOptions\n : T extends {\n readonly format:\n | 'date-time'\n | 'date'\n | 'time'\n | 'uuid'\n | 'email'\n | 'uri'\n | 'hostname'\n | 'ipv4'\n | 'ipv6'\n | 'byte';\n }\n ? StringFormatOptions\n : T extends { readonly format: 'int32' | 'int64' | 'float' | 'double' }\n ? NumberFormatOptions\n : T extends { readonly type: 'object'; readonly additionalProperties: true }\n ? OpaqueFragmentOptions\n : never;\n\n/**\n * Merge a catalog fragment with extras whose shape is dictated by the base\n * fragment's family. Passing wrong-family extras (e.g. a `contentMediaType`\n * onto `uuidFragment`) is a compile-time error.\n *\n * Returns a fresh `SchemaObject` — the original fragment is never mutated.\n */\nexport const enrich = <T extends SchemaObject>(base: T, extras: OptionsFor<T>): SchemaObject => ({\n ...base,\n ...extras,\n});\n\n// ---------------------------------------------------------------------------\n// Layer 3b — Sugar functions\n// ---------------------------------------------------------------------------\n\n/**\n * Binary-content fragment with typed enrichment. Sugar for\n * `enrich(binaryFragment, opts)` — exists because the `binary` option set\n * (`contentMediaType` / `contentEncoding`) is nuanced enough to deserve a\n * dedicated entry point and discoverable via auto-complete.\n *\n * @example\n * overrideJSONSchema(z.instanceof(File), binary());\n * overrideJSONSchema(z.instanceof(File), binary({ contentMediaType: 'application/pdf' }));\n */\nexport const binary = (opts?: BinaryFragmentOptions): SchemaObject => ({\n ...binaryFragment,\n ...opts,\n});\n\n/**\n * Opaque-object fragment with typed enrichment. Sugar for\n * `enrich(opaqueFragment, opts)` — exists for parity with {@link binary}\n * and because opaque passthrough payloads commonly carry a description\n * explaining why the API doesn't introspect them.\n *\n * @example\n * overrideJSONSchema(z.unknown(), opaque({ description: 'JWT passthrough' }));\n */\nexport const opaque = (opts?: OpaqueFragmentOptions): SchemaObject => ({\n ...opaqueFragment,\n ...opts,\n});\n","import type { z } from 'zod';\nimport type { $ZodType } from 'zod/v4/core';\nimport type { SchemaObject } from './openapi.types.js';\nimport type { Override } from './override.js';\n\n/**\n * Argument shape for {@link overrideJSONSchema}. Two forms:\n *\n * - A raw `SchemaObject` — applied to both input and output emission.\n * - A `{ input?, output? }` wrapper — distinct fragments per emission side,\n * for coercion shapes where input and output diverge (e.g.\n * `z.union([z.array(item), item.transform((v) => [v])])` accepts\n * `T | T[]` on input but always emits `T[]` on output).\n *\n * The wrapper form is detected by the presence of an `input` or `output`\n * key. Neither is a JSON Schema / OpenAPI 3.1 keyword, so the discriminator\n * is unambiguous in practice.\n */\nexport type OverrideJSONSchemaArg = SchemaObject | { input?: SchemaObject; output?: SchemaObject };\n\nexport interface StoredFragments {\n input?: SchemaObject;\n output?: SchemaObject;\n}\n\n/**\n * Per-instance JSON Schema registration store. Keyed by the Zod core type\n * (`$ZodType`) because that's what the override sees on `ctx.zodSchema`;\n * `z.ZodType` widens to `$ZodType` without a cast at the public API boundary.\n * Same WeakMap pattern as `composition.ts`'s `lineageMap`\n * (CLAUDE.md: \"transient, per-instance → WeakMap\").\n *\n * Value holds the per-direction fragments separately so the override factory\n * can pick the right one without surfacing `io` on `OverrideContext`.\n */\nconst customOverrideMap = new WeakMap<$ZodType, StoredFragments>();\n\nconst isWrapper = (\n arg: OverrideJSONSchemaArg,\n): arg is { input?: SchemaObject; output?: SchemaObject } => 'input' in arg || 'output' in arg;\n\n/**\n * Register a fixed JSON Schema fragment for a specific Zod schema instance.\n *\n * Designed for shapes JSON Schema can't model directly — `z.custom<T>()` and\n * `z.instanceof(...)` (e.g. multipart `File` fields) — which Zod emits as `{}`,\n * tripping `ZodNestUnrepresentableError` in strict mode. Also useful for\n * coercion shapes where input and output diverge (`singleOrArray`-style\n * helpers).\n *\n * Two call shapes:\n *\n * ```ts\n * // 1. Single fragment — applied verbatim to both input and output emission.\n * overrideJSONSchema(FileSchema, { type: 'string', format: 'binary' });\n *\n * // 2. Divergent fragments — separate fragments per emission side. Omit a\n * // side to leave Zod's default emission untouched on that side.\n * overrideJSONSchema(arrayOrItem, {\n * input: { anyOf: [arrFrag, itemFrag] },\n * output: arrFrag,\n * });\n * ```\n *\n * Idempotent: subsequent calls for the same schema overwrite the prior\n * registration (last-write-wins).\n */\nexport const overrideJSONSchema = <T extends z.ZodType>(\n schema: T,\n arg: OverrideJSONSchemaArg,\n): T => {\n const overrideSchema = isWrapper(arg) ? arg : { input: arg, output: arg };\n\n customOverrideMap.set(schema, overrideSchema);\n\n return schema;\n};\n\n/**\n * Read-only lookup into the registration store. Used by the engine to detect\n * when a pipe-typed schema covers its inner descent target with a relevant-io\n * fragment — see `buildToJsonSchemaOptions` in `engine.ts`. Returning a\n * possibly-`undefined` `StoredFragments` keeps the engine ignorant of the\n * underlying WeakMap.\n */\nexport const peekRegistration = (schema: $ZodType): StoredFragments | undefined =>\n customOverrideMap.get(schema);\n\n/**\n * Internal override factory consulted by the engine. Closes over the current\n * emission direction so the lookup can pick the right registered fragment\n * without surfacing `io` on the `OverrideContext` public contract. Mirrors\n * `createCompositionOverride` at `engine.ts:98`.\n *\n * Mutates `ctx.jsonSchema` in-place (clears existing keys, then assigns the\n * fragment) because Zod's override pipeline doesn't propagate\n * `ctx.jsonSchema = newObj` reassignments — only mutations on the existing\n * object reach the caller (see `composition.ts` for the same constraint).\n *\n * No-ops if the schema isn't registered, or if the registered record has no\n * fragment for the current `io` direction.\n */\nexport const createCustomOverride = (io: 'input' | 'output'): Override => {\n return ({ zodSchema, jsonSchema }) => {\n const record = customOverrideMap.get(zodSchema);\n if (record === undefined) {\n return;\n }\n const fragment = record[io];\n if (fragment === undefined) {\n return;\n }\n for (const key of Object.keys(jsonSchema)) {\n Reflect.deleteProperty(jsonSchema, key);\n }\n Object.assign(jsonSchema, fragment);\n };\n};\n","import { z } from 'zod';\n\nimport { overrideJSONSchema } from '../schema/custom-override.js';\nimport { binaryFragment } from './fragments.js';\n\n/**\n * Pre-registered Zod schemas for the common binary runtime types. Each\n * schema is wired through `overrideJSONSchema` so it emits `binaryFragment`\n * verbatim in OpenAPI — drop into a DTO without further setup.\n *\n * Node 22+ provides `File`, `Blob`, and `Buffer` as globals (zod-nest's\n * `engines.node >=22`), so `z.instanceof(...)` at module load is safe.\n *\n * @example\n * import { FileSchema } from 'zod-nest/helpers';\n *\n * class UploadDto extends createZodDto(\n * z.object({ file: FileSchema }),\n * ) {}\n */\n\nexport const FileSchema = overrideJSONSchema(z.instanceof(File), binaryFragment);\n\nexport const BlobSchema = overrideJSONSchema(z.instanceof(Blob), binaryFragment);\n\nexport const BufferSchema = overrideJSONSchema(z.instanceof(Buffer), binaryFragment);\n"]}
1
+ {"version":3,"sources":["../../src/helpers/fragments.ts","../../src/schema/custom-override.ts","../../src/helpers/presets.ts"],"names":["dateTimeFragment","type","format","dateFragment","timeFragment","uuidFragment","emailFragment","uriFragment","hostnameFragment","ipv4Fragment","ipv6Fragment","binaryFragment","byteFragment","int32Fragment","int64Fragment","floatFragment","doubleFragment","opaqueFragment","additionalProperties","enrich","base","extras","binary","opts","opaque","customOverrideMap","WeakMap","isWrapper","arg","overrideJSONSchema","schema","overrideSchema","input","output","schemaDescription","description","set","FileSchema","z","instanceof","File","BlobSchema","Blob","BufferSchema","Buffer"],"mappings":"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","file":"index.mjs","sourcesContent":["import type { SchemaObject } from '../schema/openapi.types.js';\n\n/**\n * Common JSON Schema fragment catalog — reusable building blocks for assembling\n * fragments by hand: alongside `z.custom<T>()`, in `overrideJSONSchema` calls,\n * in custom override callbacks, in tests that build expected fragments.\n *\n * The catalog overlaps with what Zod constructs already emit (`z.uuid()` →\n * `uuidFragment` shape, `z.email()` → `emailFragment` shape, etc.) — the\n * helpers don't *replace* those Zod constructs; they're a parallel catalog\n * for programmatic fragment assembly.\n *\n * Each fragment is `as const satisfies SchemaObject` so its literal type\n * powers the dispatch in {@link enrich} — passing wrong-family options\n * fails at compile time.\n */\n\n// ---------------------------------------------------------------------------\n// Layer 1 — Fragment catalog\n// ---------------------------------------------------------------------------\n\n// String formats (RFC-defined)\nexport const dateTimeFragment = {\n type: 'string',\n format: 'date-time',\n} as const satisfies SchemaObject;\nexport const dateFragment = { type: 'string', format: 'date' } as const satisfies SchemaObject;\nexport const timeFragment = { type: 'string', format: 'time' } as const satisfies SchemaObject;\nexport const uuidFragment = { type: 'string', format: 'uuid' } as const satisfies SchemaObject;\nexport const emailFragment = { type: 'string', format: 'email' } as const satisfies SchemaObject;\nexport const uriFragment = { type: 'string', format: 'uri' } as const satisfies SchemaObject;\nexport const hostnameFragment = {\n type: 'string',\n format: 'hostname',\n} as const satisfies SchemaObject;\nexport const ipv4Fragment = { type: 'string', format: 'ipv4' } as const satisfies SchemaObject;\nexport const ipv6Fragment = { type: 'string', format: 'ipv6' } as const satisfies SchemaObject;\n\n// Binary / encoded payloads\nexport const binaryFragment = { type: 'string', format: 'binary' } as const satisfies SchemaObject;\nexport const byteFragment = { type: 'string', format: 'byte' } as const satisfies SchemaObject;\n\n// Numeric formats (OpenAPI 3.1)\nexport const int32Fragment = { type: 'integer', format: 'int32' } as const satisfies SchemaObject;\nexport const int64Fragment = { type: 'integer', format: 'int64' } as const satisfies SchemaObject;\nexport const floatFragment = { type: 'number', format: 'float' } as const satisfies SchemaObject;\nexport const doubleFragment = { type: 'number', format: 'double' } as const satisfies SchemaObject;\n\n// Object passthrough\nexport const opaqueFragment = {\n type: 'object',\n additionalProperties: true,\n} as const satisfies SchemaObject;\n\n// ---------------------------------------------------------------------------\n// Layer 2 — Per-family option types\n// ---------------------------------------------------------------------------\n\nexport interface StringFormatOptions {\n description?: string;\n examples?: string[];\n minLength?: number;\n maxLength?: number;\n pattern?: string;\n}\n\nexport interface BinaryFragmentOptions {\n description?: string;\n contentMediaType?: string;\n contentEncoding?: string;\n}\n\nexport interface NumberFormatOptions {\n description?: string;\n examples?: number[];\n minimum?: number;\n maximum?: number;\n exclusiveMinimum?: number;\n exclusiveMaximum?: number;\n multipleOf?: number;\n}\n\nexport interface OpaqueFragmentOptions {\n description?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Layer 3 — Type-strict generic enrich\n// ---------------------------------------------------------------------------\n\n// `binary` discriminates before the generic string-format branch because its\n// option set diverges (contentMediaType / contentEncoding instead of length\n// constraints). Order matters here.\ntype OptionsFor<T> = T extends { readonly format: 'binary' }\n ? BinaryFragmentOptions\n : T extends {\n readonly format:\n | 'date-time'\n | 'date'\n | 'time'\n | 'uuid'\n | 'email'\n | 'uri'\n | 'hostname'\n | 'ipv4'\n | 'ipv6'\n | 'byte';\n }\n ? StringFormatOptions\n : T extends { readonly format: 'int32' | 'int64' | 'float' | 'double' }\n ? NumberFormatOptions\n : T extends { readonly type: 'object'; readonly additionalProperties: true }\n ? OpaqueFragmentOptions\n : never;\n\n/**\n * Merge a catalog fragment with extras whose shape is dictated by the base\n * fragment's family. Passing wrong-family extras (e.g. a `contentMediaType`\n * onto `uuidFragment`) is a compile-time error.\n *\n * Returns a fresh `SchemaObject` — the original fragment is never mutated.\n */\nexport const enrich = <T extends SchemaObject>(base: T, extras: OptionsFor<T>): SchemaObject => ({\n ...base,\n ...extras,\n});\n\n// ---------------------------------------------------------------------------\n// Layer 3b — Sugar functions\n// ---------------------------------------------------------------------------\n\n/**\n * Binary-content fragment with typed enrichment. Sugar for\n * `enrich(binaryFragment, opts)` — exists because the `binary` option set\n * (`contentMediaType` / `contentEncoding`) is nuanced enough to deserve a\n * dedicated entry point and discoverable via auto-complete.\n *\n * @example\n * overrideJSONSchema(z.instanceof(File), binary());\n * overrideJSONSchema(z.instanceof(File), binary({ contentMediaType: 'application/pdf' }));\n */\nexport const binary = (opts?: BinaryFragmentOptions): SchemaObject => ({\n ...binaryFragment,\n ...opts,\n});\n\n/**\n * Opaque-object fragment with typed enrichment. Sugar for\n * `enrich(opaqueFragment, opts)` — exists for parity with {@link binary}\n * and because opaque passthrough payloads commonly carry a description\n * explaining why the API doesn't introspect them.\n *\n * @example\n * overrideJSONSchema(z.unknown(), opaque({ description: 'JWT passthrough' }));\n */\nexport const opaque = (opts?: OpaqueFragmentOptions): SchemaObject => ({\n ...opaqueFragment,\n ...opts,\n});\n","import type { z } from 'zod';\nimport type { $ZodType } from 'zod/v4/core';\nimport type { SchemaObject } from './openapi.types.js';\nimport type { Override } from './override.js';\n\n/**\n * Argument shape for {@link overrideJSONSchema}. Two forms:\n *\n * - A raw `SchemaObject` — applied to both input and output emission.\n * - A `{ input?, output? }` wrapper — distinct fragments per emission side,\n * for coercion shapes where input and output diverge (e.g.\n * `z.union([z.array(item), item.transform((v) => [v])])` accepts\n * `T | T[]` on input but always emits `T[]` on output).\n *\n * The wrapper form is detected by the presence of an `input` or `output`\n * key. Neither is a JSON Schema / OpenAPI 3.1 keyword, so the discriminator\n * is unambiguous in practice.\n */\nexport type OverrideJSONSchemaArg = SchemaObject | { input?: SchemaObject; output?: SchemaObject };\n\nexport interface StoredFragments {\n input?: SchemaObject;\n output?: SchemaObject;\n /**\n * Description captured from the Zod schema at `overrideJSONSchema(...)` call\n * time (`.describe(...)` / `.meta({ description })` both write to\n * `z.globalRegistry`). Applied as a fallback at emission time when the\n * per-direction fragment doesn't supply its own `description`. Fragment-\n * supplied descriptions still win.\n */\n description?: string;\n}\n\n/**\n * Per-instance JSON Schema registration store. Keyed by the Zod core type\n * (`$ZodType`) because that's what the override sees on `ctx.zodSchema`;\n * `z.ZodType` widens to `$ZodType` without a cast at the public API boundary.\n * Same WeakMap pattern as `composition.ts`'s `lineageMap`\n * (CLAUDE.md: \"transient, per-instance → WeakMap\").\n *\n * Value holds the per-direction fragments separately so the override factory\n * can pick the right one without surfacing `io` on `OverrideContext`.\n */\nconst customOverrideMap = new WeakMap<$ZodType, StoredFragments>();\n\nconst isWrapper = (\n arg: OverrideJSONSchemaArg,\n): arg is { input?: SchemaObject; output?: SchemaObject } => 'input' in arg || 'output' in arg;\n\n/**\n * Register a fixed JSON Schema fragment for a specific Zod schema instance.\n *\n * Designed for shapes JSON Schema can't model directly — `z.custom<T>()` and\n * `z.instanceof(...)` (e.g. multipart `File` fields) — which Zod emits as `{}`,\n * tripping `ZodNestUnrepresentableError` in strict mode. Also useful for\n * coercion shapes where input and output diverge (`singleOrArray`-style\n * helpers).\n *\n * Two call shapes:\n *\n * ```ts\n * // 1. Single fragment — applied verbatim to both input and output emission.\n * overrideJSONSchema(FileSchema, { type: 'string', format: 'binary' });\n *\n * // 2. Divergent fragments — separate fragments per emission side. Omit a\n * // side to leave Zod's default emission untouched on that side.\n * overrideJSONSchema(arrayOrItem, {\n * input: { anyOf: [arrFrag, itemFrag] },\n * output: arrFrag,\n * });\n * ```\n *\n * Idempotent: subsequent calls for the same schema overwrite the prior\n * registration (last-write-wins).\n */\nexport const overrideJSONSchema = <T extends z.ZodType>(\n schema: T,\n arg: OverrideJSONSchemaArg,\n): T => {\n const overrideSchema: StoredFragments = isWrapper(arg) ? { ...arg } : { input: arg, output: arg };\n\n const schemaDescription = schema.description;\n if (typeof schemaDescription === 'string') {\n overrideSchema.description = schemaDescription;\n }\n\n customOverrideMap.set(schema, overrideSchema);\n\n return schema;\n};\n\n/**\n * Read-only lookup into the registration store. Used by the engine to detect\n * when a pipe-typed schema covers its inner descent target with a relevant-io\n * fragment — see `buildToJsonSchemaOptions` in `engine.ts`. Returning a\n * possibly-`undefined` `StoredFragments` keeps the engine ignorant of the\n * underlying WeakMap.\n */\nexport const peekRegistration = (schema: $ZodType): StoredFragments | undefined =>\n customOverrideMap.get(schema);\n\n/**\n * Internal override factory consulted by the engine. Closes over the current\n * emission direction so the lookup can pick the right registered fragment\n * without surfacing `io` on the `OverrideContext` public contract. Mirrors\n * `createCompositionOverride` at `engine.ts:98`.\n *\n * Mutates `ctx.jsonSchema` in-place (clears existing keys, then assigns the\n * fragment) because Zod's override pipeline doesn't propagate\n * `ctx.jsonSchema = newObj` reassignments — only mutations on the existing\n * object reach the caller (see `composition.ts` for the same constraint).\n *\n * Falls back to the schema's captured `description` (from `.describe(...)` /\n * `.meta({ description })`) when the per-direction fragment doesn't supply\n * one. Captured at `overrideJSONSchema(...)` call time so the inheritance\n * doesn't depend on Zod's emission-time metadata pass. `title` is\n * deliberately not inherited.\n *\n * No-ops if the schema isn't registered, or if the registered record has no\n * fragment for the current `io` direction.\n */\nexport const createCustomOverride = (io: 'input' | 'output'): Override => {\n return ({ zodSchema, jsonSchema }) => {\n const record = customOverrideMap.get(zodSchema);\n if (record === undefined) {\n return;\n }\n const fragment = record[io];\n if (fragment === undefined) {\n return;\n }\n for (const key of Object.keys(jsonSchema)) {\n Reflect.deleteProperty(jsonSchema, key);\n }\n Object.assign(jsonSchema, fragment);\n if (fragment.description === undefined && record.description !== undefined) {\n jsonSchema.description = record.description;\n }\n };\n};\n","import { z } from 'zod';\n\nimport { overrideJSONSchema } from '../schema/custom-override.js';\nimport { binaryFragment } from './fragments.js';\n\n/**\n * Pre-registered Zod schemas for the common binary runtime types. Each\n * schema is wired through `overrideJSONSchema` so it emits `binaryFragment`\n * verbatim in OpenAPI — drop into a DTO without further setup.\n *\n * Node 22+ provides `File`, `Blob`, and `Buffer` as globals (zod-nest's\n * `engines.node >=22`), so `z.instanceof(...)` at module load is safe.\n *\n * @example\n * import { FileSchema } from 'zod-nest/helpers';\n *\n * class UploadDto extends createZodDto(\n * z.object({ file: FileSchema }),\n * ) {}\n */\n\nexport const FileSchema = overrideJSONSchema(z.instanceof(File), binaryFragment);\n\nexport const BlobSchema = overrideJSONSchema(z.instanceof(Blob), binaryFragment);\n\nexport const BufferSchema = overrideJSONSchema(z.instanceof(Buffer), binaryFragment);\n"]}
package/dist/index.d.mts CHANGED
@@ -46,6 +46,25 @@ interface ZodNestRegistry {
46
46
  declare const createRegistry: () => ZodNestRegistry;
47
47
  /** Process-wide default registry, used when no explicit `options.registry` is passed. */
48
48
  declare const defaultRegistry: ZodNestRegistry;
49
+ interface RegisterSchemaOptions {
50
+ /** Forces this id, overriding any `.meta({ id })` already on the schema. */
51
+ readonly id?: string;
52
+ }
53
+ /**
54
+ * Register a schema with the given registry, resolving its id from (in order):
55
+ * an explicit `options.id`, then `.meta({ id })` on the schema. Returns the
56
+ * resolved id, or `undefined` when neither source yields one (the call is then
57
+ * a no-op — callers with their own fallback path handle that case).
58
+ *
59
+ * Shared by `createZodDto` and `extend` so a schema named via `.meta({ id })`
60
+ * gets its body emitted into `components.schemas` even when it never flows
61
+ * through `createZodDto` (e.g. used only as an `extend()` parent — Zod's
62
+ * `.extend()` produces a flat object, so the parent isn't a transitive
63
+ * descendant of the child and would otherwise be missed by `discoverDependents`).
64
+ *
65
+ * Idempotent — `registry.register` already deduplicates repeat calls.
66
+ */
67
+ declare const registerSchema: (schema: z.ZodType, registry?: ZodNestRegistry, options?: RegisterSchemaOptions) => string | undefined;
49
68
 
50
69
  /**
51
70
  * Composition layer — emits OpenAPI `allOf` for schemas derived via `extend`.
@@ -600,4 +619,4 @@ declare class ZodNestDocumentError extends ZodNestError {
600
619
  constructor(code: ZodNestDocumentErrorCode, message: string, details?: Record<string, unknown>);
601
620
  }
602
621
 
603
- export { type ApplyZodNestOptions, COMPONENTS_SCHEMAS_PREFIX, type CreateSerializationException, type CreateValidationException, type CreateZodDtoOptions, DEFAULT_MAX_LOGGED_VALUE_BYTES, DEFAULT_REDACT_KEYS, type Io, type LineageEntry, type NormalizedZodNestOptions, type Override, type OverrideContext, type OverrideJSONSchemaArg, type ResponseStatusInput, type ResponseStatusWildcard, type ResponseVariant, type ResponseVariantKind, SchemaObject, type ToOpenApiOptions, type ToOpenApiResult, ZOD_DTO_SYMBOL, ZOD_NEST_DTO_EXTENSION, ZOD_NEST_ERROR_DUPLICATE_ID, ZOD_NEST_ERROR_EXTENSION, ZOD_NEST_OPTIONS, ZOD_RESPONSES_METADATA_KEY, type ZodDto, type ZodDtoMarker, ZodNestDocumentError, type ZodNestDocumentErrorCode, ZodNestError, ZodNestModule, type ZodNestModuleOptions, type ZodNestRegistry, ZodNestUnrepresentableError, ZodResponse, type ZodResponseDescription, type ZodResponseOptions, type ZodResponseType, ZodSerializationException, ZodSerializerInterceptor, ZodValidationException, ZodValidationPipe, type ZodValidationPipeArg, type ZodValidationPipeOptions, applyZodNest, createRegistry, createZodDto, defaultRegistry, defaultStatusFor, extend, getLineage, isZodDto, isZodDtoMarker, makeZodDtoMarker, overrideJSONSchema, resolveEffectiveStatus, toOpenApi };
622
+ export { type ApplyZodNestOptions, COMPONENTS_SCHEMAS_PREFIX, type CreateSerializationException, type CreateValidationException, type CreateZodDtoOptions, DEFAULT_MAX_LOGGED_VALUE_BYTES, DEFAULT_REDACT_KEYS, type Io, type LineageEntry, type NormalizedZodNestOptions, type Override, type OverrideContext, type OverrideJSONSchemaArg, type RegisterSchemaOptions, type ResponseStatusInput, type ResponseStatusWildcard, type ResponseVariant, type ResponseVariantKind, SchemaObject, type ToOpenApiOptions, type ToOpenApiResult, ZOD_DTO_SYMBOL, ZOD_NEST_DTO_EXTENSION, ZOD_NEST_ERROR_DUPLICATE_ID, ZOD_NEST_ERROR_EXTENSION, ZOD_NEST_OPTIONS, ZOD_RESPONSES_METADATA_KEY, type ZodDto, type ZodDtoMarker, ZodNestDocumentError, type ZodNestDocumentErrorCode, ZodNestError, ZodNestModule, type ZodNestModuleOptions, type ZodNestRegistry, ZodNestUnrepresentableError, ZodResponse, type ZodResponseDescription, type ZodResponseOptions, type ZodResponseType, ZodSerializationException, ZodSerializerInterceptor, ZodValidationException, ZodValidationPipe, type ZodValidationPipeArg, type ZodValidationPipeOptions, applyZodNest, createRegistry, createZodDto, defaultRegistry, defaultStatusFor, extend, getLineage, isZodDto, isZodDtoMarker, makeZodDtoMarker, overrideJSONSchema, registerSchema, resolveEffectiveStatus, toOpenApi };
package/dist/index.d.ts CHANGED
@@ -46,6 +46,25 @@ interface ZodNestRegistry {
46
46
  declare const createRegistry: () => ZodNestRegistry;
47
47
  /** Process-wide default registry, used when no explicit `options.registry` is passed. */
48
48
  declare const defaultRegistry: ZodNestRegistry;
49
+ interface RegisterSchemaOptions {
50
+ /** Forces this id, overriding any `.meta({ id })` already on the schema. */
51
+ readonly id?: string;
52
+ }
53
+ /**
54
+ * Register a schema with the given registry, resolving its id from (in order):
55
+ * an explicit `options.id`, then `.meta({ id })` on the schema. Returns the
56
+ * resolved id, or `undefined` when neither source yields one (the call is then
57
+ * a no-op — callers with their own fallback path handle that case).
58
+ *
59
+ * Shared by `createZodDto` and `extend` so a schema named via `.meta({ id })`
60
+ * gets its body emitted into `components.schemas` even when it never flows
61
+ * through `createZodDto` (e.g. used only as an `extend()` parent — Zod's
62
+ * `.extend()` produces a flat object, so the parent isn't a transitive
63
+ * descendant of the child and would otherwise be missed by `discoverDependents`).
64
+ *
65
+ * Idempotent — `registry.register` already deduplicates repeat calls.
66
+ */
67
+ declare const registerSchema: (schema: z.ZodType, registry?: ZodNestRegistry, options?: RegisterSchemaOptions) => string | undefined;
49
68
 
50
69
  /**
51
70
  * Composition layer — emits OpenAPI `allOf` for schemas derived via `extend`.
@@ -600,4 +619,4 @@ declare class ZodNestDocumentError extends ZodNestError {
600
619
  constructor(code: ZodNestDocumentErrorCode, message: string, details?: Record<string, unknown>);
601
620
  }
602
621
 
603
- export { type ApplyZodNestOptions, COMPONENTS_SCHEMAS_PREFIX, type CreateSerializationException, type CreateValidationException, type CreateZodDtoOptions, DEFAULT_MAX_LOGGED_VALUE_BYTES, DEFAULT_REDACT_KEYS, type Io, type LineageEntry, type NormalizedZodNestOptions, type Override, type OverrideContext, type OverrideJSONSchemaArg, type ResponseStatusInput, type ResponseStatusWildcard, type ResponseVariant, type ResponseVariantKind, SchemaObject, type ToOpenApiOptions, type ToOpenApiResult, ZOD_DTO_SYMBOL, ZOD_NEST_DTO_EXTENSION, ZOD_NEST_ERROR_DUPLICATE_ID, ZOD_NEST_ERROR_EXTENSION, ZOD_NEST_OPTIONS, ZOD_RESPONSES_METADATA_KEY, type ZodDto, type ZodDtoMarker, ZodNestDocumentError, type ZodNestDocumentErrorCode, ZodNestError, ZodNestModule, type ZodNestModuleOptions, type ZodNestRegistry, ZodNestUnrepresentableError, ZodResponse, type ZodResponseDescription, type ZodResponseOptions, type ZodResponseType, ZodSerializationException, ZodSerializerInterceptor, ZodValidationException, ZodValidationPipe, type ZodValidationPipeArg, type ZodValidationPipeOptions, applyZodNest, createRegistry, createZodDto, defaultRegistry, defaultStatusFor, extend, getLineage, isZodDto, isZodDtoMarker, makeZodDtoMarker, overrideJSONSchema, resolveEffectiveStatus, toOpenApi };
622
+ export { type ApplyZodNestOptions, COMPONENTS_SCHEMAS_PREFIX, type CreateSerializationException, type CreateValidationException, type CreateZodDtoOptions, DEFAULT_MAX_LOGGED_VALUE_BYTES, DEFAULT_REDACT_KEYS, type Io, type LineageEntry, type NormalizedZodNestOptions, type Override, type OverrideContext, type OverrideJSONSchemaArg, type RegisterSchemaOptions, type ResponseStatusInput, type ResponseStatusWildcard, type ResponseVariant, type ResponseVariantKind, SchemaObject, type ToOpenApiOptions, type ToOpenApiResult, ZOD_DTO_SYMBOL, ZOD_NEST_DTO_EXTENSION, ZOD_NEST_ERROR_DUPLICATE_ID, ZOD_NEST_ERROR_EXTENSION, ZOD_NEST_OPTIONS, ZOD_RESPONSES_METADATA_KEY, type ZodDto, type ZodDtoMarker, ZodNestDocumentError, type ZodNestDocumentErrorCode, ZodNestError, ZodNestModule, type ZodNestModuleOptions, type ZodNestRegistry, ZodNestUnrepresentableError, ZodResponse, type ZodResponseDescription, type ZodResponseOptions, type ZodResponseType, ZodSerializationException, ZodSerializerInterceptor, ZodValidationException, ZodValidationPipe, type ZodValidationPipeArg, type ZodValidationPipeOptions, applyZodNest, createRegistry, createZodDto, defaultRegistry, defaultStatusFor, extend, getLineage, isZodDto, isZodDtoMarker, makeZodDtoMarker, overrideJSONSchema, registerSchema, resolveEffectiveStatus, toOpenApi };