schema-shield 1.0.5 → 1.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.
@@ -1,4 +1,24 @@
1
- import { CompiledSchema } from "../index";
1
+ import type { CompiledSchema } from "../index";
2
+
3
+ const hasOwnPropertyIntrinsic = Object.prototype.hasOwnProperty;
4
+ const hasOwnPropertyCall = Function.prototype.call.bind(
5
+ hasOwnPropertyIntrinsic
6
+ ) as (target: any, key: PropertyKey) => boolean;
7
+
8
+ export function definePropertyOrThrow<T extends object>(
9
+ target: T,
10
+ key: PropertyKey,
11
+ descriptor: PropertyDescriptor
12
+ ): T {
13
+ if (!Reflect.defineProperty(target, key, descriptor)) {
14
+ throw new TypeError(`Cannot define property "${String(key)}"`);
15
+ }
16
+ return target;
17
+ }
18
+
19
+ export function hasOwn(target: any, key: PropertyKey): boolean {
20
+ return hasOwnPropertyCall(target, key);
21
+ }
2
22
 
3
23
  interface ErrorTree {
4
24
  message: string;
@@ -11,6 +31,7 @@ interface ErrorTree {
11
31
  }
12
32
 
13
33
  export class ValidationError extends Error {
34
+ code?: string;
14
35
  message: string;
15
36
  item?: string | number;
16
37
  keyword: string;
@@ -25,56 +46,71 @@ export class ValidationError extends Error {
25
46
  this.message = message;
26
47
  }
27
48
 
28
- private _getCause(pointer = "#", instancePointer = "#"): ValidationError {
29
- let schemaPath = `${pointer}/${this.keyword}`;
30
- let instancePath = `${instancePointer}`;
31
- if (typeof this.item !== "undefined") {
49
+ getCause(): ValidationError {
50
+ let current: ValidationError = this;
51
+ let schemaPointer = "#";
52
+ let instancePointer = "#";
53
+ const seen = new Set<ValidationError>();
54
+
55
+ while (!seen.has(current)) {
56
+ seen.add(current);
57
+ let schemaPath = `${schemaPointer}/${current.keyword}`;
58
+ let instancePath = instancePointer;
59
+ if (typeof current.item !== "undefined") {
60
+ if (
61
+ typeof current.item === "string" &&
62
+ current.schema &&
63
+ typeof current.schema === "object" &&
64
+ current.item in current.schema
65
+ ) {
66
+ schemaPath += `/${escapeJsonPointerToken(current.item)}`;
67
+ }
68
+ instancePath += `/${escapeJsonPointerToken(current.item)}`;
69
+ }
70
+ current.schemaPath = schemaPath;
71
+ current.instancePath = instancePath;
72
+
32
73
  if (
33
- typeof this.item === "string" &&
34
- this.schema &&
35
- typeof this.schema === "object" &&
36
- this.item in this.schema
74
+ !(current.cause instanceof ValidationError) ||
75
+ seen.has(current.cause)
37
76
  ) {
38
- schemaPath += `/${this.item}`;
77
+ return current;
39
78
  }
40
- instancePath += `/${this.item}`;
79
+ schemaPointer = schemaPath;
80
+ instancePointer = instancePath;
81
+ current = current.cause;
41
82
  }
42
-
43
- this.instancePath = instancePath;
44
- this.schemaPath = schemaPath;
45
-
46
- // If there is no cause or the cause is not a ValidationError, return this
47
- if (!this.cause || !(this.cause instanceof ValidationError)) {
48
- return this;
49
- }
50
-
51
- return this.cause._getCause(schemaPath, instancePath);
52
- }
53
-
54
- getCause(): ValidationError {
55
- return this._getCause();
56
- }
57
-
58
- private _getTree(): ErrorTree {
59
- const tree: ErrorTree = {
60
- message: this.message,
61
- keyword: this.keyword,
62
- item: this.item,
63
- schemaPath: this.schemaPath,
64
- instancePath: this.instancePath,
65
- data: this.data
66
- };
67
-
68
- if (this.cause) {
69
- tree.cause = this.cause._getTree();
70
- }
71
-
72
- return tree;
83
+ return current;
73
84
  }
74
85
 
75
86
  getTree(): ErrorTree {
76
87
  this.getCause();
77
- return this._getTree();
88
+ let current: ValidationError | undefined = this;
89
+ let root: ErrorTree | undefined;
90
+ let target: ErrorTree | undefined;
91
+ const seen = new Set<ValidationError>();
92
+
93
+ while (current && !seen.has(current)) {
94
+ seen.add(current);
95
+ const node: ErrorTree = {
96
+ message: current.message,
97
+ keyword: current.keyword,
98
+ item: current.item,
99
+ schemaPath: current.schemaPath,
100
+ instancePath: current.instancePath,
101
+ data: current.data
102
+ };
103
+ if (!root) {
104
+ root = node;
105
+ } else if (target) {
106
+ target.cause = node;
107
+ }
108
+ target = node;
109
+ current =
110
+ current.cause instanceof ValidationError ? current.cause : undefined;
111
+ }
112
+
113
+ return root!;
78
114
  }
79
115
 
80
116
  getPath() {
@@ -87,6 +123,7 @@ export class ValidationError extends Error {
87
123
  }
88
124
 
89
125
  export interface DefineErrorOptions {
126
+ code?: string;
90
127
  item?: any; // Final item in the schemaPath
91
128
  cause?: ValidationError | true; // Cause of the error
92
129
  data?: any; // Data that caused the error
@@ -116,9 +153,12 @@ export function getDefinedErrorFunctionForKey(
116
153
 
117
154
  const defineError: DefineErrorFunction = (message, options = {}) => {
118
155
  KeywordError.message = message;
156
+ KeywordError.code = options.code;
119
157
  KeywordError.item = options.item;
120
- KeywordError.cause =
121
- options.cause && options.cause !== true ? options.cause : undefined;
158
+ if (options.cause !== KeywordError) {
159
+ KeywordError.cause =
160
+ options.cause && options.cause !== true ? options.cause : undefined;
161
+ }
122
162
  KeywordError.data = options.data;
123
163
  return KeywordError;
124
164
  };
@@ -129,6 +169,10 @@ export function getDefinedErrorFunctionForKey(
129
169
  );
130
170
  }
131
171
 
172
+ export function escapeJsonPointerToken(value: string | number): string {
173
+ return String(value).replace(/~/g, "~0").replace(/\//g, "~1");
174
+ }
175
+
132
176
  export function getUTF16Length(str) {
133
177
  let length = 0;
134
178
  for (let i = 0; i < str.length; i++) {
@@ -150,8 +194,8 @@ export function isCompiledSchema(subSchema: any): subSchema is CompiledSchema {
150
194
  );
151
195
  }
152
196
 
153
- export function getNamedFunction<T>(name: string, fn: T): T {
154
- return Object.defineProperty(fn, "name", { value: name });
197
+ export function getNamedFunction<T extends object>(name: string, fn: T): T {
198
+ return definePropertyOrThrow(fn, "name", { value: name });
155
199
  }
156
200
 
157
201
  export function resolvePath(root: any, path: string): any {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "schema-shield",
3
- "version": "1.0.5",
3
+ "version": "1.1.0",
4
4
  "description": "A fast library that protects your JSON schema from invalid data.",
5
5
  "repository": "git@github.com:Masquerade-Circus/schema-shield.git",
6
6
  "author": "Masquerade <christian@masquerade-circus.net>",
@@ -13,7 +13,7 @@
13
13
  "module": "dist/index.mjs",
14
14
  "unpkg": "dist/index.min.js",
15
15
  "browser": "dist/index.min.js",
16
- "types": "dist/**/*.d.ts",
16
+ "types": "dist/index.d.ts",
17
17
  "exports": {
18
18
  ".": {
19
19
  "types": "./dist/index.d.ts",
@@ -66,7 +66,7 @@
66
66
  "User friendly"
67
67
  ],
68
68
  "engines": {
69
- "node": ">=12.0.0"
69
+ "node": ">=16.1.0"
70
70
  },
71
71
  "scripts": {
72
72
  "test": "mocha --bail --recursive --no-timeouts --forbid-only --exit --require ts-node/register --enable-source-maps tests/**/*.test.ts",
@@ -78,12 +78,6 @@
78
78
  "release": "release-it --verbose",
79
79
  "release-test": "release-it --dry-run --verbose"
80
80
  },
81
- "dependencies": {
82
- "ts-node": "^10.9.1",
83
- "tsc-prog": "^2.2.1",
84
- "tslib": "^2.5.0",
85
- "typescript": "^5.0.2"
86
- },
87
81
  "devDependencies": {
88
82
  "@exodus/schemasafe": "^1.0.0",
89
83
  "@release-it/conventional-changelog": "^5.1.1",
@@ -101,7 +95,11 @@
101
95
  "nodemon": "^2.0.22",
102
96
  "nyc": "^15.1.0",
103
97
  "release-it": "^15.9.3",
104
- "terser": "^5.16.6"
98
+ "terser": "^5.16.6",
99
+ "ts-node": "^10.9.1",
100
+ "tsc-prog": "^2.2.1",
101
+ "tslib": "^2.5.0",
102
+ "typescript": "^5.0.2"
105
103
  },
106
104
  "nyc": {
107
105
  "exclude": [