schema-shield 0.0.3 → 0.0.4

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.mjs CHANGED
@@ -1,118 +1,3 @@
1
- var __create = Object.create;
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __getProtoOf = Object.getPrototypeOf;
6
- var __hasOwnProp = Object.prototype.hasOwnProperty;
7
- var __commonJS = (cb, mod) => function __require() {
8
- return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
9
- };
10
- var __copyProps = (to, from, except, desc) => {
11
- if (from && typeof from === "object" || typeof from === "function") {
12
- for (let key of __getOwnPropNames(from))
13
- if (!__hasOwnProp.call(to, key) && key !== except)
14
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
- }
16
- return to;
17
- };
18
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
19
- // If the importer is in node compatibility mode or this is not an ESM
20
- // file that has been converted to a CommonJS file using a Babel-
21
- // compatible transform (i.e. "__esModule" has not been set), then set
22
- // "default" to the CommonJS "module.exports" for node compatibility.
23
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
24
- mod
25
- ));
26
-
27
- // node_modules/is-my-ip-valid/index.js
28
- var require_is_my_ip_valid = __commonJS({
29
- "node_modules/is-my-ip-valid/index.js"(exports, module) {
30
- var reIpv4FirstPass = /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/;
31
- var reSubnetString = /\/\d{1,3}(?=%|$)/;
32
- var reForwardSlash = /\//;
33
- var reZone = /%.*$/;
34
- var reBadCharacters = /([^0-9a-f:/%])/i;
35
- var reBadAddress = /([0-9a-f]{5,}|:{3,}|[^:]:$|^:[^:]|\/$)/i;
36
- function validate4(input) {
37
- if (!reIpv4FirstPass.test(input))
38
- return false;
39
- var parts = input.split(".");
40
- if (parts.length !== 4)
41
- return false;
42
- if (parts[0][0] === "0" && parts[0].length > 1)
43
- return false;
44
- if (parts[1][0] === "0" && parts[1].length > 1)
45
- return false;
46
- if (parts[2][0] === "0" && parts[2].length > 1)
47
- return false;
48
- if (parts[3][0] === "0" && parts[3].length > 1)
49
- return false;
50
- var n0 = Number(parts[0]);
51
- var n1 = Number(parts[1]);
52
- var n2 = Number(parts[2]);
53
- var n3 = Number(parts[3]);
54
- return n0 >= 0 && n0 < 256 && n1 >= 0 && n1 < 256 && n2 >= 0 && n2 < 256 && n3 >= 0 && n3 < 256;
55
- }
56
- function validate6(input) {
57
- var withoutSubnet = input.replace(reSubnetString, "");
58
- var hasSubnet = input.length !== withoutSubnet.length;
59
- if (hasSubnet)
60
- return false;
61
- if (!hasSubnet) {
62
- if (reForwardSlash.test(input))
63
- return false;
64
- }
65
- var withoutZone = withoutSubnet.replace(reZone, "");
66
- var lastPartSeparator = withoutZone.lastIndexOf(":");
67
- if (lastPartSeparator === -1)
68
- return false;
69
- var lastPart = withoutZone.substring(lastPartSeparator + 1);
70
- var hasV4Part = validate4(lastPart);
71
- var address = hasV4Part ? withoutZone.substring(0, lastPartSeparator + 1) + "1234:5678" : withoutZone;
72
- if (reBadCharacters.test(address))
73
- return false;
74
- if (reBadAddress.test(address))
75
- return false;
76
- var halves = address.split("::");
77
- if (halves.length > 2)
78
- return false;
79
- if (halves.length === 2) {
80
- var first = halves[0] === "" ? [] : halves[0].split(":");
81
- var last = halves[1] === "" ? [] : halves[1].split(":");
82
- var remainingLength = 8 - (first.length + last.length);
83
- if (remainingLength <= 0)
84
- return false;
85
- } else {
86
- if (address.split(":").length !== 8)
87
- return false;
88
- }
89
- return true;
90
- }
91
- function validate(input) {
92
- return validate4(input) || validate6(input);
93
- }
94
- module.exports = function validator(options) {
95
- if (!options)
96
- options = {};
97
- if (options.version === 4)
98
- return validate4;
99
- if (options.version === 6)
100
- return validate6;
101
- if (options.version == null)
102
- return validate;
103
- throw new Error("Unknown version: " + options.version);
104
- };
105
- module.exports["__all_regexes__"] = [
106
- reIpv4FirstPass,
107
- reSubnetString,
108
- reForwardSlash,
109
- reZone,
110
- reBadCharacters,
111
- reBadAddress
112
- ];
113
- }
114
- });
115
-
116
1
  // lib/utils.ts
117
2
  var ValidationError = class extends Error {
118
3
  message;
@@ -215,52 +100,148 @@ function getNamedFunction(name, fn) {
215
100
  }
216
101
 
217
102
  // lib/formats.ts
218
- var import_is_my_ip_valid = __toESM(require_is_my_ip_valid());
219
- var RegExps = {
220
- "date-time": /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(\.\d+)?(Z|([+-])(\d{2}):(\d{2}))$/,
221
- time: /^(\d{2}):(\d{2}):(\d{2})(\.\d+)?(Z|([+-])(\d{2}):(\d{2}))$/,
222
- uri: /^[a-zA-Z][a-zA-Z0-9+\-.]*:[^\s]*$/,
223
- email: /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/,
224
- hostname: /^[a-zA-Z0-9][a-zA-Z0-9-]{0,62}(\.[a-zA-Z0-9][a-zA-Z0-9-]{0,62})*[a-zA-Z0-9]$/,
225
- date: /^(\d{4})-(\d{2})-(\d{2})$/,
226
- "json-pointer": /^\/(?:[^~]|~0|~1)*$/,
227
- "relative-json-pointer": /^([0-9]+)(#|\/(?:[^~]|~0|~1)*)?$/
228
- };
229
103
  var Formats = {
230
104
  ["date-time"](data) {
231
- const upperCaseData = data.toUpperCase();
232
- if (!RegExps["date-time"].test(upperCaseData)) {
105
+ const match = data.match(
106
+ /^(\d{4})-(0[0-9]|1[0-2])-(\d{2})T(0[0-9]|1\d|2[0-3]):([0-5]\d):((?:[0-5]\d|60))(?:.\d+)?(?:([+-])(0[0-9]|1\d|2[0-3]):([0-5]\d)|Z)?$/i
107
+ );
108
+ if (!match) {
109
+ return false;
110
+ }
111
+ let day = Number(match[3]);
112
+ if (match[2] === "02" && day > 29) {
113
+ return false;
114
+ }
115
+ const [
116
+ ,
117
+ yearStr,
118
+ monthStr,
119
+ ,
120
+ hourStr,
121
+ minuteStr,
122
+ secondStr,
123
+ timezoneSign,
124
+ timezoneHourStr,
125
+ timezoneMinuteStr
126
+ ] = match;
127
+ let year = Number(yearStr);
128
+ let month = Number(monthStr);
129
+ let hour = Number(hourStr);
130
+ let minute = Number(minuteStr);
131
+ let second = Number(secondStr);
132
+ if (timezoneSign === "-" || timezoneSign === "+") {
133
+ const timezoneHour = Number(timezoneHourStr);
134
+ const timezoneMinute = Number(timezoneMinuteStr);
135
+ if (timezoneSign === "-") {
136
+ hour += timezoneHour;
137
+ minute += timezoneMinute;
138
+ } else if (timezoneSign === "+") {
139
+ hour -= timezoneHour;
140
+ minute -= timezoneMinute;
141
+ }
142
+ if (minute > 59) {
143
+ hour += 1;
144
+ minute -= 60;
145
+ } else if (minute < 0) {
146
+ hour -= 1;
147
+ minute += 60;
148
+ }
149
+ if (hour > 23) {
150
+ day += 1;
151
+ hour -= 24;
152
+ } else if (hour < 0) {
153
+ day -= 1;
154
+ hour += 24;
155
+ }
156
+ if (day > 31) {
157
+ month += 1;
158
+ day -= 31;
159
+ } else if (day < 1) {
160
+ month -= 1;
161
+ day += 31;
162
+ }
163
+ if (month > 12) {
164
+ year += 1;
165
+ month -= 12;
166
+ } else if (month < 1) {
167
+ year -= 1;
168
+ month += 12;
169
+ }
170
+ if (year < 0) {
171
+ return false;
172
+ }
173
+ }
174
+ const daysInMonth = [31, , 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
175
+ const maxDays = month === 2 ? year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0) ? 29 : 28 : daysInMonth[month - 1];
176
+ if (day > maxDays) {
177
+ return false;
178
+ }
179
+ if (second === 60 && (minute !== 59 || hour !== 23)) {
233
180
  return false;
234
181
  }
235
- const date = new Date(upperCaseData);
236
- return !isNaN(date.getTime());
182
+ return true;
237
183
  },
238
184
  uri(data) {
239
- return RegExps.uri.test(data);
185
+ return /^[a-zA-Z][a-zA-Z0-9+\-.]*:[^\s]*$/.test(data);
240
186
  },
241
187
  email(data) {
242
- if (!RegExps.email.test(data)) {
243
- return false;
188
+ return /^(?!\.)(?!.*\.$)[a-z0-9!#$%&'*+/=?^_`{|}~-]{1,20}(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]{1,21}){0,2}@[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,60}[a-z0-9])?){0,3}$/i.test(
189
+ data
190
+ );
191
+ },
192
+ ipv4(data) {
193
+ return /^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])$/.test(
194
+ data
195
+ );
196
+ },
197
+ // ipv6: isMyIpValid({ version: 6 }),
198
+ ipv6(data) {
199
+ if (data === "::") {
200
+ return true;
244
201
  }
245
- const [local, domain] = data.split("@");
246
- if (local.length > 64 || local.indexOf("..") !== -1 || local[0] === "." || local[local.length - 1] === ".") {
202
+ if (data.indexOf(":") === -1 || /(?:\s+|:::+|^\w{5,}|\w{5}$|^:{1}\w|\w:{1}$)/.test(data)) {
247
203
  return false;
248
204
  }
249
- if (domain.length > 255 || domain.indexOf("..") !== -1 || domain[0] === "." || domain[domain.length - 1] === ".") {
250
- return false;
205
+ const hasIpv4 = data.indexOf(".") !== -1;
206
+ let addressParts = data;
207
+ if (hasIpv4) {
208
+ addressParts = data.split(":");
209
+ const ipv4Part = addressParts.pop();
210
+ if (!/^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])$/.test(
211
+ ipv4Part
212
+ )) {
213
+ return false;
214
+ }
251
215
  }
252
- return true;
216
+ const isShortened = data.indexOf("::") !== -1;
217
+ const ipv6Part = hasIpv4 ? addressParts.join(":") : data;
218
+ if (isShortened) {
219
+ if (ipv6Part.split("::").length - 1 > 1) {
220
+ return false;
221
+ }
222
+ if (!/^[0-9a-fA-F:.]*$/.test(ipv6Part)) {
223
+ return false;
224
+ }
225
+ return /^(?:(?:(?:[0-9a-fA-F]{1,4}(?::|$)){1,6}))|(?:::(?:[0-9a-fA-F]{1,4})){0,5}$/.test(
226
+ ipv6Part
227
+ );
228
+ }
229
+ const isIpv6Valid = /^(?:(?:[0-9a-fA-F]{1,4}:){7}(?:[0-9a-fA-F]{1,4}|:))$/.test(ipv6Part);
230
+ const hasInvalidChar = /(?:[0-9a-fA-F]{5,}|\D[0-9a-fA-F]{3}:)/.test(
231
+ ipv6Part
232
+ );
233
+ if (hasIpv4) {
234
+ return isIpv6Valid || !hasInvalidChar;
235
+ }
236
+ return isIpv6Valid && !hasInvalidChar;
253
237
  },
254
- ipv4: (0, import_is_my_ip_valid.default)({ version: 4 }),
255
- ipv6: (0, import_is_my_ip_valid.default)({ version: 6 }),
256
238
  hostname(data) {
257
- return RegExps.hostname.test(data);
239
+ return /^[a-z0-9][a-z0-9-]{0,62}(?:\.[a-z0-9][a-z0-9-]{0,62})*[a-z0-9]$/i.test(
240
+ data
241
+ );
258
242
  },
259
243
  date(data) {
260
- if (typeof data !== "string") {
261
- return false;
262
- }
263
- if (RegExps.date.test(data) === false) {
244
+ if (/^(\d{4})-(\d{2})-(\d{2})$/.test(data) === false) {
264
245
  return false;
265
246
  }
266
247
  return !isNaN(new Date(data).getTime());
@@ -277,26 +258,39 @@ var Formats = {
277
258
  if (data === "") {
278
259
  return true;
279
260
  }
280
- return RegExps["json-pointer"].test(data);
261
+ return /^\/(?:[^~]|~0|~1)*$/.test(data);
281
262
  },
282
263
  "relative-json-pointer"(data) {
283
264
  if (data === "") {
284
265
  return true;
285
266
  }
286
- return RegExps["relative-json-pointer"].test(data);
267
+ return /^([0-9]+)(#|\/(?:[^~]|~0|~1)*)?$/.test(data);
287
268
  },
288
269
  time(data) {
289
- return RegExps.time.test(data);
270
+ return /^(\d{2}):(\d{2}):(\d{2})(\.\d+)?(Z|([+-])(\d{2}):(\d{2}))$/.test(
271
+ data
272
+ );
273
+ },
274
+ "uri-reference"(data) {
275
+ if (/\\/.test(data)) {
276
+ return false;
277
+ }
278
+ return /^(([^:/?#]+):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#((?![^#]*\\)[^#]*))?/i.test(
279
+ data
280
+ );
281
+ },
282
+ "uri-template"(data) {
283
+ return /^(?:(?:https?:\/\/[\w.-]+)?\/?)?[\w- ;,.\/?%&=]*(?:\{[\w-]+(?::\d+)?\}[\w- ;,.\/?%&=]*)*\/?$/.test(
284
+ data
285
+ );
290
286
  },
291
287
  // Not supported yet
292
288
  duration: false,
289
+ uuid: false,
293
290
  "idn-email": false,
294
291
  "idn-hostname": false,
295
- uuid: false,
296
- "uri-reference": false,
297
292
  iri: false,
298
- "iri-reference": false,
299
- "uri-template": false
293
+ "iri-reference": false
300
294
  };
301
295
 
302
296
  // lib/types.ts
@@ -421,7 +415,7 @@ var ArrayKeywords = {
421
415
  return defineError("Array is too long", { data });
422
416
  },
423
417
  additionalItems(schema, data, defineError) {
424
- if (!Array.isArray(data) || !schema.items || !Array.isArray(schema.items)) {
418
+ if (!schema.items || isObject(schema.items)) {
425
419
  return;
426
420
  }
427
421
  if (schema.additionalItems === false) {
@@ -994,21 +988,17 @@ var StringKeywords = {
994
988
  }
995
989
  return defineError("Value does not match the pattern", { data });
996
990
  },
997
- format(schema, data, defineError, formatInstance) {
991
+ // Take into account that if we receive a format that is not defined, we
992
+ // will not throw an error, we just ignore it.
993
+ format(schema, data, defineError, instance) {
998
994
  if (typeof data !== "string") {
999
995
  return;
1000
996
  }
1001
- const formatValidate = formatInstance.formats.get(schema.format);
1002
- if (formatValidate === false) {
997
+ const formatValidate = instance.getFormat(schema.format);
998
+ if (!formatValidate || formatValidate(data)) {
1003
999
  return;
1004
1000
  }
1005
- if (typeof formatValidate === "function") {
1006
- if (formatValidate(data)) {
1007
- return;
1008
- }
1009
- return defineError("Value does not match the format", { data });
1010
- }
1011
- return defineError("Format is not supported", { data });
1001
+ return defineError("Value does not match the format", { data });
1012
1002
  }
1013
1003
  };
1014
1004
 
@@ -1023,9 +1013,9 @@ var keywords = {
1023
1013
 
1024
1014
  // lib/index.ts
1025
1015
  var SchemaShield = class {
1026
- types = /* @__PURE__ */ new Map();
1027
- formats = /* @__PURE__ */ new Map();
1028
- keywords = /* @__PURE__ */ new Map();
1016
+ types = {};
1017
+ formats = {};
1018
+ keywords = {};
1029
1019
  immutable = false;
1030
1020
  constructor({
1031
1021
  immutable = false
@@ -1045,14 +1035,32 @@ var SchemaShield = class {
1045
1035
  }
1046
1036
  }
1047
1037
  }
1048
- addType(name, validator) {
1049
- this.types.set(name, validator);
1038
+ addType(name, validator, overwrite = false) {
1039
+ if (this.types[name] && !overwrite) {
1040
+ throw new ValidationError(`Type "${name}" already exists`);
1041
+ }
1042
+ this.types[name] = validator;
1043
+ }
1044
+ getType(type) {
1045
+ return this.types[type];
1050
1046
  }
1051
- addFormat(name, validator) {
1052
- this.formats.set(name, validator);
1047
+ addFormat(name, validator, overwrite = false) {
1048
+ if (this.formats[name] && !overwrite) {
1049
+ throw new ValidationError(`Format "${name}" already exists`);
1050
+ }
1051
+ this.formats[name] = validator;
1053
1052
  }
1054
- addKeyword(name, validator) {
1055
- this.keywords.set(name, validator);
1053
+ getFormat(format) {
1054
+ return this.formats[format];
1055
+ }
1056
+ addKeyword(name, validator, overwrite = false) {
1057
+ if (this.keywords[name] && !overwrite) {
1058
+ throw new ValidationError(`Keyword "${name}" already exists`);
1059
+ }
1060
+ this.keywords[name] = validator;
1061
+ }
1062
+ getKeyword(keyword) {
1063
+ return this.keywords[keyword];
1056
1064
  }
1057
1065
  compile(schema) {
1058
1066
  const compiledSchema = this.compileSchema(schema);
@@ -1101,7 +1109,7 @@ var SchemaShield = class {
1101
1109
  if ("type" in schema) {
1102
1110
  const types = Array.isArray(schema.type) ? schema.type : schema.type.split(",").map((t) => t.trim());
1103
1111
  for (const type of types) {
1104
- const validator = this.types.get(type);
1112
+ const validator = this.getType(type);
1105
1113
  if (validator) {
1106
1114
  typeValidations.push(validator);
1107
1115
  methodName += (methodName ? "_OR_" : "") + validator.name;
@@ -1116,10 +1124,9 @@ var SchemaShield = class {
1116
1124
  compiledSchema.$validate = getNamedFunction(
1117
1125
  methodName,
1118
1126
  (data) => {
1119
- if (typeValidation(data)) {
1120
- return;
1127
+ if (!typeValidation(data)) {
1128
+ return defineTypeError("Invalid type", { data });
1121
1129
  }
1122
- return defineTypeError("Invalid type", { data });
1123
1130
  }
1124
1131
  );
1125
1132
  } else if (typeValidationsLength > 1) {
@@ -1141,15 +1148,9 @@ var SchemaShield = class {
1141
1148
  compiledSchema.type = schema.type;
1142
1149
  continue;
1143
1150
  }
1144
- const keywordValidator = this.keywords.get(key);
1151
+ const keywordValidator = this.getKeyword(key);
1145
1152
  if (keywordValidator) {
1146
1153
  const defineError = getDefinedErrorFunctionForKey(key, schema[key]);
1147
- const executeKeywordValidator = (data) => keywordValidator(
1148
- compiledSchema,
1149
- data,
1150
- defineError,
1151
- this
1152
- );
1153
1154
  if (compiledSchema.$validate) {
1154
1155
  const prevValidator = compiledSchema.$validate;
1155
1156
  methodName += `_AND_${keywordValidator.name}`;
@@ -1160,17 +1161,24 @@ var SchemaShield = class {
1160
1161
  if (error) {
1161
1162
  return error;
1162
1163
  }
1163
- const keywordError = executeKeywordValidator(data);
1164
- if (keywordError) {
1165
- return keywordError;
1166
- }
1164
+ return keywordValidator(
1165
+ compiledSchema,
1166
+ data,
1167
+ defineError,
1168
+ this
1169
+ );
1167
1170
  }
1168
1171
  );
1169
1172
  } else {
1170
1173
  methodName = keywordValidator.name;
1171
1174
  compiledSchema.$validate = getNamedFunction(
1172
1175
  methodName,
1173
- executeKeywordValidator
1176
+ (data) => keywordValidator(
1177
+ compiledSchema,
1178
+ data,
1179
+ defineError,
1180
+ this
1181
+ )
1174
1182
  );
1175
1183
  }
1176
1184
  }
@@ -1194,7 +1202,7 @@ var SchemaShield = class {
1194
1202
  return true;
1195
1203
  }
1196
1204
  for (let subKey in subSchema) {
1197
- if (this.keywords.has(subKey)) {
1205
+ if (subKey in this.keywords) {
1198
1206
  return true;
1199
1207
  }
1200
1208
  }
@@ -1 +1 @@
1
- {"version":3,"file":"string-keywords.d.ts","sourceRoot":"","sources":["../../lib/keywords/string-keywords.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAE3C,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAuD1D,CAAC"}
1
+ {"version":3,"file":"string-keywords.d.ts","sourceRoot":"","sources":["../../lib/keywords/string-keywords.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAE3C,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAiD1D,CAAC"}