@hy_ong/zod-kit 0.0.1

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.
@@ -0,0 +1,9 @@
1
+ {
2
+ "semi": false,
3
+ "singleQuote": false,
4
+ "printWidth": 200,
5
+ "tabWidth": 2,
6
+ "jsxSingleQuote": false,
7
+ "trailingComma": "es5",
8
+ "plugins": []
9
+ }
package/README.md ADDED
@@ -0,0 +1,183 @@
1
+ # Zod Kit
2
+
3
+ A TypeScript library that provides common validation schemas built on top of [Zod](https://github.com/colinhacks/zod) with internationalization support.
4
+
5
+ ## Features
6
+
7
+ - 🔍 Pre-built validation schemas for common data types
8
+ - 🌐 Internationalization support (English and Traditional Chinese)
9
+ - 📝 TypeScript-first with full type safety
10
+ - ⚡ Built on top of Zod for robust validation
11
+ - 🎯 Configurable validation options
12
+
13
+ ## Installation
14
+
15
+ ```bash
16
+ npm install zod-kit
17
+ ```
18
+
19
+ ## Quick Start
20
+
21
+ ```typescript
22
+ import { email, password, text, number } from 'zod-kit'
23
+
24
+ // Email validation
25
+ const emailSchema = email({ label: 'Email' })
26
+ emailSchema.parse('user@example.com') // ✅
27
+
28
+ // Password validation with requirements
29
+ const passwordSchema = password({
30
+ label: 'Password',
31
+ min: 8,
32
+ uppercase: true,
33
+ lowercase: true,
34
+ digits: true,
35
+ special: true
36
+ })
37
+
38
+ // Text validation
39
+ const nameSchema = text({
40
+ label: 'Name',
41
+ min: 2,
42
+ max: 50
43
+ })
44
+ ```
45
+
46
+ ## Available Schemas
47
+
48
+ ### Email
49
+
50
+ ```typescript
51
+ email({
52
+ label: string, // Field label for error messages
53
+ required? : boolean, // Default: true
54
+ domain? : string, // Restrict to specific domain
55
+ min? : number, // Minimum length
56
+ max? : number, // Maximum length
57
+ includes? : string // Must include substring
58
+ })
59
+ ```
60
+
61
+ ### Password
62
+
63
+ ```typescript
64
+ password({
65
+ label: string, // Field label for error messages
66
+ required? : boolean, // Default: true
67
+ min? : number, // Minimum length
68
+ max? : number, // Maximum length
69
+ uppercase? : boolean, // Require uppercase letters
70
+ lowercase? : boolean, // Require lowercase letters
71
+ digits? : boolean, // Require digits
72
+ special? : boolean // Require special characters
73
+ })
74
+ ```
75
+
76
+ ### Text
77
+
78
+ ```typescript
79
+ text({
80
+ label: string, // Field label for error messages
81
+ required? : boolean, // Default: true
82
+ min? : number, // Minimum length
83
+ max? : number, // Maximum length
84
+ includes? : string, // Must include substring
85
+ regex? : RegExp // Custom regex pattern
86
+ })
87
+ ```
88
+
89
+ ### Number
90
+
91
+ ```typescript
92
+ number({
93
+ label: string, // Field label for error messages
94
+ required? : boolean, // Default: true
95
+ min? : number, // Minimum value
96
+ max? : number, // Maximum value
97
+ positive? : boolean, // Must be positive
98
+ negative? : boolean, // Must be negative
99
+ finite? : boolean // Must be finite
100
+ })
101
+ ```
102
+
103
+ ### Integer
104
+
105
+ ```typescript
106
+ integer({
107
+ label: string, // Field label for error messages
108
+ required? : boolean, // Default: true
109
+ min? : number, // Minimum value
110
+ max? : number, // Maximum value
111
+ positive? : boolean, // Must be positive
112
+ negative? : boolean // Must be negative
113
+ })
114
+ ```
115
+
116
+ ### URL
117
+
118
+ ```typescript
119
+ url({
120
+ label: string, // Field label for error messages
121
+ required? : boolean, // Default: true
122
+ protocol? : string[] // Allowed protocols (e.g., ['https'])
123
+ })
124
+ ```
125
+
126
+ ### Boolean
127
+
128
+ ```typescript
129
+ boolean({
130
+ label: string, // Field label for error messages
131
+ required? : boolean // Default: true
132
+ })
133
+ ```
134
+
135
+ ## Internationalization
136
+
137
+ Set the locale for error messages:
138
+
139
+ ```typescript
140
+ import { setLocale } from 'zod-kit'
141
+
142
+ // Set to English (default is Traditional Chinese)
143
+ setLocale('en')
144
+
145
+ // Set to Traditional Chinese
146
+ setLocale('zh-TW')
147
+ ```
148
+
149
+ ## Optional Fields
150
+
151
+ All schemas support optional validation by setting `required: false`:
152
+
153
+ ```typescript
154
+ const optionalEmail = email({
155
+ label: 'Email',
156
+ required: false
157
+ })
158
+
159
+ optionalEmail.parse(null) // ✅ null
160
+ optionalEmail.parse('') // ✅ null
161
+ optionalEmail.parse('user@example.com') // ✅ 'user@example.com'
162
+ ```
163
+
164
+ ## Development
165
+
166
+ ```bash
167
+ # Install dependencies
168
+ npm install
169
+
170
+ # Run tests
171
+ npm test
172
+
173
+ # Build the package
174
+ npm run build
175
+ ```
176
+
177
+ ## License
178
+
179
+ ISC
180
+
181
+ ## Author
182
+
183
+ Ong Hoe Yuan
@@ -0,0 +1,10 @@
1
+ // @ts-check
2
+
3
+ import eslint from "@eslint/js"
4
+ import tseslint from "typescript-eslint"
5
+
6
+ export default tseslint.config(eslint.configs.recommended, tseslint.configs.recommended, {
7
+ rules: {
8
+ "@typescript-eslint/no-explicit-any": "off",
9
+ },
10
+ })
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@hy_ong/zod-kit",
3
+ "version": "0.0.1",
4
+ "description": "Zod Kit",
5
+ "keywords": [
6
+ "zod",
7
+ "kit",
8
+ "zod-kit"
9
+ ],
10
+ "homepage": "https://github.com/hy-ong/zod-kit#readme",
11
+ "bugs": {
12
+ "url": "https://github.com/hy-ong/zod-kit/issues"
13
+ },
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "git+https://github.com/hy-ong/zod-kit.git"
17
+ },
18
+ "publishConfig": {
19
+ "access": "public"
20
+ },
21
+ "license": "ISC",
22
+ "author": "Ong Hoe Yuan",
23
+ "type": "commonjs",
24
+ "main": "dist/index.js",
25
+ "scripts": {
26
+ "build": "tsup src/index.ts --format esm,cjs --dts",
27
+ "test": "vitest"
28
+ },
29
+ "dependencies": {
30
+ "dayjs": "^1.11.13",
31
+ "zod": "^4.0.14"
32
+ },
33
+ "devDependencies": {
34
+ "@eslint/js": "^9.32.0",
35
+ "eslint": "^9.32.0",
36
+ "prettier": "^3.6.2",
37
+ "typescript": "^5.8.3",
38
+ "typescript-eslint": "^8.38.0",
39
+ "vitest": "^3.2.4"
40
+ }
41
+ }
@@ -0,0 +1,37 @@
1
+ import { z, ZodBoolean, ZodNullable, ZodType } from "zod"
2
+ import { t } from "../i18n"
3
+
4
+ export type BooleanOptions<IsRequired extends boolean = true> = {
5
+ required?: IsRequired
6
+ label: string
7
+ defaultValue?: IsRequired extends true ? boolean : boolean | null
8
+ shouldBe?: boolean
9
+ }
10
+
11
+ export type BooleanSchema<IsRequired extends boolean> = IsRequired extends true ? ZodBoolean : ZodNullable<ZodBoolean>
12
+
13
+ export function boolean<IsRequired extends boolean = true>(options?: BooleanOptions<IsRequired>): BooleanSchema<IsRequired> {
14
+ const { required = true, label, defaultValue = null, shouldBe } = options ?? {}
15
+
16
+ let result: ZodType = z.preprocess(
17
+ (val) => {
18
+ if (val === "" || val === undefined || val === null) return defaultValue
19
+ if (val === "true" || val === 1 || val === "1") return true
20
+ if (val === "false" || val === 0 || val === "0") return false
21
+ return val
22
+ },
23
+ z.union([z.literal(true), z.literal(false), z.literal(null)])
24
+ )
25
+
26
+ if (required && defaultValue === null) {
27
+ result = result.refine((val) => val !== null, { message: t("common.boolean.required", { label }) })
28
+ }
29
+
30
+ if (shouldBe === true) {
31
+ result = result.refine((val) => val === true, { message: t("common.boolean.shouldBe.true", { label }) })
32
+ } else if (shouldBe === false) {
33
+ result = result.refine((val) => val === false, { message: t("common.boolean.shouldBe.false", { label }) })
34
+ }
35
+
36
+ return result as IsRequired extends true ? ZodBoolean : ZodNullable<ZodBoolean>
37
+ }
@@ -0,0 +1,44 @@
1
+ import { z, ZodNullable, ZodString } from "zod"
2
+ import { t } from "../i18n"
3
+ import dayjs from "dayjs"
4
+ import customParseFormat from "dayjs/plugin/customParseFormat"
5
+ import isSameOrAfter from "dayjs/plugin/isSameOrAfter"
6
+ import isSameOrBefore from "dayjs/plugin/isSameOrBefore"
7
+
8
+ dayjs.extend(isSameOrAfter)
9
+ dayjs.extend(isSameOrBefore)
10
+ dayjs.extend(customParseFormat)
11
+
12
+ export type DateOptions<IsRequired extends boolean = true> = {
13
+ required?: IsRequired
14
+ label: string
15
+ min?: number
16
+ max?: number
17
+ format?: string
18
+ includes?: string
19
+ defaultValue?: IsRequired extends true ? string : string | null
20
+ }
21
+
22
+ export type DateSchema<IsRequired extends boolean> = IsRequired extends true ? ZodString : ZodNullable<ZodString>
23
+
24
+ export function date<IsRequired extends boolean = true>(options?: DateOptions<IsRequired>): DateSchema<IsRequired> {
25
+ const { required = true, label, min, max, format = "YYYY-MM-DD", includes, defaultValue = null } = options ?? {}
26
+
27
+ const baseSchema = required
28
+ ? z.preprocess((val) => (val === "" || val === null || val === undefined ? defaultValue : val), z.coerce.string().trim())
29
+ : z.preprocess((val) => (val === "" || val === null || val === undefined ? defaultValue : val), z.coerce.string().trim().nullable())
30
+
31
+ const schema = baseSchema
32
+ .refine(
33
+ (val) => {
34
+ if (!val) return !required
35
+ return dayjs(val, format, true).isValid()
36
+ },
37
+ { message: t("common.date.format", { label, format }) }
38
+ )
39
+ .refine((val) => val === null || min === undefined || dayjs(val, format).isSameOrAfter(dayjs(min, format)), { message: t("common.date.min", { label, min }) })
40
+ .refine((val) => val === null || max === undefined || dayjs(val, format).isSameOrBefore(dayjs(max, format)), { message: t("common.date.max", { label, max }) })
41
+ .refine((val) => val === null || includes === undefined || val.includes(includes), { message: t("common.date.includes", { label, includes }) })
42
+
43
+ return schema as unknown as DateSchema<IsRequired>
44
+ }
@@ -0,0 +1,45 @@
1
+ import { z, ZodNullable, ZodString } from "zod"
2
+ import { t } from "../i18n"
3
+ import { TextSchema } from "./text"
4
+
5
+ export type EmailOptions<IsRequired extends boolean = true> = {
6
+ required?: IsRequired
7
+ label: string
8
+ domain?: string
9
+ min?: number
10
+ max?: number
11
+ includes?: string
12
+ }
13
+
14
+ export type EmailSchema<IsRequired extends boolean> = IsRequired extends true ? ZodString : ZodNullable<ZodString>
15
+
16
+ export function email<IsRequired extends boolean = true>(options?: EmailOptions<IsRequired>): EmailSchema<IsRequired> {
17
+ const { required = true, label, domain, min, max, includes } = options ?? {}
18
+
19
+ const baseSchema = required
20
+ ? z.preprocess(
21
+ (val) => (val === "" || val === null || val === undefined ? null : val),
22
+ z.email({
23
+ error: (issue) => {
24
+ if (issue.code === "invalid_type") return t("common.email.required", { label })
25
+ else if (issue.code === "invalid_format") return t("common.email.invalid", { label })
26
+ return t("common.email.invalid", { label })
27
+ },
28
+ })
29
+ )
30
+ : z.preprocess((val) => (val === "" || val === null || val === undefined ? null : val), z.email({ message: t("common.email.invalid", { label }) }).nullable())
31
+
32
+ const schema = baseSchema
33
+ .refine((val) => (required ? val !== "" && val !== "null" && val !== "undefined" : true), { message: t("common.text.required", { label }) })
34
+ .refine((val) => val === null || min === undefined || val.length >= min, { message: t("common.text.min", { label, min }) })
35
+ .refine((val) => val === null || max === undefined || val.length <= max, { message: t("common.text.max", { label, max }) })
36
+ .refine((val) => val === null || includes === undefined || val.includes(includes), { message: t("common.text.includes", { label, includes }) })
37
+ .refine(
38
+ (val) => {
39
+ if (val === null || domain === undefined) return true
40
+ return val.split("@")[1]?.toLowerCase() === domain.toLowerCase()
41
+ },
42
+ { message: t("common.email.domain", { label, domain }) }
43
+ )
44
+ return schema as unknown as TextSchema<IsRequired>
45
+ }
@@ -0,0 +1,47 @@
1
+ import { z, ZodNullable, ZodNumber } from "zod"
2
+ import { t } from "../i18n"
3
+
4
+ export type IntegerOptions<IsRequired extends boolean = true> = {
5
+ required?: IsRequired
6
+ label: string
7
+ min?: number
8
+ max?: number
9
+ defaultValue?: IsRequired extends true ? number : number | null
10
+ }
11
+
12
+ export type IntegerSchema<IsRequired extends boolean> = IsRequired extends true ? ZodNumber : ZodNullable<ZodNumber>
13
+
14
+ export function integer<IsRequired extends boolean = true>(options?: IntegerOptions<IsRequired>): IntegerSchema<IsRequired> {
15
+ const { required = true, label, min, max, defaultValue } = options ?? {}
16
+
17
+ const schema = z
18
+ .preprocess(
19
+ (val) => {
20
+ if (val === "" || val === undefined || val === null) return defaultValue ?? null
21
+ return typeof val === "string" ? Number(val) : val
22
+ },
23
+ z.union([
24
+ z.number({
25
+ error: (issue) => {
26
+ if (issue.code === "invalid_type") return t("common.integer.integer", { label })
27
+ return t("common.integer.required", { label })
28
+ },
29
+ }),
30
+ z.null(),
31
+ ])
32
+ )
33
+ .refine((val) => !required || val !== null, {
34
+ message: t("common.integer.required", { label }),
35
+ })
36
+ .refine((val) => val === null || Number.isInteger(val), {
37
+ message: t("common.integer.integer", { label }),
38
+ })
39
+ .refine((val) => val === null || min === undefined || val >= min, {
40
+ message: t("common.integer.min", { label, min }),
41
+ })
42
+ .refine((val) => val === null || max === undefined || val <= max, {
43
+ message: t("common.integer.max", { label, max }),
44
+ })
45
+
46
+ return schema as unknown as IntegerSchema<IsRequired>
47
+ }
@@ -0,0 +1,38 @@
1
+ import { z, ZodNullable, ZodNumber } from "zod"
2
+ import { t } from "../i18n"
3
+
4
+ export type NumberOptions<IsRequired extends boolean = true> = {
5
+ required?: IsRequired
6
+ label: string
7
+ min?: number
8
+ max?: number
9
+ defaultValue?: IsRequired extends true ? number : number | null
10
+ }
11
+
12
+ export type NumberSchema<IsRequired extends boolean> = IsRequired extends true ? ZodNumber : ZodNullable<ZodNumber>
13
+
14
+ export function number<IsRequired extends boolean = true>(options?: NumberOptions<IsRequired>): NumberSchema<IsRequired> {
15
+ const { required = true, label, min, max, defaultValue } = options ?? {}
16
+
17
+ const schema = z
18
+ .preprocess(
19
+ (val) => {
20
+ if (val === "" || val === undefined || val === null) return defaultValue ?? null
21
+ return typeof val === "string" ? Number(val) : val
22
+ },
23
+ z.union([
24
+ z.number({
25
+ error: (issue) => {
26
+ if (issue.code === "invalid_type") return t("common.number.integer", { label })
27
+ return t("common.number.required", { label })
28
+ },
29
+ }),
30
+ z.null(),
31
+ ])
32
+ )
33
+ .refine((val) => !required || val !== null, { message: t("common.number.required", { label }) })
34
+ .refine((val) => val === null || min === undefined || val >= min, { message: t("common.number.min", { label, min }) })
35
+ .refine((val) => val === null || max === undefined || val <= max, { message: t("common.number.max", { label, max }) })
36
+
37
+ return schema as unknown as NumberSchema<IsRequired>
38
+ }
@@ -0,0 +1,34 @@
1
+ import { z, ZodNullable, ZodString } from "zod"
2
+ import { t } from "../i18n"
3
+
4
+ export type PasswordOptions<IsRequired extends boolean = true> = {
5
+ required?: IsRequired
6
+ label: string
7
+ min?: number
8
+ max?: number
9
+ uppercase?: boolean
10
+ lowercase?: boolean
11
+ digits?: boolean
12
+ special?: boolean
13
+ }
14
+
15
+ export type PasswordSchema<IsRequired extends boolean> = IsRequired extends true ? ZodString : ZodNullable<ZodString>
16
+
17
+ export function password<IsRequired extends boolean = true>(options?: PasswordOptions<IsRequired>): PasswordSchema<IsRequired> {
18
+ const { required = true, label, min, max, uppercase, lowercase, digits, special } = options ?? {}
19
+
20
+ const baseSchema = required
21
+ ? z.preprocess((val) => (val === "" || val === null || val === undefined ? null : val), z.coerce.string().trim())
22
+ : z.preprocess((val) => (val === "" || val === null || val === undefined ? null : val), z.coerce.string().trim().nullable())
23
+
24
+ const schema = baseSchema
25
+ .refine((val) => (required ? val !== "" && val !== "null" && val !== "undefined" : true), { message: t("common.password.required", { label }) })
26
+ .refine((val) => val === null || min === undefined || val.length >= min, { message: t("common.password.min", { label, min }) })
27
+ .refine((val) => val === null || max === undefined || val.length <= max, { message: t("common.password.max", { label, max }) })
28
+ .refine((val) => val === null || !uppercase || /[A-Z]/.test(val), { message: t("common.password.uppercase", { label }) })
29
+ .refine((val) => val === null || !lowercase || /[a-z]/.test(val), { message: t("common.password.lowercase", { label }) })
30
+ .refine((val) => val === null || !special || /[!@#$%^&*()_+\-=[\]{};':"\\|,.<>/?]+/.test(val), { message: t("common.password.special", { label }) })
31
+ .refine((val) => val === null || !digits || /[0-9]/.test(val), { message: t("common.password.digits", { label }) })
32
+
33
+ return schema as unknown as PasswordSchema<IsRequired>
34
+ }
@@ -0,0 +1,35 @@
1
+ import { z, ZodNullable, ZodString } from "zod"
2
+ import { t } from "../i18n"
3
+
4
+ export type TextOptions<IsRequired extends boolean = true> = {
5
+ required?: IsRequired
6
+ label: string
7
+ min?: number
8
+ max?: number
9
+ startsWith?: string
10
+ endsWith?: string
11
+ includes?: string
12
+ regex?: RegExp
13
+ defaultValue?: IsRequired extends true ? string : string | null
14
+ }
15
+
16
+ export type TextSchema<IsRequired extends boolean> = IsRequired extends true ? ZodString : ZodNullable<ZodString>
17
+
18
+ export function text<IsRequired extends boolean = true>(options?: TextOptions<IsRequired>): TextSchema<IsRequired> {
19
+ const { required = true, label, min, max, startsWith, endsWith, includes, regex, defaultValue = null } = options ?? {}
20
+
21
+ const baseSchema = required
22
+ ? z.preprocess((val) => (val === "" || val === null || val === undefined ? defaultValue : val), z.coerce.string().trim())
23
+ : z.preprocess((val) => (val === "" || val === null || val === undefined ? defaultValue : val), z.coerce.string().trim().nullable())
24
+
25
+ const schema = baseSchema
26
+ .refine((val) => (required ? val !== "" && val !== "null" && val !== "undefined" : true), { message: t("common.text.required", { label }) })
27
+ .refine((val) => val === null || min === undefined || val.length >= min, { message: t("common.text.min", { label, min }) })
28
+ .refine((val) => val === null || max === undefined || val.length <= max, { message: t("common.text.max", { label, max }) })
29
+ .refine((val) => val === null || startsWith === undefined || val.startsWith(startsWith), { message: t("common.text.startsWith", { label, startsWith }) })
30
+ .refine((val) => val === null || endsWith === undefined || val.endsWith(endsWith), { message: t("common.text.endsWith", { label, endsWith }) })
31
+ .refine((val) => val === null || includes === undefined || val.includes(includes), { message: t("common.text.includes", { label, includes }) })
32
+ .refine((val) => val === null || regex === undefined || regex.test(val), { message: t("common.text.invalid", { label, regex }) })
33
+
34
+ return schema as unknown as TextSchema<IsRequired>
35
+ }
@@ -0,0 +1,38 @@
1
+ import { z, ZodNullable, ZodString } from "zod"
2
+ import { t } from "../i18n"
3
+ import { TextSchema } from "./text"
4
+
5
+ export type UrlOptions<IsRequired extends boolean = true> = {
6
+ required?: IsRequired
7
+ label: string
8
+ min?: number
9
+ max?: number
10
+ includes?: string
11
+ }
12
+
13
+ export type UrlSchema<IsRequired extends boolean> = IsRequired extends true ? ZodString : ZodNullable<ZodString>
14
+
15
+ export function url<IsRequired extends boolean = true>(options?: UrlOptions<IsRequired>): UrlSchema<IsRequired> {
16
+ const { required = true, label, min, max, includes } = options ?? {}
17
+
18
+ const baseSchema = required
19
+ ? z.preprocess(
20
+ (val) => (val === "" || val === null || val === undefined ? null : val),
21
+ z.url({
22
+ error: (issue) => {
23
+ if (issue.code === "invalid_type") return t("common.url.required", { label })
24
+ else if (issue.code === "invalid_format") return t("common.url.invalid", { label })
25
+ return t("common.url.invalid", { label })
26
+ },
27
+ })
28
+ )
29
+ : z.preprocess((val) => (val === "" || val === null || val === undefined ? null : val), z.url({ message: t("common.url.invalid", { label }) }).nullable())
30
+
31
+ const schema = baseSchema
32
+ .refine((val) => (required ? val !== "" && val !== "null" && val !== "undefined" : true), { message: t("common.text.required", { label }) })
33
+ .refine((val) => val === null || min === undefined || val.length >= min, { message: t("common.text.min", { label, min }) })
34
+ .refine((val) => val === null || max === undefined || val.length <= max, { message: t("common.text.max", { label, max }) })
35
+ .refine((val) => val === null || includes === undefined || val.includes(includes), { message: t("common.text.includes", { label, includes }) })
36
+
37
+ return schema as unknown as TextSchema<IsRequired>
38
+ }
package/src/config.ts ADDED
@@ -0,0 +1,9 @@
1
+ export type Locale = "zh-TW" | "en"
2
+
3
+ let currentLocale: Locale = "zh-TW"
4
+
5
+ export const setLocale = (locale: Locale) => {
6
+ currentLocale = locale
7
+ }
8
+
9
+ export const getLocale = (): Locale => currentLocale
@@ -0,0 +1,22 @@
1
+ import zhTW from "./locales/zh-TW.json"
2
+ import en from "./locales/en.json"
3
+ import { getLocale } from "../config"
4
+
5
+ const dicts = {
6
+ "zh-TW": zhTW,
7
+ en,
8
+ } satisfies Record<string, unknown>
9
+
10
+ export const t = (key: string, params: Record<string, any> = {}): string => {
11
+ const locale = getLocale()
12
+ const dict = dicts[locale] || {}
13
+
14
+ const template = getNestedValue(dict, key) ?? key
15
+
16
+ return template.replace(/\$\{(\w+)}/g, (_, k) => params[k] ?? "")
17
+ }
18
+
19
+ function getNestedValue(obj: Record<string, any>, path: string): string | undefined {
20
+ const result = path.split(".").reduce((acc: any, part: string) => (acc && typeof acc === "object" ? acc[part] : undefined), obj)
21
+ return typeof result === "string" ? result : undefined
22
+ }
@@ -0,0 +1,68 @@
1
+ {
2
+ "common": {
3
+ "boolean": {
4
+ "required": "${label} is required",
5
+ "shouldBe": {
6
+ "true": "${label} must be True",
7
+ "false": "${label} must be False"
8
+ }
9
+ },
10
+ "email": {
11
+ "required": "${label} is required",
12
+ "min": "${label} must be at least ${min} characters",
13
+ "max": "${label} must be at most ${max} characters",
14
+ "includes": "${label} must include ${includes}",
15
+ "invalid": "${label} is invalid format",
16
+ "domain": "${label} must be under the domain @${domain}"
17
+ },
18
+ "url": {
19
+ "required": "${label} is required",
20
+ "min": "${label} must be at least ${min} characters",
21
+ "max": "${label} must be at most ${max} characters",
22
+ "includes": "${label} must include ${includes}",
23
+ "invalid": "${label} is invalid format"
24
+ },
25
+ "password": {
26
+ "required": "${label} is required",
27
+ "min": "${label} must be at least ${min} characters",
28
+ "max": "${label} must be at most ${max} characters",
29
+ "uppercase": "${label} must include at least one uppercase letter",
30
+ "lowercase": "${label} must include at least one lowercase letter",
31
+ "digits": "${label} must include at least one digit",
32
+ "special": "${label} must include at least one special character"
33
+ },
34
+ "number": {
35
+ "required": "${label} is required",
36
+ "min": "${label} must be at least ${min}",
37
+ "max": "${label} must be at most ${max}"
38
+ },
39
+ "integer": {
40
+ "required": "${label} is required",
41
+ "min": "${label} must be at least ${min}",
42
+ "max": "${label} must be at most ${max}",
43
+ "integer": "${label} must be an integer"
44
+ },
45
+ "float": {
46
+ "required": "${label} is required",
47
+ "min": "${label} must be at least ${min}",
48
+ "max": "${label} must be at most ${max}",
49
+ "float": "${label} must be an float"
50
+ },
51
+ "text": {
52
+ "required": "${label} is required",
53
+ "min": "${label} must be at least ${min} characters",
54
+ "max": "${label} must be at most ${max} characters",
55
+ "startsWith": "${label} must start with ${startsWith}",
56
+ "endsWith": "${label} must end with ${endsWith}",
57
+ "includes": "${label} must include ${includes}",
58
+ "invalid": "${label} is invalid format"
59
+ },
60
+ "date": {
61
+ "required": "${label} is required",
62
+ "min": "${label} must be at least ${min} characters",
63
+ "max": "${label} must be at most ${max} characters",
64
+ "includes": "${label} must include ${includes}",
65
+ "format": "${label} must be in ${format} format"
66
+ }
67
+ }
68
+ }