@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.
@@ -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
@@ -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
@@ -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"]}