@ppabari/strio 1.0.2
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/CHANGELOG.md +85 -0
- package/LICENSE +21 -0
- package/README.md +565 -0
- package/dist/bin/strio.js +1492 -0
- package/dist/index.cjs +1913 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +885 -0
- package/dist/index.d.ts +885 -0
- package/dist/index.js +1881 -0
- package/dist/index.js.map +1 -0
- package/dist/zod-CINbFpp9.d.cts +211 -0
- package/dist/zod-CINbFpp9.d.ts +211 -0
- package/dist/zod.cjs +165 -0
- package/dist/zod.cjs.map +1 -0
- package/dist/zod.d.cts +1 -0
- package/dist/zod.d.ts +1 -0
- package/dist/zod.js +163 -0
- package/dist/zod.js.map +1 -0
- package/package.json +98 -0
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* types.ts
|
|
3
|
+
* All public TypeScript types and interfaces.
|
|
4
|
+
*/
|
|
5
|
+
/** Which character category the first character must belong to. */
|
|
6
|
+
type StartWith = 'alphabet' | 'numeric' | 'any';
|
|
7
|
+
/**
|
|
8
|
+
* Options for `generateRandomString` and `generateRandomStringAsync`.
|
|
9
|
+
*
|
|
10
|
+
* Character pool is built from the enabled flags, then reduced
|
|
11
|
+
* by any `exclude` characters. You may also pass a fully custom
|
|
12
|
+
* `charset` which bypasses all flags.
|
|
13
|
+
*/
|
|
14
|
+
interface RandomStringOptions {
|
|
15
|
+
/**
|
|
16
|
+
* Total length of the generated string (including prefix/suffix).
|
|
17
|
+
* The random portion will be `length - prefix.length - suffix.length`.
|
|
18
|
+
* @default 16
|
|
19
|
+
*/
|
|
20
|
+
length?: number;
|
|
21
|
+
/** Include digits 0–9. @default true */
|
|
22
|
+
numeric?: boolean;
|
|
23
|
+
/** Include lowercase a–z. @default true */
|
|
24
|
+
lowercase?: boolean;
|
|
25
|
+
/** Include uppercase A–Z. @default true */
|
|
26
|
+
uppercase?: boolean;
|
|
27
|
+
/** Include symbols `!@#$%^&*()_+[]{}<>?`. @default false */
|
|
28
|
+
symbols?: boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Exclude specific characters from the pool.
|
|
31
|
+
* Useful for removing visually ambiguous chars like `0`, `O`, `l`, `I`, `1`.
|
|
32
|
+
* Applied after charset construction; must not eliminate the entire pool.
|
|
33
|
+
*/
|
|
34
|
+
exclude?: string | string[];
|
|
35
|
+
/**
|
|
36
|
+
* Shorthand for `exclude: ['0','O','l','I','1']`.
|
|
37
|
+
* Produces strings that are easy to read and transcribe.
|
|
38
|
+
* @default false
|
|
39
|
+
*/
|
|
40
|
+
readable?: boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Fully custom character pool. Overrides `numeric`, `lowercase`,
|
|
43
|
+
* `uppercase`, `symbols`, and `readable`. Duplicate characters are
|
|
44
|
+
* removed automatically. Must have at least 2 unique characters.
|
|
45
|
+
*/
|
|
46
|
+
charset?: string;
|
|
47
|
+
/**
|
|
48
|
+
* Constrain the first character of the random portion.
|
|
49
|
+
* - `'alphabet'` — must be a–z or A–Z (whichever are enabled)
|
|
50
|
+
* - `'numeric'` — must be 0–9
|
|
51
|
+
* - `'any'` — no constraint
|
|
52
|
+
* @default 'any'
|
|
53
|
+
*/
|
|
54
|
+
startWith?: StartWith;
|
|
55
|
+
/**
|
|
56
|
+
* Fixed string prepended to the result. Does not count toward
|
|
57
|
+
* the random entropy; the random portion shrinks accordingly so
|
|
58
|
+
* the total length stays at `length`.
|
|
59
|
+
* @example `{ prefix: 'usr_', length: 20 }` → `'usr_' + 16 random chars`
|
|
60
|
+
*/
|
|
61
|
+
prefix?: string;
|
|
62
|
+
/**
|
|
63
|
+
* Fixed string appended to the result. Same length accounting as `prefix`.
|
|
64
|
+
*/
|
|
65
|
+
suffix?: string;
|
|
66
|
+
/**
|
|
67
|
+
* Pattern string where special placeholders are replaced with random chars.
|
|
68
|
+
* When `pattern` is set, `length` is ignored.
|
|
69
|
+
*
|
|
70
|
+
* Placeholders:
|
|
71
|
+
* - `#` → random digit (0–9)
|
|
72
|
+
* - `A` → random uppercase letter
|
|
73
|
+
* - `a` → random lowercase letter
|
|
74
|
+
* - `*` → random char from the full enabled charset
|
|
75
|
+
* - `?` → random alphanumeric char
|
|
76
|
+
*
|
|
77
|
+
* Escape a literal placeholder with a backslash: `\#` → `#`
|
|
78
|
+
*
|
|
79
|
+
* @example `{ pattern: '####-AAAA-####' }` → `'4821-KXPZ-0937'`
|
|
80
|
+
* @example `{ pattern: 'usr_**********' }` → `'usr_k3Xp9mQr2L'`
|
|
81
|
+
*/
|
|
82
|
+
pattern?: string;
|
|
83
|
+
/**
|
|
84
|
+
* Generate multiple unique strings in a single call.
|
|
85
|
+
* Returns a `string[]` instead of `string` when > 1.
|
|
86
|
+
* Uniqueness is guaranteed; throws if `count` exceeds the
|
|
87
|
+
* theoretical maximum unique combinations for the given config.
|
|
88
|
+
* @default 1
|
|
89
|
+
*/
|
|
90
|
+
count?: number;
|
|
91
|
+
/**
|
|
92
|
+
* Deterministic seed for reproducible output.
|
|
93
|
+
* Same seed + same options = same string every time.
|
|
94
|
+
*
|
|
95
|
+
* ⚠️ NOT cryptographically secure. Use only for tests,
|
|
96
|
+
* snapshots, and demos — never for real tokens or secrets.
|
|
97
|
+
*
|
|
98
|
+
* When `seed` is set the crypto engine is bypassed entirely.
|
|
99
|
+
*/
|
|
100
|
+
seed?: string;
|
|
101
|
+
}
|
|
102
|
+
/** Result type: single string when count=1 (default), array when count>1. */
|
|
103
|
+
type RandomStringResult<T extends RandomStringOptions> = T extends {
|
|
104
|
+
count: infer C;
|
|
105
|
+
} ? C extends 1 ? string : C extends number ? string[] : string : string;
|
|
106
|
+
/** Options for `validateRandomString`. */
|
|
107
|
+
interface ValidateOptions {
|
|
108
|
+
/** Minimum length (inclusive). */
|
|
109
|
+
minLength?: number;
|
|
110
|
+
/** Maximum length (inclusive). */
|
|
111
|
+
maxLength?: number;
|
|
112
|
+
/** Exact length. */
|
|
113
|
+
length?: number;
|
|
114
|
+
/** Must contain at least one digit. */
|
|
115
|
+
requireNumeric?: boolean;
|
|
116
|
+
/** Must contain at least one lowercase letter. */
|
|
117
|
+
requireLowercase?: boolean;
|
|
118
|
+
/** Must contain at least one uppercase letter. */
|
|
119
|
+
requireUppercase?: boolean;
|
|
120
|
+
/** Must contain at least one symbol. */
|
|
121
|
+
requireSymbols?: boolean;
|
|
122
|
+
/** All characters must belong to this set. */
|
|
123
|
+
charset?: string;
|
|
124
|
+
/** Pattern the string must match (same syntax as generation pattern). */
|
|
125
|
+
pattern?: string;
|
|
126
|
+
}
|
|
127
|
+
/** Result from `validateRandomString`. */
|
|
128
|
+
interface ValidationResult {
|
|
129
|
+
/** Whether the string passes all checks. */
|
|
130
|
+
valid: boolean;
|
|
131
|
+
/** List of rule violations. Empty when `valid` is true. */
|
|
132
|
+
errors: string[];
|
|
133
|
+
}
|
|
134
|
+
/** Result from `estimateEntropy`. */
|
|
135
|
+
interface EntropyResult {
|
|
136
|
+
/** Shannon entropy in bits. */
|
|
137
|
+
bits: number;
|
|
138
|
+
/** Human-readable strength label. */
|
|
139
|
+
strength: 'very-weak' | 'weak' | 'fair' | 'strong' | 'very-strong';
|
|
140
|
+
/** Charset size used in the calculation. */
|
|
141
|
+
charsetSize: number;
|
|
142
|
+
/** Effective random length (excluding prefix/suffix). */
|
|
143
|
+
effectiveLength: number;
|
|
144
|
+
/** Approximate number of possible combinations (as a string to avoid overflow). */
|
|
145
|
+
combinations: string;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* zod.ts
|
|
150
|
+
* Zod schema factory for @ppabari/strio.
|
|
151
|
+
*
|
|
152
|
+
* Creates a `z.string()` schema that validates a string against strio
|
|
153
|
+
* options. The Zod peer dependency is optional — import from the
|
|
154
|
+
* subpath `@ppabari/strio/zod` only when Zod is available.
|
|
155
|
+
*
|
|
156
|
+
* @example
|
|
157
|
+
* import { z } from 'zod';
|
|
158
|
+
* import { randomStringSchema } from '@ppabari/strio/zod';
|
|
159
|
+
*
|
|
160
|
+
* const tokenSchema = randomStringSchema({
|
|
161
|
+
* length: 32,
|
|
162
|
+
* charset: 'base62',
|
|
163
|
+
* });
|
|
164
|
+
*
|
|
165
|
+
* tokenSchema.parse('k3Xp9mQr2LzYw7NvTq8sHfGbJ5cD4eAK'); // ✓
|
|
166
|
+
* tokenSchema.parse('short'); // throws ZodError
|
|
167
|
+
*
|
|
168
|
+
* // In a form schema
|
|
169
|
+
* const UserSchema = z.object({
|
|
170
|
+
* id: randomStringSchema({ length: 12, charset: 'base58' }),
|
|
171
|
+
* apiKey: randomStringSchema({ length: 32 }),
|
|
172
|
+
* });
|
|
173
|
+
*/
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Options for building a Zod schema. A superset of ValidateOptions
|
|
177
|
+
* with an extra `description` for the schema's `.describe()` call.
|
|
178
|
+
*/
|
|
179
|
+
interface RandomStringSchemaOptions extends ValidateOptions {
|
|
180
|
+
/** Optional human-readable description added to the Zod schema. */
|
|
181
|
+
description?: string;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* A minimal type shim for the parts of Zod we need.
|
|
185
|
+
* Avoids importing Zod at the type level so the module loads even when
|
|
186
|
+
* Zod is not installed — errors only at runtime if you try to call this.
|
|
187
|
+
*/
|
|
188
|
+
interface ZodStringLike {
|
|
189
|
+
refine(check: (val: string) => boolean, params: {
|
|
190
|
+
message: string;
|
|
191
|
+
}): ZodStringLike;
|
|
192
|
+
describe(text: string): ZodStringLike;
|
|
193
|
+
}
|
|
194
|
+
interface ZodLike {
|
|
195
|
+
string(): ZodStringLike;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Create a Zod string schema that validates against strio's ValidateOptions.
|
|
199
|
+
*
|
|
200
|
+
* @param options - Validation rules (length, charset, required character types, etc.)
|
|
201
|
+
* @param zod - The `z` object from zod. Pass explicitly to avoid a hard dependency.
|
|
202
|
+
*
|
|
203
|
+
* @example
|
|
204
|
+
* import { z } from 'zod';
|
|
205
|
+
* import { randomStringSchema } from '@ppabari/strio/zod';
|
|
206
|
+
*
|
|
207
|
+
* const schema = randomStringSchema({ length: 16, requireUppercase: true }, z);
|
|
208
|
+
*/
|
|
209
|
+
declare function randomStringSchema(options?: RandomStringSchemaOptions, zod?: ZodLike): ZodStringLike;
|
|
210
|
+
|
|
211
|
+
export { type EntropyResult as E, type RandomStringOptions as R, type StartWith as S, type ValidateOptions as V, type RandomStringResult as a, type RandomStringSchemaOptions as b, type ValidationResult as c, randomStringSchema as r };
|
package/dist/zod.cjs
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/validate.ts
|
|
4
|
+
var DIGIT_RE = /[0-9]/;
|
|
5
|
+
var LOWER_RE = /[a-z]/;
|
|
6
|
+
var UPPER_RE = /[A-Z]/;
|
|
7
|
+
var SYMBOL_RE = /[!@#$%^&*()_+\[\]{}<>?]/;
|
|
8
|
+
function validateRandomString(str, options = {}) {
|
|
9
|
+
const errors = [];
|
|
10
|
+
const {
|
|
11
|
+
minLength,
|
|
12
|
+
maxLength,
|
|
13
|
+
length,
|
|
14
|
+
requireNumeric,
|
|
15
|
+
requireLowercase,
|
|
16
|
+
requireUppercase,
|
|
17
|
+
requireSymbols,
|
|
18
|
+
charset,
|
|
19
|
+
pattern
|
|
20
|
+
} = options;
|
|
21
|
+
if (length !== void 0 && str.length !== length) {
|
|
22
|
+
errors.push(`Length must be exactly ${length}, got ${str.length}.`);
|
|
23
|
+
}
|
|
24
|
+
if (minLength !== void 0 && str.length < minLength) {
|
|
25
|
+
errors.push(`Length must be at least ${minLength}, got ${str.length}.`);
|
|
26
|
+
}
|
|
27
|
+
if (maxLength !== void 0 && str.length > maxLength) {
|
|
28
|
+
errors.push(`Length must be at most ${maxLength}, got ${str.length}.`);
|
|
29
|
+
}
|
|
30
|
+
if (requireNumeric && !DIGIT_RE.test(str)) {
|
|
31
|
+
errors.push("Must contain at least one numeric digit.");
|
|
32
|
+
}
|
|
33
|
+
if (requireLowercase && !LOWER_RE.test(str)) {
|
|
34
|
+
errors.push("Must contain at least one lowercase letter.");
|
|
35
|
+
}
|
|
36
|
+
if (requireUppercase && !UPPER_RE.test(str)) {
|
|
37
|
+
errors.push("Must contain at least one uppercase letter.");
|
|
38
|
+
}
|
|
39
|
+
if (requireSymbols && !SYMBOL_RE.test(str)) {
|
|
40
|
+
errors.push("Must contain at least one symbol.");
|
|
41
|
+
}
|
|
42
|
+
if (charset !== void 0) {
|
|
43
|
+
const charsetSet = new Set(charset);
|
|
44
|
+
const invalidChars = [...new Set(str)].filter((ch) => !charsetSet.has(ch));
|
|
45
|
+
if (invalidChars.length > 0) {
|
|
46
|
+
errors.push(
|
|
47
|
+
`Contains characters not in the allowed charset: ${invalidChars.map((c) => JSON.stringify(c)).join(", ")}.`
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
if (pattern !== void 0) {
|
|
52
|
+
const patternErrors = validatePattern(str, pattern);
|
|
53
|
+
errors.push(...patternErrors);
|
|
54
|
+
}
|
|
55
|
+
return { valid: errors.length === 0, errors };
|
|
56
|
+
}
|
|
57
|
+
function validatePattern(str, pattern) {
|
|
58
|
+
const errors = [];
|
|
59
|
+
const expandedPattern = [];
|
|
60
|
+
let i = 0;
|
|
61
|
+
while (i < pattern.length) {
|
|
62
|
+
const ch = pattern[i];
|
|
63
|
+
if (ch === "\\" && i + 1 < pattern.length) {
|
|
64
|
+
expandedPattern.push({ type: "literal", value: pattern[i + 1] });
|
|
65
|
+
i += 2;
|
|
66
|
+
} else if (["#", "A", "a", "*", "?"].includes(ch)) {
|
|
67
|
+
expandedPattern.push({ type: "placeholder", value: ch });
|
|
68
|
+
i++;
|
|
69
|
+
} else {
|
|
70
|
+
expandedPattern.push({ type: "literal", value: ch });
|
|
71
|
+
i++;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (str.length !== expandedPattern.length) {
|
|
75
|
+
errors.push(
|
|
76
|
+
`Pattern expects length ${expandedPattern.length}, got ${str.length}.`
|
|
77
|
+
);
|
|
78
|
+
return errors;
|
|
79
|
+
}
|
|
80
|
+
for (let pos = 0; pos < expandedPattern.length; pos++) {
|
|
81
|
+
const token = expandedPattern[pos];
|
|
82
|
+
const ch = str[pos];
|
|
83
|
+
if (token.type === "literal") {
|
|
84
|
+
if (ch !== token.value) {
|
|
85
|
+
errors.push(
|
|
86
|
+
`Position ${pos}: expected literal '${token.value}', got '${ch}'.`
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
} else {
|
|
90
|
+
switch (token.value) {
|
|
91
|
+
case "#":
|
|
92
|
+
if (!DIGIT_RE.test(ch)) {
|
|
93
|
+
errors.push(`Position ${pos}: '#' expects a digit, got '${ch}'.`);
|
|
94
|
+
}
|
|
95
|
+
break;
|
|
96
|
+
case "A":
|
|
97
|
+
if (!/[A-Z]/.test(ch)) {
|
|
98
|
+
errors.push(`Position ${pos}: 'A' expects an uppercase letter, got '${ch}'.`);
|
|
99
|
+
}
|
|
100
|
+
break;
|
|
101
|
+
case "a":
|
|
102
|
+
if (!LOWER_RE.test(ch)) {
|
|
103
|
+
errors.push(`Position ${pos}: 'a' expects a lowercase letter, got '${ch}'.`);
|
|
104
|
+
}
|
|
105
|
+
break;
|
|
106
|
+
case "?":
|
|
107
|
+
if (!/[a-zA-Z0-9]/.test(ch)) {
|
|
108
|
+
errors.push(`Position ${pos}: '?' expects an alphanumeric character, got '${ch}'.`);
|
|
109
|
+
}
|
|
110
|
+
break;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return errors;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// src/zod.ts
|
|
118
|
+
function randomStringSchema(options = {}, zod) {
|
|
119
|
+
let z;
|
|
120
|
+
if (zod) {
|
|
121
|
+
z = zod;
|
|
122
|
+
} else {
|
|
123
|
+
try {
|
|
124
|
+
const req = typeof globalThis !== "undefined" && globalThis["require"];
|
|
125
|
+
z = req ? req("zod") : (() => {
|
|
126
|
+
throw new Error("no require");
|
|
127
|
+
})();
|
|
128
|
+
} catch {
|
|
129
|
+
throw new Error(
|
|
130
|
+
"@ppabari/strio: Zod is not installed. Run `npm install zod` or pass the `z` object explicitly: `randomStringSchema(options, z)`."
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
const { description, ...validateOpts } = options;
|
|
135
|
+
let schema = z.string().refine(
|
|
136
|
+
(val) => {
|
|
137
|
+
const result = validateRandomString(val, validateOpts);
|
|
138
|
+
return result.valid;
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
message: buildMessage(validateOpts)
|
|
142
|
+
}
|
|
143
|
+
);
|
|
144
|
+
if (description) {
|
|
145
|
+
schema = schema.describe(description);
|
|
146
|
+
}
|
|
147
|
+
return schema;
|
|
148
|
+
}
|
|
149
|
+
function buildMessage(opts) {
|
|
150
|
+
const parts = [];
|
|
151
|
+
if (opts.length !== void 0) parts.push(`exactly ${opts.length} characters`);
|
|
152
|
+
if (opts.minLength !== void 0) parts.push(`at least ${opts.minLength} characters`);
|
|
153
|
+
if (opts.maxLength !== void 0) parts.push(`at most ${opts.maxLength} characters`);
|
|
154
|
+
if (opts.requireNumeric) parts.push("at least one digit");
|
|
155
|
+
if (opts.requireLowercase) parts.push("at least one lowercase letter");
|
|
156
|
+
if (opts.requireUppercase) parts.push("at least one uppercase letter");
|
|
157
|
+
if (opts.requireSymbols) parts.push("at least one symbol");
|
|
158
|
+
if (opts.charset) parts.push(`only characters from '${opts.charset}'`);
|
|
159
|
+
if (opts.pattern) parts.push(`matching pattern '${opts.pattern}'`);
|
|
160
|
+
return parts.length > 0 ? `String must contain ${parts.join(", ")}.` : "Invalid string format.";
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
exports.randomStringSchema = randomStringSchema;
|
|
164
|
+
//# sourceMappingURL=zod.cjs.map
|
|
165
|
+
//# sourceMappingURL=zod.cjs.map
|
package/dist/zod.cjs.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/validate.ts","../src/zod.ts"],"names":[],"mappings":"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","file":"zod.cjs","sourcesContent":["/**\n * validate.ts\n * Validate that a string conforms to a given RandomStringOptions config.\n */\n\nimport type { ValidateOptions, ValidationResult } from './types.js';\n\nconst DIGIT_RE = /[0-9]/;\nconst LOWER_RE = /[a-z]/;\nconst UPPER_RE = /[A-Z]/;\nconst SYMBOL_RE = /[!@#$%^&*()_+\\[\\]{}<>?]/;\n\n/**\n * Checks whether `str` satisfies the given validation rules.\n *\n * @example\n * const result = validateRandomString('abc123', {\n * minLength: 6,\n * requireNumeric: true,\n * requireLowercase: true,\n * });\n * result.valid; // true\n *\n * @example\n * const result = validateRandomString('abc', { requireNumeric: true });\n * result.valid; // false\n * result.errors; // ['Must contain at least one numeric digit.']\n */\nexport function validateRandomString(\n str: string,\n options: ValidateOptions = {}\n): ValidationResult {\n const errors: string[] = [];\n const {\n minLength,\n maxLength,\n length,\n requireNumeric,\n requireLowercase,\n requireUppercase,\n requireSymbols,\n charset,\n pattern,\n } = options;\n\n if (length !== undefined && str.length !== length) {\n errors.push(`Length must be exactly ${length}, got ${str.length}.`);\n }\n\n if (minLength !== undefined && str.length < minLength) {\n errors.push(`Length must be at least ${minLength}, got ${str.length}.`);\n }\n\n if (maxLength !== undefined && str.length > maxLength) {\n errors.push(`Length must be at most ${maxLength}, got ${str.length}.`);\n }\n\n if (requireNumeric && !DIGIT_RE.test(str)) {\n errors.push('Must contain at least one numeric digit.');\n }\n\n if (requireLowercase && !LOWER_RE.test(str)) {\n errors.push('Must contain at least one lowercase letter.');\n }\n\n if (requireUppercase && !UPPER_RE.test(str)) {\n errors.push('Must contain at least one uppercase letter.');\n }\n\n if (requireSymbols && !SYMBOL_RE.test(str)) {\n errors.push('Must contain at least one symbol.');\n }\n\n if (charset !== undefined) {\n const charsetSet = new Set(charset);\n const invalidChars = [...new Set(str)].filter(ch => !charsetSet.has(ch));\n if (invalidChars.length > 0) {\n errors.push(\n `Contains characters not in the allowed charset: ${invalidChars.map(c => JSON.stringify(c)).join(', ')}.`\n );\n }\n }\n\n if (pattern !== undefined) {\n const patternErrors = validatePattern(str, pattern);\n errors.push(...patternErrors);\n }\n\n return { valid: errors.length === 0, errors };\n}\n\nfunction validatePattern(str: string, pattern: string): string[] {\n const errors: string[] = [];\n const expandedPattern: Array<{ type: 'literal' | 'placeholder'; value: string }> = [];\n\n let i = 0;\n while (i < pattern.length) {\n const ch = pattern[i] as string;\n if (ch === '\\\\' && i + 1 < pattern.length) {\n expandedPattern.push({ type: 'literal', value: pattern[i + 1] as string });\n i += 2;\n } else if (['#', 'A', 'a', '*', '?'].includes(ch)) {\n expandedPattern.push({ type: 'placeholder', value: ch });\n i++;\n } else {\n expandedPattern.push({ type: 'literal', value: ch });\n i++;\n }\n }\n\n if (str.length !== expandedPattern.length) {\n errors.push(\n `Pattern expects length ${expandedPattern.length}, got ${str.length}.`\n );\n return errors;\n }\n\n for (let pos = 0; pos < expandedPattern.length; pos++) {\n const token = expandedPattern[pos]!;\n const ch = str[pos] as string;\n\n if (token.type === 'literal') {\n if (ch !== token.value) {\n errors.push(\n `Position ${pos}: expected literal '${token.value}', got '${ch}'.`\n );\n }\n } else {\n switch (token.value) {\n case '#':\n if (!DIGIT_RE.test(ch)) {\n errors.push(`Position ${pos}: '#' expects a digit, got '${ch}'.`);\n }\n break;\n case 'A':\n if (!/[A-Z]/.test(ch)) {\n errors.push(`Position ${pos}: 'A' expects an uppercase letter, got '${ch}'.`);\n }\n break;\n case 'a':\n if (!LOWER_RE.test(ch)) {\n errors.push(`Position ${pos}: 'a' expects a lowercase letter, got '${ch}'.`);\n }\n break;\n case '?':\n if (!/[a-zA-Z0-9]/.test(ch)) {\n errors.push(`Position ${pos}: '?' expects an alphanumeric character, got '${ch}'.`);\n }\n break;\n case '*':\n // Any character is valid\n break;\n }\n }\n }\n\n return errors;\n}\n","/**\n * zod.ts\n * Zod schema factory for @ppabari/strio.\n *\n * Creates a `z.string()` schema that validates a string against strio\n * options. The Zod peer dependency is optional — import from the\n * subpath `@ppabari/strio/zod` only when Zod is available.\n *\n * @example\n * import { z } from 'zod';\n * import { randomStringSchema } from '@ppabari/strio/zod';\n *\n * const tokenSchema = randomStringSchema({\n * length: 32,\n * charset: 'base62',\n * });\n *\n * tokenSchema.parse('k3Xp9mQr2LzYw7NvTq8sHfGbJ5cD4eAK'); // ✓\n * tokenSchema.parse('short'); // throws ZodError\n *\n * // In a form schema\n * const UserSchema = z.object({\n * id: randomStringSchema({ length: 12, charset: 'base58' }),\n * apiKey: randomStringSchema({ length: 32 }),\n * });\n */\n\nimport type { ValidateOptions } from './types.js';\nimport { validateRandomString } from './validate.js';\n\n/**\n * Options for building a Zod schema. A superset of ValidateOptions\n * with an extra `description` for the schema's `.describe()` call.\n */\nexport interface RandomStringSchemaOptions extends ValidateOptions {\n /** Optional human-readable description added to the Zod schema. */\n description?: string;\n}\n\n/**\n * A minimal type shim for the parts of Zod we need.\n * Avoids importing Zod at the type level so the module loads even when\n * Zod is not installed — errors only at runtime if you try to call this.\n */\ninterface ZodStringLike {\n refine(\n check: (val: string) => boolean,\n params: { message: string }\n ): ZodStringLike;\n describe(text: string): ZodStringLike;\n}\n\ninterface ZodLike {\n string(): ZodStringLike;\n}\n\n/**\n * Create a Zod string schema that validates against strio's ValidateOptions.\n *\n * @param options - Validation rules (length, charset, required character types, etc.)\n * @param zod - The `z` object from zod. Pass explicitly to avoid a hard dependency.\n *\n * @example\n * import { z } from 'zod';\n * import { randomStringSchema } from '@ppabari/strio/zod';\n *\n * const schema = randomStringSchema({ length: 16, requireUppercase: true }, z);\n */\nexport function randomStringSchema(\n options: RandomStringSchemaOptions = {},\n zod?: ZodLike\n): ZodStringLike {\n // Attempt to auto-import Zod if not provided — works in Node/bundler envs\n // Falls back gracefully with a helpful error if unavailable\n let z: ZodLike;\n\n if (zod) {\n z = zod;\n } else {\n try {\n // Use indirect eval to avoid TypeScript's require type check.\n // This is intentional: zod is an optional peer dependency.\n const req = (typeof globalThis !== 'undefined' &&\n (globalThis as Record<string, unknown>)['require']) as\n | ((id: string) => ZodLike)\n | undefined;\n z = req ? req('zod') : (() => { throw new Error('no require'); })();\n } catch {\n throw new Error(\n '@ppabari/strio: Zod is not installed. Run `npm install zod` or pass the `z` ' +\n 'object explicitly: `randomStringSchema(options, z)`.'\n );\n }\n }\n\n const { description, ...validateOpts } = options;\n\n let schema = z.string().refine(\n (val: string) => {\n const result = validateRandomString(val, validateOpts);\n return result.valid;\n },\n {\n message: buildMessage(validateOpts),\n }\n );\n\n if (description) {\n schema = schema.describe(description);\n }\n\n return schema;\n}\n\nfunction buildMessage(opts: ValidateOptions): string {\n const parts: string[] = [];\n\n if (opts.length !== undefined) parts.push(`exactly ${opts.length} characters`);\n if (opts.minLength !== undefined) parts.push(`at least ${opts.minLength} characters`);\n if (opts.maxLength !== undefined) parts.push(`at most ${opts.maxLength} characters`);\n if (opts.requireNumeric) parts.push('at least one digit');\n if (opts.requireLowercase) parts.push('at least one lowercase letter');\n if (opts.requireUppercase) parts.push('at least one uppercase letter');\n if (opts.requireSymbols) parts.push('at least one symbol');\n if (opts.charset) parts.push(`only characters from '${opts.charset}'`);\n if (opts.pattern) parts.push(`matching pattern '${opts.pattern}'`);\n\n return parts.length > 0\n ? `String must contain ${parts.join(', ')}.`\n : 'Invalid string format.';\n}\n"]}
|
package/dist/zod.d.cts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { b as RandomStringSchemaOptions, r as randomStringSchema } from './zod-CINbFpp9.cjs';
|
package/dist/zod.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { b as RandomStringSchemaOptions, r as randomStringSchema } from './zod-CINbFpp9.js';
|
package/dist/zod.js
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
// src/validate.ts
|
|
2
|
+
var DIGIT_RE = /[0-9]/;
|
|
3
|
+
var LOWER_RE = /[a-z]/;
|
|
4
|
+
var UPPER_RE = /[A-Z]/;
|
|
5
|
+
var SYMBOL_RE = /[!@#$%^&*()_+\[\]{}<>?]/;
|
|
6
|
+
function validateRandomString(str, options = {}) {
|
|
7
|
+
const errors = [];
|
|
8
|
+
const {
|
|
9
|
+
minLength,
|
|
10
|
+
maxLength,
|
|
11
|
+
length,
|
|
12
|
+
requireNumeric,
|
|
13
|
+
requireLowercase,
|
|
14
|
+
requireUppercase,
|
|
15
|
+
requireSymbols,
|
|
16
|
+
charset,
|
|
17
|
+
pattern
|
|
18
|
+
} = options;
|
|
19
|
+
if (length !== void 0 && str.length !== length) {
|
|
20
|
+
errors.push(`Length must be exactly ${length}, got ${str.length}.`);
|
|
21
|
+
}
|
|
22
|
+
if (minLength !== void 0 && str.length < minLength) {
|
|
23
|
+
errors.push(`Length must be at least ${minLength}, got ${str.length}.`);
|
|
24
|
+
}
|
|
25
|
+
if (maxLength !== void 0 && str.length > maxLength) {
|
|
26
|
+
errors.push(`Length must be at most ${maxLength}, got ${str.length}.`);
|
|
27
|
+
}
|
|
28
|
+
if (requireNumeric && !DIGIT_RE.test(str)) {
|
|
29
|
+
errors.push("Must contain at least one numeric digit.");
|
|
30
|
+
}
|
|
31
|
+
if (requireLowercase && !LOWER_RE.test(str)) {
|
|
32
|
+
errors.push("Must contain at least one lowercase letter.");
|
|
33
|
+
}
|
|
34
|
+
if (requireUppercase && !UPPER_RE.test(str)) {
|
|
35
|
+
errors.push("Must contain at least one uppercase letter.");
|
|
36
|
+
}
|
|
37
|
+
if (requireSymbols && !SYMBOL_RE.test(str)) {
|
|
38
|
+
errors.push("Must contain at least one symbol.");
|
|
39
|
+
}
|
|
40
|
+
if (charset !== void 0) {
|
|
41
|
+
const charsetSet = new Set(charset);
|
|
42
|
+
const invalidChars = [...new Set(str)].filter((ch) => !charsetSet.has(ch));
|
|
43
|
+
if (invalidChars.length > 0) {
|
|
44
|
+
errors.push(
|
|
45
|
+
`Contains characters not in the allowed charset: ${invalidChars.map((c) => JSON.stringify(c)).join(", ")}.`
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
if (pattern !== void 0) {
|
|
50
|
+
const patternErrors = validatePattern(str, pattern);
|
|
51
|
+
errors.push(...patternErrors);
|
|
52
|
+
}
|
|
53
|
+
return { valid: errors.length === 0, errors };
|
|
54
|
+
}
|
|
55
|
+
function validatePattern(str, pattern) {
|
|
56
|
+
const errors = [];
|
|
57
|
+
const expandedPattern = [];
|
|
58
|
+
let i = 0;
|
|
59
|
+
while (i < pattern.length) {
|
|
60
|
+
const ch = pattern[i];
|
|
61
|
+
if (ch === "\\" && i + 1 < pattern.length) {
|
|
62
|
+
expandedPattern.push({ type: "literal", value: pattern[i + 1] });
|
|
63
|
+
i += 2;
|
|
64
|
+
} else if (["#", "A", "a", "*", "?"].includes(ch)) {
|
|
65
|
+
expandedPattern.push({ type: "placeholder", value: ch });
|
|
66
|
+
i++;
|
|
67
|
+
} else {
|
|
68
|
+
expandedPattern.push({ type: "literal", value: ch });
|
|
69
|
+
i++;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (str.length !== expandedPattern.length) {
|
|
73
|
+
errors.push(
|
|
74
|
+
`Pattern expects length ${expandedPattern.length}, got ${str.length}.`
|
|
75
|
+
);
|
|
76
|
+
return errors;
|
|
77
|
+
}
|
|
78
|
+
for (let pos = 0; pos < expandedPattern.length; pos++) {
|
|
79
|
+
const token = expandedPattern[pos];
|
|
80
|
+
const ch = str[pos];
|
|
81
|
+
if (token.type === "literal") {
|
|
82
|
+
if (ch !== token.value) {
|
|
83
|
+
errors.push(
|
|
84
|
+
`Position ${pos}: expected literal '${token.value}', got '${ch}'.`
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
} else {
|
|
88
|
+
switch (token.value) {
|
|
89
|
+
case "#":
|
|
90
|
+
if (!DIGIT_RE.test(ch)) {
|
|
91
|
+
errors.push(`Position ${pos}: '#' expects a digit, got '${ch}'.`);
|
|
92
|
+
}
|
|
93
|
+
break;
|
|
94
|
+
case "A":
|
|
95
|
+
if (!/[A-Z]/.test(ch)) {
|
|
96
|
+
errors.push(`Position ${pos}: 'A' expects an uppercase letter, got '${ch}'.`);
|
|
97
|
+
}
|
|
98
|
+
break;
|
|
99
|
+
case "a":
|
|
100
|
+
if (!LOWER_RE.test(ch)) {
|
|
101
|
+
errors.push(`Position ${pos}: 'a' expects a lowercase letter, got '${ch}'.`);
|
|
102
|
+
}
|
|
103
|
+
break;
|
|
104
|
+
case "?":
|
|
105
|
+
if (!/[a-zA-Z0-9]/.test(ch)) {
|
|
106
|
+
errors.push(`Position ${pos}: '?' expects an alphanumeric character, got '${ch}'.`);
|
|
107
|
+
}
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return errors;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// src/zod.ts
|
|
116
|
+
function randomStringSchema(options = {}, zod) {
|
|
117
|
+
let z;
|
|
118
|
+
if (zod) {
|
|
119
|
+
z = zod;
|
|
120
|
+
} else {
|
|
121
|
+
try {
|
|
122
|
+
const req = typeof globalThis !== "undefined" && globalThis["require"];
|
|
123
|
+
z = req ? req("zod") : (() => {
|
|
124
|
+
throw new Error("no require");
|
|
125
|
+
})();
|
|
126
|
+
} catch {
|
|
127
|
+
throw new Error(
|
|
128
|
+
"@ppabari/strio: Zod is not installed. Run `npm install zod` or pass the `z` object explicitly: `randomStringSchema(options, z)`."
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
const { description, ...validateOpts } = options;
|
|
133
|
+
let schema = z.string().refine(
|
|
134
|
+
(val) => {
|
|
135
|
+
const result = validateRandomString(val, validateOpts);
|
|
136
|
+
return result.valid;
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
message: buildMessage(validateOpts)
|
|
140
|
+
}
|
|
141
|
+
);
|
|
142
|
+
if (description) {
|
|
143
|
+
schema = schema.describe(description);
|
|
144
|
+
}
|
|
145
|
+
return schema;
|
|
146
|
+
}
|
|
147
|
+
function buildMessage(opts) {
|
|
148
|
+
const parts = [];
|
|
149
|
+
if (opts.length !== void 0) parts.push(`exactly ${opts.length} characters`);
|
|
150
|
+
if (opts.minLength !== void 0) parts.push(`at least ${opts.minLength} characters`);
|
|
151
|
+
if (opts.maxLength !== void 0) parts.push(`at most ${opts.maxLength} characters`);
|
|
152
|
+
if (opts.requireNumeric) parts.push("at least one digit");
|
|
153
|
+
if (opts.requireLowercase) parts.push("at least one lowercase letter");
|
|
154
|
+
if (opts.requireUppercase) parts.push("at least one uppercase letter");
|
|
155
|
+
if (opts.requireSymbols) parts.push("at least one symbol");
|
|
156
|
+
if (opts.charset) parts.push(`only characters from '${opts.charset}'`);
|
|
157
|
+
if (opts.pattern) parts.push(`matching pattern '${opts.pattern}'`);
|
|
158
|
+
return parts.length > 0 ? `String must contain ${parts.join(", ")}.` : "Invalid string format.";
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export { randomStringSchema };
|
|
162
|
+
//# sourceMappingURL=zod.js.map
|
|
163
|
+
//# sourceMappingURL=zod.js.map
|
package/dist/zod.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/validate.ts","../src/zod.ts"],"names":[],"mappings":"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","file":"zod.js","sourcesContent":["/**\n * validate.ts\n * Validate that a string conforms to a given RandomStringOptions config.\n */\n\nimport type { ValidateOptions, ValidationResult } from './types.js';\n\nconst DIGIT_RE = /[0-9]/;\nconst LOWER_RE = /[a-z]/;\nconst UPPER_RE = /[A-Z]/;\nconst SYMBOL_RE = /[!@#$%^&*()_+\\[\\]{}<>?]/;\n\n/**\n * Checks whether `str` satisfies the given validation rules.\n *\n * @example\n * const result = validateRandomString('abc123', {\n * minLength: 6,\n * requireNumeric: true,\n * requireLowercase: true,\n * });\n * result.valid; // true\n *\n * @example\n * const result = validateRandomString('abc', { requireNumeric: true });\n * result.valid; // false\n * result.errors; // ['Must contain at least one numeric digit.']\n */\nexport function validateRandomString(\n str: string,\n options: ValidateOptions = {}\n): ValidationResult {\n const errors: string[] = [];\n const {\n minLength,\n maxLength,\n length,\n requireNumeric,\n requireLowercase,\n requireUppercase,\n requireSymbols,\n charset,\n pattern,\n } = options;\n\n if (length !== undefined && str.length !== length) {\n errors.push(`Length must be exactly ${length}, got ${str.length}.`);\n }\n\n if (minLength !== undefined && str.length < minLength) {\n errors.push(`Length must be at least ${minLength}, got ${str.length}.`);\n }\n\n if (maxLength !== undefined && str.length > maxLength) {\n errors.push(`Length must be at most ${maxLength}, got ${str.length}.`);\n }\n\n if (requireNumeric && !DIGIT_RE.test(str)) {\n errors.push('Must contain at least one numeric digit.');\n }\n\n if (requireLowercase && !LOWER_RE.test(str)) {\n errors.push('Must contain at least one lowercase letter.');\n }\n\n if (requireUppercase && !UPPER_RE.test(str)) {\n errors.push('Must contain at least one uppercase letter.');\n }\n\n if (requireSymbols && !SYMBOL_RE.test(str)) {\n errors.push('Must contain at least one symbol.');\n }\n\n if (charset !== undefined) {\n const charsetSet = new Set(charset);\n const invalidChars = [...new Set(str)].filter(ch => !charsetSet.has(ch));\n if (invalidChars.length > 0) {\n errors.push(\n `Contains characters not in the allowed charset: ${invalidChars.map(c => JSON.stringify(c)).join(', ')}.`\n );\n }\n }\n\n if (pattern !== undefined) {\n const patternErrors = validatePattern(str, pattern);\n errors.push(...patternErrors);\n }\n\n return { valid: errors.length === 0, errors };\n}\n\nfunction validatePattern(str: string, pattern: string): string[] {\n const errors: string[] = [];\n const expandedPattern: Array<{ type: 'literal' | 'placeholder'; value: string }> = [];\n\n let i = 0;\n while (i < pattern.length) {\n const ch = pattern[i] as string;\n if (ch === '\\\\' && i + 1 < pattern.length) {\n expandedPattern.push({ type: 'literal', value: pattern[i + 1] as string });\n i += 2;\n } else if (['#', 'A', 'a', '*', '?'].includes(ch)) {\n expandedPattern.push({ type: 'placeholder', value: ch });\n i++;\n } else {\n expandedPattern.push({ type: 'literal', value: ch });\n i++;\n }\n }\n\n if (str.length !== expandedPattern.length) {\n errors.push(\n `Pattern expects length ${expandedPattern.length}, got ${str.length}.`\n );\n return errors;\n }\n\n for (let pos = 0; pos < expandedPattern.length; pos++) {\n const token = expandedPattern[pos]!;\n const ch = str[pos] as string;\n\n if (token.type === 'literal') {\n if (ch !== token.value) {\n errors.push(\n `Position ${pos}: expected literal '${token.value}', got '${ch}'.`\n );\n }\n } else {\n switch (token.value) {\n case '#':\n if (!DIGIT_RE.test(ch)) {\n errors.push(`Position ${pos}: '#' expects a digit, got '${ch}'.`);\n }\n break;\n case 'A':\n if (!/[A-Z]/.test(ch)) {\n errors.push(`Position ${pos}: 'A' expects an uppercase letter, got '${ch}'.`);\n }\n break;\n case 'a':\n if (!LOWER_RE.test(ch)) {\n errors.push(`Position ${pos}: 'a' expects a lowercase letter, got '${ch}'.`);\n }\n break;\n case '?':\n if (!/[a-zA-Z0-9]/.test(ch)) {\n errors.push(`Position ${pos}: '?' expects an alphanumeric character, got '${ch}'.`);\n }\n break;\n case '*':\n // Any character is valid\n break;\n }\n }\n }\n\n return errors;\n}\n","/**\n * zod.ts\n * Zod schema factory for @ppabari/strio.\n *\n * Creates a `z.string()` schema that validates a string against strio\n * options. The Zod peer dependency is optional — import from the\n * subpath `@ppabari/strio/zod` only when Zod is available.\n *\n * @example\n * import { z } from 'zod';\n * import { randomStringSchema } from '@ppabari/strio/zod';\n *\n * const tokenSchema = randomStringSchema({\n * length: 32,\n * charset: 'base62',\n * });\n *\n * tokenSchema.parse('k3Xp9mQr2LzYw7NvTq8sHfGbJ5cD4eAK'); // ✓\n * tokenSchema.parse('short'); // throws ZodError\n *\n * // In a form schema\n * const UserSchema = z.object({\n * id: randomStringSchema({ length: 12, charset: 'base58' }),\n * apiKey: randomStringSchema({ length: 32 }),\n * });\n */\n\nimport type { ValidateOptions } from './types.js';\nimport { validateRandomString } from './validate.js';\n\n/**\n * Options for building a Zod schema. A superset of ValidateOptions\n * with an extra `description` for the schema's `.describe()` call.\n */\nexport interface RandomStringSchemaOptions extends ValidateOptions {\n /** Optional human-readable description added to the Zod schema. */\n description?: string;\n}\n\n/**\n * A minimal type shim for the parts of Zod we need.\n * Avoids importing Zod at the type level so the module loads even when\n * Zod is not installed — errors only at runtime if you try to call this.\n */\ninterface ZodStringLike {\n refine(\n check: (val: string) => boolean,\n params: { message: string }\n ): ZodStringLike;\n describe(text: string): ZodStringLike;\n}\n\ninterface ZodLike {\n string(): ZodStringLike;\n}\n\n/**\n * Create a Zod string schema that validates against strio's ValidateOptions.\n *\n * @param options - Validation rules (length, charset, required character types, etc.)\n * @param zod - The `z` object from zod. Pass explicitly to avoid a hard dependency.\n *\n * @example\n * import { z } from 'zod';\n * import { randomStringSchema } from '@ppabari/strio/zod';\n *\n * const schema = randomStringSchema({ length: 16, requireUppercase: true }, z);\n */\nexport function randomStringSchema(\n options: RandomStringSchemaOptions = {},\n zod?: ZodLike\n): ZodStringLike {\n // Attempt to auto-import Zod if not provided — works in Node/bundler envs\n // Falls back gracefully with a helpful error if unavailable\n let z: ZodLike;\n\n if (zod) {\n z = zod;\n } else {\n try {\n // Use indirect eval to avoid TypeScript's require type check.\n // This is intentional: zod is an optional peer dependency.\n const req = (typeof globalThis !== 'undefined' &&\n (globalThis as Record<string, unknown>)['require']) as\n | ((id: string) => ZodLike)\n | undefined;\n z = req ? req('zod') : (() => { throw new Error('no require'); })();\n } catch {\n throw new Error(\n '@ppabari/strio: Zod is not installed. Run `npm install zod` or pass the `z` ' +\n 'object explicitly: `randomStringSchema(options, z)`.'\n );\n }\n }\n\n const { description, ...validateOpts } = options;\n\n let schema = z.string().refine(\n (val: string) => {\n const result = validateRandomString(val, validateOpts);\n return result.valid;\n },\n {\n message: buildMessage(validateOpts),\n }\n );\n\n if (description) {\n schema = schema.describe(description);\n }\n\n return schema;\n}\n\nfunction buildMessage(opts: ValidateOptions): string {\n const parts: string[] = [];\n\n if (opts.length !== undefined) parts.push(`exactly ${opts.length} characters`);\n if (opts.minLength !== undefined) parts.push(`at least ${opts.minLength} characters`);\n if (opts.maxLength !== undefined) parts.push(`at most ${opts.maxLength} characters`);\n if (opts.requireNumeric) parts.push('at least one digit');\n if (opts.requireLowercase) parts.push('at least one lowercase letter');\n if (opts.requireUppercase) parts.push('at least one uppercase letter');\n if (opts.requireSymbols) parts.push('at least one symbol');\n if (opts.charset) parts.push(`only characters from '${opts.charset}'`);\n if (opts.pattern) parts.push(`matching pattern '${opts.pattern}'`);\n\n return parts.length > 0\n ? `String must contain ${parts.join(', ')}.`\n : 'Invalid string format.';\n}\n"]}
|