ascertain 3.0.0 → 3.1.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ascertain",
3
- "version": "3.0.0",
3
+ "version": "3.1.0",
4
4
  "description": "0-Deps, simple, fast, for browser and node js object schema validator",
5
5
  "type": "module",
6
6
  "types": "build/index.d.ts",
@@ -39,29 +39,30 @@
39
39
  "homepage": "https://github.com/3axap4eHko/ascertain#readme",
40
40
  "devDependencies": {
41
41
  "@eslint/js": "^10.0.1",
42
- "@types/node": "^25.3.0",
43
- "@typescript-eslint/eslint-plugin": "^8.56.0",
44
- "@typescript-eslint/parser": "^8.56.0",
45
- "@typescript-eslint/typescript-estree": "^8.56.0",
42
+ "@types/node": "^25.3.3",
43
+ "@typescript-eslint/eslint-plugin": "^8.56.1",
44
+ "@typescript-eslint/parser": "^8.56.1",
45
+ "@typescript-eslint/typescript-estree": "^8.56.1",
46
46
  "@vitest/coverage-v8": "^4.0.18",
47
47
  "@vuepress/bundler-vite": "2.0.0-rc.26",
48
- "@vuepress/theme-default": "2.0.0-rc.121",
48
+ "@vuepress/theme-default": "2.0.0-rc.124",
49
+ "ajv": "^8.18.0",
49
50
  "ascertain": "latest",
50
- "eslint": "^10.0.1",
51
+ "eslint": "^10.0.2",
51
52
  "eslint-config-prettier": "^10.1.8",
52
53
  "eslint-plugin-prettier": "^5.5.5",
53
54
  "handlebars": "^4.7.8",
54
55
  "husky": "^9.1.7",
55
56
  "inop": "^0.9.0",
56
- "overtake": "^1.3.2",
57
+ "overtake": "^1.4.0",
57
58
  "prettier": "^3.8.1",
58
59
  "recast": "^0.23.11",
59
60
  "sass-embedded": "^1.97.3",
60
61
  "ts-node": "^10.9.2",
61
62
  "typescript": "^5.9.3",
62
- "typescript-eslint": "^8.56.0",
63
+ "typescript-eslint": "^8.56.1",
63
64
  "vitest": "^4.0.18",
64
- "vue": "^3.5.28",
65
+ "vue": "^3.5.29",
65
66
  "vuepress": "2.0.0-rc.26",
66
67
  "zod": "^4.3.6"
67
68
  },
package/src/index.ts CHANGED
@@ -18,6 +18,7 @@ const AND = Symbol.for('@@and');
18
18
  const OPTIONAL = Symbol.for('@@optional');
19
19
  const TUPLE = Symbol.for('@@tuple');
20
20
  const DISCRIMINATED = Symbol.for('@@discriminated');
21
+ const CHECK = Symbol.for('@@check');
21
22
 
22
23
  type Mutable<T> = { -readonly [K in keyof T]: T[K] };
23
24
 
@@ -42,8 +43,15 @@ interface DiscriminatedShape<T> {
42
43
  readonly [$op]: typeof DISCRIMINATED;
43
44
  readonly key: string;
44
45
  }
46
+ export interface CheckContext {
47
+ ref(value: unknown): string;
48
+ }
49
+ interface CheckShape {
50
+ readonly [$op]: typeof CHECK;
51
+ readonly compile: (value: string, ctx: CheckContext) => { check: string; message: string };
52
+ }
45
53
 
46
- type Tagged<T> = OrShape<T> | AndShape<T> | OptionalShape<T> | TupleShape<T> | DiscriminatedShape<T>;
54
+ type Tagged<T> = OrShape<T> | AndShape<T> | OptionalShape<T> | TupleShape<T> | DiscriminatedShape<T> | CheckShape;
47
55
 
48
56
  const OrCtor = function <T>(this: OrShape<T>, schemas: Schema<T>[]) {
49
57
  (this as Mutable<OrShape<T>>).schemas = schemas;
@@ -71,6 +79,11 @@ const DiscriminatedCtor = function <T>(this: DiscriminatedShape<T>, schemas: Sch
71
79
  } as unknown as { new <T>(schemas: Schema<T>[], key: string): DiscriminatedShape<T>; prototype: { [$op]: typeof DISCRIMINATED } };
72
80
  DiscriminatedCtor.prototype[$op] = DISCRIMINATED;
73
81
 
82
+ const CheckCtor = function (this: CheckShape, compileFn: CheckShape['compile']) {
83
+ (this as Mutable<CheckShape>).compile = compileFn;
84
+ } as unknown as { new (compileFn: CheckShape['compile']): CheckShape; prototype: { [$op]: typeof CHECK } };
85
+ CheckCtor.prototype[$op] = CHECK;
86
+
74
87
  /**
75
88
  * Represents a schema for validating data.
76
89
  *
@@ -138,6 +151,76 @@ export const discriminated = <T>(schemas: Schema<T>[], key: string): Discriminat
138
151
  return new DiscriminatedCtor(schemas, key);
139
152
  };
140
153
 
154
+ export const check = (
155
+ fnOrOpts: ((v: unknown) => boolean) | { compile: (value: string, ctx: CheckContext) => { check: string; message: string } },
156
+ message?: string,
157
+ ): CheckShape => {
158
+ if (typeof fnOrOpts === 'function') {
159
+ return new CheckCtor((v, ctx) => {
160
+ const fnRef = ctx.ref(fnOrOpts);
161
+ return {
162
+ check: `!${fnRef}(${v})`,
163
+ message: message ? JSON.stringify(message) : `\`check failed for value \${${v}}\``,
164
+ };
165
+ });
166
+ }
167
+ return new CheckCtor(fnOrOpts.compile);
168
+ };
169
+
170
+ export const min = (n: number, message?: string): CheckShape =>
171
+ new CheckCtor((v) => ({
172
+ check: `${v} < ${n}`,
173
+ message: message ? JSON.stringify(message) : `\`must be >= ${n}, got \${${v}}\``,
174
+ }));
175
+
176
+ export const max = (n: number, message?: string): CheckShape =>
177
+ new CheckCtor((v) => ({
178
+ check: `${v} > ${n}`,
179
+ message: message ? JSON.stringify(message) : `\`must be <= ${n}, got \${${v}}\``,
180
+ }));
181
+
182
+ export const integer = (message?: string): CheckShape =>
183
+ new CheckCtor((v) => ({
184
+ check: `!Number.isInteger(${v})`,
185
+ message: message ? JSON.stringify(message) : `\`must be an integer, got \${${v}}\``,
186
+ }));
187
+
188
+ export const minLength = (n: number, message?: string): CheckShape =>
189
+ new CheckCtor((v) => ({
190
+ check: `${v}.length < ${n}`,
191
+ message: message ? JSON.stringify(message) : `\`length must be >= ${n}, got \${${v}.length}\``,
192
+ }));
193
+
194
+ export const maxLength = (n: number, message?: string): CheckShape =>
195
+ new CheckCtor((v) => ({
196
+ check: `${v}.length > ${n}`,
197
+ message: message ? JSON.stringify(message) : `\`length must be <= ${n}, got \${${v}.length}\``,
198
+ }));
199
+
200
+ export const gt = (n: number, message?: string): CheckShape =>
201
+ new CheckCtor((v) => ({
202
+ check: `${v} <= ${n}`,
203
+ message: message ? JSON.stringify(message) : `\`must be > ${n}, got \${${v}}\``,
204
+ }));
205
+
206
+ export const lt = (n: number, message?: string): CheckShape =>
207
+ new CheckCtor((v) => ({
208
+ check: `${v} >= ${n}`,
209
+ message: message ? JSON.stringify(message) : `\`must be < ${n}, got \${${v}}\``,
210
+ }));
211
+
212
+ export const multipleOf = (n: number, message?: string): CheckShape =>
213
+ new CheckCtor((v) => ({
214
+ check: `${v} % ${n} !== 0`,
215
+ message: message ? JSON.stringify(message) : `\`must be a multiple of ${n}, got \${${v}}\``,
216
+ }));
217
+
218
+ export const uniqueItems = (message?: string): CheckShape =>
219
+ new CheckCtor((v) => ({
220
+ check: `new Set(${v}).size !== ${v}.length`,
221
+ message: message ? JSON.stringify(message) : `\`must have unique items\``,
222
+ }));
223
+
141
224
  /**
142
225
  * Decodes a base64-encoded string to UTF-8.
143
226
  *
@@ -295,6 +378,87 @@ export const as = {
295
378
  },
296
379
  };
297
380
 
381
+ const DATETIME_RE = /^\d{4}-[01]\d-[0-3]\d[t\s](?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)(?:\.\d+)?(?:z|[+-]\d{2}(?::?\d{2})?)$/i;
382
+ const TIME_FMT_RE = /^(?:(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d|23:59:60)(?:\.\d+)?(?:z|[+-]\d{2}(?::?\d{2})?)$/i;
383
+ const DURATION_RE = /^P(?!$)(\d+Y)?(\d+M)?(\d+W)?(\d+D)?(T(?=\d)(\d+H)?(\d+M)?(\d+S)?)?$/;
384
+ const EMAIL_RE = /^[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i;
385
+ const IDN_EMAIL_RE =
386
+ /^[a-z0-9!#$%&'*+/=?^_`{|}~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF-]+)*@(?:[a-z0-9\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF](?:[a-z0-9\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF-]*[a-z0-9\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])?\.)+[a-z0-9\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF](?:[a-z0-9\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF-]*[a-z0-9\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])?$/i;
387
+ const HOSTNAME_RE = /^(?=.{1,253}\.?$)[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)*\.?$/i;
388
+ const IDN_HOSTNAME_RE =
389
+ /^(?=.{1,253}\.?$)[a-z0-9\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF](?:[a-z0-9\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF-]{0,61}[a-z0-9\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])?(?:\.[a-z0-9\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF](?:[a-z0-9\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF-]{0,61}[a-z0-9\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])?)*\.?$/i;
390
+ const IPV4_RE = /^(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)$/;
391
+ const IPV6_RE =
392
+ /^((([0-9a-f]{1,4}:){7}([0-9a-f]{1,4}|:))|(([0-9a-f]{1,4}:){6}(:[0-9a-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9a-f]{1,4}:){5}(((:[0-9a-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9a-f]{1,4}:){4}(((:[0-9a-f]{1,4}){1,3})|((:[0-9a-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){3}(((:[0-9a-f]{1,4}){1,4})|((:[0-9a-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){2}(((:[0-9a-f]{1,4}){1,5})|((:[0-9a-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){1}(((:[0-9a-f]{1,4}){1,6})|((:[0-9a-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9a-f]{1,4}){1,7})|((:[0-9a-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?$/i;
393
+ const URI_RE =
394
+ /^(?:[a-z][a-z0-9+\-.]*:)(?:\/?\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:]|%[0-9a-f]{2})*@)?(?:\[(?:(?:(?:(?:[0-9a-f]{1,4}:){6}|::(?:[0-9a-f]{1,4}:){5}|(?:[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){4}|(?:(?:[0-9a-f]{1,4}:){0,1}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){3}|(?:(?:[0-9a-f]{1,4}:){0,2}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){2}|(?:(?:[0-9a-f]{1,4}:){0,3}[0-9a-f]{1,4})?::[0-9a-f]{1,4}:|(?:(?:[0-9a-f]{1,4}:){0,4}[0-9a-f]{1,4})?::)(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?))|(?:(?:[0-9a-f]{1,4}:){0,5}[0-9a-f]{1,4})?::[0-9a-f]{1,4}|(?:(?:[0-9a-f]{1,4}:){0,6}[0-9a-f]{1,4})?::)|[Vv][0-9a-f]+\.[a-z0-9\-._~!$&'()*+,;=:]+)\]|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)|(?:[a-z0-9\-._~!$&'()*+,;=]|%[0-9a-f]{2})*)(?::\d*)?(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*|\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*)?|(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*)(?:\?(?:[a-z0-9\-._~!$&'()*+,;=:@/?]|%[0-9a-f]{2})*)?(?:#(?:[a-z0-9\-._~!$&'()*+,;=:@/?]|%[0-9a-f]{2})*)?$/i;
395
+ const isUriRef = (s: string): boolean => URI_RE.test(s) || /^[a-z0-9\-._~:/?#\[\]@!$&'()*+,;=%]*$/i.test(s);
396
+ const IRI_RE =
397
+ /^(?:[a-z][a-z0-9+\-.]*:)(?:\/?\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|%[0-9a-f]{2})*@)?(?:\[(?:(?:(?:(?:[0-9a-f]{1,4}:){6}|::(?:[0-9a-f]{1,4}:){5}|(?:[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){4}|(?:(?:[0-9a-f]{1,4}:){0,1}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){3}|(?:(?:[0-9a-f]{1,4}:){0,2}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){2}|(?:(?:[0-9a-f]{1,4}:){0,3}[0-9a-f]{1,4})?::[0-9a-f]{1,4}:|(?:(?:[0-9a-f]{1,4}:){0,4}[0-9a-f]{1,4})?::)(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?))|(?:(?:[0-9a-f]{1,4}:){0,5}[0-9a-f]{1,4})?::[0-9a-f]{1,4}|(?:(?:[0-9a-f]{1,4}:){0,6}[0-9a-f]{1,4})?::)|[Vv][0-9a-f]+\.[a-z0-9\-._~!$&'()*+,;=:]+)\]|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)|(?:[a-z0-9\-._~!$&'()*+,;=\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|%[0-9a-f]{2})*)(?::\d*)?(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|%[0-9a-f]{2})*)*|\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:@\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|%[0-9a-f]{2})*)*)?|(?:[a-z0-9\-._~!$&'()*+,;=:@\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|%[0-9a-f]{2})*)*)(?:\?(?:[a-z0-9\-._~!$&'()*+,;=:@/?\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|%[0-9a-f]{2})*)?(?:#(?:[a-z0-9\-._~!$&'()*+,;=:@/?\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|%[0-9a-f]{2})*)?$/i;
398
+ const isIriRef = (s: string): boolean => IRI_RE.test(s) || /^[a-z0-9\-._~:/?#\[\]@!$&'()*+,;=\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF%]*$/i.test(s);
399
+ const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
400
+ const URI_TEMPLATE_RE =
401
+ /^(?:(?:[^\x00-\x20"'<>%\\^`{|}]|%[0-9a-f]{2})|\{[+#./;?&=,!@|]?(?:[a-z0-9_]|%[0-9a-f]{2})+(?::[1-9][0-9]{0,3}|\*)?(?:,(?:[a-z0-9_]|%[0-9a-f]{2})+(?::[1-9][0-9]{0,3}|\*)?)*\})*$/i;
402
+ const JSON_POINTER_RE = /^(?:\/(?:[^~/]|~0|~1)*)*$/;
403
+ const REL_JSON_POINTER_RE = /^(?:0|[1-9][0-9]*)(?:#|(?:\/(?:[^~/]|~0|~1)*)*)$/;
404
+
405
+ const DAYS = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
406
+ const isValidDate = (s: string): boolean => {
407
+ const m = /^\d{4}-(\d{2})-(\d{2})$/.exec(s);
408
+ if (!m) return false;
409
+ const month = +m[1],
410
+ day = +m[2];
411
+ if (month < 1 || month > 12 || day < 1) return false;
412
+ if (month === 2) {
413
+ const y = +s.slice(0, 4);
414
+ return day <= (y % 4 === 0 && (y % 100 !== 0 || y % 400 === 0) ? 29 : 28);
415
+ }
416
+ return day <= DAYS[month];
417
+ };
418
+
419
+ const isValidRegex = (s: string): boolean => {
420
+ try {
421
+ new RegExp(s);
422
+ return true;
423
+ } catch {
424
+ return false;
425
+ }
426
+ };
427
+
428
+ const regexFormat = (re: RegExp, name: string, message?: string): CheckShape =>
429
+ new CheckCtor((v, ctx) => ({
430
+ check: `!${ctx.ref(re)}.test(${v})`,
431
+ message: message ? JSON.stringify(message) : `\`must be a valid ${name}, got \${${v}}\``,
432
+ }));
433
+
434
+ const fnFormat = (fn: (s: string) => boolean, name: string, message?: string): CheckShape =>
435
+ new CheckCtor((v, ctx) => ({
436
+ check: `!${ctx.ref(fn)}(${v})`,
437
+ message: message ? JSON.stringify(message) : `\`must be a valid ${name}, got \${${v}}\``,
438
+ }));
439
+
440
+ export const format = {
441
+ dateTime: (message?: string): CheckShape => regexFormat(DATETIME_RE, 'date-time', message),
442
+ date: (message?: string): CheckShape => fnFormat(isValidDate, 'date', message),
443
+ time: (message?: string): CheckShape => regexFormat(TIME_FMT_RE, 'time', message),
444
+ duration: (message?: string): CheckShape => regexFormat(DURATION_RE, 'duration', message),
445
+ email: (message?: string): CheckShape => regexFormat(EMAIL_RE, 'email', message),
446
+ idnEmail: (message?: string): CheckShape => regexFormat(IDN_EMAIL_RE, 'idn-email', message),
447
+ hostname: (message?: string): CheckShape => regexFormat(HOSTNAME_RE, 'hostname', message),
448
+ idnHostname: (message?: string): CheckShape => regexFormat(IDN_HOSTNAME_RE, 'idn-hostname', message),
449
+ ipv4: (message?: string): CheckShape => regexFormat(IPV4_RE, 'ipv4', message),
450
+ ipv6: (message?: string): CheckShape => regexFormat(IPV6_RE, 'ipv6', message),
451
+ uri: (message?: string): CheckShape => regexFormat(URI_RE, 'uri', message),
452
+ uriReference: (message?: string): CheckShape => fnFormat(isUriRef, 'uri-reference', message),
453
+ iri: (message?: string): CheckShape => regexFormat(IRI_RE, 'iri', message),
454
+ iriReference: (message?: string): CheckShape => fnFormat(isIriRef, 'iri-reference', message),
455
+ uuid: (message?: string): CheckShape => regexFormat(UUID_RE, 'uuid', message),
456
+ uriTemplate: (message?: string): CheckShape => regexFormat(URI_TEMPLATE_RE, 'uri-template', message),
457
+ jsonPointer: (message?: string): CheckShape => regexFormat(JSON_POINTER_RE, 'json-pointer', message),
458
+ relativeJsonPointer: (message?: string): CheckShape => regexFormat(REL_JSON_POINTER_RE, 'relative-json-pointer', message),
459
+ regex: (message?: string): CheckShape => fnFormat(isValidRegex, 'regex', message),
460
+ };
461
+
298
462
  /**
299
463
  * A class representing the context for schema validation.
300
464
  *
@@ -419,6 +583,14 @@ const codeGen = <T>(schema: Schema<T>, context: Context, valuePath: string, mode
419
583
  `else { ${schema.schemas.map((s, idx) => codeGen(s, context, `${valueAlias}[${idx}]`, childMode(mode, idx))).join('\n')} }`,
420
584
  ].join('\n');
421
585
  }
586
+ } else if (tag === CHECK) {
587
+ const valueAlias = context.unique('v');
588
+ const ref = (v: unknown) => `ctx.registry[${context.register(v)}]`;
589
+ const { check: cond, message } = (schema as CheckShape).compile(valueAlias, { ref });
590
+ if (mode.fast) {
591
+ return `const ${valueAlias} = ${valuePath};\nif (${cond}) { ${fail} }`;
592
+ }
593
+ return `const ${valueAlias} = ${valuePath};\nif (${cond}) { ${emit!(message)} }`;
422
594
  } else {
423
595
  const { key, schemas } = schema as DiscriminatedShape<T>;
424
596
  const valueAlias = context.unique('v');