@oscarpalmer/jhunal 0.10.0 → 0.12.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/dist/constants.js CHANGED
@@ -1,23 +1,37 @@
1
- const EXPRESSION_HAS_NUMBER = /\d+/;
1
+ const ERROR_NAME = "SchematicError";
2
2
  const EXPRESSION_INDEX = /\.\d+$/;
3
- const EXPRESSION_PROPERTY = /\.\$(required|type|validators)(\.|$)/;
3
+ const EXPRESSION_KEY_PREFIX = /\.\w+$/;
4
+ const EXPRESSION_KEY_VALUE = /^.*\.(\w+)$/;
5
+ const EXPRESSION_PROPERTY = /(^|\.)\$(required|type|validators)(\.|$)/;
6
+ const MESSAGE_CONSTRUCTOR = "Expected a constructor function";
7
+ const MESSAGE_SCHEMA_INVALID_EMPTY = "Schema must have at least one property";
8
+ const MESSAGE_SCHEMA_INVALID_PROPERTY_REQUIRED = "'<>.$required' property must be a boolean";
9
+ const MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE = "'<>' property must be of a valid type";
10
+ const MESSAGE_SCHEMA_INVALID_TYPE = "Schema must be an object";
11
+ const MESSAGE_VALIDATOR_INVALID_KEY = "Validator '<>' does not exist";
12
+ const MESSAGE_VALIDATOR_INVALID_TYPE = "Validators must be an object";
13
+ const MESSAGE_VALIDATOR_INVALID_VALUE = "Validator '<>' must be a function or an array of functions";
4
14
  const PROPERTY_REQUIRED = "$required";
5
15
  const PROPERTY_TYPE = "$type";
6
16
  const PROPERTY_VALIDATORS = "$validators";
7
17
  const SCHEMATIC_NAME = "$schematic";
18
+ const TEMPLATE_PATTERN = "<>";
8
19
  const TYPE_OBJECT = "object";
9
20
  const TYPE_UNDEFINED = "undefined";
10
- const TYPE_ALL = new Set([
21
+ const VALIDATABLE_TYPES = new Set([
11
22
  "array",
12
23
  "bigint",
13
24
  "boolean",
14
25
  "date",
15
26
  "function",
16
- "null",
17
27
  "number",
18
28
  "string",
19
29
  "symbol",
20
- TYPE_OBJECT,
30
+ TYPE_OBJECT
31
+ ]);
32
+ const TYPE_ALL = new Set([
33
+ ...VALIDATABLE_TYPES,
34
+ "null",
21
35
  TYPE_UNDEFINED
22
36
  ]);
23
- export { EXPRESSION_HAS_NUMBER, EXPRESSION_INDEX, EXPRESSION_PROPERTY, PROPERTY_REQUIRED, PROPERTY_TYPE, PROPERTY_VALIDATORS, SCHEMATIC_NAME, TYPE_ALL, TYPE_OBJECT, TYPE_UNDEFINED };
37
+ export { ERROR_NAME, EXPRESSION_INDEX, EXPRESSION_KEY_PREFIX, EXPRESSION_KEY_VALUE, EXPRESSION_PROPERTY, MESSAGE_CONSTRUCTOR, MESSAGE_SCHEMA_INVALID_EMPTY, MESSAGE_SCHEMA_INVALID_PROPERTY_REQUIRED, MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE, MESSAGE_SCHEMA_INVALID_TYPE, MESSAGE_VALIDATOR_INVALID_KEY, MESSAGE_VALIDATOR_INVALID_TYPE, MESSAGE_VALIDATOR_INVALID_VALUE, PROPERTY_REQUIRED, PROPERTY_TYPE, PROPERTY_VALIDATORS, SCHEMATIC_NAME, TEMPLATE_PATTERN, TYPE_ALL, TYPE_OBJECT, TYPE_UNDEFINED, VALIDATABLE_TYPES };
package/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
- import { isInstance } from "./is.js";
1
+ import { instanceOf } from "./is.js";
2
+ import { SchematicError } from "./models.js";
2
3
  import { schematic } from "./schematic.js";
3
- export { isInstance, schematic };
4
+ export { SchematicError, instanceOf, schematic };
package/dist/is.js CHANGED
@@ -1,7 +1,7 @@
1
- import "./constants.js";
1
+ import { MESSAGE_CONSTRUCTOR } from "./constants.js";
2
2
  import { isConstructor } from "@oscarpalmer/atoms/is";
3
- function isInstance(constructor) {
4
- if (!isConstructor(constructor)) throw new TypeError("Expected a constructor function");
3
+ function instanceOf(constructor) {
4
+ if (!isConstructor(constructor)) throw new TypeError(MESSAGE_CONSTRUCTOR);
5
5
  return (value) => {
6
6
  return value instanceof constructor;
7
7
  };
@@ -9,4 +9,4 @@ function isInstance(constructor) {
9
9
  function isSchematic(value) {
10
10
  return typeof value === "object" && value !== null && "$schematic" in value && value["$schematic"] === true;
11
11
  }
12
- export { isInstance, isSchematic };
12
+ export { instanceOf, isSchematic };
@@ -58,30 +58,44 @@ function getString(value) {
58
58
  function join(value, delimiter) {
59
59
  return compact(value).map(getString).join(typeof delimiter === "string" ? delimiter : "");
60
60
  }
61
- const EXPRESSION_HAS_NUMBER = /\d+/;
61
+ const ERROR_NAME = "SchematicError";
62
62
  const EXPRESSION_INDEX = /\.\d+$/;
63
- const EXPRESSION_PROPERTY = /\.\$(required|type|validators)(\.|$)/;
63
+ const EXPRESSION_KEY_PREFIX = /\.\w+$/;
64
+ const EXPRESSION_KEY_VALUE = /^.*\.(\w+)$/;
65
+ const EXPRESSION_PROPERTY = /(^|\.)\$(required|type|validators)(\.|$)/;
66
+ const MESSAGE_CONSTRUCTOR = "Expected a constructor function";
67
+ const MESSAGE_SCHEMA_INVALID_EMPTY = "Schema must have at least one property";
68
+ const MESSAGE_SCHEMA_INVALID_PROPERTY_REQUIRED = "'<>.$required' property must be a boolean";
69
+ const MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE = "'<>' property must be of a valid type";
70
+ const MESSAGE_SCHEMA_INVALID_TYPE = "Schema must be an object";
71
+ const MESSAGE_VALIDATOR_INVALID_KEY = "Validator '<>' does not exist";
72
+ const MESSAGE_VALIDATOR_INVALID_TYPE = "Validators must be an object";
73
+ const MESSAGE_VALIDATOR_INVALID_VALUE = "Validator '<>' must be a function or an array of functions";
64
74
  const PROPERTY_REQUIRED = "$required";
65
75
  const PROPERTY_TYPE = "$type";
66
76
  const PROPERTY_VALIDATORS = "$validators";
67
77
  const SCHEMATIC_NAME = "$schematic";
78
+ const TEMPLATE_PATTERN = "<>";
68
79
  const TYPE_OBJECT = "object";
69
80
  const TYPE_UNDEFINED = "undefined";
70
- const TYPE_ALL = new Set([
81
+ const VALIDATABLE_TYPES = new Set([
71
82
  "array",
72
83
  "bigint",
73
84
  "boolean",
74
85
  "date",
75
86
  "function",
76
- "null",
77
87
  "number",
78
88
  "string",
79
89
  "symbol",
80
- TYPE_OBJECT,
90
+ TYPE_OBJECT
91
+ ]);
92
+ const TYPE_ALL = new Set([
93
+ ...VALIDATABLE_TYPES,
94
+ "null",
81
95
  TYPE_UNDEFINED
82
96
  ]);
83
- function isInstance(constructor) {
84
- if (!isConstructor(constructor)) throw new TypeError("Expected a constructor function");
97
+ function instanceOf(constructor) {
98
+ if (!isConstructor(constructor)) throw new TypeError(MESSAGE_CONSTRUCTOR);
85
99
  return (value) => {
86
100
  return value instanceof constructor;
87
101
  };
@@ -89,6 +103,12 @@ function isInstance(constructor) {
89
103
  function isSchematic(value) {
90
104
  return typeof value === "object" && value !== null && SCHEMATIC_NAME in value && value[SCHEMATIC_NAME] === true;
91
105
  }
106
+ var SchematicError = class extends Error {
107
+ constructor(message) {
108
+ super(message);
109
+ this.name = ERROR_NAME;
110
+ }
111
+ };
92
112
  function flattenObject(value, depth, smushed, prefix) {
93
113
  if (depth >= MAX_DEPTH) return {};
94
114
  if (smushed.has(value)) return smushed.get(value);
@@ -122,122 +142,116 @@ function smush(value) {
122
142
  return typeof value === "object" && value !== null ? flattenObject(value, 0, /* @__PURE__ */ new WeakMap()) : {};
123
143
  }
124
144
  var MAX_DEPTH = 100;
125
- function addPropertyType(to, key, values, validators, required) {
126
- if (to.keys.set.has(key)) {
127
- const property = to.properties[key];
128
- for (const type of values) if (!property.types.includes(type)) property.types.push(type);
129
- } else {
130
- to.keys.array.push(key);
131
- to.keys.set.add(key);
132
- to.properties[key] = {
133
- required,
134
- types: values,
135
- validators: {}
136
- };
137
- }
138
- if (!required && !to.properties[key].types.includes(TYPE_UNDEFINED)) to.properties[key].types.push(TYPE_UNDEFINED);
139
- to.properties[key].validators = validators;
145
+ function getKeyPrefix(key) {
146
+ const prefix = key.replace(EXPRESSION_KEY_PREFIX, "");
147
+ return prefix === key ? void 0 : prefix;
140
148
  }
141
- function getSchema(schema) {
142
- const validated = {
143
- enabled: false,
144
- keys: {
145
- array: [],
146
- set: /* @__PURE__ */ new Set()
147
- },
148
- properties: {}
149
- };
150
- return typeof schema === "object" && schema !== null ? getValidatedSchema(schema, validated) : validated;
149
+ function getKeyValue(key) {
150
+ return key.replace(EXPRESSION_KEY_VALUE, "$1");
151
151
  }
152
- function getTypes(value, validated, prefix) {
153
- const propertyTypes = [];
154
- const values = Array.isArray(value) ? value : [value];
155
- const { length } = values;
156
- for (let index = 0; index < length; index += 1) {
157
- const type = values[index];
158
- const typeOfType = typeof type;
159
- if (isSchematic(type) || typeOfType === "string" && TYPE_ALL.has(type)) {
160
- propertyTypes.push(type);
161
- continue;
162
- }
163
- if (typeOfType === "function") {
164
- propertyTypes.push(isConstructor(type) ? isInstance(type) : type);
165
- continue;
166
- }
167
- if (typeOfType !== "object" || type === null) continue;
168
- if (PROPERTY_TYPE in type) {
169
- propertyTypes.push(...getTypes(type[PROPERTY_TYPE], validated, prefix));
170
- continue;
171
- }
172
- addPropertyType(validated, prefix, [TYPE_OBJECT], {}, type[PROPERTY_REQUIRED] !== false);
173
- propertyTypes.push(TYPE_OBJECT);
174
- getValidatedSchema(type, validated, prefix);
175
- }
176
- return propertyTypes;
177
- }
178
- function getValidatedSchema(schema, validated, prefix) {
179
- const smushed = smush(schema);
152
+ function getProperties(original) {
153
+ if (Object.keys(original).length === 0) throw new SchematicError(MESSAGE_SCHEMA_INVALID_EMPTY);
154
+ const smushed = smush(original);
180
155
  const keys = Object.keys(smushed);
181
- const { length } = keys;
182
- const arrayKeys = /* @__PURE__ */ new Set();
183
- const noPrefix = prefix == null;
184
- prefix = noPrefix ? "" : `${prefix}.`;
185
- for (let index = 0; index < length; index += 1) {
186
- const key = keys[index];
156
+ const keysLength = keys.length;
157
+ const properties = [];
158
+ for (let keyIndex = 0; keyIndex < keysLength; keyIndex += 1) {
159
+ const key = keys[keyIndex];
160
+ if (EXPRESSION_INDEX.test(key) || EXPRESSION_PROPERTY.test(key)) continue;
161
+ const keyPrefix = getKeyPrefix(key);
162
+ const keyValue = getKeyValue(key);
187
163
  const value = smushed[key];
188
- if (Array.isArray(value)) arrayKeys.add(key);
189
- if (EXPRESSION_PROPERTY.test(key)) continue;
190
- if (EXPRESSION_HAS_NUMBER.test(key) && arrayKeys.has(key.replace(EXPRESSION_INDEX, ""))) continue;
164
+ const types = [];
191
165
  let required = true;
192
166
  let validators = {};
193
- const isObject = typeof value === "object" && value !== null;
194
- if (isObject && PROPERTY_REQUIRED in value) required = typeof value[PROPERTY_REQUIRED] === "boolean" ? value[PROPERTY_REQUIRED] : true;
195
- if (isObject && PROPERTY_VALIDATORS in value) validators = getValidators(value[PROPERTY_VALIDATORS]);
196
- const prefixedKey = `${prefix}${key}`;
197
- const types = getTypes(value, validated, prefixedKey);
198
- if (types.length > 0) addPropertyType(validated, prefixedKey, types, validators, required);
167
+ if (isPlainObject(value)) {
168
+ required = getRequired(key, value) ?? required;
169
+ validators = getValidators(value[PROPERTY_VALIDATORS]);
170
+ if (PROPERTY_TYPE in value) types.push(...getTypes(key, value[PROPERTY_TYPE]));
171
+ else types.push(TYPE_OBJECT);
172
+ } else types.push(...getTypes(key, value));
173
+ if (!required && !types.includes(TYPE_UNDEFINED)) types.push(TYPE_UNDEFINED);
174
+ properties.push({
175
+ types,
176
+ validators,
177
+ key: {
178
+ full: key,
179
+ prefix: keyPrefix,
180
+ value: keyValue
181
+ },
182
+ required: required && !types.includes(TYPE_UNDEFINED)
183
+ });
199
184
  }
200
- if (noPrefix) validated.keys.array.sort();
201
- return validated;
185
+ return properties;
186
+ }
187
+ function getRequired(key, value) {
188
+ if (!(PROPERTY_REQUIRED in value)) return;
189
+ if (typeof value[PROPERTY_REQUIRED] !== "boolean") throw new SchematicError(MESSAGE_SCHEMA_INVALID_PROPERTY_REQUIRED.replace(TEMPLATE_PATTERN, key));
190
+ return value[PROPERTY_REQUIRED];
191
+ }
192
+ function getTypes(key, original) {
193
+ const array = Array.isArray(original) ? original : [original];
194
+ const { length } = array;
195
+ const types = [];
196
+ for (let index = 0; index < length; index += 1) {
197
+ const value = array[index];
198
+ switch (true) {
199
+ case typeof value === "function":
200
+ types.push(isConstructor(value) ? instanceOf(value) : value);
201
+ break;
202
+ case isPlainObject(value):
203
+ types.push(schematic(value));
204
+ break;
205
+ case isSchematic(value):
206
+ types.push(value);
207
+ break;
208
+ case TYPE_ALL.has(value):
209
+ types.push(value);
210
+ break;
211
+ default: throw new SchematicError(MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE.replace(TEMPLATE_PATTERN, key));
212
+ }
213
+ }
214
+ if (types.length === 0) throw new SchematicError(MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE.replace(TEMPLATE_PATTERN, key));
215
+ return types;
202
216
  }
203
217
  function getValidators(original) {
204
218
  const validators = {};
205
- if (typeof original !== "object" || original === null) return validators;
206
- for (const type of TYPE_ALL) {
207
- const value = original[type];
208
- validators[type] = (Array.isArray(value) ? value : [value]).filter((validator) => typeof validator === "function");
219
+ if (original == null) return validators;
220
+ if (!isPlainObject(original)) throw new TypeError(MESSAGE_VALIDATOR_INVALID_TYPE);
221
+ const keys = Object.keys(original);
222
+ const { length } = keys;
223
+ for (let index = 0; index < length; index += 1) {
224
+ const key = keys[index];
225
+ if (!VALIDATABLE_TYPES.has(key)) throw new TypeError(MESSAGE_VALIDATOR_INVALID_KEY.replace(TEMPLATE_PATTERN, key));
226
+ const value = original[key];
227
+ validators[key] = (Array.isArray(value) ? value : [value]).filter((item) => {
228
+ if (typeof item !== "function") throw new TypeError(MESSAGE_VALIDATOR_INVALID_VALUE.replace(TEMPLATE_PATTERN, key));
229
+ return true;
230
+ });
209
231
  }
210
232
  return validators;
211
233
  }
212
- function validateType(type, property, value) {
213
- switch (true) {
214
- case typeof type === "function": return type(value);
215
- case typeof type === "string": return validators[type](value) && (property.validators[type]?.every((validator) => validator(value)) ?? true);
216
- default: return type.is(value);
217
- }
218
- }
219
- function validateValue(validated, obj) {
220
- if (typeof obj !== "object" || obj === null) return false;
221
- const { keys, properties } = validated;
222
- const keysLength = keys.array.length;
223
- const ignore = /* @__PURE__ */ new Set();
224
- const smushed = smush(obj);
225
- outer: for (let keyIndex = 0; keyIndex < keysLength; keyIndex += 1) {
226
- const key = keys.array[keyIndex];
227
- const prefix = key.replace(EXPRESSION_SUFFIX, "");
228
- if (ignore.has(prefix)) continue;
229
- const property = properties[key];
230
- const value = smushed[key];
231
- if (value === void 0 && property.required && !property.types.includes(TYPE_UNDEFINED)) return false;
232
- const typesLength = property.types.length;
233
- if (typesLength === 1) {
234
- if (!validateType(property.types[0], property, value)) return false;
234
+ function validateObject(obj, properties) {
235
+ if (!isPlainObject(obj)) return false;
236
+ const ignoredKeys = /* @__PURE__ */ new Set();
237
+ const propertiesLength = properties.length;
238
+ let key;
239
+ let value;
240
+ outer: for (let propertyIndex = 0; propertyIndex < propertiesLength; propertyIndex += 1) {
241
+ const property = properties[propertyIndex];
242
+ if (ignoredKeys.has(property.key.prefix)) {
243
+ key = void 0;
244
+ ignoredKeys.add(property.key.full);
235
245
  continue;
236
246
  }
247
+ key = property.key.full;
248
+ value = obj[key];
249
+ if (value === void 0 && property.required) return false;
250
+ const typesLength = property.types.length;
237
251
  for (let typeIndex = 0; typeIndex < typesLength; typeIndex += 1) {
238
252
  const type = property.types[typeIndex];
239
- if (validateType(type, property, value)) {
240
- if (type !== "object") ignore.add(key);
253
+ if (validateValue(type, property, value)) {
254
+ ignoredKeys.add(property.key.full);
241
255
  continue outer;
242
256
  }
243
257
  }
@@ -245,7 +259,13 @@ function validateValue(validated, obj) {
245
259
  }
246
260
  return true;
247
261
  }
248
- const EXPRESSION_SUFFIX = /\.\w+$/;
262
+ function validateValue(type, property, value) {
263
+ switch (true) {
264
+ case typeof type === "function": return type(value);
265
+ case isSchematic(type): return type.is(value);
266
+ default: return validators[type](value) && (property.validators[type]?.every((validator) => validator(value)) ?? true);
267
+ }
268
+ }
249
269
  const validators = {
250
270
  array: Array.isArray,
251
271
  bigint: (value) => typeof value === "bigint",
@@ -253,7 +273,7 @@ const validators = {
253
273
  date: (value) => value instanceof Date,
254
274
  function: (value) => typeof value === "function",
255
275
  null: (value) => value === null,
256
- number: (value) => typeof value === "number" && !Number.isNaN(value),
276
+ number: (value) => typeof value === "number",
257
277
  object: (value) => typeof value === "object" && value !== null,
258
278
  string: (value) => typeof value === "string",
259
279
  symbol: (value) => typeof value === "symbol",
@@ -263,23 +283,21 @@ const validators = {
263
283
  * A schematic for validating objects
264
284
  */
265
285
  var Schematic = class {
266
- #schema;
267
- get enabled() {
268
- return this.#schema.enabled;
269
- }
270
- constructor(schema) {
286
+ #properties;
287
+ constructor(properties) {
271
288
  Object.defineProperty(this, SCHEMATIC_NAME, { value: true });
272
- this.#schema = getSchema(schema);
273
- this.#schema.enabled = this.#schema.keys.array.length > 0;
289
+ this.#properties = properties;
274
290
  }
275
291
  /**
276
292
  * Does the value match the schema?
277
293
  */
278
294
  is(value) {
279
- return this.#schema.enabled && validateValue(this.#schema, value);
295
+ return validateObject(value, this.#properties);
280
296
  }
281
297
  };
282
298
  function schematic(schema) {
283
- return new Schematic(schema);
299
+ if (isSchematic(schema)) return schema;
300
+ if (!isPlainObject(schema)) throw new SchematicError(MESSAGE_SCHEMA_INVALID_TYPE);
301
+ return new Schematic(getProperties(schema));
284
302
  }
285
- export { isInstance, schematic };
303
+ export { SchematicError, instanceOf, schematic };
package/dist/models.js CHANGED
@@ -0,0 +1,8 @@
1
+ import { ERROR_NAME } from "./constants.js";
2
+ var SchematicError = class extends Error {
3
+ constructor(message) {
4
+ super(message);
5
+ this.name = ERROR_NAME;
6
+ }
7
+ };
8
+ export { SchematicError };
package/dist/schematic.js CHANGED
@@ -1,27 +1,28 @@
1
- import { SCHEMATIC_NAME } from "./constants.js";
2
- import { getSchema } from "./validation/schema.validation.js";
3
- import { validateValue } from "./validation/value.validation.js";
1
+ import { MESSAGE_SCHEMA_INVALID_TYPE, SCHEMATIC_NAME } from "./constants.js";
2
+ import { isSchematic } from "./is.js";
3
+ import { SchematicError } from "./models.js";
4
+ import { getProperties } from "./validation/property.validation.js";
5
+ import { validateObject } from "./validation/value.validation.js";
6
+ import { isPlainObject } from "@oscarpalmer/atoms/is";
4
7
  /**
5
8
  * A schematic for validating objects
6
9
  */
7
10
  var Schematic = class {
8
- #schema;
9
- get enabled() {
10
- return this.#schema.enabled;
11
- }
12
- constructor(schema) {
11
+ #properties;
12
+ constructor(properties) {
13
13
  Object.defineProperty(this, SCHEMATIC_NAME, { value: true });
14
- this.#schema = getSchema(schema);
15
- this.#schema.enabled = this.#schema.keys.array.length > 0;
14
+ this.#properties = properties;
16
15
  }
17
16
  /**
18
17
  * Does the value match the schema?
19
18
  */
20
19
  is(value) {
21
- return this.#schema.enabled && validateValue(this.#schema, value);
20
+ return validateObject(value, this.#properties);
22
21
  }
23
22
  };
24
23
  function schematic(schema) {
25
- return new Schematic(schema);
24
+ if (isSchematic(schema)) return schema;
25
+ if (!isPlainObject(schema)) throw new SchematicError(MESSAGE_SCHEMA_INVALID_TYPE);
26
+ return new Schematic(getProperties(schema));
26
27
  }
27
28
  export { Schematic, schematic };
@@ -0,0 +1,96 @@
1
+ import { EXPRESSION_INDEX, EXPRESSION_KEY_PREFIX, EXPRESSION_KEY_VALUE, EXPRESSION_PROPERTY, MESSAGE_SCHEMA_INVALID_EMPTY, MESSAGE_SCHEMA_INVALID_PROPERTY_REQUIRED, MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE, MESSAGE_VALIDATOR_INVALID_KEY, MESSAGE_VALIDATOR_INVALID_TYPE, MESSAGE_VALIDATOR_INVALID_VALUE, PROPERTY_REQUIRED, PROPERTY_TYPE, PROPERTY_VALIDATORS, TYPE_ALL, TYPE_OBJECT, TYPE_UNDEFINED, VALIDATABLE_TYPES } from "../constants.js";
2
+ import { instanceOf, isSchematic } from "../is.js";
3
+ import { SchematicError } from "../models.js";
4
+ import { schematic } from "../schematic.js";
5
+ import { isConstructor, isPlainObject } from "@oscarpalmer/atoms/is";
6
+ import { smush } from "@oscarpalmer/atoms/value/misc";
7
+ function getKeyPrefix(key) {
8
+ const prefix = key.replace(EXPRESSION_KEY_PREFIX, "");
9
+ return prefix === key ? void 0 : prefix;
10
+ }
11
+ function getKeyValue(key) {
12
+ return key.replace(EXPRESSION_KEY_VALUE, "$1");
13
+ }
14
+ function getProperties(original) {
15
+ if (Object.keys(original).length === 0) throw new SchematicError(MESSAGE_SCHEMA_INVALID_EMPTY);
16
+ const smushed = smush(original);
17
+ const keys = Object.keys(smushed);
18
+ const keysLength = keys.length;
19
+ const properties = [];
20
+ for (let keyIndex = 0; keyIndex < keysLength; keyIndex += 1) {
21
+ const key = keys[keyIndex];
22
+ if (EXPRESSION_INDEX.test(key) || EXPRESSION_PROPERTY.test(key)) continue;
23
+ const keyPrefix = getKeyPrefix(key);
24
+ const keyValue = getKeyValue(key);
25
+ const value = smushed[key];
26
+ const types = [];
27
+ let required = true;
28
+ let validators = {};
29
+ if (isPlainObject(value)) {
30
+ required = getRequired(key, value) ?? required;
31
+ validators = getValidators(value[PROPERTY_VALIDATORS]);
32
+ if ("$type" in value) types.push(...getTypes(key, value[PROPERTY_TYPE]));
33
+ else types.push(TYPE_OBJECT);
34
+ } else types.push(...getTypes(key, value));
35
+ if (!required && !types.includes("undefined")) types.push(TYPE_UNDEFINED);
36
+ properties.push({
37
+ types,
38
+ validators,
39
+ key: {
40
+ full: key,
41
+ prefix: keyPrefix,
42
+ value: keyValue
43
+ },
44
+ required: required && !types.includes("undefined")
45
+ });
46
+ }
47
+ return properties;
48
+ }
49
+ function getRequired(key, value) {
50
+ if (!("$required" in value)) return;
51
+ if (typeof value["$required"] !== "boolean") throw new SchematicError(MESSAGE_SCHEMA_INVALID_PROPERTY_REQUIRED.replace("<>", key));
52
+ return value[PROPERTY_REQUIRED];
53
+ }
54
+ function getTypes(key, original) {
55
+ const array = Array.isArray(original) ? original : [original];
56
+ const { length } = array;
57
+ const types = [];
58
+ for (let index = 0; index < length; index += 1) {
59
+ const value = array[index];
60
+ switch (true) {
61
+ case typeof value === "function":
62
+ types.push(isConstructor(value) ? instanceOf(value) : value);
63
+ break;
64
+ case isPlainObject(value):
65
+ types.push(schematic(value));
66
+ break;
67
+ case isSchematic(value):
68
+ types.push(value);
69
+ break;
70
+ case TYPE_ALL.has(value):
71
+ types.push(value);
72
+ break;
73
+ default: throw new SchematicError(MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE.replace("<>", key));
74
+ }
75
+ }
76
+ if (types.length === 0) throw new SchematicError(MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE.replace("<>", key));
77
+ return types;
78
+ }
79
+ function getValidators(original) {
80
+ const validators = {};
81
+ if (original == null) return validators;
82
+ if (!isPlainObject(original)) throw new TypeError(MESSAGE_VALIDATOR_INVALID_TYPE);
83
+ const keys = Object.keys(original);
84
+ const { length } = keys;
85
+ for (let index = 0; index < length; index += 1) {
86
+ const key = keys[index];
87
+ if (!VALIDATABLE_TYPES.has(key)) throw new TypeError(MESSAGE_VALIDATOR_INVALID_KEY.replace("<>", key));
88
+ const value = original[key];
89
+ validators[key] = (Array.isArray(value) ? value : [value]).filter((item) => {
90
+ if (typeof item !== "function") throw new TypeError(MESSAGE_VALIDATOR_INVALID_VALUE.replace("<>", key));
91
+ return true;
92
+ });
93
+ }
94
+ return validators;
95
+ }
96
+ export { getProperties };
@@ -1,34 +1,26 @@
1
- import "../constants.js";
2
- import { smush } from "../node_modules/@oscarpalmer/atoms/dist/value/smush.js";
3
- function validateType(type, property, value) {
4
- switch (true) {
5
- case typeof type === "function": return type(value);
6
- case typeof type === "string": return validators[type](value) && (property.validators[type]?.every((validator) => validator(value)) ?? true);
7
- default: return type.is(value);
8
- }
9
- }
10
- function validateValue(validated, obj) {
11
- if (typeof obj !== "object" || obj === null) return false;
12
- const { keys, properties } = validated;
13
- const keysLength = keys.array.length;
14
- const ignore = /* @__PURE__ */ new Set();
15
- const smushed = smush(obj);
16
- outer: for (let keyIndex = 0; keyIndex < keysLength; keyIndex += 1) {
17
- const key = keys.array[keyIndex];
18
- const prefix = key.replace(EXPRESSION_SUFFIX, "");
19
- if (ignore.has(prefix)) continue;
20
- const property = properties[key];
21
- const value = smushed[key];
22
- if (value === void 0 && property.required && !property.types.includes("undefined")) return false;
23
- const typesLength = property.types.length;
24
- if (typesLength === 1) {
25
- if (!validateType(property.types[0], property, value)) return false;
1
+ import { isSchematic } from "../is.js";
2
+ import { isPlainObject } from "@oscarpalmer/atoms/is";
3
+ function validateObject(obj, properties) {
4
+ if (!isPlainObject(obj)) return false;
5
+ const ignoredKeys = /* @__PURE__ */ new Set();
6
+ const propertiesLength = properties.length;
7
+ let key;
8
+ let value;
9
+ outer: for (let propertyIndex = 0; propertyIndex < propertiesLength; propertyIndex += 1) {
10
+ const property = properties[propertyIndex];
11
+ if (ignoredKeys.has(property.key.prefix)) {
12
+ key = void 0;
13
+ ignoredKeys.add(property.key.full);
26
14
  continue;
27
15
  }
16
+ key = property.key.full;
17
+ value = obj[key];
18
+ if (value === void 0 && property.required) return false;
19
+ const typesLength = property.types.length;
28
20
  for (let typeIndex = 0; typeIndex < typesLength; typeIndex += 1) {
29
21
  const type = property.types[typeIndex];
30
- if (validateType(type, property, value)) {
31
- if (type !== "object") ignore.add(key);
22
+ if (validateValue(type, property, value)) {
23
+ ignoredKeys.add(property.key.full);
32
24
  continue outer;
33
25
  }
34
26
  }
@@ -36,7 +28,13 @@ function validateValue(validated, obj) {
36
28
  }
37
29
  return true;
38
30
  }
39
- var EXPRESSION_SUFFIX = /\.\w+$/;
31
+ function validateValue(type, property, value) {
32
+ switch (true) {
33
+ case typeof type === "function": return type(value);
34
+ case isSchematic(type): return type.is(value);
35
+ default: return validators[type](value) && (property.validators[type]?.every((validator) => validator(value)) ?? true);
36
+ }
37
+ }
40
38
  var validators = {
41
39
  array: Array.isArray,
42
40
  bigint: (value) => typeof value === "bigint",
@@ -44,10 +42,10 @@ var validators = {
44
42
  date: (value) => value instanceof Date,
45
43
  function: (value) => typeof value === "function",
46
44
  null: (value) => value === null,
47
- number: (value) => typeof value === "number" && !Number.isNaN(value),
45
+ number: (value) => typeof value === "number",
48
46
  object: (value) => typeof value === "object" && value !== null,
49
47
  string: (value) => typeof value === "string",
50
48
  symbol: (value) => typeof value === "symbol",
51
49
  undefined: (value) => value === void 0
52
50
  };
53
- export { validateType, validateValue };
51
+ export { validateObject };
package/package.json CHANGED
@@ -45,5 +45,5 @@
45
45
  },
46
46
  "type": "module",
47
47
  "types": "./types/index.d.ts",
48
- "version": "0.10.0"
48
+ "version": "0.12.0"
49
49
  }