@whatsaaaa/form-builder-schema 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -78,58 +78,514 @@ const defaultKinds = {
78
78
  };
79
79
  // src/rules/define-rule.ts
80
80
  const defineRule = (def) => def;
81
- // src/rules/default-rules.ts
82
- const nonNegInt = v.pipe(v.number(), v.integer(), v.minValue(0));
83
- const defaultRules = {
84
- trim: defineRule({
81
+ // src/rules/param-schemas.ts
82
+ /** A length, count or size: a whole number from 0. */
83
+ const count = v.pipe(v.number(), v.integer(), v.minValue(0));
84
+ const countParams = v.object({ value: count });
85
+ const isIsoValue = (input) => v.ISO_DATE_REGEX.test(input) || v.ISO_DATE_TIME_REGEX.test(input) || v.ISO_TIME_REGEX.test(input);
86
+ /** Valibot compares dates as ISO strings, so the param must be one too. */
87
+ const isoValue = v.pipe(v.string(), v.check(isIsoValue, "Use an ISO date, date-time or time, such as 2026-01-01"));
88
+ const valueSchema = (kind) => {
89
+ switch (kind) {
90
+ case "date": return isoValue;
91
+ case "string": return v.string();
92
+ case "boolean": return v.boolean();
93
+ default: return v.number();
94
+ }
95
+ };
96
+ // src/rules/comparison-rules.ts
97
+ const ordered = ["number", "date"];
98
+ const any = [
99
+ "number",
100
+ "date",
101
+ "string",
102
+ "boolean"
103
+ ];
104
+ const meta$6 = (label) => ({
105
+ label,
106
+ category: "comparison"
107
+ });
108
+ const valueParams = (kind) => v.object({ value: valueSchema(kind) });
109
+ const valuesParams = (kind) => v.object({ values: v.pipe(v.array(valueSchema(kind)), v.minLength(1, "Give at least one value")) });
110
+ /** Compares the value with a param of the same type. */
111
+ const comparisonRules = {
112
+ minValue: defineRule({
113
+ kinds: ordered,
114
+ params: valueParams,
115
+ meta: meta$6("Minimum"),
116
+ create: (p, m) => v.minValue(p.value, m)
117
+ }),
118
+ maxValue: defineRule({
119
+ kinds: ordered,
120
+ params: valueParams,
121
+ meta: meta$6("Maximum"),
122
+ create: (p, m) => v.maxValue(p.value, m)
123
+ }),
124
+ gtValue: defineRule({
125
+ kinds: ordered,
126
+ params: valueParams,
127
+ meta: meta$6("Greater than"),
128
+ create: (p, m) => v.gtValue(p.value, m)
129
+ }),
130
+ ltValue: defineRule({
131
+ kinds: ordered,
132
+ params: valueParams,
133
+ meta: meta$6("Less than"),
134
+ create: (p, m) => v.ltValue(p.value, m)
135
+ }),
136
+ value: defineRule({
137
+ kinds: any,
138
+ params: valueParams,
139
+ meta: meta$6("Equals"),
140
+ create: (p, m) => v.value(p.value, m)
141
+ }),
142
+ notValue: defineRule({
143
+ kinds: any,
144
+ params: valueParams,
145
+ meta: meta$6("Does not equal"),
146
+ create: (p, m) => v.notValue(p.value, m)
147
+ }),
148
+ values: defineRule({
149
+ kinds: ordered,
150
+ params: valuesParams,
151
+ meta: meta$6("One of"),
152
+ create: (p, m) => v.values(p.values, m)
153
+ }),
154
+ notValues: defineRule({
155
+ kinds: ordered,
156
+ params: valuesParams,
157
+ meta: meta$6("None of"),
158
+ create: (p, m) => v.notValues(p.values, m)
159
+ })
160
+ };
161
+ // src/rules/content-rules.ts
162
+ const meta$5 = (label, description) => ({
163
+ label,
164
+ description,
165
+ category: "content"
166
+ });
167
+ const regexError = (pattern, flags = "") => {
168
+ try {
169
+ new RegExp(pattern, flags);
170
+ return;
171
+ } catch (error) {
172
+ return error.message;
173
+ }
174
+ };
175
+ const FLAGS = /^[dgimsuvy]*$/;
176
+ const regexParams = v.pipe(v.object({
177
+ pattern: v.string(),
178
+ flags: v.optional(v.pipe(v.string(), v.regex(FLAGS, "Flags can be d, g, i, m, s, u, v and y")))
179
+ }), v.forward(v.check((p) => !FLAGS.test(p.flags ?? "") || regexError(p.pattern, p.flags) === void 0, (issue) => regexError(issue.input.pattern, issue.input.flags) ?? "Invalid pattern"), ["pattern"]));
180
+ const item = v.union([
181
+ v.string(),
182
+ v.number(),
183
+ v.boolean()
184
+ ]);
185
+ const stringParams = v.object({ value: v.string() });
186
+ const contentParams = (kind) => v.object({ value: kind === "array" ? item : v.string() });
187
+ const contentRules = {
188
+ regex: defineRule({
85
189
  kinds: ["string"],
86
- stage: "normalize",
87
- create: () => v.trim()
190
+ params: regexParams,
191
+ meta: meta$5("Matches pattern", "A JavaScript regular expression, without the slashes."),
192
+ create: (p, m) => v.regex(new RegExp(p.pattern, p.flags?.replace(/[gy]/g, "")), m)
88
193
  }),
89
- minLength: defineRule({
194
+ startsWith: defineRule({
90
195
  kinds: ["string"],
91
- params: v.object({ value: nonNegInt }),
196
+ params: stringParams,
197
+ meta: meta$5("Starts with"),
198
+ create: (p, m) => v.startsWith(p.value, m)
199
+ }),
200
+ endsWith: defineRule({
201
+ kinds: ["string"],
202
+ params: stringParams,
203
+ meta: meta$5("Ends with"),
204
+ create: (p, m) => v.endsWith(p.value, m)
205
+ }),
206
+ includes: defineRule({
207
+ kinds: ["string", "array"],
208
+ params: contentParams,
209
+ meta: meta$5("Contains"),
210
+ create: (p, m) => v.includes(p.value, m)
211
+ }),
212
+ excludes: defineRule({
213
+ kinds: ["string", "array"],
214
+ params: contentParams,
215
+ meta: meta$5("Does not contain"),
216
+ create: (p, m) => v.excludes(p.value, m)
217
+ })
218
+ };
219
+ // src/rules/date-rules.ts
220
+ const iso = (label, description, create) => defineRule({
221
+ kinds: ["string", "date"],
222
+ meta: {
223
+ label,
224
+ description,
225
+ category: "date"
226
+ },
227
+ create: (_, message) => create(message)
228
+ });
229
+ const dateRules = {
230
+ isoDate: iso("ISO date", "Like 2026-10-28", (m) => v.isoDate(m)),
231
+ isoDateTime: iso("ISO date and time", "Like 2026-10-28T14:30", (m) => v.isoDateTime(m)),
232
+ isoTime: iso("ISO time", "Like 14:30", (m) => v.isoTime(m)),
233
+ isoTimeSecond: iso("ISO time with seconds", "Like 14:30:15", (m) => v.isoTimeSecond(m)),
234
+ isoTimestamp: iso("ISO timestamp", "Like 2026-10-28T14:30:15.000z", (m) => v.isoTimestamp(m)),
235
+ isoWeek: iso("ISO week", "Like 2026-W44", (m) => v.isoWeek(m))
236
+ };
237
+ // src/rules/file-rules.ts
238
+ const kinds$3 = ["file"];
239
+ const meta$4 = (label, description) => ({
240
+ label,
241
+ description,
242
+ category: "file"
243
+ });
244
+ const bytes = "In bytes, e.g. 1048576 for 1 MiB.";
245
+ const mimeTypeParams = v.object({ types: v.pipe(v.array(v.pipe(v.string(), v.regex(/^[\w.+-]+\/[\w.+-]+$/, "Use a MIME type, such as image/png"))), v.minLength(1, "Give at least one MIME type")) });
246
+ /** Size and type of a File or Blob. */
247
+ const fileRules = {
248
+ minSize: defineRule({
249
+ kinds: kinds$3,
250
+ params: countParams,
251
+ meta: meta$4("Minimum size", bytes),
252
+ create: (p, m) => v.minSize(p.value, m)
253
+ }),
254
+ maxSize: defineRule({
255
+ kinds: kinds$3,
256
+ params: countParams,
257
+ meta: meta$4("Maximum size", bytes),
258
+ create: (p, m) => v.maxSize(p.value, m)
259
+ }),
260
+ size: defineRule({
261
+ kinds: kinds$3,
262
+ params: countParams,
263
+ meta: meta$4("Exact size", bytes),
264
+ create: (p, m) => v.size(p.value, m)
265
+ }),
266
+ notSize: defineRule({
267
+ kinds: kinds$3,
268
+ params: countParams,
269
+ meta: meta$4("Not this size", bytes),
270
+ create: (p, m) => v.notSize(p.value, m)
271
+ }),
272
+ mimeType: defineRule({
273
+ kinds: kinds$3,
274
+ params: mimeTypeParams,
275
+ meta: meta$4("File type"),
276
+ create: (p, m) => v.mimeType(p.types, m)
277
+ })
278
+ };
279
+ // src/rules/format-rules.ts
280
+ /** A string format without params. */
281
+ const format = (label, create) => defineRule({
282
+ kinds: ["string"],
283
+ meta: {
284
+ label,
285
+ category: "format"
286
+ },
287
+ create: (_, message) => create(message)
288
+ });
289
+ const hashParams = v.object({ types: v.pipe(v.array(v.picklist([
290
+ "md4",
291
+ "md5",
292
+ "sha1",
293
+ "sha256",
294
+ "sha384",
295
+ "sha512",
296
+ "ripemd128",
297
+ "ripemd160",
298
+ "tiger128",
299
+ "tiger160",
300
+ "tiger192",
301
+ "crc32",
302
+ "crc32b",
303
+ "adler32"
304
+ ])), v.minLength(1, "Pick at least one hash type")) });
305
+ /** Formats Valibot can check on a string. */
306
+ const formatRules = {
307
+ email: format("Email", (m) => v.email(m)),
308
+ rfcEmail: format("Email (RFC 5322)", (m) => v.rfcEmail(m)),
309
+ url: format("URL", (m) => v.url(m)),
310
+ domain: format("Domain", (m) => v.domain(m)),
311
+ slug: format("Slug", (m) => v.slug(m)),
312
+ uuid: format("UUID", (m) => v.uuid(m)),
313
+ ulid: format("ULID", (m) => v.ulid(m)),
314
+ cuid2: format("CUID2", (m) => v.cuid2(m)),
315
+ nanoid: format("Nano ID", (m) => v.nanoid(m)),
316
+ ip: format("IP address", (m) => v.ip(m)),
317
+ ipv4: format("IPv4 address", (m) => v.ipv4(m)),
318
+ ipv6: format("IPv6 address", (m) => v.ipv6(m)),
319
+ mac: format("MAC address", (m) => v.mac(m)),
320
+ mac48: format("MAC-48 address", (m) => v.mac48(m)),
321
+ mac64: format("MAC-64 address", (m) => v.mac64(m)),
322
+ hexColor: format("Hex colour", (m) => v.hexColor(m)),
323
+ hexadecimal: format("Hexadecimal", (m) => v.hexadecimal(m)),
324
+ octal: format("Octal", (m) => v.octal(m)),
325
+ decimal: format("Decimal number", (m) => v.decimal(m)),
326
+ digits: format("Digits only", (m) => v.digits(m)),
327
+ base64: format("Base64", (m) => v.base64(m)),
328
+ bic: format("BIC (SWIFT code)", (m) => v.bic(m)),
329
+ creditCard: format("Credit card number", (m) => v.creditCard(m)),
330
+ imei: format("IMEI", (m) => v.imei(m)),
331
+ isbn: format("ISBN", (m) => v.isbn(m)),
332
+ isrc: format("ISRC", (m) => v.isrc(m)),
333
+ emoji: format("Emoji", (m) => v.emoji(m)),
334
+ hash: defineRule({
335
+ kinds: ["string"],
336
+ params: hashParams,
337
+ meta: {
338
+ label: "Hash",
339
+ category: "format"
340
+ },
341
+ create: (p, m) => v.hash(p.types, m)
342
+ })
343
+ };
344
+ // src/rules/length-rules.ts
345
+ const kinds$2 = ["string", "array"];
346
+ const meta$3 = (label) => ({
347
+ label,
348
+ category: "length"
349
+ });
350
+ /** Characters of a string, or items of an array */
351
+ const lengthRules = {
352
+ length: defineRule({
353
+ kinds: kinds$2,
354
+ params: countParams,
355
+ meta: meta$3("Exact length"),
356
+ create: (p, m) => v.length(p.value, m)
357
+ }),
358
+ minLength: defineRule({
359
+ kinds: kinds$2,
360
+ params: countParams,
361
+ meta: meta$3("Minimum length"),
92
362
  create: (p, m) => v.minLength(p.value, m)
93
363
  }),
94
364
  maxLength: defineRule({
95
- kinds: ["string"],
96
- params: v.object({ value: nonNegInt }),
365
+ kinds: kinds$2,
366
+ params: countParams,
367
+ meta: meta$3("Maximum length"),
97
368
  create: (p, m) => v.maxLength(p.value, m)
98
369
  }),
99
- regex: defineRule({
370
+ notLength: defineRule({
371
+ kinds: kinds$2,
372
+ params: countParams,
373
+ meta: meta$3("Not this length"),
374
+ create: (p, m) => v.notLength(p.value, m)
375
+ })
376
+ };
377
+ // src/rules/normalize-rules.ts
378
+ const stage = "normalize";
379
+ const meta$2 = (label, description) => ({
380
+ label,
381
+ description,
382
+ category: "normalize"
383
+ });
384
+ const limit = v.object({ value: v.number() });
385
+ /**
386
+ * Rules that change the value instead of checking it. They run before the required check, so
387
+ * `' '` with `trim` is empty. Their `message` is never shown.
388
+ */
389
+ const normalizeRules = {
390
+ trim: defineRule({
100
391
  kinds: ["string"],
101
- params: v.object({
102
- pattern: v.string(),
103
- flags: v.optional(v.string())
104
- }),
105
- create: (p, m) => v.regex(new RegExp(p.pattern, p.flags), m)
392
+ stage,
393
+ meta: meta$2("Trim"),
394
+ create: () => v.trim()
106
395
  }),
107
- email: defineRule({
396
+ trimStart: defineRule({
108
397
  kinds: ["string"],
109
- create: (_, m) => v.email(m)
398
+ stage,
399
+ meta: meta$2("Trim start"),
400
+ create: () => v.trimStart()
110
401
  }),
111
- url: defineRule({
402
+ trimEnd: defineRule({
112
403
  kinds: ["string"],
113
- create: (_, m) => v.url(m)
404
+ stage,
405
+ meta: meta$2("Trim end"),
406
+ create: () => v.trimEnd()
114
407
  }),
115
- minValue: defineRule({
116
- kinds: ["number"],
117
- params: v.object({ value: v.number() }),
118
- create: (p, m) => v.minValue(p.value, m)
408
+ toLowerCase: defineRule({
409
+ kinds: ["string"],
410
+ stage,
411
+ meta: meta$2("Lowercase"),
412
+ create: () => v.toLowerCase()
119
413
  }),
120
- maxValue: defineRule({
414
+ toUpperCase: defineRule({
415
+ kinds: ["string"],
416
+ stage,
417
+ meta: meta$2("Uppercase"),
418
+ create: () => v.toUpperCase()
419
+ }),
420
+ normalize: defineRule({
421
+ kinds: ["string"],
422
+ stage,
423
+ params: v.object({ form: v.optional(v.picklist([
424
+ "NFC",
425
+ "NFD",
426
+ "NFKC",
427
+ "NFKD"
428
+ ])) }),
429
+ meta: meta$2("Unicode normalize", "Defaults to NFC."),
430
+ create: (p) => v.normalize(p.form)
431
+ }),
432
+ toMinValue: defineRule({
121
433
  kinds: ["number"],
122
- params: v.object({ value: v.number() }),
123
- create: (p, m) => v.maxValue(p.value, m)
434
+ stage,
435
+ params: limit,
436
+ meta: meta$2("Raise to minimum", "Replaces a smaller number with the minimum."),
437
+ create: (p) => v.toMinValue(p.value)
124
438
  }),
125
- integer: defineRule({
439
+ toMaxValue: defineRule({
126
440
  kinds: ["number"],
441
+ stage,
442
+ params: limit,
443
+ meta: meta$2("Lower to maximum", "Replaces a larger number with the maximum."),
444
+ create: (p) => v.toMaxValue(p.value)
445
+ })
446
+ };
447
+ // src/rules/number-rules.ts
448
+ const kinds$1 = ["number"];
449
+ const meta$1 = (label) => ({
450
+ label,
451
+ category: "number"
452
+ });
453
+ const divisor = v.pipe(v.number(), v.finite(), v.notValue(0, "Can't be 0"));
454
+ /** Checks only numbers have. */
455
+ const numberRules = {
456
+ integer: defineRule({
457
+ kinds: kinds$1,
458
+ meta: meta$1("Whole number"),
127
459
  create: (_, m) => v.integer(m)
460
+ }),
461
+ safeInteger: defineRule({
462
+ kinds: kinds$1,
463
+ meta: meta$1("Safe whole number"),
464
+ create: (_, m) => v.safeInteger(m)
465
+ }),
466
+ finite: defineRule({
467
+ kinds: kinds$1,
468
+ meta: meta$1("Finite number"),
469
+ create: (_, m) => v.finite(m)
470
+ }),
471
+ multipleOf: defineRule({
472
+ kinds: kinds$1,
473
+ params: v.object({ value: divisor }),
474
+ meta: meta$1("Multiple of"),
475
+ create: (p, m) => v.multipleOf(p.value, m)
128
476
  })
129
477
  };
478
+ // src/rules/text-unit-rules.ts
479
+ const kinds = ["string"];
480
+ const meta = (label, description) => ({
481
+ label,
482
+ description,
483
+ category: "text"
484
+ });
485
+ const isLocale = (input) => {
486
+ try {
487
+ return Intl.getCanonicalLocales(input).length === 1;
488
+ } catch {
489
+ return false;
490
+ }
491
+ };
492
+ const wordsParams = v.object({
493
+ locale: v.optional(v.pipe(v.string(), v.check(isLocale, "Use a language tag, such as en or sr-Latn"))),
494
+ value: count
495
+ });
496
+ const graphemesInfo = "Counts characters as people see them, so an emoji is one.";
497
+ const bytesInfo = "Counts UTF-8 bytes, e.g. to fit a database column,";
498
+ const textUnitRules = {
499
+ graphemes: defineRule({
500
+ kinds,
501
+ params: countParams,
502
+ meta: meta("Exact characters", graphemesInfo),
503
+ create: (p, m) => v.graphemes(p.value, m)
504
+ }),
505
+ minGraphemes: defineRule({
506
+ kinds,
507
+ params: countParams,
508
+ meta: meta("Minimum characters", graphemesInfo),
509
+ create: (p, m) => v.minGraphemes(p.value, m)
510
+ }),
511
+ maxGraphemes: defineRule({
512
+ kinds,
513
+ params: countParams,
514
+ meta: meta("Maximum characters", graphemesInfo),
515
+ create: (p, m) => v.maxGraphemes(p.value, m)
516
+ }),
517
+ notGraphemes: defineRule({
518
+ kinds,
519
+ params: countParams,
520
+ meta: meta("Not this many characters", graphemesInfo),
521
+ create: (p, m) => v.notGraphemes(p.value, m)
522
+ }),
523
+ words: defineRule({
524
+ kinds,
525
+ params: wordsParams,
526
+ meta: meta("Exact words"),
527
+ create: (p, m) => v.words(p.locale, p.value, m)
528
+ }),
529
+ minWords: defineRule({
530
+ kinds,
531
+ params: wordsParams,
532
+ meta: meta("Minimum words"),
533
+ create: (p, m) => v.minWords(p.locale, p.value, m)
534
+ }),
535
+ maxWords: defineRule({
536
+ kinds,
537
+ params: wordsParams,
538
+ meta: meta("Maximum words"),
539
+ create: (p, m) => v.maxWords(p.locale, p.value, m)
540
+ }),
541
+ notWords: defineRule({
542
+ kinds,
543
+ params: wordsParams,
544
+ meta: meta("Not this many words"),
545
+ create: (p, m) => v.notWords(p.locale, p.value, m)
546
+ }),
547
+ bytes: defineRule({
548
+ kinds,
549
+ params: countParams,
550
+ meta: meta("Exact bytes", bytesInfo),
551
+ create: (p, m) => v.bytes(p.value, m)
552
+ }),
553
+ minBytes: defineRule({
554
+ kinds,
555
+ params: countParams,
556
+ meta: meta("Minimum bytes", bytesInfo),
557
+ create: (p, m) => v.minBytes(p.value, m)
558
+ }),
559
+ maxBytes: defineRule({
560
+ kinds,
561
+ params: countParams,
562
+ meta: meta("Maximum bytes", bytesInfo),
563
+ create: (p, m) => v.maxBytes(p.value, m)
564
+ }),
565
+ notBytes: defineRule({
566
+ kinds,
567
+ params: countParams,
568
+ meta: meta("Not this many bytes", bytesInfo),
569
+ create: (p, m) => v.notBytes(p.value, m)
570
+ })
571
+ };
572
+ // src/rules/default-rules.ts
573
+ /** Every built-in rule. Register single families with `includeDefaultRules: false`. */
574
+ const defaultRules = {
575
+ ...lengthRules,
576
+ ...textUnitRules,
577
+ ...contentRules,
578
+ ...formatRules,
579
+ ...dateRules,
580
+ ...comparisonRules,
581
+ ...numberRules,
582
+ ...fileRules,
583
+ ...normalizeRules
584
+ };
130
585
  // src/builder/registry.ts
131
586
  /** Consumer entries replace defaults with the same key. */
132
587
  function mergeRegistry(custom) {
588
+ const rules = custom.includeDefaultRules === false ? {} : defaultRules;
133
589
  return {
134
590
  kinds: Object.freeze({
135
591
  ...defaultKinds,
@@ -140,7 +596,7 @@ function mergeRegistry(custom) {
140
596
  ...custom.fieldTypes
141
597
  }),
142
598
  rules: Object.freeze({
143
- ...defaultRules,
599
+ ...rules,
144
600
  ...custom.rules
145
601
  })
146
602
  };
@@ -165,6 +621,22 @@ var DefinitionError = class extends Error {
165
621
  this.issues = issues;
166
622
  }
167
623
  };
624
+ // src/rules/rule-params.ts
625
+ const cache = /* @__PURE__ */ new WeakMap();
626
+ function paramsFor(def, kind) {
627
+ if (typeof def.params !== "function") return def.params;
628
+ let byKind = cache.get(def);
629
+ if (!byKind) {
630
+ byKind = /* @__PURE__ */ new Map();
631
+ cache.set(def, byKind);
632
+ }
633
+ let params = byKind.get(kind);
634
+ if (!params) {
635
+ params = def.params(kind);
636
+ byKind.set(kind, params);
637
+ }
638
+ return params;
639
+ }
168
640
  // src/shared/reserved-keys.ts
169
641
  /**
170
642
  * Field keys this package owns. A field type's props can't use them.
@@ -312,19 +784,21 @@ function checkRule(raw, kind, path, registry, issues) {
312
784
  issues.push(issue("unknown_rule", [...path, "rule"], `Unknown rule "${rule.rule}"`));
313
785
  return;
314
786
  }
315
- const issueCount = issues.length;
316
787
  if (!def.kinds.includes(kind)) {
317
- const message = `Rule "${rule.rule} can't be used on ${kind} fields`;
788
+ const message = `Rule "${rule.rule}" can't be used on ${kind} fields`;
318
789
  issues.push(issue("kind_mismatch", [...path, "rule"], message));
790
+ return;
319
791
  }
320
- const paramKeys = Object.keys(def.params?.entries ?? {});
792
+ const issueCount = issues.length;
793
+ const params = paramsFor(def, kind);
794
+ const paramKeys = Object.keys(params?.entries ?? {});
321
795
  const input = {};
322
796
  for (const key of Object.keys(rule)) {
323
797
  if (isReservedRuleKey(key)) continue;
324
798
  if (paramKeys.includes(key)) input[key] = rule[key];
325
799
  else issues.push(issue("unknown_param", [...path, key], `Rule "${rule.rule}" has no param "${key}"`));
326
800
  }
327
- const parsed = v.safeParse(def.params ?? noParams, input);
801
+ const parsed = v.safeParse(params ?? noParams, input);
328
802
  if (!parsed.success) issues.push(...toIssues("invalid_params", path, parsed.issues));
329
803
  if (!parsed.success || issues.length > issueCount) return void 0;
330
804
  return {
@@ -335,7 +809,7 @@ function checkRule(raw, kind, path, registry, issues) {
335
809
  };
336
810
  }
337
811
  function checkRegistry(registry) {
338
- for (const [name, rule] of Object.entries(registry.rules)) for (const key of Object.keys(rule.params?.entries ?? {})) if (isReservedRuleKey(key)) throw new TypeError(`Rule "${name}": params can't use the reserved key "${key}"`);
812
+ for (const [name, rule] of Object.entries(registry.rules)) for (const kind of rule.kinds) for (const key of Object.keys(paramsFor(rule, kind)?.entries ?? {})) if (isReservedRuleKey(key)) throw new TypeError(`Rule "${name}": params can't use the reserved key "${key}"`);
339
813
  for (const [name, type] of Object.entries(registry.fieldTypes)) {
340
814
  const at = `Field type "${name}"`;
341
815
  if (!lookup(registry.kinds, type.kind)) throw new TypeError(`${at}: kind "${type.kind}" is not registered`);
@@ -388,9 +862,16 @@ function createSchemaBuilder(options = {}) {
388
862
  for (const field of fields) entries[field.name] = buildFieldSchema(field, messages);
389
863
  return v.object(entries);
390
864
  }
865
+ /**
866
+ * The registered rules that can be used on a field of `kind`, e.g. for a rule picker.
867
+ */
868
+ function rulesForKind(kind) {
869
+ return Object.entries(registry.rules).filter(([, def]) => def.kinds.includes(kind));
870
+ }
391
871
  return {
392
872
  build,
393
873
  validate,
874
+ rulesForKind,
394
875
  kinds: registry.kinds,
395
876
  fieldTypes: registry.fieldTypes,
396
877
  rules: registry.rules
@@ -398,6 +879,6 @@ function createSchemaBuilder(options = {}) {
398
879
  }
399
880
  // src/field-types/define-field-type.ts
400
881
  const defineFieldType = (def) => def;
401
- export { DefinitionError, RESERVED_FIELD_KEYS, RESERVED_RULE_KEYS, createSchemaBuilder, defaultFieldTypes, defaultKinds, defaultRules, defineFieldType, defineRule, formatPath };
882
+ export { DefinitionError, RESERVED_FIELD_KEYS, RESERVED_RULE_KEYS, comparisonRules, contentRules, createSchemaBuilder, dateRules, defaultFieldTypes, defaultKinds, defaultRules, defineFieldType, defineRule, fileRules, formatPath, formatRules, lengthRules, normalizeRules, numberRules, textUnitRules };
402
883
 
403
884
  //# sourceMappingURL=index.js.map