zod-error-map 0.2.2 → 0.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs ADDED
@@ -0,0 +1,193 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.js
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ ErrorCode: () => ErrorCode,
24
+ FormatType: () => FormatType,
25
+ buildInvalidTypeMessage: () => buildInvalidTypeMessage,
26
+ buildTooBigMessage: () => buildTooBigMessage,
27
+ buildTooSmallMessage: () => buildTooSmallMessage,
28
+ createCustomMessageBuilder: () => createCustomMessageBuilder,
29
+ createDefaultBuilders: () => createDefaultBuilders,
30
+ createErrorMapper: () => createErrorMapper,
31
+ createFormatMessageBuilder: () => createFormatMessageBuilder,
32
+ createZodErrorMap: () => createZodErrorMap,
33
+ defaultErrorMapper: () => defaultErrorMapper,
34
+ defaultFormatMessages: () => defaultFormatMessages,
35
+ getInputDescription: () => getInputDescription,
36
+ setZodErrorMap: () => setZodErrorMap
37
+ });
38
+ module.exports = __toCommonJS(index_exports);
39
+
40
+ // src/core/constants/error-codes.js
41
+ var ErrorCode = (
42
+ /** @type {const} */
43
+ {
44
+ INVALID_TYPE: "invalid_type",
45
+ TOO_SMALL: "too_small",
46
+ TOO_BIG: "too_big",
47
+ INVALID_FORMAT: "invalid_format",
48
+ CUSTOM: "custom"
49
+ }
50
+ );
51
+ var FormatType = (
52
+ /** @type {const} */
53
+ {
54
+ EMAIL: "email",
55
+ UUID: "uuid",
56
+ URL: "url",
57
+ REGEX: "regex",
58
+ CUID: "cuid",
59
+ CUID2: "cuid2",
60
+ ULID: "ulid",
61
+ IP: "ip",
62
+ EMOJI: "emoji",
63
+ DATE: "date",
64
+ DATETIME: "datetime",
65
+ TIME: "time"
66
+ }
67
+ );
68
+
69
+ // src/domain/builders/message-builders.js
70
+ function getInputDescription(input) {
71
+ if (input === void 0) return "undefined";
72
+ if (input === null) return "null";
73
+ if (Array.isArray(input)) return "array";
74
+ return typeof input;
75
+ }
76
+ var defaultFormatMessages = {
77
+ [FormatType.EMAIL]: (label) => `The ${label.quoted} must be a valid email address`,
78
+ [FormatType.UUID]: (label) => `The ${label.quoted} must be a valid UUID`,
79
+ [FormatType.URL]: (label) => `The ${label.quoted} is invalid`,
80
+ [FormatType.REGEX]: (label) => `The ${label.quoted} has an invalid format`,
81
+ [FormatType.CUID]: (label) => `The ${label.quoted} must be a valid CUID`,
82
+ [FormatType.CUID2]: (label) => `The ${label.quoted} must be a valid CUID2`,
83
+ [FormatType.ULID]: (label) => `The ${label.quoted} must be a valid ULID`,
84
+ [FormatType.IP]: (label) => `The ${label.quoted} must be a valid IP address`,
85
+ [FormatType.DATE]: (label) => `The ${label.quoted} must be a valid date`,
86
+ [FormatType.DATETIME]: (label) => `The ${label.quoted} must be a valid datetime`,
87
+ [FormatType.TIME]: (label) => `The ${label.quoted} must be a valid time`
88
+ };
89
+ function buildInvalidTypeMessage(issue, label) {
90
+ const received = getInputDescription(issue.input);
91
+ if (received === "undefined") {
92
+ return `The ${label.bare} is required`;
93
+ }
94
+ return `Expected ${issue.expected} for ${label.quoted}, received ${received}`;
95
+ }
96
+ function buildTooSmallMessage(issue, label) {
97
+ const min = Number(issue.minimum) || 0;
98
+ return `The ${label.quoted} must contain at least ${min} characters`;
99
+ }
100
+ function buildTooBigMessage(issue, label) {
101
+ const max = Number(issue.maximum) || 0;
102
+ return `The ${label.quoted} must contain at most ${max} characters`;
103
+ }
104
+ function createFormatMessageBuilder(formatMessages = defaultFormatMessages) {
105
+ return (issue, label) => {
106
+ const format = issue.format || "";
107
+ const messageBuilder = formatMessages[format];
108
+ if (messageBuilder) {
109
+ return messageBuilder(label);
110
+ }
111
+ return `The ${label.quoted} has an invalid format`;
112
+ };
113
+ }
114
+ function createCustomMessageBuilder(defaultError) {
115
+ return (issue, _label) => issue.message || defaultError;
116
+ }
117
+ function createDefaultBuilders(defaultError, formatMessages) {
118
+ return {
119
+ [ErrorCode.INVALID_TYPE]: buildInvalidTypeMessage,
120
+ [ErrorCode.TOO_SMALL]: buildTooSmallMessage,
121
+ [ErrorCode.TOO_BIG]: buildTooBigMessage,
122
+ [ErrorCode.INVALID_FORMAT]: createFormatMessageBuilder(formatMessages),
123
+ [ErrorCode.CUSTOM]: createCustomMessageBuilder(defaultError)
124
+ };
125
+ }
126
+
127
+ // src/domain/mappers/error-mapper.js
128
+ var DEFAULT_ERROR = "Invalid input";
129
+ function isRawIssue(issue) {
130
+ return typeof issue === "object" && issue !== null && "code" in issue;
131
+ }
132
+ function getLabel(issue) {
133
+ const path = issue.path ?? [];
134
+ const field = path[path.length - 1];
135
+ if (typeof field === "string") {
136
+ return { bare: field, quoted: `'${field}'` };
137
+ }
138
+ return { bare: "value", quoted: "value" };
139
+ }
140
+ function createErrorMapper(config = {}) {
141
+ const defaultError = config.defaultError ?? DEFAULT_ERROR;
142
+ const defaultBuilders = createDefaultBuilders(defaultError, config.formatMessages);
143
+ const builders = { ...defaultBuilders, ...config.builders };
144
+ function buildMessage(issue) {
145
+ const label = getLabel(issue);
146
+ const builder = builders[issue.code];
147
+ if (builder) {
148
+ return builder(issue, label);
149
+ }
150
+ return issue.message || defaultError;
151
+ }
152
+ function format(issue) {
153
+ if (!isRawIssue(issue)) return defaultError;
154
+ return buildMessage(issue);
155
+ }
156
+ function createErrorMap() {
157
+ return format;
158
+ }
159
+ return { format, createErrorMap };
160
+ }
161
+ var defaultErrorMapper = createErrorMapper();
162
+
163
+ // src/adapters/zod/integration.js
164
+ function setZodErrorMap(z, config) {
165
+ const mapper = createErrorMapper(config);
166
+ z.config({
167
+ customError: (issue) => mapper.format(issue)
168
+ });
169
+ }
170
+ function createZodErrorMap(config) {
171
+ const mapper = createErrorMapper(config);
172
+ return (issue, _ctx) => {
173
+ const message = mapper.format(issue);
174
+ return { message };
175
+ };
176
+ }
177
+ // Annotate the CommonJS export names for ESM import in node:
178
+ 0 && (module.exports = {
179
+ ErrorCode,
180
+ FormatType,
181
+ buildInvalidTypeMessage,
182
+ buildTooBigMessage,
183
+ buildTooSmallMessage,
184
+ createCustomMessageBuilder,
185
+ createDefaultBuilders,
186
+ createErrorMapper,
187
+ createFormatMessageBuilder,
188
+ createZodErrorMap,
189
+ defaultErrorMapper,
190
+ defaultFormatMessages,
191
+ getInputDescription,
192
+ setZodErrorMap
193
+ });
@@ -0,0 +1,79 @@
1
+ import * as zod from 'zod';
2
+
3
+ interface Label {
4
+ bare: string
5
+ quoted: string
6
+ }
7
+
8
+ interface RawIssue {
9
+ code: string
10
+ path: Array<string | number>
11
+ input?: unknown
12
+ expected?: string
13
+ minimum?: number
14
+ maximum?: number
15
+ format?: string
16
+ message?: string
17
+ }
18
+
19
+ type MessageBuilder = (issue: RawIssue, label: Label) => string
20
+
21
+ type FormatMessageBuilder = (label: Label) => string
22
+
23
+ interface ErrorMapConfig {
24
+ defaultError?: string
25
+ builders?: Record<string, MessageBuilder>
26
+ formatMessages?: Record<string, FormatMessageBuilder>
27
+ }
28
+
29
+ interface ErrorMapper {
30
+ format: (issue: unknown) => string
31
+ createErrorMap: () => (issue: unknown) => string
32
+ }
33
+
34
+ declare const ErrorCode: {
35
+ readonly INVALID_TYPE: 'invalid_type'
36
+ readonly TOO_SMALL: 'too_small'
37
+ readonly TOO_BIG: 'too_big'
38
+ readonly INVALID_FORMAT: 'invalid_format'
39
+ readonly CUSTOM: 'custom'
40
+ }
41
+
42
+ declare const FormatType: {
43
+ readonly EMAIL: 'email'
44
+ readonly UUID: 'uuid'
45
+ readonly URL: 'url'
46
+ readonly REGEX: 'regex'
47
+ readonly CUID: 'cuid'
48
+ readonly CUID2: 'cuid2'
49
+ readonly ULID: 'ulid'
50
+ readonly IP: 'ip'
51
+ readonly EMOJI: 'emoji'
52
+ readonly DATE: 'date'
53
+ readonly DATETIME: 'datetime'
54
+ readonly TIME: 'time'
55
+ }
56
+
57
+ declare function createErrorMapper(config?: ErrorMapConfig): ErrorMapper
58
+ declare const defaultErrorMapper: ErrorMapper
59
+
60
+ declare function getInputDescription(input: unknown): string
61
+ declare const defaultFormatMessages: Record<string, (label: Label) => string>
62
+ declare function buildInvalidTypeMessage(issue: RawIssue, label: Label): string
63
+ declare function buildTooSmallMessage(issue: RawIssue, label: Label): string
64
+ declare function buildTooBigMessage(issue: RawIssue, label: Label): string
65
+ declare function createFormatMessageBuilder(
66
+ formatMessages?: Record<string, (label: Label) => string>
67
+ ): MessageBuilder
68
+ declare function createCustomMessageBuilder(defaultError: string): MessageBuilder
69
+ declare function createDefaultBuilders(
70
+ defaultError: string,
71
+ formatMessages?: Record<string, (label: Label) => string>
72
+ ): Record<string, MessageBuilder>
73
+
74
+ declare function setZodErrorMap(z: typeof zod.z, config?: ErrorMapConfig): void
75
+ declare function createZodErrorMap(
76
+ config?: ErrorMapConfig
77
+ ): (issue: unknown, ctx: unknown) => { message: string }
78
+
79
+ export { ErrorCode, type ErrorMapConfig, type ErrorMapper, type FormatMessageBuilder, FormatType, type Label, type MessageBuilder, type RawIssue, buildInvalidTypeMessage, buildTooBigMessage, buildTooSmallMessage, createCustomMessageBuilder, createDefaultBuilders, createErrorMapper, createFormatMessageBuilder, createZodErrorMap, defaultErrorMapper, defaultFormatMessages, getInputDescription, setZodErrorMap };
@@ -0,0 +1,79 @@
1
+ import * as zod from 'zod';
2
+
3
+ interface Label {
4
+ bare: string
5
+ quoted: string
6
+ }
7
+
8
+ interface RawIssue {
9
+ code: string
10
+ path: Array<string | number>
11
+ input?: unknown
12
+ expected?: string
13
+ minimum?: number
14
+ maximum?: number
15
+ format?: string
16
+ message?: string
17
+ }
18
+
19
+ type MessageBuilder = (issue: RawIssue, label: Label) => string
20
+
21
+ type FormatMessageBuilder = (label: Label) => string
22
+
23
+ interface ErrorMapConfig {
24
+ defaultError?: string
25
+ builders?: Record<string, MessageBuilder>
26
+ formatMessages?: Record<string, FormatMessageBuilder>
27
+ }
28
+
29
+ interface ErrorMapper {
30
+ format: (issue: unknown) => string
31
+ createErrorMap: () => (issue: unknown) => string
32
+ }
33
+
34
+ declare const ErrorCode: {
35
+ readonly INVALID_TYPE: 'invalid_type'
36
+ readonly TOO_SMALL: 'too_small'
37
+ readonly TOO_BIG: 'too_big'
38
+ readonly INVALID_FORMAT: 'invalid_format'
39
+ readonly CUSTOM: 'custom'
40
+ }
41
+
42
+ declare const FormatType: {
43
+ readonly EMAIL: 'email'
44
+ readonly UUID: 'uuid'
45
+ readonly URL: 'url'
46
+ readonly REGEX: 'regex'
47
+ readonly CUID: 'cuid'
48
+ readonly CUID2: 'cuid2'
49
+ readonly ULID: 'ulid'
50
+ readonly IP: 'ip'
51
+ readonly EMOJI: 'emoji'
52
+ readonly DATE: 'date'
53
+ readonly DATETIME: 'datetime'
54
+ readonly TIME: 'time'
55
+ }
56
+
57
+ declare function createErrorMapper(config?: ErrorMapConfig): ErrorMapper
58
+ declare const defaultErrorMapper: ErrorMapper
59
+
60
+ declare function getInputDescription(input: unknown): string
61
+ declare const defaultFormatMessages: Record<string, (label: Label) => string>
62
+ declare function buildInvalidTypeMessage(issue: RawIssue, label: Label): string
63
+ declare function buildTooSmallMessage(issue: RawIssue, label: Label): string
64
+ declare function buildTooBigMessage(issue: RawIssue, label: Label): string
65
+ declare function createFormatMessageBuilder(
66
+ formatMessages?: Record<string, (label: Label) => string>
67
+ ): MessageBuilder
68
+ declare function createCustomMessageBuilder(defaultError: string): MessageBuilder
69
+ declare function createDefaultBuilders(
70
+ defaultError: string,
71
+ formatMessages?: Record<string, (label: Label) => string>
72
+ ): Record<string, MessageBuilder>
73
+
74
+ declare function setZodErrorMap(z: typeof zod.z, config?: ErrorMapConfig): void
75
+ declare function createZodErrorMap(
76
+ config?: ErrorMapConfig
77
+ ): (issue: unknown, ctx: unknown) => { message: string }
78
+
79
+ export { ErrorCode, type ErrorMapConfig, type ErrorMapper, type FormatMessageBuilder, FormatType, type Label, type MessageBuilder, type RawIssue, buildInvalidTypeMessage, buildTooBigMessage, buildTooSmallMessage, createCustomMessageBuilder, createDefaultBuilders, createErrorMapper, createFormatMessageBuilder, createZodErrorMap, defaultErrorMapper, defaultFormatMessages, getInputDescription, setZodErrorMap };
package/dist/index.js ADDED
@@ -0,0 +1,153 @@
1
+ // src/core/constants/error-codes.js
2
+ var ErrorCode = (
3
+ /** @type {const} */
4
+ {
5
+ INVALID_TYPE: "invalid_type",
6
+ TOO_SMALL: "too_small",
7
+ TOO_BIG: "too_big",
8
+ INVALID_FORMAT: "invalid_format",
9
+ CUSTOM: "custom"
10
+ }
11
+ );
12
+ var FormatType = (
13
+ /** @type {const} */
14
+ {
15
+ EMAIL: "email",
16
+ UUID: "uuid",
17
+ URL: "url",
18
+ REGEX: "regex",
19
+ CUID: "cuid",
20
+ CUID2: "cuid2",
21
+ ULID: "ulid",
22
+ IP: "ip",
23
+ EMOJI: "emoji",
24
+ DATE: "date",
25
+ DATETIME: "datetime",
26
+ TIME: "time"
27
+ }
28
+ );
29
+
30
+ // src/domain/builders/message-builders.js
31
+ function getInputDescription(input) {
32
+ if (input === void 0) return "undefined";
33
+ if (input === null) return "null";
34
+ if (Array.isArray(input)) return "array";
35
+ return typeof input;
36
+ }
37
+ var defaultFormatMessages = {
38
+ [FormatType.EMAIL]: (label) => `The ${label.quoted} must be a valid email address`,
39
+ [FormatType.UUID]: (label) => `The ${label.quoted} must be a valid UUID`,
40
+ [FormatType.URL]: (label) => `The ${label.quoted} is invalid`,
41
+ [FormatType.REGEX]: (label) => `The ${label.quoted} has an invalid format`,
42
+ [FormatType.CUID]: (label) => `The ${label.quoted} must be a valid CUID`,
43
+ [FormatType.CUID2]: (label) => `The ${label.quoted} must be a valid CUID2`,
44
+ [FormatType.ULID]: (label) => `The ${label.quoted} must be a valid ULID`,
45
+ [FormatType.IP]: (label) => `The ${label.quoted} must be a valid IP address`,
46
+ [FormatType.DATE]: (label) => `The ${label.quoted} must be a valid date`,
47
+ [FormatType.DATETIME]: (label) => `The ${label.quoted} must be a valid datetime`,
48
+ [FormatType.TIME]: (label) => `The ${label.quoted} must be a valid time`
49
+ };
50
+ function buildInvalidTypeMessage(issue, label) {
51
+ const received = getInputDescription(issue.input);
52
+ if (received === "undefined") {
53
+ return `The ${label.bare} is required`;
54
+ }
55
+ return `Expected ${issue.expected} for ${label.quoted}, received ${received}`;
56
+ }
57
+ function buildTooSmallMessage(issue, label) {
58
+ const min = Number(issue.minimum) || 0;
59
+ return `The ${label.quoted} must contain at least ${min} characters`;
60
+ }
61
+ function buildTooBigMessage(issue, label) {
62
+ const max = Number(issue.maximum) || 0;
63
+ return `The ${label.quoted} must contain at most ${max} characters`;
64
+ }
65
+ function createFormatMessageBuilder(formatMessages = defaultFormatMessages) {
66
+ return (issue, label) => {
67
+ const format = issue.format || "";
68
+ const messageBuilder = formatMessages[format];
69
+ if (messageBuilder) {
70
+ return messageBuilder(label);
71
+ }
72
+ return `The ${label.quoted} has an invalid format`;
73
+ };
74
+ }
75
+ function createCustomMessageBuilder(defaultError) {
76
+ return (issue, _label) => issue.message || defaultError;
77
+ }
78
+ function createDefaultBuilders(defaultError, formatMessages) {
79
+ return {
80
+ [ErrorCode.INVALID_TYPE]: buildInvalidTypeMessage,
81
+ [ErrorCode.TOO_SMALL]: buildTooSmallMessage,
82
+ [ErrorCode.TOO_BIG]: buildTooBigMessage,
83
+ [ErrorCode.INVALID_FORMAT]: createFormatMessageBuilder(formatMessages),
84
+ [ErrorCode.CUSTOM]: createCustomMessageBuilder(defaultError)
85
+ };
86
+ }
87
+
88
+ // src/domain/mappers/error-mapper.js
89
+ var DEFAULT_ERROR = "Invalid input";
90
+ function isRawIssue(issue) {
91
+ return typeof issue === "object" && issue !== null && "code" in issue;
92
+ }
93
+ function getLabel(issue) {
94
+ const path = issue.path ?? [];
95
+ const field = path[path.length - 1];
96
+ if (typeof field === "string") {
97
+ return { bare: field, quoted: `'${field}'` };
98
+ }
99
+ return { bare: "value", quoted: "value" };
100
+ }
101
+ function createErrorMapper(config = {}) {
102
+ const defaultError = config.defaultError ?? DEFAULT_ERROR;
103
+ const defaultBuilders = createDefaultBuilders(defaultError, config.formatMessages);
104
+ const builders = { ...defaultBuilders, ...config.builders };
105
+ function buildMessage(issue) {
106
+ const label = getLabel(issue);
107
+ const builder = builders[issue.code];
108
+ if (builder) {
109
+ return builder(issue, label);
110
+ }
111
+ return issue.message || defaultError;
112
+ }
113
+ function format(issue) {
114
+ if (!isRawIssue(issue)) return defaultError;
115
+ return buildMessage(issue);
116
+ }
117
+ function createErrorMap() {
118
+ return format;
119
+ }
120
+ return { format, createErrorMap };
121
+ }
122
+ var defaultErrorMapper = createErrorMapper();
123
+
124
+ // src/adapters/zod/integration.js
125
+ function setZodErrorMap(z, config) {
126
+ const mapper = createErrorMapper(config);
127
+ z.config({
128
+ customError: (issue) => mapper.format(issue)
129
+ });
130
+ }
131
+ function createZodErrorMap(config) {
132
+ const mapper = createErrorMapper(config);
133
+ return (issue, _ctx) => {
134
+ const message = mapper.format(issue);
135
+ return { message };
136
+ };
137
+ }
138
+ export {
139
+ ErrorCode,
140
+ FormatType,
141
+ buildInvalidTypeMessage,
142
+ buildTooBigMessage,
143
+ buildTooSmallMessage,
144
+ createCustomMessageBuilder,
145
+ createDefaultBuilders,
146
+ createErrorMapper,
147
+ createFormatMessageBuilder,
148
+ createZodErrorMap,
149
+ defaultErrorMapper,
150
+ defaultFormatMessages,
151
+ getInputDescription,
152
+ setZodErrorMap
153
+ };
package/package.json CHANGED
@@ -1,23 +1,29 @@
1
1
  {
2
2
  "name": "zod-error-map",
3
- "version": "0.2.2",
3
+ "version": "0.2.4",
4
4
  "description": "Type-safe, customizable error message mapping for Zod validation",
5
5
  "type": "module",
6
- "main": "src/index.js",
7
- "types": "src/index.d.ts",
6
+ "main": "dist/index.cjs",
7
+ "module": "dist/index.js",
8
+ "types": "dist/index.d.ts",
8
9
  "exports": {
9
10
  ".": {
10
- "import": "./src/index.js",
11
- "types": "./src/index.d.ts"
11
+ "import": {
12
+ "types": "./dist/index.d.ts",
13
+ "default": "./dist/index.js"
14
+ },
15
+ "require": {
16
+ "types": "./dist/index.d.cts",
17
+ "default": "./dist/index.cjs"
18
+ }
12
19
  }
13
20
  },
14
21
  "files": [
15
- "src/**/*.js",
16
- "src/**/*.d.ts",
17
- "!src/**/*.test.js"
22
+ "dist"
18
23
  ],
19
24
  "scripts": {
20
- "test": "node --test 'tests/**/*.test.js'",
25
+ "build": "tsup",
26
+ "test": "node --test tests/*.test.js",
21
27
  "typecheck": "tsc"
22
28
  },
23
29
  "keywords": [
@@ -42,6 +48,7 @@
42
48
  "zod": "^3.0.0 || ^4.0.0"
43
49
  },
44
50
  "devDependencies": {
51
+ "tsup": "^8.0.0",
45
52
  "typescript": "^5.0.0",
46
53
  "zod": "^4.1.0"
47
54
  },
@@ -1,4 +0,0 @@
1
- export {
2
- setZodErrorMap,
3
- createZodErrorMap,
4
- } from './zod/index.js'
@@ -1,4 +0,0 @@
1
- export {
2
- setZodErrorMap,
3
- createZodErrorMap,
4
- } from './integration.js'
@@ -1,31 +0,0 @@
1
- /**
2
- * @typedef {import('../../core/types/index.js').ErrorMapConfig} ErrorMapConfig
3
- */
4
-
5
- import { createErrorMapper } from '../../domain/mappers/index.js'
6
-
7
- /**
8
- * Sets the global Zod error map using z.config()
9
- * @param {import('zod')} z - The Zod instance
10
- * @param {ErrorMapConfig} [config] - Optional configuration
11
- * @returns {void}
12
- */
13
- export function setZodErrorMap(z, config) {
14
- const mapper = createErrorMapper(config)
15
- z.config({
16
- customError: (issue) => mapper.format(issue),
17
- })
18
- }
19
-
20
- /**
21
- * Creates a Zod error map function compatible with z.setErrorMap() (Zod v3)
22
- * @param {ErrorMapConfig} [config] - Optional configuration
23
- * @returns {(issue: unknown, ctx: unknown) => { message: string }}
24
- */
25
- export function createZodErrorMap(config) {
26
- const mapper = createErrorMapper(config)
27
- return (issue, _ctx) => {
28
- const message = mapper.format(issue)
29
- return { message }
30
- }
31
- }
@@ -1,32 +0,0 @@
1
- /**
2
- * Zod error codes
3
- * @readonly
4
- * @enum {string}
5
- */
6
- export const ErrorCode = /** @type {const} */ ({
7
- INVALID_TYPE: 'invalid_type',
8
- TOO_SMALL: 'too_small',
9
- TOO_BIG: 'too_big',
10
- INVALID_FORMAT: 'invalid_format',
11
- CUSTOM: 'custom',
12
- })
13
-
14
- /**
15
- * Format types for invalid_format errors
16
- * @readonly
17
- * @enum {string}
18
- */
19
- export const FormatType = /** @type {const} */ ({
20
- EMAIL: 'email',
21
- UUID: 'uuid',
22
- URL: 'url',
23
- REGEX: 'regex',
24
- CUID: 'cuid',
25
- CUID2: 'cuid2',
26
- ULID: 'ulid',
27
- IP: 'ip',
28
- EMOJI: 'emoji',
29
- DATE: 'date',
30
- DATETIME: 'datetime',
31
- TIME: 'time',
32
- })
@@ -1 +0,0 @@
1
- export { ErrorCode, FormatType } from './error-codes.js'
package/src/core/index.js DELETED
@@ -1 +0,0 @@
1
- export { ErrorCode, FormatType } from './constants/index.js'
@@ -1,37 +0,0 @@
1
- /**
2
- * @typedef {object} Label
3
- * @property {string} bare - The field name without quotes
4
- * @property {string} quoted - The field name with quotes
5
- */
6
-
7
- /**
8
- * @typedef {object} RawIssue
9
- * @property {string} code - The error code from Zod
10
- * @property {Array<string | number>} path - The path to the field
11
- * @property {unknown} [input] - The input value that caused the error
12
- * @property {string} [expected] - The expected type
13
- * @property {number} [minimum] - Minimum value/length constraint
14
- * @property {number} [maximum] - Maximum value/length constraint
15
- * @property {string} [format] - Format type (email, uuid, url, etc.)
16
- * @property {string} [message] - Custom error message
17
- */
18
-
19
- /**
20
- * @callback MessageBuilder
21
- * @param {RawIssue} issue - The raw Zod issue
22
- * @param {Label} label - The field label
23
- * @returns {string} The formatted error message
24
- */
25
-
26
- /**
27
- * @typedef {(label: Label) => string} FormatMessageBuilder
28
- */
29
-
30
- /**
31
- * @typedef {object} ErrorMapConfig
32
- * @property {string} [defaultError] - Default error message
33
- * @property {Record<string, MessageBuilder>} [builders] - Custom message builders by error code
34
- * @property {Record<string, FormatMessageBuilder>} [formatMessages] - Custom messages for format errors
35
- */
36
-
37
- export {}
@@ -1,10 +0,0 @@
1
- export {
2
- getInputDescription,
3
- defaultFormatMessages,
4
- buildInvalidTypeMessage,
5
- buildTooSmallMessage,
6
- buildTooBigMessage,
7
- createFormatMessageBuilder,
8
- createCustomMessageBuilder,
9
- createDefaultBuilders,
10
- } from './message-builders.js'
@@ -1,108 +0,0 @@
1
- import { ErrorCode, FormatType } from '../../core/constants/index.js'
2
-
3
- /**
4
- * @typedef {import('../../core/types/index.js').RawIssue} RawIssue
5
- * @typedef {import('../../core/types/index.js').Label} Label
6
- * @typedef {import('../../core/types/index.js').MessageBuilder} MessageBuilder
7
- */
8
-
9
- /**
10
- * Gets a human-readable description of the input type
11
- * @param {unknown} input
12
- * @returns {string}
13
- */
14
- export function getInputDescription(input) {
15
- if (input === undefined) return 'undefined'
16
- if (input === null) return 'null'
17
- if (Array.isArray(input)) return 'array'
18
- return typeof input
19
- }
20
-
21
- /**
22
- * Default format error messages
23
- * @type {Record<string, (label: Label) => string>}
24
- */
25
- export const defaultFormatMessages = {
26
- [FormatType.EMAIL]: (label) => `The ${label.quoted} must be a valid email address`,
27
- [FormatType.UUID]: (label) => `The ${label.quoted} must be a valid UUID`,
28
- [FormatType.URL]: (label) => `The ${label.quoted} is invalid`,
29
- [FormatType.REGEX]: (label) => `The ${label.quoted} has an invalid format`,
30
- [FormatType.CUID]: (label) => `The ${label.quoted} must be a valid CUID`,
31
- [FormatType.CUID2]: (label) => `The ${label.quoted} must be a valid CUID2`,
32
- [FormatType.ULID]: (label) => `The ${label.quoted} must be a valid ULID`,
33
- [FormatType.IP]: (label) => `The ${label.quoted} must be a valid IP address`,
34
- [FormatType.DATE]: (label) => `The ${label.quoted} must be a valid date`,
35
- [FormatType.DATETIME]: (label) => `The ${label.quoted} must be a valid datetime`,
36
- [FormatType.TIME]: (label) => `The ${label.quoted} must be a valid time`,
37
- }
38
-
39
- /**
40
- * Builds error message for invalid_type errors
41
- * @type {MessageBuilder}
42
- */
43
- export function buildInvalidTypeMessage(issue, label) {
44
- const received = getInputDescription(issue.input)
45
- if (received === 'undefined') {
46
- return `The ${label.bare} is required`
47
- }
48
- return `Expected ${issue.expected} for ${label.quoted}, received ${received}`
49
- }
50
-
51
- /**
52
- * Builds error message for too_small errors
53
- * @type {MessageBuilder}
54
- */
55
- export function buildTooSmallMessage(issue, label) {
56
- const min = Number(issue.minimum) || 0
57
- return `The ${label.quoted} must contain at least ${min} characters`
58
- }
59
-
60
- /**
61
- * Builds error message for too_big errors
62
- * @type {MessageBuilder}
63
- */
64
- export function buildTooBigMessage(issue, label) {
65
- const max = Number(issue.maximum) || 0
66
- return `The ${label.quoted} must contain at most ${max} characters`
67
- }
68
-
69
- /**
70
- * Creates a format message builder with custom format messages
71
- * @param {Record<string, (label: Label) => string>} [formatMessages]
72
- * @returns {MessageBuilder}
73
- */
74
- export function createFormatMessageBuilder(formatMessages = defaultFormatMessages) {
75
- return (issue, label) => {
76
- const format = issue.format || ''
77
- const messageBuilder = formatMessages[format]
78
- if (messageBuilder) {
79
- return messageBuilder(label)
80
- }
81
- return `The ${label.quoted} has an invalid format`
82
- }
83
- }
84
-
85
- /**
86
- * Builds error message for custom errors
87
- * @param {string} defaultError
88
- * @returns {MessageBuilder}
89
- */
90
- export function createCustomMessageBuilder(defaultError) {
91
- return (issue, _label) => issue.message || defaultError
92
- }
93
-
94
- /**
95
- * Creates the default message builders map
96
- * @param {string} defaultError
97
- * @param {Record<string, (label: Label) => string>} [formatMessages]
98
- * @returns {Record<string, MessageBuilder>}
99
- */
100
- export function createDefaultBuilders(defaultError, formatMessages) {
101
- return {
102
- [ErrorCode.INVALID_TYPE]: buildInvalidTypeMessage,
103
- [ErrorCode.TOO_SMALL]: buildTooSmallMessage,
104
- [ErrorCode.TOO_BIG]: buildTooBigMessage,
105
- [ErrorCode.INVALID_FORMAT]: createFormatMessageBuilder(formatMessages),
106
- [ErrorCode.CUSTOM]: createCustomMessageBuilder(defaultError),
107
- }
108
- }
@@ -1,15 +0,0 @@
1
- export {
2
- createErrorMapper,
3
- defaultErrorMapper,
4
- } from './mappers/index.js'
5
-
6
- export {
7
- getInputDescription,
8
- defaultFormatMessages,
9
- buildInvalidTypeMessage,
10
- buildTooSmallMessage,
11
- buildTooBigMessage,
12
- createFormatMessageBuilder,
13
- createCustomMessageBuilder,
14
- createDefaultBuilders,
15
- } from './builders/index.js'
@@ -1,87 +0,0 @@
1
- import { createDefaultBuilders } from '../builders/index.js'
2
-
3
- /**
4
- * @typedef {import('../../core/types/index.js').RawIssue} RawIssue
5
- * @typedef {import('../../core/types/index.js').Label} Label
6
- * @typedef {import('../../core/types/index.js').MessageBuilder} MessageBuilder
7
- * @typedef {import('../../core/types/index.js').ErrorMapConfig} ErrorMapConfig
8
- */
9
-
10
- const DEFAULT_ERROR = 'Invalid input'
11
-
12
- /**
13
- * Checks if the issue is a valid RawIssue
14
- * @param {unknown} issue
15
- * @returns {issue is RawIssue}
16
- */
17
- function isRawIssue(issue) {
18
- return (
19
- typeof issue === 'object' &&
20
- issue !== null &&
21
- 'code' in issue
22
- )
23
- }
24
-
25
- /**
26
- * Gets the label from the issue path
27
- * @param {RawIssue} issue
28
- * @returns {Label}
29
- */
30
- function getLabel(issue) {
31
- const path = issue.path ?? []
32
- const field = path[path.length - 1]
33
- if (typeof field === 'string') {
34
- return { bare: field, quoted: `'${field}'` }
35
- }
36
- return { bare: 'value', quoted: 'value' }
37
- }
38
-
39
- /**
40
- * Creates a Zod error mapper with customizable message builders
41
- * @param {ErrorMapConfig} [config]
42
- * @returns {{ format: (issue: unknown) => string, createErrorMap: () => (issue: unknown) => string }}
43
- */
44
- export function createErrorMapper(config = {}) {
45
- const defaultError = config.defaultError ?? DEFAULT_ERROR
46
- const defaultBuilders = createDefaultBuilders(defaultError, config.formatMessages)
47
- const builders = { ...defaultBuilders, ...config.builders }
48
-
49
- /**
50
- * Builds the error message for a Zod issue
51
- * @param {RawIssue} issue
52
- * @returns {string}
53
- */
54
- function buildMessage(issue) {
55
- const label = getLabel(issue)
56
- const builder = builders[issue.code]
57
- if (builder) {
58
- return builder(issue, label)
59
- }
60
- return issue.message || defaultError
61
- }
62
-
63
- /**
64
- * Formats a Zod issue into a human-readable message
65
- * @param {unknown} issue
66
- * @returns {string}
67
- */
68
- function format(issue) {
69
- if (!isRawIssue(issue)) return defaultError
70
- return buildMessage(issue)
71
- }
72
-
73
- /**
74
- * Creates an error map function for Zod
75
- * @returns {(issue: unknown) => string}
76
- */
77
- function createErrorMap() {
78
- return format
79
- }
80
-
81
- return { format, createErrorMap }
82
- }
83
-
84
- /**
85
- * Default error mapper instance
86
- */
87
- export const defaultErrorMapper = createErrorMapper()
@@ -1,4 +0,0 @@
1
- export {
2
- createErrorMapper,
3
- defaultErrorMapper,
4
- } from './error-mapper.js'
package/src/index.d.ts DELETED
@@ -1,77 +0,0 @@
1
- import type { z } from 'zod'
2
-
3
- export interface Label {
4
- bare: string
5
- quoted: string
6
- }
7
-
8
- export interface RawIssue {
9
- code: string
10
- path: Array<string | number>
11
- input?: unknown
12
- expected?: string
13
- minimum?: number
14
- maximum?: number
15
- format?: string
16
- message?: string
17
- }
18
-
19
- export type MessageBuilder = (issue: RawIssue, label: Label) => string
20
-
21
- export type FormatMessageBuilder = (label: Label) => string
22
-
23
- export interface ErrorMapConfig {
24
- defaultError?: string
25
- builders?: Record<string, MessageBuilder>
26
- formatMessages?: Record<string, FormatMessageBuilder>
27
- }
28
-
29
- export interface ErrorMapper {
30
- format: (issue: unknown) => string
31
- createErrorMap: () => (issue: unknown) => string
32
- }
33
-
34
- export declare const ErrorCode: {
35
- readonly INVALID_TYPE: 'invalid_type'
36
- readonly TOO_SMALL: 'too_small'
37
- readonly TOO_BIG: 'too_big'
38
- readonly INVALID_FORMAT: 'invalid_format'
39
- readonly CUSTOM: 'custom'
40
- }
41
-
42
- export declare const FormatType: {
43
- readonly EMAIL: 'email'
44
- readonly UUID: 'uuid'
45
- readonly URL: 'url'
46
- readonly REGEX: 'regex'
47
- readonly CUID: 'cuid'
48
- readonly CUID2: 'cuid2'
49
- readonly ULID: 'ulid'
50
- readonly IP: 'ip'
51
- readonly EMOJI: 'emoji'
52
- readonly DATE: 'date'
53
- readonly DATETIME: 'datetime'
54
- readonly TIME: 'time'
55
- }
56
-
57
- export declare function createErrorMapper(config?: ErrorMapConfig): ErrorMapper
58
- export declare const defaultErrorMapper: ErrorMapper
59
-
60
- export declare function getInputDescription(input: unknown): string
61
- export declare const defaultFormatMessages: Record<string, (label: Label) => string>
62
- export declare function buildInvalidTypeMessage(issue: RawIssue, label: Label): string
63
- export declare function buildTooSmallMessage(issue: RawIssue, label: Label): string
64
- export declare function buildTooBigMessage(issue: RawIssue, label: Label): string
65
- export declare function createFormatMessageBuilder(
66
- formatMessages?: Record<string, (label: Label) => string>
67
- ): MessageBuilder
68
- export declare function createCustomMessageBuilder(defaultError: string): MessageBuilder
69
- export declare function createDefaultBuilders(
70
- defaultError: string,
71
- formatMessages?: Record<string, (label: Label) => string>
72
- ): Record<string, MessageBuilder>
73
-
74
- export declare function setZodErrorMap(z: typeof import('zod').z, config?: ErrorMapConfig): void
75
- export declare function createZodErrorMap(
76
- config?: ErrorMapConfig
77
- ): (issue: unknown, ctx: unknown) => { message: string }
package/src/index.js DELETED
@@ -1,19 +0,0 @@
1
- export { ErrorCode, FormatType } from './core/index.js'
2
-
3
- export {
4
- createErrorMapper,
5
- defaultErrorMapper,
6
- getInputDescription,
7
- defaultFormatMessages,
8
- buildInvalidTypeMessage,
9
- buildTooSmallMessage,
10
- buildTooBigMessage,
11
- createFormatMessageBuilder,
12
- createCustomMessageBuilder,
13
- createDefaultBuilders,
14
- } from './domain/index.js'
15
-
16
- export {
17
- setZodErrorMap,
18
- createZodErrorMap,
19
- } from './adapters/index.js'