z-schema 9.0.1 → 10.0.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.
Files changed (59) hide show
  1. package/README.md +123 -191
  2. package/cjs/index.d.ts +33 -9
  3. package/cjs/index.js +4799 -3984
  4. package/dist/errors.js +5 -0
  5. package/dist/format-validators.js +65 -0
  6. package/dist/json-schema-versions.js +5 -0
  7. package/dist/json-schema.js +11 -4
  8. package/dist/json-validation.js +151 -10
  9. package/dist/report.js +2 -3
  10. package/dist/schema-cache.js +23 -2
  11. package/dist/schema-compiler.js +25 -10
  12. package/dist/schema-validator.js +66 -45
  13. package/dist/schemas/draft-06-hyper-schema.json +132 -0
  14. package/dist/schemas/draft-06-links.json +43 -0
  15. package/dist/schemas/draft-06-schema.json +155 -0
  16. package/dist/types/errors.d.ts +4 -0
  17. package/dist/types/index.d.ts +2 -1
  18. package/dist/types/json-schema-versions.d.ts +23 -0
  19. package/dist/types/json-schema.d.ts +5 -9
  20. package/dist/types/json-validation.d.ts +3 -3
  21. package/dist/types/report.d.ts +3 -3
  22. package/dist/types/schema-cache.d.ts +1 -1
  23. package/dist/types/schema-compiler.d.ts +1 -1
  24. package/dist/types/schema-validator.d.ts +1 -1
  25. package/dist/types/utils/what-is.d.ts +1 -0
  26. package/dist/types/z-schema-base.d.ts +1 -1
  27. package/dist/types/z-schema-options.d.ts +1 -1
  28. package/dist/types/z-schema-reader.d.ts +1 -1
  29. package/dist/types/z-schema-versions.d.ts +1 -0
  30. package/dist/types/z-schema.d.ts +10 -1
  31. package/dist/utils/schema-regex.js +4 -3
  32. package/dist/utils/what-is.js +4 -1
  33. package/dist/z-schema-base.js +4 -5
  34. package/dist/z-schema-options.js +3 -1
  35. package/dist/z-schema-versions.js +27 -0
  36. package/dist/z-schema.js +21 -7
  37. package/package.json +2 -2
  38. package/src/errors.ts +6 -0
  39. package/src/format-validators.ts +65 -0
  40. package/src/index.ts +2 -1
  41. package/src/json-schema-versions.ts +34 -0
  42. package/src/json-schema.ts +22 -16
  43. package/src/json-validation.ts +183 -13
  44. package/src/report.ts +5 -6
  45. package/src/schema-cache.ts +25 -3
  46. package/src/schema-compiler.ts +25 -11
  47. package/src/schema-validator.ts +128 -62
  48. package/src/schemas/draft-06-hyper-schema.json +133 -0
  49. package/src/schemas/draft-06-links.json +43 -0
  50. package/src/schemas/draft-06-schema.json +155 -0
  51. package/src/utils/schema-regex.ts +5 -3
  52. package/src/utils/what-is.ts +5 -1
  53. package/src/z-schema-base.ts +5 -6
  54. package/src/z-schema-options.ts +3 -2
  55. package/src/z-schema-reader.ts +1 -1
  56. package/src/z-schema-versions.ts +38 -0
  57. package/src/z-schema.ts +27 -11
  58. package/umd/ZSchema.js +5100 -4285
  59. package/umd/ZSchema.min.js +1 -1
@@ -1,11 +1,10 @@
1
+ import type { JsonSchema, JsonSchemaInternal } from './json-schema-versions.js';
1
2
  import type { Reference } from './schema-compiler.js';
2
3
  import type { ZSchemaOptions } from './z-schema-options.js';
3
- export type JsonSchemaVersion = 'draft-04';
4
- export declare const VERSION_SCHEMA_URL_MAPPING: Record<JsonSchemaVersion, string>;
5
- export interface JsonSchema {
4
+ export interface JsonSchemaCommon {
6
5
  $ref?: string;
7
- id?: string;
8
6
  $schema?: string;
7
+ id?: string;
9
8
  title?: string;
10
9
  description?: string;
11
10
  default?: unknown;
@@ -13,12 +12,9 @@ export interface JsonSchema {
13
12
  type?: string | string[];
14
13
  properties?: Record<string, JsonSchema>;
15
14
  patternProperties?: Record<string, JsonSchema>;
16
- dependencies?: Record<string, string[]>;
17
15
  multipleOf?: number;
18
16
  minimum?: number;
19
- exclusiveMinimum?: boolean;
20
17
  maximum?: number;
21
- exclusiveMaximum?: boolean;
22
18
  minLength?: number;
23
19
  maxLength?: number;
24
20
  pattern?: string;
@@ -31,6 +27,7 @@ export interface JsonSchema {
31
27
  maxProperties?: number;
32
28
  required?: string[];
33
29
  additionalProperties?: boolean | JsonSchema;
30
+ dependencies?: Record<string, string[] | JsonSchema>;
34
31
  enum?: Array<unknown>;
35
32
  format?: string;
36
33
  allOf?: JsonSchema[];
@@ -48,6 +45,5 @@ export interface ZSchemaInternalProperties {
48
45
  __$validationOptions?: ZSchemaOptions;
49
46
  __$visited?: boolean;
50
47
  }
51
- export interface JsonSchemaInternal extends JsonSchema, ZSchemaInternalProperties {
52
- }
48
+ export declare const getId: (schema: JsonSchemaInternal) => string | undefined;
53
49
  export declare const findId: (schema: JsonSchemaInternal, id: string) => JsonSchemaInternal | undefined;
@@ -1,7 +1,7 @@
1
- import type { JsonSchema, JsonSchemaInternal } from './json-schema.js';
1
+ import type { JsonSchema, JsonSchemaAll, JsonSchemaInternal } from './json-schema-versions.js';
2
2
  import type { ZSchemaBase } from './z-schema-base.js';
3
3
  import { Report } from './report.js';
4
4
  type JsonValidatorFn = (this: ZSchemaBase, report: Report, schema: JsonSchema, json: unknown) => void;
5
- export declare const JsonValidators: Record<keyof JsonSchema, JsonValidatorFn>;
6
- export declare function validate(this: ZSchemaBase, report: Report, schema: JsonSchemaInternal, json: unknown): boolean;
5
+ export declare const JsonValidators: Record<keyof JsonSchemaAll, JsonValidatorFn>;
6
+ export declare function validate(this: ZSchemaBase, report: Report, schema: boolean | JsonSchemaInternal, json: unknown): boolean;
7
7
  export {};
@@ -1,5 +1,5 @@
1
1
  import type { ErrorCode, ErrorParam } from './errors.js';
2
- import type { JsonSchema, JsonSchemaInternal } from './json-schema.js';
2
+ import type { JsonSchema, JsonSchemaInternal } from './json-schema-versions.js';
3
3
  import type { ValidateCallback, ValidateOptions } from './z-schema-base.js';
4
4
  import type { ZSchemaOptions } from './z-schema-options.js';
5
5
  export interface SchemaErrorDetail {
@@ -77,8 +77,8 @@ export declare class Report {
77
77
  getSchemaPath(): Array<string | number>;
78
78
  getSchemaId(): string | undefined;
79
79
  hasError(errCode: string, errParams: Array<any>): boolean;
80
- addError(errCode: ErrorCode, errParams?: ErrorParam[], subReports?: Report | Report[], schema?: JsonSchema, keyword?: keyof JsonSchema): void;
80
+ addError(errCode: ErrorCode, errParams?: ErrorParam[], subReports?: Report | Report[], schema?: JsonSchema | boolean, keyword?: keyof JsonSchema): void;
81
81
  getJson(): unknown;
82
- addCustomError(errorCode: ErrorCode, errorMessage: string, params?: ErrorParam[], subReports?: Report | Report[], schema?: JsonSchema, keyword?: keyof JsonSchema): void;
82
+ addCustomError(errorCode: ErrorCode, errorMessage: string, params?: ErrorParam[], subReports?: Report | Report[], schema?: JsonSchema | boolean, keyword?: keyof JsonSchema): void;
83
83
  }
84
84
  export {};
@@ -1,4 +1,4 @@
1
- import type { JsonSchema, JsonSchemaInternal } from './json-schema.js';
1
+ import type { JsonSchema, JsonSchemaInternal } from './json-schema-versions.js';
2
2
  import type { ZSchemaBase } from './z-schema-base.js';
3
3
  import { Report } from './report.js';
4
4
  export type SchemaCacheStorage = Record<string, JsonSchemaInternal>;
@@ -1,4 +1,4 @@
1
- import type { JsonSchemaInternal } from './json-schema.js';
1
+ import type { JsonSchemaInternal } from './json-schema-versions.js';
2
2
  import type { ZSchemaBase } from './z-schema-base.js';
3
3
  import { Report } from './report.js';
4
4
  interface Id {
@@ -1,4 +1,4 @@
1
- import type { JsonSchemaInternal } from './json-schema.js';
1
+ import type { JsonSchemaInternal } from './json-schema-versions.js';
2
2
  import type { ZSchemaBase } from './z-schema-base.js';
3
3
  import { Report } from './report.js';
4
4
  export declare class SchemaValidator {
@@ -1,3 +1,4 @@
1
1
  export type WHAT_IS = 'undefined' | 'null' | 'object' | 'array' | 'integer' | 'number' | 'string' | 'not-a-number' | 'unknown-number' | 'bigint' | 'boolean' | 'symbol' | 'function';
2
2
  export declare const whatIs: (what: unknown) => WHAT_IS;
3
3
  export declare function isObject(value: unknown): value is Record<any, any>;
4
+ export declare function isInteger(value: unknown): value is number;
@@ -1,8 +1,8 @@
1
1
  import type { ValidateError } from './errors.js';
2
2
  import type { FormatValidatorFn } from './format-validators.js';
3
+ import type { JsonSchema } from './json-schema-versions.js';
3
4
  import type { ZSchemaOptions } from './z-schema-options.js';
4
5
  import { type Errors } from './errors.js';
5
- import { type JsonSchema } from './json-schema.js';
6
6
  import { SchemaCache } from './schema-cache.js';
7
7
  import { SchemaCompiler } from './schema-compiler.js';
8
8
  import { SchemaValidator } from './schema-validator.js';
@@ -1,6 +1,6 @@
1
1
  import type { FormatValidatorFn } from './format-validators.js';
2
- import type { JsonSchemaVersion } from './json-schema.js';
3
2
  import type { Report } from './report.js';
3
+ import { type JsonSchemaVersion } from './json-schema-versions.js';
4
4
  export interface ZSchemaOptions {
5
5
  version?: JsonSchemaVersion | 'none';
6
6
  asyncTimeout?: number;
@@ -1,4 +1,4 @@
1
- import type { JsonSchema } from './json-schema.js';
1
+ import type { JsonSchema } from './json-schema-versions.js';
2
2
  export type SchemaReader = (uri: string) => JsonSchema;
3
3
  export declare function getSchemaReader(): SchemaReader | undefined;
4
4
  export declare function setSchemaReader(schemaReader: SchemaReader | undefined): void;
@@ -0,0 +1 @@
1
+ export {};
@@ -1,10 +1,13 @@
1
1
  import type { FormatValidatorFn } from './format-validators.js';
2
- import type { JsonSchema } from './json-schema.js';
2
+ import type { JsonSchema } from './json-schema-versions.js';
3
3
  import type { ValidateOptions, ValidateResponse } from './z-schema-base.js';
4
4
  import type { ZSchemaOptions } from './z-schema-options.js';
5
5
  import type { SchemaReader } from './z-schema-reader.js';
6
+ import './z-schema-versions.js';
6
7
  import { ZSchemaBase } from './z-schema-base.js';
7
8
  export declare class ZSchema extends ZSchemaBase {
9
+ /** @deprecated Use ZSchema.create() instead. */
10
+ private constructor();
8
11
  static registerFormat(name: string, validatorFunction: FormatValidatorFn): void;
9
12
  static unregisterFormat(name: string): void;
10
13
  static getRegisteredFormats(): string[];
@@ -33,14 +36,20 @@ export declare class ZSchema extends ZSchemaBase {
33
36
  validateSchemaSafe(schemaOrArr: JsonSchema | JsonSchema[]): ValidateResponse;
34
37
  }
35
38
  export declare class ZSchemaSafe extends ZSchemaBase {
39
+ /** @deprecated Use ZSchema.create() instead. */
40
+ private constructor();
36
41
  validate(json: unknown, schema: JsonSchema | string, options?: ValidateOptions): ValidateResponse;
37
42
  validateSchema(schemaOrArr: JsonSchema | JsonSchema[]): ValidateResponse;
38
43
  }
39
44
  export declare class ZSchemaAsync extends ZSchemaBase {
45
+ /** @deprecated Use ZSchema.create() instead. */
46
+ private constructor();
40
47
  validate(json: unknown, schema: JsonSchema | string, options?: ValidateOptions): Promise<true>;
41
48
  validateSchema(schemaOrArr: JsonSchema | JsonSchema[]): true;
42
49
  }
43
50
  export declare class ZSchemaAsyncSafe extends ZSchemaBase {
51
+ /** @deprecated Use ZSchema.create() instead. */
52
+ private constructor();
44
53
  validate(json: unknown, schema: JsonSchema | string, options?: ValidateOptions): Promise<ValidateResponse>;
45
54
  validateSchema(schemaOrArr: JsonSchema | JsonSchema[]): ValidateResponse;
46
55
  }
@@ -1,9 +1,10 @@
1
1
  // Shared regex compilation helper for JSON Schema patterns
2
2
  // Returns { ok: true, value: RegExp } or { ok: false, error: { pattern, message } }
3
3
  export function compileSchemaRegex(pattern) {
4
- // Detect Unicode property escapes
5
- const unicodeEscape = /\\[pP]{/;
6
- const needsUnicode = unicodeEscape.test(pattern);
4
+ const unicodePropertyEscape = /\\[pP]{/;
5
+ const nonBmpCharacter = /[\u{10000}-\u{10FFFF}]/u;
6
+ const surrogatePairEscape = /\\uD[89AB][0-9A-Fa-f]{2}\\uD[CDEF][0-9A-Fa-f]{2}/;
7
+ const needsUnicode = unicodePropertyEscape.test(pattern) || nonBmpCharacter.test(pattern) || surrogatePairEscape.test(pattern);
7
8
  // Try compiling without 'u' flag if not needed
8
9
  if (needsUnicode) {
9
10
  // Try compiling with 'u' flag only
@@ -25,5 +25,8 @@ export const whatIs = (what) => {
25
25
  return typeof what; // undefined, boolean, string, function
26
26
  };
27
27
  export function isObject(value) {
28
- return whatIs(value) === 'object';
28
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
29
+ }
30
+ export function isInteger(value) {
31
+ return typeof value === 'number' && Number.isFinite(value) && value % 1 === 0;
29
32
  }
@@ -1,6 +1,6 @@
1
1
  import { getValidateError } from './errors.js';
2
2
  import { getSupportedFormats } from './format-validators.js';
3
- import { VERSION_SCHEMA_URL_MAPPING } from './json-schema.js';
3
+ import { VERSION_SCHEMA_URL_MAPPING } from './json-schema-versions.js';
4
4
  import { validate as validateJson } from './json-validation.js';
5
5
  import { Report } from './report.js';
6
6
  import { SchemaCache } from './schema-cache.js';
@@ -10,7 +10,7 @@ import { deepClone } from './utils/clone.js';
10
10
  import { get, sortedKeys } from './utils/json.js';
11
11
  import { copyProp } from './utils/properties.js';
12
12
  import { getRemotePath } from './utils/uri.js';
13
- import { whatIs } from './utils/what-is.js';
13
+ import { isObject, whatIs } from './utils/what-is.js';
14
14
  import { defaultOptions, normalizeOptions } from './z-schema-options.js';
15
15
  export class ZSchemaBase {
16
16
  scache;
@@ -42,9 +42,8 @@ export class ZSchemaBase {
42
42
  options = {};
43
43
  }
44
44
  this.validateOptions = options;
45
- const schemaType = whatIs(schema);
46
- if (schemaType !== 'string' && schemaType !== 'object') {
47
- const e = new Error('Invalid .validate call - schema must be a string or object but ' + schemaType + ' was passed!');
45
+ if (typeof schema !== 'string' && typeof schema !== 'boolean' && !isObject(schema)) {
46
+ const e = new Error('Invalid .validate call - schema must be a string or object but ' + whatIs(schema) + ' was passed!');
48
47
  if (callback) {
49
48
  setTimeout(function () {
50
49
  callback(e, false);
@@ -1,6 +1,8 @@
1
+ import { CURRENT_DEFAULT_SCHEMA_VERSION } from './json-schema-versions.js';
1
2
  import { shallowClone } from './utils/clone.js';
2
3
  export const defaultOptions = {
3
- version: 'draft-04',
4
+ // default version to validate against
5
+ version: CURRENT_DEFAULT_SCHEMA_VERSION,
4
6
  // default timeout for all async tasks
5
7
  asyncTimeout: 2000,
6
8
  // force additionalProperties and additionalItems to be defined on "object" and "array" types
@@ -0,0 +1,27 @@
1
+ import { SchemaCache } from './schema-cache.js';
2
+ import { normalizeOptions } from './z-schema-options.js';
3
+ import _Draft4HyperSchema from './schemas/draft-04-hyper-schema.json' with { type: 'json' };
4
+ import _Draft4Schema from './schemas/draft-04-schema.json' with { type: 'json' };
5
+ import _Draft6HyperSchema from './schemas/draft-06-hyper-schema.json' with { type: 'json' };
6
+ import _Draft6Links from './schemas/draft-06-links.json' with { type: 'json' };
7
+ import _Draft6Schema from './schemas/draft-06-schema.json' with { type: 'json' };
8
+ const Draft4Schema = _Draft4Schema;
9
+ const Draft4HyperSchema = _Draft4HyperSchema;
10
+ const Draft6Schema = _Draft6Schema;
11
+ const Draft6HyperSchema = _Draft6HyperSchema;
12
+ const Draft6Links = _Draft6Links;
13
+ const registerRemoteReference = (uri, schema, validationOptions) => {
14
+ const preparedSchema = schema;
15
+ if (!preparedSchema.id) {
16
+ preparedSchema.id = uri;
17
+ }
18
+ if (validationOptions) {
19
+ preparedSchema.__$validationOptions = normalizeOptions(validationOptions);
20
+ }
21
+ SchemaCache.cacheSchemaByUri(uri, preparedSchema);
22
+ };
23
+ registerRemoteReference('http://json-schema.org/draft-04/schema', Draft4Schema, { version: 'none' });
24
+ registerRemoteReference('http://json-schema.org/draft-04/hyper-schema', Draft4HyperSchema, { version: 'none' });
25
+ registerRemoteReference('http://json-schema.org/draft-06/schema', Draft6Schema, { version: 'none' });
26
+ registerRemoteReference('http://json-schema.org/draft-06/hyper-schema', Draft6HyperSchema, { version: 'none' });
27
+ registerRemoteReference('http://json-schema.org/draft-06/links', Draft6Links, { version: 'none' });
package/dist/z-schema.js CHANGED
@@ -1,3 +1,4 @@
1
+ import './z-schema-versions.js';
1
2
  import { getRegisteredFormats, registerFormat, unregisterFormat } from './format-validators.js';
2
3
  import { SchemaCache } from './schema-cache.js';
3
4
  import { deepClone } from './utils/clone.js';
@@ -5,10 +6,11 @@ import { jsonSymbol, schemaSymbol } from './utils/symbols.js';
5
6
  import { ZSchemaBase } from './z-schema-base.js';
6
7
  import { defaultOptions, normalizeOptions } from './z-schema-options.js';
7
8
  import { getSchemaReader, setSchemaReader } from './z-schema-reader.js';
8
- // import schemas so they don't have to be downloaded for validation purposes
9
- import _Draft4HyperSchema from './schemas/draft-04-hyper-schema.json' with { type: 'json' };
10
- import _Draft4Schema from './schemas/draft-04-schema.json' with { type: 'json' };
11
9
  export class ZSchema extends ZSchemaBase {
10
+ /** @deprecated Use ZSchema.create() instead. */
11
+ constructor(options) {
12
+ super(options);
13
+ }
12
14
  // ----- static methods start -----
13
15
  // class scoped format functions
14
16
  static registerFormat(name, validatorFunction) {
@@ -55,14 +57,18 @@ export class ZSchema extends ZSchemaBase {
55
57
  delete options.safe;
56
58
  options.__called_from_factory__ = true;
57
59
  if (isAsync && isSafe) {
60
+ // @ts-expect-error Factory can use private/deprecated constructor
58
61
  return new ZSchemaAsyncSafe(options);
59
62
  }
60
63
  if (isAsync) {
64
+ // @ts-expect-error Factory can use private/deprecated constructor
61
65
  return new ZSchemaAsync(options);
62
66
  }
63
67
  if (isSafe) {
68
+ // @ts-expect-error Factory can use private/deprecated constructor
64
69
  return new ZSchemaSafe(options);
65
70
  }
71
+ // Factory can use private/deprecated constructor
66
72
  return new ZSchema(options);
67
73
  }
68
74
  validate(json, schema, options = {}) {
@@ -113,6 +119,10 @@ export class ZSchema extends ZSchemaBase {
113
119
  }
114
120
  }
115
121
  export class ZSchemaSafe extends ZSchemaBase {
122
+ /** @deprecated Use ZSchema.create() instead. */
123
+ constructor(options) {
124
+ super(options);
125
+ }
116
126
  validate(json, schema, options = {}) {
117
127
  try {
118
128
  this._validate(json, schema, options);
@@ -133,6 +143,10 @@ export class ZSchemaSafe extends ZSchemaBase {
133
143
  }
134
144
  }
135
145
  export class ZSchemaAsync extends ZSchemaBase {
146
+ /** @deprecated Use ZSchema.create() instead. */
147
+ constructor(options) {
148
+ super(options);
149
+ }
136
150
  validate(json, schema, options = {}) {
137
151
  return new Promise((resolve, reject) => {
138
152
  try {
@@ -148,6 +162,10 @@ export class ZSchemaAsync extends ZSchemaBase {
148
162
  }
149
163
  }
150
164
  export class ZSchemaAsyncSafe extends ZSchemaBase {
165
+ /** @deprecated Use ZSchema.create() instead. */
166
+ constructor(options) {
167
+ super(options);
168
+ }
151
169
  validate(json, schema, options = {}) {
152
170
  return new Promise((resolve) => {
153
171
  try {
@@ -170,7 +188,3 @@ export class ZSchemaAsyncSafe extends ZSchemaBase {
170
188
  }
171
189
  }
172
190
  }
173
- const Draft4Schema = _Draft4Schema;
174
- const Draft4HyperSchema = _Draft4HyperSchema;
175
- ZSchema.setRemoteReference('http://json-schema.org/draft-04/schema', Draft4Schema, { version: 'none' });
176
- ZSchema.setRemoteReference('http://json-schema.org/draft-04/hyper-schema', Draft4HyperSchema, { version: 'none' });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "z-schema",
3
- "version": "9.0.1",
3
+ "version": "10.0.0",
4
4
  "engines": {
5
5
  "node": ">=22.0.0"
6
6
  },
@@ -67,7 +67,7 @@
67
67
  "test:coverage": "vitest run --coverage",
68
68
  "test:node": "vitest run --project node --silent=false",
69
69
  "test:browser": "vitest run --project browser --silent=false",
70
- "test:sample": "npx vitest run --silent=false --hideSkippedTests --project node -t \"invalid definition\""
70
+ "test:sample": "npx vitest run --silent=false --hideSkippedTests --project node -t \"draft6/exclusiveMinimum.json\""
71
71
  },
72
72
  "dependencies": {
73
73
  "validator": "^13.15.26"
package/src/errors.ts CHANGED
@@ -59,6 +59,12 @@ export const Errors = {
59
59
  ASYNC_TIMEOUT: '{0} asynchronous task(s) have timed out after {1} ms',
60
60
  PARENT_SCHEMA_VALIDATION_FAILED: 'Schema failed to validate against its parent schema, see inner errors for details.',
61
61
  REMOTE_NOT_VALID: "Remote reference didn't compile successfully: {0}",
62
+
63
+ // Draft-06 errors
64
+ SCHEMA_IS_FALSE: 'Boolean schema "false" is always invalid.',
65
+ CONST: 'Value does not match const: {0}',
66
+ CONTAINS: 'Array does not contain an item matching the schema',
67
+ PROPERTY_NAMES: 'Property name {0} does not match the propertyNames schema',
62
68
  };
63
69
 
64
70
  export class ValidateError extends Error {
@@ -200,6 +200,65 @@ const uriValidator: FormatValidatorFn = function (uri: unknown) {
200
200
  return /^[a-zA-Z][a-zA-Z0-9+.-]*:[^"\\<>^{}^`| ]*$/.test(uri);
201
201
  };
202
202
 
203
+ const uriReferenceValidator: FormatValidatorFn = (uri: unknown) => {
204
+ if (typeof uri !== 'string') return true;
205
+ // eslint-disable-next-line no-control-regex
206
+ if (/[^\x00-\x7F]/.test(uri)) return false;
207
+ // URI-reference allows relative URIs
208
+ return /^([a-zA-Z][a-zA-Z0-9+.-]*:)?[^"\\<>^{}^`| ]*$/.test(uri);
209
+ };
210
+
211
+ const uriTemplateValidator: FormatValidatorFn = (uri: unknown) => {
212
+ if (typeof uri !== 'string') return true;
213
+ // URI template allows braces for expressions.
214
+ if (!/^([a-zA-Z][a-zA-Z0-9+.-]*:)?[^"\\<>^`| ]*$/.test(uri)) {
215
+ return false;
216
+ }
217
+
218
+ let inExpression = false;
219
+ for (let idx = 0; idx < uri.length; idx++) {
220
+ const ch = uri[idx];
221
+ if (ch === '{') {
222
+ if (inExpression) {
223
+ return false;
224
+ }
225
+ inExpression = true;
226
+ } else if (ch === '}') {
227
+ if (!inExpression) {
228
+ return false;
229
+ }
230
+ inExpression = false;
231
+ }
232
+ }
233
+
234
+ return !inExpression;
235
+ };
236
+
237
+ const jsonPointerValidator: FormatValidatorFn = (pointer: unknown) => {
238
+ if (typeof pointer !== 'string') return true;
239
+ // JSON Pointer: empty, or a sequence of '/'-prefixed reference tokens.
240
+ // In each token, '~' must be escaped as '~0' or '~1'.
241
+ return pointer === '' || /^(?:\/(?:[^~]|~0|~1)*)+$/.test(pointer);
242
+ };
243
+
244
+ const relativeJsonPointerValidator: FormatValidatorFn = (pointer: unknown) => {
245
+ if (typeof pointer !== 'string') return true;
246
+ // Relative JSON Pointer: number#path or empty
247
+ return /^\d+(#.*)?$/.test(pointer) || pointer === '';
248
+ };
249
+
250
+ const timeValidator: FormatValidatorFn = (time: unknown) => {
251
+ if (typeof time !== 'string') return true;
252
+ // time: hh:mm:ss[.fraction]
253
+ return /^([01]\d|2[0-3]):([0-5]\d):([0-5]\d)(\.\d+)?$/.test(time);
254
+ };
255
+
256
+ const idnEmailValidator: FormatValidatorFn = (email: unknown) => {
257
+ if (typeof email !== 'string') return true;
258
+ // Simple email check, allowing international chars
259
+ return /^[^\s@]+@[^\s@]+$/.test(email);
260
+ };
261
+
203
262
  export interface FormatValidatorsOptions {
204
263
  strictUris?: boolean;
205
264
  customFormats?: Record<string, FormatValidatorFn | null>;
@@ -216,6 +275,12 @@ const inbuiltValidators: Record<string, FormatValidatorFn> = {
216
275
  regex: regexValidator,
217
276
  uri: uriValidator,
218
277
  'strict-uri': strictUriValidator,
278
+ 'uri-reference': uriReferenceValidator,
279
+ 'uri-template': uriTemplateValidator,
280
+ 'json-pointer': jsonPointerValidator,
281
+ 'relative-json-pointer': relativeJsonPointerValidator,
282
+ time: timeValidator,
283
+ 'idn-email': idnEmailValidator,
219
284
  } as const;
220
285
 
221
286
  const customValidators: Record<string, FormatValidatorFn> = {};
package/src/index.ts CHANGED
@@ -13,7 +13,8 @@ export {
13
13
  registerFormat,
14
14
  unregisterFormat,
15
15
  } from './format-validators.js';
16
- export type { JsonSchema, JsonSchemaType } from './json-schema.js';
16
+ export type { JsonSchemaType } from './json-schema.js';
17
+ export type { JsonSchema } from './json-schema-versions.js';
17
18
  export type { Report, SchemaErrorDetail } from './report.js';
18
19
  export type { ZSchema, ZSchemaAsync, ZSchemaAsyncSafe, ZSchemaSafe } from './z-schema.js';
19
20
  export { ValidateOptions, ValidateResponse } from './z-schema-base.js';
@@ -0,0 +1,34 @@
1
+ import type { JsonSchemaCommon, ZSchemaInternalProperties } from './json-schema.js';
2
+
3
+ // TODO: currently unsupported 'draft-07', '2019-09', '2020-12'
4
+ export type JsonSchemaVersion = 'draft-04' | 'draft-06';
5
+
6
+ export const CURRENT_DEFAULT_SCHEMA_VERSION: JsonSchemaVersion = 'draft-06';
7
+
8
+ export const VERSION_SCHEMA_URL_MAPPING: Record<JsonSchemaVersion, string> = {
9
+ 'draft-04': 'http://json-schema.org/draft-04/schema#',
10
+ 'draft-06': 'http://json-schema.org/draft-06/schema#',
11
+ };
12
+
13
+ export type JsonSchema = JsonSchemaDraft4 | JsonSchemaDraft6;
14
+ export type JsonSchemaAll = JsonSchemaDraft4 & JsonSchemaDraft6;
15
+
16
+ export type JsonSchemaInternal = JsonSchema & ZSchemaInternalProperties;
17
+ export type JsonSchemaInternalAll = JsonSchemaAll & ZSchemaInternalProperties;
18
+ export type JsonSchemaInternalD4 = JsonSchemaDraft4 & ZSchemaInternalProperties;
19
+ export type JsonSchemaInternalD6 = JsonSchemaDraft6 & ZSchemaInternalProperties;
20
+
21
+ export interface JsonSchemaDraft4 extends JsonSchemaCommon {
22
+ exclusiveMaximum?: boolean;
23
+ exclusiveMinimum?: boolean;
24
+ }
25
+
26
+ export interface JsonSchemaDraft6 extends JsonSchemaCommon {
27
+ $id?: string;
28
+ examples?: unknown[];
29
+ const?: unknown;
30
+ contains?: JsonSchema;
31
+ propertyNames?: JsonSchema;
32
+ exclusiveMaximum?: number;
33
+ exclusiveMinimum?: number;
34
+ }
@@ -1,19 +1,19 @@
1
+ import type {
2
+ JsonSchema,
3
+ JsonSchemaInternal,
4
+ JsonSchemaInternalD4,
5
+ JsonSchemaInternalD6,
6
+ } from './json-schema-versions.js';
1
7
  import type { Reference } from './schema-compiler.js';
2
8
  import type { ZSchemaOptions } from './z-schema-options.js';
3
9
 
4
10
  import { isObject } from './utils/what-is.js';
5
11
 
6
- // TODO: currently unsupported 'draft-06', 'draft-07', '2019-09', '2020-12'
7
- export type JsonSchemaVersion = 'draft-04';
8
-
9
- export const VERSION_SCHEMA_URL_MAPPING: Record<JsonSchemaVersion, string> = {
10
- 'draft-04': 'http://json-schema.org/draft-04/schema#',
11
- };
12
-
13
- export interface JsonSchema {
12
+ // common properties of all JSON Schema versions
13
+ export interface JsonSchemaCommon {
14
14
  $ref?: string;
15
- id?: string;
16
15
  $schema?: string;
16
+ id?: string;
17
17
  title?: string;
18
18
  description?: string;
19
19
  default?: unknown;
@@ -21,13 +21,9 @@ export interface JsonSchema {
21
21
  type?: string | string[];
22
22
  properties?: Record<string, JsonSchema>;
23
23
  patternProperties?: Record<string, JsonSchema>;
24
- dependencies?: Record<string, string[]>;
25
- // properties
26
24
  multipleOf?: number;
27
25
  minimum?: number;
28
- exclusiveMinimum?: boolean;
29
26
  maximum?: number;
30
- exclusiveMaximum?: boolean;
31
27
  minLength?: number;
32
28
  maxLength?: number;
33
29
  pattern?: string;
@@ -40,6 +36,7 @@ export interface JsonSchema {
40
36
  maxProperties?: number;
41
37
  required?: string[];
42
38
  additionalProperties?: boolean | JsonSchema;
39
+ dependencies?: Record<string, string[] | JsonSchema>;
43
40
  enum?: Array<unknown>;
44
41
  format?: string;
45
42
  allOf?: JsonSchema[];
@@ -60,7 +57,15 @@ export interface ZSchemaInternalProperties {
60
57
  __$visited?: boolean;
61
58
  }
62
59
 
63
- export interface JsonSchemaInternal extends JsonSchema, ZSchemaInternalProperties {}
60
+ export const getId = (schema: JsonSchemaInternal) => {
61
+ if ((schema as JsonSchemaInternalD6).$id) {
62
+ return (schema as JsonSchemaInternalD6).$id;
63
+ }
64
+ if ((schema as JsonSchemaInternalD4).id) {
65
+ return (schema as JsonSchemaInternalD4).id;
66
+ }
67
+ return undefined;
68
+ };
64
69
 
65
70
  export const findId = (schema: JsonSchemaInternal, id: string): JsonSchemaInternal | undefined => {
66
71
  // process only arrays and objects
@@ -73,8 +78,9 @@ export const findId = (schema: JsonSchemaInternal, id: string): JsonSchemaIntern
73
78
  return schema;
74
79
  }
75
80
 
76
- if (schema.id) {
77
- if (schema.id === id || (schema.id[0] === '#' && schema.id.substring(1) === id)) {
81
+ const schemaId = getId(schema);
82
+ if (schemaId) {
83
+ if (schemaId === id || (schemaId[0] === '#' && schemaId.substring(1) === id)) {
78
84
  return schema;
79
85
  }
80
86
  }