z-schema 7.0.9 → 7.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/README.md CHANGED
@@ -142,6 +142,7 @@ ZSchema.setSchemaReader(function (uri) {
142
142
  - [Turn on z-schema strict mode](#strictmode)
143
143
  - [Set validator to collect as many errors as possible](#breakonfirsterror)
144
144
  - [Report paths in errors as arrays so they can be processed easier](#reportpathasarray)
145
+ - [Unicode Property Escapes Support](#unicode-property-escapes-support)
145
146
 
146
147
  ### Validate against subschema
147
148
 
@@ -254,6 +255,21 @@ validator.validate(data, schema);
254
255
  // data.hello === "world"
255
256
  ```
256
257
 
258
+ ### Unicode Property Escapes Support
259
+
260
+ Fully supports [Unicode property escapes](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_expressions/Unicode_property_escapes) in JSON Schema `pattern` values (e.g., `/\p{L}/u`). This allows you to write patterns that match Unicode character properties, such as letters, numbers, or scripts, provided your JavaScript environment supports them (Node.js ≥ 10, all modern browsers).
261
+
262
+ **Example:**
263
+
264
+ ```json
265
+ {
266
+ "type": "string",
267
+ "pattern": "^\\p{L}+$" // matches only Unicode letters
268
+ }
269
+ ```
270
+
271
+ z-schema will automatically use the `u` (Unicode) flag for all patterns containing Unicode property escapes, both in Node.js and browser environments. If your environment does not support Unicode property escapes, such patterns will be reported as invalid.
272
+
257
273
  ## Options
258
274
 
259
275
  ### asyncTimeout
package/cjs/index.js CHANGED
@@ -8570,6 +8570,46 @@ const deepClone = (src) => {
8570
8570
  return cloneDeepInner(src);
8571
8571
  };
8572
8572
 
8573
+ // Shared regex compilation helper for JSON Schema patterns
8574
+ // Returns { ok: true, value: RegExp } or { ok: false, error: { pattern, message } }
8575
+ function compileSchemaRegex(pattern) {
8576
+ // Detect Unicode property escapes
8577
+ const unicodeEscape = /\\[pP]{/;
8578
+ const needsUnicode = unicodeEscape.test(pattern);
8579
+ // Try compiling without 'u' flag if not needed
8580
+ if (needsUnicode) {
8581
+ // Try compiling with 'u' flag only
8582
+ try {
8583
+ const re = new RegExp(pattern, 'u');
8584
+ return { ok: true, value: re };
8585
+ }
8586
+ catch (e) {
8587
+ return {
8588
+ ok: false,
8589
+ error: {
8590
+ pattern,
8591
+ message: e && e.message ? e.message : 'Invalid regular expression',
8592
+ },
8593
+ };
8594
+ }
8595
+ }
8596
+ else {
8597
+ try {
8598
+ const re = new RegExp(pattern);
8599
+ return { ok: true, value: re };
8600
+ }
8601
+ catch (e) {
8602
+ return {
8603
+ ok: false,
8604
+ error: {
8605
+ pattern,
8606
+ message: e && e.message ? e.message : 'Invalid regular expression',
8607
+ },
8608
+ };
8609
+ }
8610
+ }
8611
+ }
8612
+
8573
8613
  const shouldSkipValidate = function (options, errors) {
8574
8614
  return (options &&
8575
8615
  Array.isArray(options.includeErrors) &&
@@ -8675,7 +8715,13 @@ const JsonValidators = {
8675
8715
  if (typeof json !== 'string') {
8676
8716
  return;
8677
8717
  }
8678
- if (RegExp(schema.pattern).test(json) === false) {
8718
+ const result = compileSchemaRegex(schema.pattern);
8719
+ if (!result.ok) {
8720
+ // Should not happen: schema should have been validated already
8721
+ report.addError('PATTERN', [schema.pattern, json, result.error.message], undefined, schema);
8722
+ return;
8723
+ }
8724
+ if (!result.value.test(json)) {
8679
8725
  report.addError('PATTERN', [schema.pattern, json], undefined, schema);
8680
8726
  }
8681
8727
  },
@@ -11501,11 +11547,11 @@ const SchemaValidators = {
11501
11547
  report.addError('KEYWORD_TYPE_EXPECTED', ['pattern', 'string']);
11502
11548
  }
11503
11549
  else {
11504
- try {
11505
- RegExp(schema.pattern);
11506
- }
11507
- catch (_e) {
11508
- report.addError('KEYWORD_PATTERN', ['pattern', schema.pattern]);
11550
+ // Use shared regex compilation helper
11551
+ // Import at top of file
11552
+ const result = compileSchemaRegex(schema.pattern);
11553
+ if (!result.ok) {
11554
+ report.addError('KEYWORD_PATTERN', ['pattern', schema.pattern, result.error.message]);
11509
11555
  }
11510
11556
  }
11511
11557
  },
@@ -11661,15 +11707,14 @@ const SchemaValidators = {
11661
11707
  report.addError('KEYWORD_TYPE_EXPECTED', ['patternProperties', 'object']);
11662
11708
  return;
11663
11709
  }
11710
+ // Use shared regex compilation helper
11664
11711
  const keys = Object.keys(schema.patternProperties);
11665
11712
  let idx = keys.length;
11666
11713
  while (idx--) {
11667
11714
  const key = keys[idx], val = schema.patternProperties[key];
11668
- try {
11669
- RegExp(key);
11670
- }
11671
- catch (_e) {
11672
- report.addError('KEYWORD_PATTERN', ['patternProperties', key]);
11715
+ const result = compileSchemaRegex(key);
11716
+ if (!result.ok) {
11717
+ report.addError('KEYWORD_PATTERN', ['patternProperties', key, result.error.message]);
11673
11718
  }
11674
11719
  report.path.push('patternProperties');
11675
11720
  report.path.push(key);
@@ -4,6 +4,7 @@ import { ucs2decode } from './utils/unicode.js';
4
4
  import { difference, isUniqueArray } from './utils/array.js';
5
5
  import { areEqual } from './utils/json.js';
6
6
  import { shallowClone } from './utils/clone.js';
7
+ import { compileSchemaRegex } from './utils/schema-regex.js';
7
8
  import { getFormatValidators } from './format-validators.js';
8
9
  const shouldSkipValidate = function (options, errors) {
9
10
  return (options &&
@@ -110,7 +111,13 @@ export const JsonValidators = {
110
111
  if (typeof json !== 'string') {
111
112
  return;
112
113
  }
113
- if (RegExp(schema.pattern).test(json) === false) {
114
+ const result = compileSchemaRegex(schema.pattern);
115
+ if (!result.ok) {
116
+ // Should not happen: schema should have been validated already
117
+ report.addError('PATTERN', [schema.pattern, json, result.error.message], undefined, schema);
118
+ return;
119
+ }
120
+ if (!result.value.test(json)) {
114
121
  report.addError('PATTERN', [schema.pattern, json], undefined, schema);
115
122
  }
116
123
  },
@@ -4,6 +4,7 @@ import { isObject, whatIs } from './utils/what-is.js';
4
4
  import { shallowClone } from './utils/clone.js';
5
5
  import { isUniqueArray } from './utils/array.js';
6
6
  import { isFormatSupported } from './format-validators.js';
7
+ import { compileSchemaRegex } from './utils/schema-regex.js';
7
8
  const SchemaValidators = {
8
9
  $ref: function (report, schema) {
9
10
  // http://tools.ietf.org/html/draft-ietf-appsawg-json-pointer-07
@@ -81,11 +82,11 @@ const SchemaValidators = {
81
82
  report.addError('KEYWORD_TYPE_EXPECTED', ['pattern', 'string']);
82
83
  }
83
84
  else {
84
- try {
85
- RegExp(schema.pattern);
86
- }
87
- catch (_e) {
88
- report.addError('KEYWORD_PATTERN', ['pattern', schema.pattern]);
85
+ // Use shared regex compilation helper
86
+ // Import at top of file
87
+ const result = compileSchemaRegex(schema.pattern);
88
+ if (!result.ok) {
89
+ report.addError('KEYWORD_PATTERN', ['pattern', schema.pattern, result.error.message]);
89
90
  }
90
91
  }
91
92
  },
@@ -241,15 +242,14 @@ const SchemaValidators = {
241
242
  report.addError('KEYWORD_TYPE_EXPECTED', ['patternProperties', 'object']);
242
243
  return;
243
244
  }
245
+ // Use shared regex compilation helper
244
246
  const keys = Object.keys(schema.patternProperties);
245
247
  let idx = keys.length;
246
248
  while (idx--) {
247
249
  const key = keys[idx], val = schema.patternProperties[key];
248
- try {
249
- RegExp(key);
250
- }
251
- catch (_e) {
252
- report.addError('KEYWORD_PATTERN', ['patternProperties', key]);
250
+ const result = compileSchemaRegex(key);
251
+ if (!result.ok) {
252
+ report.addError('KEYWORD_PATTERN', ['patternProperties', key, result.error.message]);
253
253
  }
254
254
  report.path.push('patternProperties');
255
255
  report.path.push(key);
@@ -0,0 +1,10 @@
1
+ export declare function compileSchemaRegex(pattern: string): {
2
+ ok: true;
3
+ value: RegExp;
4
+ } | {
5
+ ok: false;
6
+ error: {
7
+ pattern: string;
8
+ message: string;
9
+ };
10
+ };
@@ -0,0 +1,39 @@
1
+ // Shared regex compilation helper for JSON Schema patterns
2
+ // Returns { ok: true, value: RegExp } or { ok: false, error: { pattern, message } }
3
+ export function compileSchemaRegex(pattern) {
4
+ // Detect Unicode property escapes
5
+ const unicodeEscape = /\\[pP]{/;
6
+ const needsUnicode = unicodeEscape.test(pattern);
7
+ // Try compiling without 'u' flag if not needed
8
+ if (needsUnicode) {
9
+ // Try compiling with 'u' flag only
10
+ try {
11
+ const re = new RegExp(pattern, 'u');
12
+ return { ok: true, value: re };
13
+ }
14
+ catch (e) {
15
+ return {
16
+ ok: false,
17
+ error: {
18
+ pattern,
19
+ message: e && e.message ? e.message : 'Invalid regular expression',
20
+ },
21
+ };
22
+ }
23
+ }
24
+ else {
25
+ try {
26
+ const re = new RegExp(pattern);
27
+ return { ok: true, value: re };
28
+ }
29
+ catch (e) {
30
+ return {
31
+ ok: false,
32
+ error: {
33
+ pattern,
34
+ message: e && e.message ? e.message : 'Invalid regular expression',
35
+ },
36
+ };
37
+ }
38
+ }
39
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "z-schema",
3
- "version": "7.0.9",
3
+ "version": "7.1.0",
4
4
  "engines": {
5
5
  "node": ">=22.0.0"
6
6
  },
@@ -67,7 +67,7 @@
67
67
  "test": "vitest run",
68
68
  "test:quick": "npm run build && npm run test:node",
69
69
  "test:browsers": "vitest run --project browsers --silent=true",
70
- "test:node": "vitest run --project node --silent=true",
70
+ "test:node": "vitest run --project node --silent=false",
71
71
  "test:sample": "npx vitest run --silent=false --hideSkippedTests --project node -t \"invalid definition\"",
72
72
  "release:commit": "node -e \"const v=process.argv[1]; if(!v) throw new Error('Usage: npm run release:empty-commit -- <version>'); require('child_process').execSync(`git commit --allow-empty -m \\\"chore: release ${v}\\\" -m \\\"Release-As: ${v}\\\"`, {stdio:'inherit'});\" --"
73
73
  },
@@ -5,6 +5,7 @@ import { difference, isUniqueArray } from './utils/array.js';
5
5
  import { areEqual } from './utils/json.js';
6
6
  import { shallowClone } from './utils/clone.js';
7
7
  import { JsonSchema, JsonSchemaInternal } from './json-schema.js';
8
+ import { compileSchemaRegex } from './utils/schema-regex.js';
8
9
  import type { ValidateOptions, ZSchema } from './z-schema.js';
9
10
  import { getFormatValidators } from './format-validators.js';
10
11
 
@@ -117,7 +118,13 @@ export const JsonValidators: Record<keyof JsonSchema, JsonValidatorFn> = {
117
118
  if (typeof json !== 'string') {
118
119
  return;
119
120
  }
120
- if (RegExp(schema.pattern!).test(json) === false) {
121
+ const result = compileSchemaRegex(schema.pattern!);
122
+ if (!result.ok) {
123
+ // Should not happen: schema should have been validated already
124
+ report.addError('PATTERN', [schema.pattern!, json, result.error.message], undefined, schema);
125
+ return;
126
+ }
127
+ if (!result.value.test(json)) {
121
128
  report.addError('PATTERN', [schema.pattern!, json], undefined, schema);
122
129
  }
123
130
  },
@@ -6,6 +6,7 @@ import { shallowClone } from './utils/clone.js';
6
6
  import { JsonSchema, JsonSchemaInternal } from './json-schema.js';
7
7
  import { isUniqueArray } from './utils/array.js';
8
8
  import { isFormatSupported } from './format-validators.js';
9
+ import { compileSchemaRegex } from './utils/schema-regex.js';
9
10
 
10
11
  const SchemaValidators = {
11
12
  $ref: function (this: SchemaValidator, report: Report, schema: JsonSchemaInternal) {
@@ -78,10 +79,11 @@ const SchemaValidators = {
78
79
  if (typeof schema.pattern !== 'string') {
79
80
  report.addError('KEYWORD_TYPE_EXPECTED', ['pattern', 'string']);
80
81
  } else {
81
- try {
82
- RegExp(schema.pattern);
83
- } catch (_e) {
84
- report.addError('KEYWORD_PATTERN', ['pattern', schema.pattern]);
82
+ // Use shared regex compilation helper
83
+ // Import at top of file
84
+ const result = compileSchemaRegex(schema.pattern);
85
+ if (!result.ok) {
86
+ report.addError('KEYWORD_PATTERN', ['pattern', schema.pattern, result.error.message]);
85
87
  }
86
88
  }
87
89
  },
@@ -231,15 +233,15 @@ const SchemaValidators = {
231
233
  return;
232
234
  }
233
235
 
236
+ // Use shared regex compilation helper
234
237
  const keys = Object.keys(schema.patternProperties!);
235
238
  let idx = keys.length;
236
239
  while (idx--) {
237
240
  const key = keys[idx],
238
241
  val = schema.patternProperties![key];
239
- try {
240
- RegExp(key);
241
- } catch (_e) {
242
- report.addError('KEYWORD_PATTERN', ['patternProperties', key]);
242
+ const result = compileSchemaRegex(key);
243
+ if (!result.ok) {
244
+ report.addError('KEYWORD_PATTERN', ['patternProperties', key, result.error.message]);
243
245
  }
244
246
  report.path.push('patternProperties');
245
247
  report.path.push(key);
@@ -0,0 +1,39 @@
1
+ // Shared regex compilation helper for JSON Schema patterns
2
+ // Returns { ok: true, value: RegExp } or { ok: false, error: { pattern, message } }
3
+
4
+ export function compileSchemaRegex(
5
+ pattern: string
6
+ ): { ok: true; value: RegExp } | { ok: false; error: { pattern: string; message: string } } {
7
+ // Detect Unicode property escapes
8
+ const unicodeEscape = /\\[pP]{/;
9
+ const needsUnicode = unicodeEscape.test(pattern);
10
+ // Try compiling without 'u' flag if not needed
11
+ if (needsUnicode) {
12
+ // Try compiling with 'u' flag only
13
+ try {
14
+ const re = new RegExp(pattern, 'u');
15
+ return { ok: true, value: re };
16
+ } catch (e: any) {
17
+ return {
18
+ ok: false,
19
+ error: {
20
+ pattern,
21
+ message: e && e.message ? e.message : 'Invalid regular expression',
22
+ },
23
+ };
24
+ }
25
+ } else {
26
+ try {
27
+ const re = new RegExp(pattern);
28
+ return { ok: true, value: re };
29
+ } catch (e: any) {
30
+ return {
31
+ ok: false,
32
+ error: {
33
+ pattern,
34
+ message: e && e.message ? e.message : 'Invalid regular expression',
35
+ },
36
+ };
37
+ }
38
+ }
39
+ }
package/umd/ZSchema.js CHANGED
@@ -8574,6 +8574,46 @@
8574
8574
  return cloneDeepInner(src);
8575
8575
  };
8576
8576
 
8577
+ // Shared regex compilation helper for JSON Schema patterns
8578
+ // Returns { ok: true, value: RegExp } or { ok: false, error: { pattern, message } }
8579
+ function compileSchemaRegex(pattern) {
8580
+ // Detect Unicode property escapes
8581
+ const unicodeEscape = /\\[pP]{/;
8582
+ const needsUnicode = unicodeEscape.test(pattern);
8583
+ // Try compiling without 'u' flag if not needed
8584
+ if (needsUnicode) {
8585
+ // Try compiling with 'u' flag only
8586
+ try {
8587
+ const re = new RegExp(pattern, 'u');
8588
+ return { ok: true, value: re };
8589
+ }
8590
+ catch (e) {
8591
+ return {
8592
+ ok: false,
8593
+ error: {
8594
+ pattern,
8595
+ message: e && e.message ? e.message : 'Invalid regular expression',
8596
+ },
8597
+ };
8598
+ }
8599
+ }
8600
+ else {
8601
+ try {
8602
+ const re = new RegExp(pattern);
8603
+ return { ok: true, value: re };
8604
+ }
8605
+ catch (e) {
8606
+ return {
8607
+ ok: false,
8608
+ error: {
8609
+ pattern,
8610
+ message: e && e.message ? e.message : 'Invalid regular expression',
8611
+ },
8612
+ };
8613
+ }
8614
+ }
8615
+ }
8616
+
8577
8617
  const shouldSkipValidate = function (options, errors) {
8578
8618
  return (options &&
8579
8619
  Array.isArray(options.includeErrors) &&
@@ -8679,7 +8719,13 @@
8679
8719
  if (typeof json !== 'string') {
8680
8720
  return;
8681
8721
  }
8682
- if (RegExp(schema.pattern).test(json) === false) {
8722
+ const result = compileSchemaRegex(schema.pattern);
8723
+ if (!result.ok) {
8724
+ // Should not happen: schema should have been validated already
8725
+ report.addError('PATTERN', [schema.pattern, json, result.error.message], undefined, schema);
8726
+ return;
8727
+ }
8728
+ if (!result.value.test(json)) {
8683
8729
  report.addError('PATTERN', [schema.pattern, json], undefined, schema);
8684
8730
  }
8685
8731
  },
@@ -11505,11 +11551,11 @@
11505
11551
  report.addError('KEYWORD_TYPE_EXPECTED', ['pattern', 'string']);
11506
11552
  }
11507
11553
  else {
11508
- try {
11509
- RegExp(schema.pattern);
11510
- }
11511
- catch (_e) {
11512
- report.addError('KEYWORD_PATTERN', ['pattern', schema.pattern]);
11554
+ // Use shared regex compilation helper
11555
+ // Import at top of file
11556
+ const result = compileSchemaRegex(schema.pattern);
11557
+ if (!result.ok) {
11558
+ report.addError('KEYWORD_PATTERN', ['pattern', schema.pattern, result.error.message]);
11513
11559
  }
11514
11560
  }
11515
11561
  },
@@ -11665,15 +11711,14 @@
11665
11711
  report.addError('KEYWORD_TYPE_EXPECTED', ['patternProperties', 'object']);
11666
11712
  return;
11667
11713
  }
11714
+ // Use shared regex compilation helper
11668
11715
  const keys = Object.keys(schema.patternProperties);
11669
11716
  let idx = keys.length;
11670
11717
  while (idx--) {
11671
11718
  const key = keys[idx], val = schema.patternProperties[key];
11672
- try {
11673
- RegExp(key);
11674
- }
11675
- catch (_e) {
11676
- report.addError('KEYWORD_PATTERN', ['patternProperties', key]);
11719
+ const result = compileSchemaRegex(key);
11720
+ if (!result.ok) {
11721
+ report.addError('KEYWORD_PATTERN', ['patternProperties', key, result.error.message]);
11677
11722
  }
11678
11723
  report.path.push('patternProperties');
11679
11724
  report.path.push(key);