@oscarpalmer/jhunal 0.14.0 → 0.16.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,55 +1,6 @@
1
- /**
2
- * Is the value a constructor function?
3
- * @param value Value to check
4
- * @returns `true` if the value is a constructor function, otherwise `false`
5
- */
6
- function isConstructor(value) {
7
- return typeof value === "function" && value.prototype?.constructor === value;
8
- }
9
- /**
10
- * Is the value a plain object?
11
- * @param value Value to check
12
- * @returns `true` if the value is a plain object, otherwise `false`
13
- */
14
- function isPlainObject(value) {
15
- if (value === null || typeof value !== "object") return false;
16
- if (Symbol.toStringTag in value || Symbol.iterator in value) return false;
17
- const prototype = Object.getPrototypeOf(value);
18
- return prototype === null || prototype === Object.prototype || Object.getPrototypeOf(prototype) === null;
19
- }
20
- function compact(array, strict) {
21
- if (!Array.isArray(array)) return [];
22
- if (strict === true) return array.filter(Boolean);
23
- const { length } = array;
24
- const compacted = [];
25
- for (let index = 0; index < length; index += 1) {
26
- const item = array[index];
27
- if (item != null) compacted.push(item);
28
- }
29
- return compacted;
30
- }
31
- /**
32
- * Get the string value from any value
33
- * @param value Original value
34
- * @returns String representation of the value
35
- */
36
- function getString(value) {
37
- if (typeof value === "string") return value;
38
- if (value == null) return "";
39
- if (typeof value === "function") return getString(value());
40
- if (typeof value !== "object") return String(value);
41
- const asString = String(value.valueOf?.() ?? value);
42
- return asString.startsWith("[object ") ? JSON.stringify(value) : asString;
43
- }
44
- /**
45
- * Join an array of values into a string
46
- * @param value Array of values
47
- * @param delimiter Delimiter to use between values
48
- * @returns Joined string
49
- */
50
- function join(value, delimiter) {
51
- return compact(value).map(getString).join(typeof delimiter === "string" ? delimiter : "");
52
- }
1
+ import { isConstructor, isPlainObject } from "@oscarpalmer/atoms/is";
2
+ import { join } from "@oscarpalmer/atoms/string";
3
+ //#region src/constants.ts
53
4
  const ERROR_NAME = "SchematicError";
54
5
  const EXPRESSION_PROPERTY = /(^|\.)\$(required|type|validators)(\.|$)/;
55
6
  const MESSAGE_CONSTRUCTOR = "Expected a constructor function";
@@ -65,7 +16,6 @@ const PROPERTY_REQUIRED = "$required";
65
16
  const PROPERTY_TYPE = "$type";
66
17
  const PROPERTY_VALIDATORS = "$validators";
67
18
  const SCHEMATIC_NAME = "$schematic";
68
- const TEMPLATE_PATTERN = "<>";
69
19
  const TEMPLATE_PATTERN_KEY = "<key>";
70
20
  const TEMPLATE_PATTERN_PROPERTY = "<property>";
71
21
  const TYPE_OBJECT = "object";
@@ -86,25 +36,50 @@ const TYPE_ALL = new Set([
86
36
  "null",
87
37
  TYPE_UNDEFINED
88
38
  ]);
39
+ //#endregion
40
+ //#region src/helpers.ts
41
+ /**
42
+ * Creates a validator function for a given constructor
43
+ * @param constructor - Constructor to check against
44
+ * @throws Will throw a `TypeError` if the provided argument is not a valid constructor
45
+ * @returns Validator function that checks if a value is an instance of the constructor
46
+ */
89
47
  function instanceOf(constructor) {
90
48
  if (!isConstructor(constructor)) throw new TypeError(MESSAGE_CONSTRUCTOR);
91
49
  return (value) => {
92
50
  return value instanceof constructor;
93
51
  };
94
52
  }
53
+ /**
54
+ * Is the value a schematic?
55
+ * @param value Value to check
56
+ * @returns `true` if the value is a schematic, `false` otherwise
57
+ */
95
58
  function isSchematic(value) {
96
- return typeof value === "object" && value !== null && SCHEMATIC_NAME in value && value[SCHEMATIC_NAME] === true;
59
+ return typeof value === "object" && value !== null && "$schematic" in value && value["$schematic"] === true;
97
60
  }
61
+ //#endregion
62
+ //#region src/models.ts
63
+ /**
64
+ * A custom error class for schema validation failures, with its `name` set to {@link ERROR_NAME}
65
+ *
66
+ * @example
67
+ * ```ts
68
+ * throw new SchematicError('Expected a string, received a number');
69
+ * ```
70
+ */
98
71
  var SchematicError = class extends Error {
99
72
  constructor(message) {
100
73
  super(message);
101
74
  this.name = ERROR_NAME;
102
75
  }
103
76
  };
77
+ //#endregion
78
+ //#region src/validation/property.validation.ts
104
79
  function getDisallowedProperty(obj) {
105
- if (PROPERTY_REQUIRED in obj) return PROPERTY_REQUIRED;
106
- if (PROPERTY_TYPE in obj) return PROPERTY_TYPE;
107
- if (PROPERTY_VALIDATORS in obj) return PROPERTY_VALIDATORS;
80
+ if ("$required" in obj) return PROPERTY_REQUIRED;
81
+ if ("$type" in obj) return PROPERTY_TYPE;
82
+ if ("$validators" in obj) return PROPERTY_VALIDATORS;
108
83
  }
109
84
  function getProperties(original, prefix, fromType) {
110
85
  if (Object.keys(original).length === 0) throw new SchematicError(MESSAGE_SCHEMA_INVALID_EMPTY);
@@ -125,22 +100,22 @@ function getProperties(original, prefix, fromType) {
125
100
  if (isPlainObject(value)) {
126
101
  required = getRequired(key, value) ?? required;
127
102
  validators = getValidators(value[PROPERTY_VALIDATORS]);
128
- if (PROPERTY_TYPE in value) types.push(TYPE_OBJECT, ...getTypes(key, value[PROPERTY_TYPE], prefix, true));
103
+ if ("$type" in value) types.push(TYPE_OBJECT, ...getTypes(key, value[PROPERTY_TYPE], prefix, true));
129
104
  else types.push(TYPE_OBJECT, ...getTypes(key, value, prefix));
130
105
  } else types.push(...getTypes(key, value, prefix));
131
- if (!required && !types.includes(TYPE_UNDEFINED)) types.push(TYPE_UNDEFINED);
106
+ if (!required && !types.includes("undefined")) types.push(TYPE_UNDEFINED);
132
107
  properties.push({
133
108
  key,
134
109
  types,
135
110
  validators,
136
- required: required && !types.includes(TYPE_UNDEFINED)
111
+ required: required && !types.includes("undefined")
137
112
  });
138
113
  }
139
114
  return properties;
140
115
  }
141
116
  function getRequired(key, obj) {
142
- if (!(PROPERTY_REQUIRED in obj)) return;
143
- if (typeof obj[PROPERTY_REQUIRED] !== "boolean") throw new SchematicError(MESSAGE_SCHEMA_INVALID_PROPERTY_REQUIRED.replace(TEMPLATE_PATTERN, key));
117
+ if (!("$required" in obj)) return;
118
+ if (typeof obj["$required"] !== "boolean") throw new SchematicError(MESSAGE_SCHEMA_INVALID_PROPERTY_REQUIRED.replace("<>", key));
144
119
  return obj[PROPERTY_REQUIRED];
145
120
  }
146
121
  function getTypes(key, original, prefix, fromType) {
@@ -162,10 +137,10 @@ function getTypes(key, original, prefix, fromType) {
162
137
  case TYPE_ALL.has(value):
163
138
  types.push(value);
164
139
  break;
165
- default: throw new SchematicError(MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE.replace(TEMPLATE_PATTERN, join([prefix, key], ".")));
140
+ default: throw new SchematicError(MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE.replace("<>", join([prefix, key], ".")));
166
141
  }
167
142
  }
168
- if (types.length === 0) throw new SchematicError(MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE.replace(TEMPLATE_PATTERN, join([prefix, key], ".")));
143
+ if (types.length === 0) throw new SchematicError(MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE.replace("<>", join([prefix, key], ".")));
169
144
  return types;
170
145
  }
171
146
  function getValidators(original) {
@@ -176,15 +151,17 @@ function getValidators(original) {
176
151
  const { length } = keys;
177
152
  for (let index = 0; index < length; index += 1) {
178
153
  const key = keys[index];
179
- if (!VALIDATABLE_TYPES.has(key)) throw new TypeError(MESSAGE_VALIDATOR_INVALID_KEY.replace(TEMPLATE_PATTERN, key));
154
+ if (!VALIDATABLE_TYPES.has(key)) throw new TypeError(MESSAGE_VALIDATOR_INVALID_KEY.replace("<>", key));
180
155
  const value = original[key];
181
156
  validators[key] = (Array.isArray(value) ? value : [value]).filter((item) => {
182
- if (typeof item !== "function") throw new TypeError(MESSAGE_VALIDATOR_INVALID_VALUE.replace(TEMPLATE_PATTERN, key));
157
+ if (typeof item !== "function") throw new TypeError(MESSAGE_VALIDATOR_INVALID_VALUE.replace("<>", key));
183
158
  return true;
184
159
  });
185
160
  }
186
161
  return validators;
187
162
  }
163
+ //#endregion
164
+ //#region src/validation/value.validation.ts
188
165
  function validateObject(obj, properties) {
189
166
  if (!isPlainObject(obj)) return false;
190
167
  const propertiesLength = properties.length;
@@ -223,6 +200,8 @@ const validators = {
223
200
  symbol: (value) => typeof value === "symbol",
224
201
  undefined: (value) => value === void 0
225
202
  };
203
+ //#endregion
204
+ //#region src/schematic.ts
226
205
  /**
227
206
  * A schematic for validating objects
228
207
  */
@@ -234,6 +213,8 @@ var Schematic = class {
234
213
  }
235
214
  /**
236
215
  * Does the value match the schema?
216
+ * @param value - Value to validate
217
+ * @returns `true` if the value matches the schema, otherwise `false`
237
218
  */
238
219
  is(value) {
239
220
  return validateObject(value, this.#properties);
@@ -244,4 +225,5 @@ function schematic(schema) {
244
225
  if (!isPlainObject(schema)) throw new SchematicError(MESSAGE_SCHEMA_INVALID_TYPE);
245
226
  return new Schematic(getProperties(schema));
246
227
  }
247
- export { SchematicError, instanceOf, schematic };
228
+ //#endregion
229
+ export { SchematicError, instanceOf, isSchematic, schematic };