@oscarpalmer/jhunal 0.11.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,7 +1,8 @@
1
1
  const ERROR_NAME = "SchematicError";
2
- const EXPRESSION_HAS_NUMBER = /\d+/;
3
2
  const EXPRESSION_INDEX = /\.\d+$/;
4
- 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)(\.|$)/;
5
6
  const MESSAGE_CONSTRUCTOR = "Expected a constructor function";
6
7
  const MESSAGE_SCHEMA_INVALID_EMPTY = "Schema must have at least one property";
7
8
  const MESSAGE_SCHEMA_INVALID_PROPERTY_REQUIRED = "'<>.$required' property must be a boolean";
@@ -33,4 +34,4 @@ const TYPE_ALL = new Set([
33
34
  "null",
34
35
  TYPE_UNDEFINED
35
36
  ]);
36
- export { ERROR_NAME, EXPRESSION_HAS_NUMBER, EXPRESSION_INDEX, 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 };
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,4 +1,4 @@
1
- import { isInstance } from "./is.js";
1
+ import { instanceOf } from "./is.js";
2
2
  import { SchematicError } from "./models.js";
3
3
  import { schematic } from "./schematic.js";
4
- export { SchematicError, isInstance, schematic };
4
+ export { SchematicError, instanceOf, schematic };
package/dist/is.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { MESSAGE_CONSTRUCTOR } from "./constants.js";
2
2
  import { isConstructor } from "@oscarpalmer/atoms/is";
3
- function isInstance(constructor) {
3
+ function instanceOf(constructor) {
4
4
  if (!isConstructor(constructor)) throw new TypeError(MESSAGE_CONSTRUCTOR);
5
5
  return (value) => {
6
6
  return value instanceof constructor;
@@ -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 };
@@ -59,9 +59,10 @@ function join(value, delimiter) {
59
59
  return compact(value).map(getString).join(typeof delimiter === "string" ? delimiter : "");
60
60
  }
61
61
  const ERROR_NAME = "SchematicError";
62
- const EXPRESSION_HAS_NUMBER = /\d+/;
63
62
  const EXPRESSION_INDEX = /\.\d+$/;
64
- 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)(\.|$)/;
65
66
  const MESSAGE_CONSTRUCTOR = "Expected a constructor function";
66
67
  const MESSAGE_SCHEMA_INVALID_EMPTY = "Schema must have at least one property";
67
68
  const MESSAGE_SCHEMA_INVALID_PROPERTY_REQUIRED = "'<>.$required' property must be a boolean";
@@ -93,7 +94,7 @@ const TYPE_ALL = new Set([
93
94
  "null",
94
95
  TYPE_UNDEFINED
95
96
  ]);
96
- function isInstance(constructor) {
97
+ function instanceOf(constructor) {
97
98
  if (!isConstructor(constructor)) throw new TypeError(MESSAGE_CONSTRUCTOR);
98
99
  return (value) => {
99
100
  return value instanceof constructor;
@@ -141,87 +142,77 @@ function smush(value) {
141
142
  return typeof value === "object" && value !== null ? flattenObject(value, 0, /* @__PURE__ */ new WeakMap()) : {};
142
143
  }
143
144
  var MAX_DEPTH = 100;
144
- function addPropertyType(to, key, values, validators, required) {
145
- if (to.keys.set.has(key)) {
146
- const property = to.properties[key];
147
- for (const type of values) property.types.push(type);
148
- } else {
149
- to.keys.array.push(key);
150
- to.keys.set.add(key);
151
- to.properties[key] = {
152
- required,
153
- types: values,
154
- validators: {}
155
- };
156
- }
157
- if (!required && !to.properties[key].types.includes(TYPE_UNDEFINED)) to.properties[key].types.push(TYPE_UNDEFINED);
158
- to.properties[key].validators = validators;
145
+ function getKeyPrefix(key) {
146
+ const prefix = key.replace(EXPRESSION_KEY_PREFIX, "");
147
+ return prefix === key ? void 0 : prefix;
159
148
  }
160
- function getSchema(schema) {
161
- return getValidatedSchema(schema, {
162
- keys: {
163
- array: [],
164
- set: /* @__PURE__ */ new Set()
165
- },
166
- properties: {}
167
- });
149
+ function getKeyValue(key) {
150
+ return key.replace(EXPRESSION_KEY_VALUE, "$1");
168
151
  }
169
- function getTypes(value, validated, prefix) {
170
- const propertyTypes = [];
171
- const values = Array.isArray(value) ? value : [value];
172
- const { length } = values;
173
- for (let index = 0; index < length; index += 1) {
174
- const type = values[index];
175
- if (isSchematic(type) || TYPE_ALL.has(type)) {
176
- propertyTypes.push(type);
177
- continue;
178
- }
179
- if (typeof type === "function") {
180
- propertyTypes.push(isConstructor(type) ? isInstance(type) : type);
181
- continue;
182
- }
183
- if (!isPlainObject(type)) throw new SchematicError(MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE.replace(TEMPLATE_PATTERN, prefix));
184
- if (PROPERTY_TYPE in type) {
185
- propertyTypes.push(...getTypes(type[PROPERTY_TYPE], validated, prefix));
186
- continue;
187
- }
188
- const { [PROPERTY_REQUIRED]: required, ...nested } = type;
189
- if (PROPERTY_REQUIRED in type && typeof required !== "boolean") throw new SchematicError(MESSAGE_SCHEMA_INVALID_PROPERTY_REQUIRED.replace(TEMPLATE_PATTERN, prefix));
190
- addPropertyType(validated, prefix, [TYPE_OBJECT], {}, required !== false);
191
- propertyTypes.push(TYPE_OBJECT);
192
- getValidatedSchema(nested, validated, prefix);
193
- }
194
- return propertyTypes;
195
- }
196
- function getValidatedSchema(schema, validated, prefix) {
197
- 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);
198
155
  const keys = Object.keys(smushed);
199
- const { length } = keys;
200
- const arrayKeys = /* @__PURE__ */ new Set();
201
- const noPrefix = prefix == null;
202
- prefix = noPrefix ? "" : `${prefix}.`;
203
- for (let index = 0; index < length; index += 1) {
204
- 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);
205
163
  const value = smushed[key];
206
- if (Array.isArray(value)) arrayKeys.add(key);
207
- if (EXPRESSION_PROPERTY.test(key)) continue;
208
- if (EXPRESSION_HAS_NUMBER.test(key) && arrayKeys.has(key.replace(EXPRESSION_INDEX, ""))) continue;
164
+ const types = [];
209
165
  let required = true;
210
166
  let validators = {};
211
- const isObject = isPlainObject(value);
212
- if (isObject && PROPERTY_REQUIRED in value) {
213
- if (typeof value[PROPERTY_REQUIRED] !== "boolean") throw new SchematicError(MESSAGE_SCHEMA_INVALID_PROPERTY_REQUIRED.replace(TEMPLATE_PATTERN, key));
214
- required = value[PROPERTY_REQUIRED] === true;
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
+ });
184
+ }
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));
215
212
  }
216
- if (isObject && PROPERTY_VALIDATORS in value) validators = getValidators(value[PROPERTY_VALIDATORS]);
217
- const prefixedKey = `${prefix}${key}`;
218
- const types = getTypes(value, validated, prefixedKey);
219
- if (types.length === 0) throw new SchematicError(MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE.replace(TEMPLATE_PATTERN, key));
220
- addPropertyType(validated, prefixedKey, types, validators, required);
221
213
  }
222
- if (noPrefix) validated.keys.array.sort();
223
- if (noPrefix && validated.keys.array.length === 0) throw new SchematicError(MESSAGE_SCHEMA_INVALID_EMPTY);
224
- return validated;
214
+ if (types.length === 0) throw new SchematicError(MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE.replace(TEMPLATE_PATTERN, key));
215
+ return types;
225
216
  }
226
217
  function getValidators(original) {
227
218
  const validators = {};
@@ -240,35 +231,27 @@ function getValidators(original) {
240
231
  }
241
232
  return validators;
242
233
  }
243
- function validateType(type, property, value) {
244
- switch (true) {
245
- case typeof type === "function": return type(value);
246
- case typeof type === "string": return validators[type](value) && (property.validators[type]?.every((validator) => validator(value)) ?? true);
247
- default: return type.is(value);
248
- }
249
- }
250
- function validateValue(validated, obj) {
251
- if (typeof obj !== "object" || obj === null) return false;
252
- const { keys, properties } = validated;
253
- const keysLength = keys.array.length;
254
- const ignore = /* @__PURE__ */ new Set();
255
- const smushed = smush(obj);
256
- outer: for (let keyIndex = 0; keyIndex < keysLength; keyIndex += 1) {
257
- const key = keys.array[keyIndex];
258
- const prefix = key.replace(EXPRESSION_SUFFIX, "");
259
- if (ignore.has(prefix)) continue;
260
- const property = properties[key];
261
- const value = smushed[key];
262
- if (value === void 0 && property.required && !property.types.includes(TYPE_UNDEFINED)) return false;
263
- const typesLength = property.types.length;
264
- if (typesLength === 1) {
265
- 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);
266
245
  continue;
267
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;
268
251
  for (let typeIndex = 0; typeIndex < typesLength; typeIndex += 1) {
269
252
  const type = property.types[typeIndex];
270
- if (validateType(type, property, value)) {
271
- if (type !== "object") ignore.add(key);
253
+ if (validateValue(type, property, value)) {
254
+ ignoredKeys.add(property.key.full);
272
255
  continue outer;
273
256
  }
274
257
  }
@@ -276,7 +259,13 @@ function validateValue(validated, obj) {
276
259
  }
277
260
  return true;
278
261
  }
279
- 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
+ }
280
269
  const validators = {
281
270
  array: Array.isArray,
282
271
  bigint: (value) => typeof value === "bigint",
@@ -284,7 +273,7 @@ const validators = {
284
273
  date: (value) => value instanceof Date,
285
274
  function: (value) => typeof value === "function",
286
275
  null: (value) => value === null,
287
- number: (value) => typeof value === "number" && !Number.isNaN(value),
276
+ number: (value) => typeof value === "number",
288
277
  object: (value) => typeof value === "object" && value !== null,
289
278
  string: (value) => typeof value === "string",
290
279
  symbol: (value) => typeof value === "symbol",
@@ -294,20 +283,21 @@ const validators = {
294
283
  * A schematic for validating objects
295
284
  */
296
285
  var Schematic = class {
297
- #schema;
298
- constructor(schema) {
286
+ #properties;
287
+ constructor(properties) {
299
288
  Object.defineProperty(this, SCHEMATIC_NAME, { value: true });
300
- this.#schema = schema;
289
+ this.#properties = properties;
301
290
  }
302
291
  /**
303
292
  * Does the value match the schema?
304
293
  */
305
294
  is(value) {
306
- return validateValue(this.#schema, value);
295
+ return validateObject(value, this.#properties);
307
296
  }
308
297
  };
309
298
  function schematic(schema) {
299
+ if (isSchematic(schema)) return schema;
310
300
  if (!isPlainObject(schema)) throw new SchematicError(MESSAGE_SCHEMA_INVALID_TYPE);
311
- return new Schematic(getSchema(schema));
301
+ return new Schematic(getProperties(schema));
312
302
  }
313
- export { SchematicError, isInstance, schematic };
303
+ export { SchematicError, instanceOf, schematic };
package/dist/schematic.js CHANGED
@@ -1,26 +1,28 @@
1
1
  import { MESSAGE_SCHEMA_INVALID_TYPE, SCHEMATIC_NAME } from "./constants.js";
2
+ import { isSchematic } from "./is.js";
2
3
  import { SchematicError } from "./models.js";
3
- import { getSchema } from "./validation/schema.validation.js";
4
- import { validateValue } from "./validation/value.validation.js";
4
+ import { getProperties } from "./validation/property.validation.js";
5
+ import { validateObject } from "./validation/value.validation.js";
5
6
  import { isPlainObject } from "@oscarpalmer/atoms/is";
6
7
  /**
7
8
  * A schematic for validating objects
8
9
  */
9
10
  var Schematic = class {
10
- #schema;
11
- constructor(schema) {
11
+ #properties;
12
+ constructor(properties) {
12
13
  Object.defineProperty(this, SCHEMATIC_NAME, { value: true });
13
- this.#schema = schema;
14
+ this.#properties = properties;
14
15
  }
15
16
  /**
16
17
  * Does the value match the schema?
17
18
  */
18
19
  is(value) {
19
- return validateValue(this.#schema, value);
20
+ return validateObject(value, this.#properties);
20
21
  }
21
22
  };
22
23
  function schematic(schema) {
24
+ if (isSchematic(schema)) return schema;
23
25
  if (!isPlainObject(schema)) throw new SchematicError(MESSAGE_SCHEMA_INVALID_TYPE);
24
- return new Schematic(getSchema(schema));
26
+ return new Schematic(getProperties(schema));
25
27
  }
26
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 "@oscarpalmer/atoms/value/misc";
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.11.0"
48
+ "version": "0.12.0"
49
49
  }
package/src/constants.ts CHANGED
@@ -2,11 +2,13 @@ import type {ValueName} from './models';
2
2
 
3
3
  export const ERROR_NAME = 'SchematicError';
4
4
 
5
- export const EXPRESSION_HAS_NUMBER = /\d+/;
6
-
7
5
  export const EXPRESSION_INDEX = /\.\d+$/;
8
6
 
9
- export const EXPRESSION_PROPERTY = /\.\$(required|type|validators)(\.|$)/;
7
+ export const EXPRESSION_KEY_PREFIX = /\.\w+$/;
8
+
9
+ export const EXPRESSION_KEY_VALUE = /^.*\.(\w+)$/;
10
+
11
+ export const EXPRESSION_PROPERTY = /(^|\.)\$(required|type|validators)(\.|$)/;
10
12
 
11
13
  export const MESSAGE_CONSTRUCTOR = 'Expected a constructor function';
12
14
 
package/src/index.ts CHANGED
@@ -1,3 +1,3 @@
1
- export {isInstance} from './is';
1
+ export {instanceOf} from './is';
2
2
  export {SchematicError, type Schema, type TypedSchema} from './models';
3
3
  export {schematic, type Schematic} from './schematic';
package/src/is.ts CHANGED
@@ -3,7 +3,7 @@ import {MESSAGE_CONSTRUCTOR, SCHEMATIC_NAME} from './constants';
3
3
  import type {Constructor} from './models';
4
4
  import type {Schematic} from './schematic';
5
5
 
6
- export function isInstance<Instance>(
6
+ export function instanceOf<Instance>(
7
7
  constructor: Constructor<Instance>,
8
8
  ): (value: unknown) => value is Instance {
9
9
  if (!isConstructor(constructor)) {
package/src/models.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type {PlainObject, Simplify} from '@oscarpalmer/atoms/models';
1
+ import type {GenericCallback, PlainObject, Simplify} from '@oscarpalmer/atoms/models';
2
2
  import {ERROR_NAME} from './constants';
3
3
  import type {Schematic} from './schematic';
4
4
 
@@ -122,10 +122,16 @@ type RequiredKeys<Value> = Exclude<keyof Value, OptionalKeys<Value>>;
122
122
  */
123
123
  export type Schema = SchemaIndex;
124
124
 
125
- type SchemaEntry = Constructor | SchemaProperty | Schematic<unknown> | ValueName | NestedSchema;
125
+ type SchemaEntry =
126
+ | Constructor
127
+ | Schema
128
+ | SchemaProperty
129
+ | Schematic<unknown>
130
+ | ValueName
131
+ | ((value: unknown) => boolean);
126
132
 
127
133
  interface SchemaIndex {
128
- [key: string]: SchemaEntry | SchemaEntry[];
134
+ [key: string]: NestedSchema | SchemaEntry | SchemaEntry[];
129
135
  }
130
136
 
131
137
  /**
@@ -137,7 +143,12 @@ export type SchemaProperty = {
137
143
  $validators?: PropertyValidators<SchemaPropertyType | SchemaPropertyType[]>;
138
144
  };
139
145
 
140
- type SchemaPropertyType = Constructor | Schema | Schematic<unknown> | ValueName;
146
+ type SchemaPropertyType =
147
+ | Constructor
148
+ | Schema
149
+ | Schematic<unknown>
150
+ | ValueName
151
+ | ((value: unknown) => boolean);
141
152
 
142
153
  export class SchematicError extends Error {
143
154
  constructor(message: string) {
@@ -279,25 +290,24 @@ type UnwrapSingle<Value extends unknown[]> = Value extends [infer Only]
279
290
  : Value;
280
291
 
281
292
  export type ValidatedProperty = {
293
+ key: ValidatedPropertyKey;
282
294
  required: boolean;
283
295
  types: ValidatedPropertyType[];
284
296
  validators: ValidatedPropertyValidators;
285
297
  };
286
298
 
287
- export type ValidatedPropertyType = Schematic<unknown> | ValueName;
299
+ export type ValidatedPropertyKey = {
300
+ full: string;
301
+ prefix: string | undefined;
302
+ value: string;
303
+ };
304
+
305
+ export type ValidatedPropertyType = GenericCallback | Schematic<unknown> | ValueName;
288
306
 
289
307
  export type ValidatedPropertyValidators = {
290
308
  [Key in ValueName]?: Array<(value: unknown) => boolean>;
291
309
  };
292
310
 
293
- export type ValidatedSchema = {
294
- keys: {
295
- array: string[];
296
- set: Set<string>;
297
- };
298
- properties: Record<string, ValidatedProperty>;
299
- };
300
-
301
311
  /**
302
312
  * Valid type name strings
303
313
  */