zod-nest 1.3.1 → 1.5.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 +8 -0
- package/dist/helpers/index.d.mts +167 -0
- package/dist/helpers/index.d.ts +167 -0
- package/dist/helpers/index.js +126 -0
- package/dist/helpers/index.js.map +1 -0
- package/dist/helpers/index.mjs +103 -0
- package/dist/helpers/index.mjs.map +1 -0
- package/dist/index.d.mts +23 -11
- package/dist/index.d.ts +23 -11
- package/dist/index.js +97 -32
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +97 -32
- package/dist/index.mjs.map +1 -1
- package/dist/openapi.types-CFBG3Zz9.d.mts +5 -0
- package/dist/openapi.types-CFBG3Zz9.d.ts +5 -0
- package/package.json +11 -1
package/README.md
CHANGED
|
@@ -433,6 +433,14 @@ A compact, link-out index. Type signatures and detailed semantics live in the co
|
|
|
433
433
|
**Schema engine** — single-schema mode and extension points
|
|
434
434
|
- `toOpenApi(schema, opts)`, `createRegistry()`, `defaultRegistry`, `ZodNestRegistry`, `Override`, `OverrideContext`, `overrideJSONSchema(schema, fragment | { input?, output? })`, `OverrideJSONSchemaArg`, `ZodNestError`, `ZodNestUnrepresentableError`, `extend`, `getLineage`, `LineageEntry`
|
|
435
435
|
|
|
436
|
+
**Helpers** (subpath: `zod-nest/helpers`) — common JSON Schema fragments + presets for assembling overrides
|
|
437
|
+
- **Fragment catalog** (frozen consts): `dateTimeFragment`, `dateFragment`, `timeFragment`, `uuidFragment`, `emailFragment`, `uriFragment`, `hostnameFragment`, `ipv4Fragment`, `ipv6Fragment`, `binaryFragment`, `byteFragment`, `int32Fragment`, `int64Fragment`, `floatFragment`, `doubleFragment`, `opaqueFragment`
|
|
438
|
+
- **Sugar functions**: `binary(opts?)`, `opaque(opts?)`
|
|
439
|
+
- **Type-strict composition**: `enrich(base, extras)` — extras are typed per fragment family
|
|
440
|
+
- **Pre-registered Zod schemas**: `FileSchema`, `BlobSchema`, `BufferSchema` (all `z.instanceof(...)` + `binaryFragment`)
|
|
441
|
+
|
|
442
|
+
See [`docs/recipes/custom-openapi-overrides.md`](docs/recipes/custom-openapi-overrides.md) for the full catalog and usage patterns.
|
|
443
|
+
|
|
436
444
|
## Documentation
|
|
437
445
|
|
|
438
446
|
| Topic | Doc |
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { S as SchemaObject } from '../openapi.types-CFBG3Zz9.mjs';
|
|
2
|
+
import * as buffer from 'buffer';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
import 'zod/v4/core';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Common JSON Schema fragment catalog — reusable building blocks for assembling
|
|
8
|
+
* fragments by hand: alongside `z.custom<T>()`, in `overrideJSONSchema` calls,
|
|
9
|
+
* in custom override callbacks, in tests that build expected fragments.
|
|
10
|
+
*
|
|
11
|
+
* The catalog overlaps with what Zod constructs already emit (`z.uuid()` →
|
|
12
|
+
* `uuidFragment` shape, `z.email()` → `emailFragment` shape, etc.) — the
|
|
13
|
+
* helpers don't *replace* those Zod constructs; they're a parallel catalog
|
|
14
|
+
* for programmatic fragment assembly.
|
|
15
|
+
*
|
|
16
|
+
* Each fragment is `as const satisfies SchemaObject` so its literal type
|
|
17
|
+
* powers the dispatch in {@link enrich} — passing wrong-family options
|
|
18
|
+
* fails at compile time.
|
|
19
|
+
*/
|
|
20
|
+
declare const dateTimeFragment: {
|
|
21
|
+
readonly type: "string";
|
|
22
|
+
readonly format: "date-time";
|
|
23
|
+
};
|
|
24
|
+
declare const dateFragment: {
|
|
25
|
+
readonly type: "string";
|
|
26
|
+
readonly format: "date";
|
|
27
|
+
};
|
|
28
|
+
declare const timeFragment: {
|
|
29
|
+
readonly type: "string";
|
|
30
|
+
readonly format: "time";
|
|
31
|
+
};
|
|
32
|
+
declare const uuidFragment: {
|
|
33
|
+
readonly type: "string";
|
|
34
|
+
readonly format: "uuid";
|
|
35
|
+
};
|
|
36
|
+
declare const emailFragment: {
|
|
37
|
+
readonly type: "string";
|
|
38
|
+
readonly format: "email";
|
|
39
|
+
};
|
|
40
|
+
declare const uriFragment: {
|
|
41
|
+
readonly type: "string";
|
|
42
|
+
readonly format: "uri";
|
|
43
|
+
};
|
|
44
|
+
declare const hostnameFragment: {
|
|
45
|
+
readonly type: "string";
|
|
46
|
+
readonly format: "hostname";
|
|
47
|
+
};
|
|
48
|
+
declare const ipv4Fragment: {
|
|
49
|
+
readonly type: "string";
|
|
50
|
+
readonly format: "ipv4";
|
|
51
|
+
};
|
|
52
|
+
declare const ipv6Fragment: {
|
|
53
|
+
readonly type: "string";
|
|
54
|
+
readonly format: "ipv6";
|
|
55
|
+
};
|
|
56
|
+
declare const binaryFragment: {
|
|
57
|
+
readonly type: "string";
|
|
58
|
+
readonly format: "binary";
|
|
59
|
+
};
|
|
60
|
+
declare const byteFragment: {
|
|
61
|
+
readonly type: "string";
|
|
62
|
+
readonly format: "byte";
|
|
63
|
+
};
|
|
64
|
+
declare const int32Fragment: {
|
|
65
|
+
readonly type: "integer";
|
|
66
|
+
readonly format: "int32";
|
|
67
|
+
};
|
|
68
|
+
declare const int64Fragment: {
|
|
69
|
+
readonly type: "integer";
|
|
70
|
+
readonly format: "int64";
|
|
71
|
+
};
|
|
72
|
+
declare const floatFragment: {
|
|
73
|
+
readonly type: "number";
|
|
74
|
+
readonly format: "float";
|
|
75
|
+
};
|
|
76
|
+
declare const doubleFragment: {
|
|
77
|
+
readonly type: "number";
|
|
78
|
+
readonly format: "double";
|
|
79
|
+
};
|
|
80
|
+
declare const opaqueFragment: {
|
|
81
|
+
readonly type: "object";
|
|
82
|
+
readonly additionalProperties: true;
|
|
83
|
+
};
|
|
84
|
+
interface StringFormatOptions {
|
|
85
|
+
description?: string;
|
|
86
|
+
examples?: string[];
|
|
87
|
+
minLength?: number;
|
|
88
|
+
maxLength?: number;
|
|
89
|
+
pattern?: string;
|
|
90
|
+
}
|
|
91
|
+
interface BinaryFragmentOptions {
|
|
92
|
+
description?: string;
|
|
93
|
+
contentMediaType?: string;
|
|
94
|
+
contentEncoding?: string;
|
|
95
|
+
}
|
|
96
|
+
interface NumberFormatOptions {
|
|
97
|
+
description?: string;
|
|
98
|
+
examples?: number[];
|
|
99
|
+
minimum?: number;
|
|
100
|
+
maximum?: number;
|
|
101
|
+
exclusiveMinimum?: number;
|
|
102
|
+
exclusiveMaximum?: number;
|
|
103
|
+
multipleOf?: number;
|
|
104
|
+
}
|
|
105
|
+
interface OpaqueFragmentOptions {
|
|
106
|
+
description?: string;
|
|
107
|
+
}
|
|
108
|
+
type OptionsFor<T> = T extends {
|
|
109
|
+
readonly format: 'binary';
|
|
110
|
+
} ? BinaryFragmentOptions : T extends {
|
|
111
|
+
readonly format: 'date-time' | 'date' | 'time' | 'uuid' | 'email' | 'uri' | 'hostname' | 'ipv4' | 'ipv6' | 'byte';
|
|
112
|
+
} ? StringFormatOptions : T extends {
|
|
113
|
+
readonly format: 'int32' | 'int64' | 'float' | 'double';
|
|
114
|
+
} ? NumberFormatOptions : T extends {
|
|
115
|
+
readonly type: 'object';
|
|
116
|
+
readonly additionalProperties: true;
|
|
117
|
+
} ? OpaqueFragmentOptions : never;
|
|
118
|
+
/**
|
|
119
|
+
* Merge a catalog fragment with extras whose shape is dictated by the base
|
|
120
|
+
* fragment's family. Passing wrong-family extras (e.g. a `contentMediaType`
|
|
121
|
+
* onto `uuidFragment`) is a compile-time error.
|
|
122
|
+
*
|
|
123
|
+
* Returns a fresh `SchemaObject` — the original fragment is never mutated.
|
|
124
|
+
*/
|
|
125
|
+
declare const enrich: <T extends SchemaObject>(base: T, extras: OptionsFor<T>) => SchemaObject;
|
|
126
|
+
/**
|
|
127
|
+
* Binary-content fragment with typed enrichment. Sugar for
|
|
128
|
+
* `enrich(binaryFragment, opts)` — exists because the `binary` option set
|
|
129
|
+
* (`contentMediaType` / `contentEncoding`) is nuanced enough to deserve a
|
|
130
|
+
* dedicated entry point and discoverable via auto-complete.
|
|
131
|
+
*
|
|
132
|
+
* @example
|
|
133
|
+
* overrideJSONSchema(z.instanceof(File), binary());
|
|
134
|
+
* overrideJSONSchema(z.instanceof(File), binary({ contentMediaType: 'application/pdf' }));
|
|
135
|
+
*/
|
|
136
|
+
declare const binary: (opts?: BinaryFragmentOptions) => SchemaObject;
|
|
137
|
+
/**
|
|
138
|
+
* Opaque-object fragment with typed enrichment. Sugar for
|
|
139
|
+
* `enrich(opaqueFragment, opts)` — exists for parity with {@link binary}
|
|
140
|
+
* and because opaque passthrough payloads commonly carry a description
|
|
141
|
+
* explaining why the API doesn't introspect them.
|
|
142
|
+
*
|
|
143
|
+
* @example
|
|
144
|
+
* overrideJSONSchema(z.unknown(), opaque({ description: 'JWT passthrough' }));
|
|
145
|
+
*/
|
|
146
|
+
declare const opaque: (opts?: OpaqueFragmentOptions) => SchemaObject;
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Pre-registered Zod schemas for the common binary runtime types. Each
|
|
150
|
+
* schema is wired through `overrideJSONSchema` so it emits `binaryFragment`
|
|
151
|
+
* verbatim in OpenAPI — drop into a DTO without further setup.
|
|
152
|
+
*
|
|
153
|
+
* Node 22+ provides `File`, `Blob`, and `Buffer` as globals (zod-nest's
|
|
154
|
+
* `engines.node >=22`), so `z.instanceof(...)` at module load is safe.
|
|
155
|
+
*
|
|
156
|
+
* @example
|
|
157
|
+
* import { FileSchema } from 'zod-nest/helpers';
|
|
158
|
+
*
|
|
159
|
+
* class UploadDto extends createZodDto(
|
|
160
|
+
* z.object({ file: FileSchema }),
|
|
161
|
+
* ) {}
|
|
162
|
+
*/
|
|
163
|
+
declare const FileSchema: z.ZodCustom<buffer.File, buffer.File>;
|
|
164
|
+
declare const BlobSchema: z.ZodCustom<buffer.Blob, buffer.Blob>;
|
|
165
|
+
declare const BufferSchema: z.ZodCustom<Buffer<ArrayBufferLike>, Buffer<ArrayBufferLike>>;
|
|
166
|
+
|
|
167
|
+
export { type BinaryFragmentOptions, BlobSchema, BufferSchema, FileSchema, type NumberFormatOptions, type OpaqueFragmentOptions, type StringFormatOptions, binary, binaryFragment, byteFragment, dateFragment, dateTimeFragment, doubleFragment, emailFragment, enrich, floatFragment, hostnameFragment, int32Fragment, int64Fragment, ipv4Fragment, ipv6Fragment, opaque, opaqueFragment, timeFragment, uriFragment, uuidFragment };
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { S as SchemaObject } from '../openapi.types-CFBG3Zz9.js';
|
|
2
|
+
import * as buffer from 'buffer';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
import 'zod/v4/core';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Common JSON Schema fragment catalog — reusable building blocks for assembling
|
|
8
|
+
* fragments by hand: alongside `z.custom<T>()`, in `overrideJSONSchema` calls,
|
|
9
|
+
* in custom override callbacks, in tests that build expected fragments.
|
|
10
|
+
*
|
|
11
|
+
* The catalog overlaps with what Zod constructs already emit (`z.uuid()` →
|
|
12
|
+
* `uuidFragment` shape, `z.email()` → `emailFragment` shape, etc.) — the
|
|
13
|
+
* helpers don't *replace* those Zod constructs; they're a parallel catalog
|
|
14
|
+
* for programmatic fragment assembly.
|
|
15
|
+
*
|
|
16
|
+
* Each fragment is `as const satisfies SchemaObject` so its literal type
|
|
17
|
+
* powers the dispatch in {@link enrich} — passing wrong-family options
|
|
18
|
+
* fails at compile time.
|
|
19
|
+
*/
|
|
20
|
+
declare const dateTimeFragment: {
|
|
21
|
+
readonly type: "string";
|
|
22
|
+
readonly format: "date-time";
|
|
23
|
+
};
|
|
24
|
+
declare const dateFragment: {
|
|
25
|
+
readonly type: "string";
|
|
26
|
+
readonly format: "date";
|
|
27
|
+
};
|
|
28
|
+
declare const timeFragment: {
|
|
29
|
+
readonly type: "string";
|
|
30
|
+
readonly format: "time";
|
|
31
|
+
};
|
|
32
|
+
declare const uuidFragment: {
|
|
33
|
+
readonly type: "string";
|
|
34
|
+
readonly format: "uuid";
|
|
35
|
+
};
|
|
36
|
+
declare const emailFragment: {
|
|
37
|
+
readonly type: "string";
|
|
38
|
+
readonly format: "email";
|
|
39
|
+
};
|
|
40
|
+
declare const uriFragment: {
|
|
41
|
+
readonly type: "string";
|
|
42
|
+
readonly format: "uri";
|
|
43
|
+
};
|
|
44
|
+
declare const hostnameFragment: {
|
|
45
|
+
readonly type: "string";
|
|
46
|
+
readonly format: "hostname";
|
|
47
|
+
};
|
|
48
|
+
declare const ipv4Fragment: {
|
|
49
|
+
readonly type: "string";
|
|
50
|
+
readonly format: "ipv4";
|
|
51
|
+
};
|
|
52
|
+
declare const ipv6Fragment: {
|
|
53
|
+
readonly type: "string";
|
|
54
|
+
readonly format: "ipv6";
|
|
55
|
+
};
|
|
56
|
+
declare const binaryFragment: {
|
|
57
|
+
readonly type: "string";
|
|
58
|
+
readonly format: "binary";
|
|
59
|
+
};
|
|
60
|
+
declare const byteFragment: {
|
|
61
|
+
readonly type: "string";
|
|
62
|
+
readonly format: "byte";
|
|
63
|
+
};
|
|
64
|
+
declare const int32Fragment: {
|
|
65
|
+
readonly type: "integer";
|
|
66
|
+
readonly format: "int32";
|
|
67
|
+
};
|
|
68
|
+
declare const int64Fragment: {
|
|
69
|
+
readonly type: "integer";
|
|
70
|
+
readonly format: "int64";
|
|
71
|
+
};
|
|
72
|
+
declare const floatFragment: {
|
|
73
|
+
readonly type: "number";
|
|
74
|
+
readonly format: "float";
|
|
75
|
+
};
|
|
76
|
+
declare const doubleFragment: {
|
|
77
|
+
readonly type: "number";
|
|
78
|
+
readonly format: "double";
|
|
79
|
+
};
|
|
80
|
+
declare const opaqueFragment: {
|
|
81
|
+
readonly type: "object";
|
|
82
|
+
readonly additionalProperties: true;
|
|
83
|
+
};
|
|
84
|
+
interface StringFormatOptions {
|
|
85
|
+
description?: string;
|
|
86
|
+
examples?: string[];
|
|
87
|
+
minLength?: number;
|
|
88
|
+
maxLength?: number;
|
|
89
|
+
pattern?: string;
|
|
90
|
+
}
|
|
91
|
+
interface BinaryFragmentOptions {
|
|
92
|
+
description?: string;
|
|
93
|
+
contentMediaType?: string;
|
|
94
|
+
contentEncoding?: string;
|
|
95
|
+
}
|
|
96
|
+
interface NumberFormatOptions {
|
|
97
|
+
description?: string;
|
|
98
|
+
examples?: number[];
|
|
99
|
+
minimum?: number;
|
|
100
|
+
maximum?: number;
|
|
101
|
+
exclusiveMinimum?: number;
|
|
102
|
+
exclusiveMaximum?: number;
|
|
103
|
+
multipleOf?: number;
|
|
104
|
+
}
|
|
105
|
+
interface OpaqueFragmentOptions {
|
|
106
|
+
description?: string;
|
|
107
|
+
}
|
|
108
|
+
type OptionsFor<T> = T extends {
|
|
109
|
+
readonly format: 'binary';
|
|
110
|
+
} ? BinaryFragmentOptions : T extends {
|
|
111
|
+
readonly format: 'date-time' | 'date' | 'time' | 'uuid' | 'email' | 'uri' | 'hostname' | 'ipv4' | 'ipv6' | 'byte';
|
|
112
|
+
} ? StringFormatOptions : T extends {
|
|
113
|
+
readonly format: 'int32' | 'int64' | 'float' | 'double';
|
|
114
|
+
} ? NumberFormatOptions : T extends {
|
|
115
|
+
readonly type: 'object';
|
|
116
|
+
readonly additionalProperties: true;
|
|
117
|
+
} ? OpaqueFragmentOptions : never;
|
|
118
|
+
/**
|
|
119
|
+
* Merge a catalog fragment with extras whose shape is dictated by the base
|
|
120
|
+
* fragment's family. Passing wrong-family extras (e.g. a `contentMediaType`
|
|
121
|
+
* onto `uuidFragment`) is a compile-time error.
|
|
122
|
+
*
|
|
123
|
+
* Returns a fresh `SchemaObject` — the original fragment is never mutated.
|
|
124
|
+
*/
|
|
125
|
+
declare const enrich: <T extends SchemaObject>(base: T, extras: OptionsFor<T>) => SchemaObject;
|
|
126
|
+
/**
|
|
127
|
+
* Binary-content fragment with typed enrichment. Sugar for
|
|
128
|
+
* `enrich(binaryFragment, opts)` — exists because the `binary` option set
|
|
129
|
+
* (`contentMediaType` / `contentEncoding`) is nuanced enough to deserve a
|
|
130
|
+
* dedicated entry point and discoverable via auto-complete.
|
|
131
|
+
*
|
|
132
|
+
* @example
|
|
133
|
+
* overrideJSONSchema(z.instanceof(File), binary());
|
|
134
|
+
* overrideJSONSchema(z.instanceof(File), binary({ contentMediaType: 'application/pdf' }));
|
|
135
|
+
*/
|
|
136
|
+
declare const binary: (opts?: BinaryFragmentOptions) => SchemaObject;
|
|
137
|
+
/**
|
|
138
|
+
* Opaque-object fragment with typed enrichment. Sugar for
|
|
139
|
+
* `enrich(opaqueFragment, opts)` — exists for parity with {@link binary}
|
|
140
|
+
* and because opaque passthrough payloads commonly carry a description
|
|
141
|
+
* explaining why the API doesn't introspect them.
|
|
142
|
+
*
|
|
143
|
+
* @example
|
|
144
|
+
* overrideJSONSchema(z.unknown(), opaque({ description: 'JWT passthrough' }));
|
|
145
|
+
*/
|
|
146
|
+
declare const opaque: (opts?: OpaqueFragmentOptions) => SchemaObject;
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Pre-registered Zod schemas for the common binary runtime types. Each
|
|
150
|
+
* schema is wired through `overrideJSONSchema` so it emits `binaryFragment`
|
|
151
|
+
* verbatim in OpenAPI — drop into a DTO without further setup.
|
|
152
|
+
*
|
|
153
|
+
* Node 22+ provides `File`, `Blob`, and `Buffer` as globals (zod-nest's
|
|
154
|
+
* `engines.node >=22`), so `z.instanceof(...)` at module load is safe.
|
|
155
|
+
*
|
|
156
|
+
* @example
|
|
157
|
+
* import { FileSchema } from 'zod-nest/helpers';
|
|
158
|
+
*
|
|
159
|
+
* class UploadDto extends createZodDto(
|
|
160
|
+
* z.object({ file: FileSchema }),
|
|
161
|
+
* ) {}
|
|
162
|
+
*/
|
|
163
|
+
declare const FileSchema: z.ZodCustom<buffer.File, buffer.File>;
|
|
164
|
+
declare const BlobSchema: z.ZodCustom<buffer.Blob, buffer.Blob>;
|
|
165
|
+
declare const BufferSchema: z.ZodCustom<Buffer<ArrayBufferLike>, Buffer<ArrayBufferLike>>;
|
|
166
|
+
|
|
167
|
+
export { type BinaryFragmentOptions, BlobSchema, BufferSchema, FileSchema, type NumberFormatOptions, type OpaqueFragmentOptions, type StringFormatOptions, binary, binaryFragment, byteFragment, dateFragment, dateTimeFragment, doubleFragment, emailFragment, enrich, floatFragment, hostnameFragment, int32Fragment, int64Fragment, ipv4Fragment, ipv6Fragment, opaque, opaqueFragment, timeFragment, uriFragment, uuidFragment };
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var zod = require('zod');
|
|
4
|
+
|
|
5
|
+
var __defProp = Object.defineProperty;
|
|
6
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
7
|
+
|
|
8
|
+
// src/helpers/fragments.ts
|
|
9
|
+
var dateTimeFragment = {
|
|
10
|
+
type: "string",
|
|
11
|
+
format: "date-time"
|
|
12
|
+
};
|
|
13
|
+
var dateFragment = {
|
|
14
|
+
type: "string",
|
|
15
|
+
format: "date"
|
|
16
|
+
};
|
|
17
|
+
var timeFragment = {
|
|
18
|
+
type: "string",
|
|
19
|
+
format: "time"
|
|
20
|
+
};
|
|
21
|
+
var uuidFragment = {
|
|
22
|
+
type: "string",
|
|
23
|
+
format: "uuid"
|
|
24
|
+
};
|
|
25
|
+
var emailFragment = {
|
|
26
|
+
type: "string",
|
|
27
|
+
format: "email"
|
|
28
|
+
};
|
|
29
|
+
var uriFragment = {
|
|
30
|
+
type: "string",
|
|
31
|
+
format: "uri"
|
|
32
|
+
};
|
|
33
|
+
var hostnameFragment = {
|
|
34
|
+
type: "string",
|
|
35
|
+
format: "hostname"
|
|
36
|
+
};
|
|
37
|
+
var ipv4Fragment = {
|
|
38
|
+
type: "string",
|
|
39
|
+
format: "ipv4"
|
|
40
|
+
};
|
|
41
|
+
var ipv6Fragment = {
|
|
42
|
+
type: "string",
|
|
43
|
+
format: "ipv6"
|
|
44
|
+
};
|
|
45
|
+
var binaryFragment = {
|
|
46
|
+
type: "string",
|
|
47
|
+
format: "binary"
|
|
48
|
+
};
|
|
49
|
+
var byteFragment = {
|
|
50
|
+
type: "string",
|
|
51
|
+
format: "byte"
|
|
52
|
+
};
|
|
53
|
+
var int32Fragment = {
|
|
54
|
+
type: "integer",
|
|
55
|
+
format: "int32"
|
|
56
|
+
};
|
|
57
|
+
var int64Fragment = {
|
|
58
|
+
type: "integer",
|
|
59
|
+
format: "int64"
|
|
60
|
+
};
|
|
61
|
+
var floatFragment = {
|
|
62
|
+
type: "number",
|
|
63
|
+
format: "float"
|
|
64
|
+
};
|
|
65
|
+
var doubleFragment = {
|
|
66
|
+
type: "number",
|
|
67
|
+
format: "double"
|
|
68
|
+
};
|
|
69
|
+
var opaqueFragment = {
|
|
70
|
+
type: "object",
|
|
71
|
+
additionalProperties: true
|
|
72
|
+
};
|
|
73
|
+
var enrich = /* @__PURE__ */ __name((base, extras) => ({
|
|
74
|
+
...base,
|
|
75
|
+
...extras
|
|
76
|
+
}), "enrich");
|
|
77
|
+
var binary = /* @__PURE__ */ __name((opts) => ({
|
|
78
|
+
...binaryFragment,
|
|
79
|
+
...opts
|
|
80
|
+
}), "binary");
|
|
81
|
+
var opaque = /* @__PURE__ */ __name((opts) => ({
|
|
82
|
+
...opaqueFragment,
|
|
83
|
+
...opts
|
|
84
|
+
}), "opaque");
|
|
85
|
+
|
|
86
|
+
// src/schema/custom-override.ts
|
|
87
|
+
var customOverrideMap = /* @__PURE__ */ new WeakMap();
|
|
88
|
+
var isWrapper = /* @__PURE__ */ __name((arg) => "input" in arg || "output" in arg, "isWrapper");
|
|
89
|
+
var overrideJSONSchema = /* @__PURE__ */ __name((schema, arg) => {
|
|
90
|
+
const overrideSchema = isWrapper(arg) ? arg : {
|
|
91
|
+
input: arg,
|
|
92
|
+
output: arg
|
|
93
|
+
};
|
|
94
|
+
customOverrideMap.set(schema, overrideSchema);
|
|
95
|
+
return schema;
|
|
96
|
+
}, "overrideJSONSchema");
|
|
97
|
+
|
|
98
|
+
// src/helpers/presets.ts
|
|
99
|
+
var FileSchema = overrideJSONSchema(zod.z.instanceof(File), binaryFragment);
|
|
100
|
+
var BlobSchema = overrideJSONSchema(zod.z.instanceof(Blob), binaryFragment);
|
|
101
|
+
var BufferSchema = overrideJSONSchema(zod.z.instanceof(Buffer), binaryFragment);
|
|
102
|
+
|
|
103
|
+
exports.BlobSchema = BlobSchema;
|
|
104
|
+
exports.BufferSchema = BufferSchema;
|
|
105
|
+
exports.FileSchema = FileSchema;
|
|
106
|
+
exports.binary = binary;
|
|
107
|
+
exports.binaryFragment = binaryFragment;
|
|
108
|
+
exports.byteFragment = byteFragment;
|
|
109
|
+
exports.dateFragment = dateFragment;
|
|
110
|
+
exports.dateTimeFragment = dateTimeFragment;
|
|
111
|
+
exports.doubleFragment = doubleFragment;
|
|
112
|
+
exports.emailFragment = emailFragment;
|
|
113
|
+
exports.enrich = enrich;
|
|
114
|
+
exports.floatFragment = floatFragment;
|
|
115
|
+
exports.hostnameFragment = hostnameFragment;
|
|
116
|
+
exports.int32Fragment = int32Fragment;
|
|
117
|
+
exports.int64Fragment = int64Fragment;
|
|
118
|
+
exports.ipv4Fragment = ipv4Fragment;
|
|
119
|
+
exports.ipv6Fragment = ipv6Fragment;
|
|
120
|
+
exports.opaque = opaque;
|
|
121
|
+
exports.opaqueFragment = opaqueFragment;
|
|
122
|
+
exports.timeFragment = timeFragment;
|
|
123
|
+
exports.uriFragment = uriFragment;
|
|
124
|
+
exports.uuidFragment = uuidFragment;
|
|
125
|
+
//# sourceMappingURL=index.js.map
|
|
126
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +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"]}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
5
|
+
|
|
6
|
+
// src/helpers/fragments.ts
|
|
7
|
+
var dateTimeFragment = {
|
|
8
|
+
type: "string",
|
|
9
|
+
format: "date-time"
|
|
10
|
+
};
|
|
11
|
+
var dateFragment = {
|
|
12
|
+
type: "string",
|
|
13
|
+
format: "date"
|
|
14
|
+
};
|
|
15
|
+
var timeFragment = {
|
|
16
|
+
type: "string",
|
|
17
|
+
format: "time"
|
|
18
|
+
};
|
|
19
|
+
var uuidFragment = {
|
|
20
|
+
type: "string",
|
|
21
|
+
format: "uuid"
|
|
22
|
+
};
|
|
23
|
+
var emailFragment = {
|
|
24
|
+
type: "string",
|
|
25
|
+
format: "email"
|
|
26
|
+
};
|
|
27
|
+
var uriFragment = {
|
|
28
|
+
type: "string",
|
|
29
|
+
format: "uri"
|
|
30
|
+
};
|
|
31
|
+
var hostnameFragment = {
|
|
32
|
+
type: "string",
|
|
33
|
+
format: "hostname"
|
|
34
|
+
};
|
|
35
|
+
var ipv4Fragment = {
|
|
36
|
+
type: "string",
|
|
37
|
+
format: "ipv4"
|
|
38
|
+
};
|
|
39
|
+
var ipv6Fragment = {
|
|
40
|
+
type: "string",
|
|
41
|
+
format: "ipv6"
|
|
42
|
+
};
|
|
43
|
+
var binaryFragment = {
|
|
44
|
+
type: "string",
|
|
45
|
+
format: "binary"
|
|
46
|
+
};
|
|
47
|
+
var byteFragment = {
|
|
48
|
+
type: "string",
|
|
49
|
+
format: "byte"
|
|
50
|
+
};
|
|
51
|
+
var int32Fragment = {
|
|
52
|
+
type: "integer",
|
|
53
|
+
format: "int32"
|
|
54
|
+
};
|
|
55
|
+
var int64Fragment = {
|
|
56
|
+
type: "integer",
|
|
57
|
+
format: "int64"
|
|
58
|
+
};
|
|
59
|
+
var floatFragment = {
|
|
60
|
+
type: "number",
|
|
61
|
+
format: "float"
|
|
62
|
+
};
|
|
63
|
+
var doubleFragment = {
|
|
64
|
+
type: "number",
|
|
65
|
+
format: "double"
|
|
66
|
+
};
|
|
67
|
+
var opaqueFragment = {
|
|
68
|
+
type: "object",
|
|
69
|
+
additionalProperties: true
|
|
70
|
+
};
|
|
71
|
+
var enrich = /* @__PURE__ */ __name((base, extras) => ({
|
|
72
|
+
...base,
|
|
73
|
+
...extras
|
|
74
|
+
}), "enrich");
|
|
75
|
+
var binary = /* @__PURE__ */ __name((opts) => ({
|
|
76
|
+
...binaryFragment,
|
|
77
|
+
...opts
|
|
78
|
+
}), "binary");
|
|
79
|
+
var opaque = /* @__PURE__ */ __name((opts) => ({
|
|
80
|
+
...opaqueFragment,
|
|
81
|
+
...opts
|
|
82
|
+
}), "opaque");
|
|
83
|
+
|
|
84
|
+
// src/schema/custom-override.ts
|
|
85
|
+
var customOverrideMap = /* @__PURE__ */ new WeakMap();
|
|
86
|
+
var isWrapper = /* @__PURE__ */ __name((arg) => "input" in arg || "output" in arg, "isWrapper");
|
|
87
|
+
var overrideJSONSchema = /* @__PURE__ */ __name((schema, arg) => {
|
|
88
|
+
const overrideSchema = isWrapper(arg) ? arg : {
|
|
89
|
+
input: arg,
|
|
90
|
+
output: arg
|
|
91
|
+
};
|
|
92
|
+
customOverrideMap.set(schema, overrideSchema);
|
|
93
|
+
return schema;
|
|
94
|
+
}, "overrideJSONSchema");
|
|
95
|
+
|
|
96
|
+
// src/helpers/presets.ts
|
|
97
|
+
var FileSchema = overrideJSONSchema(z.instanceof(File), binaryFragment);
|
|
98
|
+
var BlobSchema = overrideJSONSchema(z.instanceof(Blob), binaryFragment);
|
|
99
|
+
var BufferSchema = overrideJSONSchema(z.instanceof(Buffer), binaryFragment);
|
|
100
|
+
|
|
101
|
+
export { BlobSchema, BufferSchema, FileSchema, binary, binaryFragment, byteFragment, dateFragment, dateTimeFragment, doubleFragment, emailFragment, enrich, floatFragment, hostnameFragment, int32Fragment, int64Fragment, ipv4Fragment, ipv6Fragment, opaque, opaqueFragment, timeFragment, uriFragment, uuidFragment };
|
|
102
|
+
//# sourceMappingURL=index.mjs.map
|
|
103
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +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"]}
|