@gesslar/bedoc 1.10.0 → 2.0.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/LICENSE.txt +12 -0
- package/README.md +15 -3
- package/dist/schema/bedoc.action.json +42 -0
- package/dist/types/Action.d.ts +3 -0
- package/dist/types/Action.d.ts.map +1 -0
- package/dist/types/BeDoc.d.ts +208 -0
- package/dist/types/BeDoc.d.ts.map +1 -0
- package/dist/types/Configuration.d.ts +11 -0
- package/dist/types/Configuration.d.ts.map +1 -0
- package/dist/types/ConfigurationParameters.d.ts +3 -0
- package/dist/types/ConfigurationParameters.d.ts.map +1 -0
- package/dist/types/Conveyor.d.ts +27 -0
- package/dist/types/Conveyor.d.ts.map +1 -0
- package/dist/types/Discovery.d.ts +215 -0
- package/dist/types/Discovery.d.ts.map +1 -0
- package/dist/types/Environment.d.ts +3 -0
- package/dist/types/Environment.d.ts.map +1 -0
- package/dist/types/Logger.d.ts +47 -0
- package/dist/types/Logger.d.ts.map +1 -0
- package/dist/types/Schema.d.ts +3 -0
- package/dist/types/Schema.d.ts.map +1 -0
- package/dist/types/cli.d.ts +2 -2
- package/dist/types/cli.d.ts.map +1 -10
- package/package.json +24 -23
- package/src/Action.js +9 -0
- package/src/BeDoc.js +276 -0
- package/src/CLIOutput.js +198 -0
- package/src/{core/Configuration.js → Configuration.js} +72 -58
- package/src/{core/ConfigurationParameters.js → ConfigurationParameters.js} +35 -27
- package/src/Conveyor.js +256 -0
- package/src/Discovery.js +442 -0
- package/src/Environment.js +8 -0
- package/src/{core/Logger.js → Logger.js} +30 -18
- package/src/Schema.js +6 -0
- package/src/cli.js +77 -34
- package/tsconfig.types.json +42 -0
- package/LICENSE +0 -24
- package/dist/types/core/ActionManager.d.ts +0 -58
- package/dist/types/core/ActionManager.d.ts.map +0 -10
- package/dist/types/core/Configuration.d.ts +0 -27
- package/dist/types/core/Configuration.d.ts.map +0 -10
- package/dist/types/core/ConfigurationParameters.d.ts +0 -38
- package/dist/types/core/ConfigurationParameters.d.ts.map +0 -10
- package/dist/types/core/Conveyor.d.ts +0 -49
- package/dist/types/core/Conveyor.d.ts.map +0 -10
- package/dist/types/core/Core.d.ts +0 -48
- package/dist/types/core/Core.d.ts.map +0 -10
- package/dist/types/core/Discovery.d.ts +0 -73
- package/dist/types/core/Discovery.d.ts.map +0 -10
- package/dist/types/core/HookManager.d.ts +0 -60
- package/dist/types/core/HookManager.d.ts.map +0 -10
- package/dist/types/core/Logger.d.ts +0 -63
- package/dist/types/core/Logger.d.ts.map +0 -10
- package/dist/types/core/action/ParseManager.d.ts +0 -8
- package/dist/types/core/action/ParseManager.d.ts.map +0 -10
- package/dist/types/core/action/PrintManager.d.ts +0 -8
- package/dist/types/core/action/PrintManager.d.ts.map +0 -10
- package/dist/types/core/util/ActionUtil.d.ts +0 -35
- package/dist/types/core/util/ActionUtil.d.ts.map +0 -10
- package/dist/types/core/util/DataUtil.d.ts +0 -52
- package/dist/types/core/util/DataUtil.d.ts.map +0 -10
- package/dist/types/core/util/FDUtil.d.ts +0 -171
- package/dist/types/core/util/FDUtil.d.ts.map +0 -10
- package/dist/types/core/util/ModuleUtil.d.ts +0 -27
- package/dist/types/core/util/ModuleUtil.d.ts.map +0 -10
- package/dist/types/core/util/StringUtil.d.ts +0 -5
- package/dist/types/core/util/StringUtil.d.ts.map +0 -10
- package/dist/types/core/util/TypeSpec.d.ts +0 -42
- package/dist/types/core/util/TypeSpec.d.ts.map +0 -10
- package/dist/types/core/util/ValidUtil.d.ts +0 -29
- package/dist/types/core/util/ValidUtil.d.ts.map +0 -10
- package/src/core/ActionManager.js +0 -147
- package/src/core/ContractManager.js +0 -112
- package/src/core/Conveyor.js +0 -185
- package/src/core/Core.js +0 -166
- package/src/core/Discovery.js +0 -403
- package/src/core/HookManager.js +0 -143
- package/src/core/action/ParseManager.js +0 -7
- package/src/core/action/PrintManager.js +0 -7
- package/src/core/contract/ParseContract.js +0 -7
- package/src/core/contract/PrintContract.js +0 -7
- package/src/core/util/ActionUtil.js +0 -53
- package/src/core/util/ContractUtil.js +0 -63
- package/src/core/util/DataUtil.js +0 -540
- package/src/core/util/FDUtil.js +0 -388
- package/src/core/util/ModuleUtil.js +0 -40
- package/src/core/util/StringUtil.js +0 -11
- package/src/core/util/TypeSpec.js +0 -114
- package/src/core/util/ValidUtil.js +0 -50
|
@@ -1,540 +0,0 @@
|
|
|
1
|
-
import Logger from "../Logger.js"
|
|
2
|
-
import TypeSpec from "./TypeSpec.js"
|
|
3
|
-
|
|
4
|
-
import * as ValidUtil from "./ValidUtil.js"
|
|
5
|
-
|
|
6
|
-
const {validType} = ValidUtil
|
|
7
|
-
|
|
8
|
-
const primitives = [
|
|
9
|
-
// Primitives
|
|
10
|
-
"undefined",
|
|
11
|
-
"boolean",
|
|
12
|
-
"number",
|
|
13
|
-
"bigint",
|
|
14
|
-
"string",
|
|
15
|
-
"symbol",
|
|
16
|
-
|
|
17
|
-
// Object Categories from typeof
|
|
18
|
-
"object",
|
|
19
|
-
"function",
|
|
20
|
-
]
|
|
21
|
-
|
|
22
|
-
const constructors = [
|
|
23
|
-
// Object Constructors
|
|
24
|
-
"Object",
|
|
25
|
-
"Array",
|
|
26
|
-
"Function",
|
|
27
|
-
"Date",
|
|
28
|
-
"RegExp",
|
|
29
|
-
"Error",
|
|
30
|
-
"Map",
|
|
31
|
-
"Set",
|
|
32
|
-
"WeakMap",
|
|
33
|
-
"WeakSet",
|
|
34
|
-
"Promise",
|
|
35
|
-
"Int8Array",
|
|
36
|
-
"Uint8Array",
|
|
37
|
-
"Float32Array",
|
|
38
|
-
"Float64Array",
|
|
39
|
-
]
|
|
40
|
-
|
|
41
|
-
const dataTypes = [...primitives, ...constructors.map(c => c.toLowerCase())]
|
|
42
|
-
|
|
43
|
-
const emptyableTypes = ["string", "array", "object"]
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Appends a string to another string if it does not already end with it.
|
|
47
|
-
*
|
|
48
|
-
* @param {string} string The string to append to
|
|
49
|
-
* @param {string} append The string to append
|
|
50
|
-
* @returns {string} The appended string
|
|
51
|
-
*/
|
|
52
|
-
function appendString(string, append) {
|
|
53
|
-
return string.endsWith(append) ? string : `${string}${append}`
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Prepends a string to another string if it does not already start with it.
|
|
58
|
-
*
|
|
59
|
-
* @param {string} string The string to prepend to
|
|
60
|
-
* @param {string} prepend The string to prepend
|
|
61
|
-
* @returns {string} The prepended string
|
|
62
|
-
*/
|
|
63
|
-
function prependString(string, prepend) {
|
|
64
|
-
return string.startsWith(prepend) ? string : `${prepend}${string}`
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Checks if all elements in an array are of a specified type
|
|
69
|
-
*
|
|
70
|
-
* @param {Array} arr - The array to check
|
|
71
|
-
* @param {string} type - The type to check for (optional, defaults to the
|
|
72
|
-
* type of the first element)
|
|
73
|
-
* @returns {boolean} Whether all elements are of the specified type
|
|
74
|
-
*/
|
|
75
|
-
function isArrayUniform(arr, type) {
|
|
76
|
-
return arr.every(
|
|
77
|
-
(item, _index, arr) => typeof item === (type || typeof arr[0]),
|
|
78
|
-
)
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Checks if an array is unique
|
|
83
|
-
*
|
|
84
|
-
* @param {Array} arr - The array of which to remove duplicates
|
|
85
|
-
* @returns {Array} The unique elements of the array
|
|
86
|
-
*/
|
|
87
|
-
function isArrayUnique(arr) {
|
|
88
|
-
return arr.filter((item, index, self) => self.indexOf(item) === index)
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Returns the intersection of two arrays.
|
|
93
|
-
*
|
|
94
|
-
* @param {Array} arr1 - The first array.
|
|
95
|
-
* @param {Array} arr2 - The second array.
|
|
96
|
-
* @returns {Array} The intersection of the two arrays.
|
|
97
|
-
*/
|
|
98
|
-
function arrayIntersection(arr1, arr2) {
|
|
99
|
-
return arr1.filter(value => arr2.includes(value))
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* Pads an array to a specified length with a value. This operation
|
|
104
|
-
* occurs in-place.
|
|
105
|
-
*
|
|
106
|
-
* @param {Array} arr - The array to pad.
|
|
107
|
-
* @param {number} length - The length to pad the array to.
|
|
108
|
-
* @param {any} value - The value to pad the array with.
|
|
109
|
-
* @param {number} position - The position to pad the array at.
|
|
110
|
-
* @returns {Array} The padded array.
|
|
111
|
-
*/
|
|
112
|
-
function arrayPad(arr, length, value, position = 0) {
|
|
113
|
-
const diff = length - arr.length
|
|
114
|
-
if(diff <= 0) return arr
|
|
115
|
-
|
|
116
|
-
const padding = Array(diff).fill(value)
|
|
117
|
-
|
|
118
|
-
if(position === 0)
|
|
119
|
-
// prepend - default
|
|
120
|
-
return padding.concat(arr)
|
|
121
|
-
else if(position === -1)
|
|
122
|
-
// append
|
|
123
|
-
return arr.concat(padding) // somewhere in the middle - THAT IS ILLEGAL
|
|
124
|
-
else throw new SyntaxError("Invalid position")
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Clones an object
|
|
129
|
-
*
|
|
130
|
-
* @param {object} obj - The object to clone
|
|
131
|
-
* @param {boolean} freeze - Whether to freeze the cloned object
|
|
132
|
-
* @returns {object} The cloned object
|
|
133
|
-
*/
|
|
134
|
-
function cloneObject(obj, freeze = false) {
|
|
135
|
-
const result = {}
|
|
136
|
-
|
|
137
|
-
for(const [key, value] of Object.entries(obj)) {
|
|
138
|
-
if(isType(value, "object"))
|
|
139
|
-
result[key] = cloneObject(value)
|
|
140
|
-
else
|
|
141
|
-
result[key] = value
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
return freeze ? Object.freeze(result) : result
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/**
|
|
148
|
-
* Allocates an object from a source array and a spec array or function.
|
|
149
|
-
*
|
|
150
|
-
* @param {any} source The source array
|
|
151
|
-
* @param {any|Function} spec The spec array or function
|
|
152
|
-
* @returns {Promise<object>} The allocated object
|
|
153
|
-
*/
|
|
154
|
-
async function allocateObject(source, spec) {
|
|
155
|
-
// Data
|
|
156
|
-
const workSource = [],
|
|
157
|
-
workSpec = [],
|
|
158
|
-
result = {}
|
|
159
|
-
|
|
160
|
-
if(!isType(source, "array", {allowEmpty: false}))
|
|
161
|
-
throw new Error("Source must be an array.")
|
|
162
|
-
|
|
163
|
-
workSource.push(...source)
|
|
164
|
-
|
|
165
|
-
if(
|
|
166
|
-
!isType(spec, "array", {allowEmpty: false}) &&
|
|
167
|
-
!isType(spec, "function")
|
|
168
|
-
)
|
|
169
|
-
throw new Error("Spec must be an array or a function.")
|
|
170
|
-
|
|
171
|
-
if(isType(spec, "function")) {
|
|
172
|
-
const specResult = await spec(workSource)
|
|
173
|
-
|
|
174
|
-
if(!isType(specResult, "array"))
|
|
175
|
-
throw new Error("Spec resulting from function must be an array.")
|
|
176
|
-
|
|
177
|
-
workSpec.push(...specResult)
|
|
178
|
-
} else if(isType(spec, "array", {allowEmpty: false})) {
|
|
179
|
-
workSpec.push(...spec)
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
if(workSource.length !== workSpec.length)
|
|
183
|
-
throw new Error("Source and spec must have the same number of elements.")
|
|
184
|
-
|
|
185
|
-
// Objects must always be indexed by strings.
|
|
186
|
-
workSource.map((element, index, arr) => (arr[index] = String(element)))
|
|
187
|
-
|
|
188
|
-
// Check that all keys are strings
|
|
189
|
-
if(!isArrayUniform(workSource, "string"))
|
|
190
|
-
throw new Error("Indices of an Object must be of type string.")
|
|
191
|
-
|
|
192
|
-
workSource.forEach((element, index) => (result[element] = workSpec[index]))
|
|
193
|
-
|
|
194
|
-
return result
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
/**
|
|
198
|
-
* Maps an object using a transformer function
|
|
199
|
-
*
|
|
200
|
-
* @param {object} original The original object
|
|
201
|
-
* @param {Function} transformer The transformer function
|
|
202
|
-
* @param {boolean} mutate Whether to mutate the original object
|
|
203
|
-
* @returns {Promise<object>} The mapped object
|
|
204
|
-
*/
|
|
205
|
-
async function mapObject(original, transformer, mutate = false) {
|
|
206
|
-
validType(original, "object", true)
|
|
207
|
-
validType(transformer, "function")
|
|
208
|
-
validType(mutate, "boolean")
|
|
209
|
-
|
|
210
|
-
const result = mutate ? original : {}
|
|
211
|
-
|
|
212
|
-
for(const [key, value] of Object.entries(original))
|
|
213
|
-
result[key] = isType(value, "object")
|
|
214
|
-
? await mapObject(value, transformer, mutate)
|
|
215
|
-
: (result[key] = await transformer(key, value))
|
|
216
|
-
|
|
217
|
-
return result
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
/**
|
|
221
|
-
* Checks if an object is empty
|
|
222
|
-
*
|
|
223
|
-
* @param {object} obj - The object to check
|
|
224
|
-
* @returns {boolean} Whether the object is empty
|
|
225
|
-
*/
|
|
226
|
-
function isObjectEmpty(obj) {
|
|
227
|
-
return Object.keys(obj).length === 0
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
/**
|
|
231
|
-
* Creates a type spec from a string. A type spec is an array of objects
|
|
232
|
-
* defining the type of a value and whether an array is expected.
|
|
233
|
-
*
|
|
234
|
-
* @param {string} string - The string to parse into a type spec.
|
|
235
|
-
* @param {object} options - Additional options for parsing.
|
|
236
|
-
* @returns {object[]} An array of type specs.
|
|
237
|
-
*/
|
|
238
|
-
function newTypeSpec(string, options) {
|
|
239
|
-
return new TypeSpec(string, options)
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
/**
|
|
243
|
-
* Checks if a value is of a specified type
|
|
244
|
-
*
|
|
245
|
-
* @param {any} value The value to check
|
|
246
|
-
* @param {string|TypeSpec} type The type to check for
|
|
247
|
-
* @param {object} options Additional options for checking
|
|
248
|
-
* @returns {boolean} Whether the value is of the specified type
|
|
249
|
-
*/
|
|
250
|
-
function isType(value, type, options = {}) {
|
|
251
|
-
const typeSpec = type instanceof TypeSpec ? type : newTypeSpec(type, options)
|
|
252
|
-
// we're comparing a typeSpec object to a File object. this will always
|
|
253
|
-
// return false. do fix.
|
|
254
|
-
return typeSpec.match(value, options)
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
/**
|
|
258
|
-
* Checks if a type is valid
|
|
259
|
-
*
|
|
260
|
-
* @param {string} type - The type to check
|
|
261
|
-
* @returns {boolean} Whether the type is valid
|
|
262
|
-
*/
|
|
263
|
-
function isValidType(type) {
|
|
264
|
-
return dataTypes.includes(type)
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
/**
|
|
268
|
-
* Checks if a value is of a specified type. Unlike the type function, this
|
|
269
|
-
* function does not parse the type string, and only checks for primitive
|
|
270
|
-
* or constructor types.
|
|
271
|
-
*
|
|
272
|
-
* @param {any} value - The value to check
|
|
273
|
-
* @param {string} type - The type to check for
|
|
274
|
-
* @returns {boolean} Whether the value is of the specified type
|
|
275
|
-
*/
|
|
276
|
-
function isBaseType(value, type) {
|
|
277
|
-
if(!isValidType(type))
|
|
278
|
-
return false
|
|
279
|
-
|
|
280
|
-
const valueType = typeOf(value)
|
|
281
|
-
|
|
282
|
-
switch(type.toLowerCase()) {
|
|
283
|
-
case "array":
|
|
284
|
-
return Array.isArray(value) // Native array check
|
|
285
|
-
case "string":
|
|
286
|
-
return valueType === "string"
|
|
287
|
-
case "boolean":
|
|
288
|
-
return valueType === "boolean"
|
|
289
|
-
case "number":
|
|
290
|
-
return valueType === "number" && !isNaN(value) // Excludes NaN
|
|
291
|
-
case "object":
|
|
292
|
-
return value !== null && valueType === "object" && !Array.isArray(value) // Excludes arrays and null
|
|
293
|
-
case "function":
|
|
294
|
-
return valueType === "function"
|
|
295
|
-
case "symbol":
|
|
296
|
-
return valueType === "symbol" // ES6 Symbol type
|
|
297
|
-
case "bigint":
|
|
298
|
-
return valueType === "bigint" // BigInt support
|
|
299
|
-
case "null":
|
|
300
|
-
return value === null // Explicit null check
|
|
301
|
-
case "undefined":
|
|
302
|
-
return valueType === "undefined" // Explicit undefined check
|
|
303
|
-
default:
|
|
304
|
-
return false // Unknown type
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
/**
|
|
309
|
-
* Returns the type of a value, whether it be a primitive, object, or function.
|
|
310
|
-
*
|
|
311
|
-
* @param {any} value - The value to check
|
|
312
|
-
* @returns {string} The type of the value
|
|
313
|
-
*/
|
|
314
|
-
function typeOf(value) {
|
|
315
|
-
return Array.isArray(value) ? "array" : typeof value
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
/**
|
|
319
|
-
* Checks a value is undefined or null.
|
|
320
|
-
*
|
|
321
|
-
* @param {any} value The value to check
|
|
322
|
-
* @returns {boolean} Whether the value is undefined or null
|
|
323
|
-
*/
|
|
324
|
-
function isNothing(value) {
|
|
325
|
-
return value === undefined || value === null
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
/**
|
|
329
|
-
* Checks if a value is empty. This function is used to check if an object,
|
|
330
|
-
* array, or string is empty. Null and undefined values are considered empty.
|
|
331
|
-
*
|
|
332
|
-
* @param {any} value The value to check
|
|
333
|
-
* @param {boolean} checkForNothing Whether to check for null or undefined
|
|
334
|
-
* values
|
|
335
|
-
* @returns {boolean} Whether the value is empty
|
|
336
|
-
*/
|
|
337
|
-
function isEmpty(value, checkForNothing = true) {
|
|
338
|
-
const type = typeOf(value)
|
|
339
|
-
|
|
340
|
-
if(checkForNothing && isNothing(value))
|
|
341
|
-
return true
|
|
342
|
-
|
|
343
|
-
if(!emptyableTypes.includes(type))
|
|
344
|
-
return false
|
|
345
|
-
|
|
346
|
-
switch(type) {
|
|
347
|
-
case "array":
|
|
348
|
-
return value.length === 0
|
|
349
|
-
case "object":
|
|
350
|
-
return Object.keys(value).length === 0
|
|
351
|
-
case "string":
|
|
352
|
-
return value.trim().length === 0
|
|
353
|
-
default:
|
|
354
|
-
return false
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
/**
|
|
359
|
-
* Freezes an object and all of its properties recursively.
|
|
360
|
-
*
|
|
361
|
-
* @param {object} obj The object to freeze.
|
|
362
|
-
* @returns {object} The frozen object.
|
|
363
|
-
*/
|
|
364
|
-
function deepFreezeObject(obj) {
|
|
365
|
-
if(obj === null || typeof obj !== "object")
|
|
366
|
-
return obj // Skip null and non-objects
|
|
367
|
-
|
|
368
|
-
// Retrieve and freeze properties
|
|
369
|
-
const propNames = Object.getOwnPropertyNames(obj)
|
|
370
|
-
|
|
371
|
-
for(const name of propNames) {
|
|
372
|
-
const value = obj[name]
|
|
373
|
-
|
|
374
|
-
// Recursively freeze nested objects
|
|
375
|
-
if(value && typeof value === "object")
|
|
376
|
-
deepFreezeObject(value)
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
// Freeze the object itself
|
|
380
|
-
return Object.freeze(obj)
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
/**
|
|
384
|
-
* Validates that a schema matches the expected structure.
|
|
385
|
-
*
|
|
386
|
-
* TODO get rid of this and all of its uses. We have a new
|
|
387
|
-
* contract system now.
|
|
388
|
-
*
|
|
389
|
-
* @param {object} schema - The schema to validate.
|
|
390
|
-
* @param {object} definition - The expected structure.
|
|
391
|
-
* @param {Array} stack - The stack trace for nested validation.
|
|
392
|
-
* @param {object} logger - The logger to use.
|
|
393
|
-
* @returns {boolean} - True if valid, throws an error otherwise.
|
|
394
|
-
*/
|
|
395
|
-
function schemaCompare(schema, definition, stack = [], logger = new Logger()) {
|
|
396
|
-
const breadcrumb = key => (stack.length ? `@${stack.join(".")}` : key)
|
|
397
|
-
const tag = "[DataUtil.schemaCompare]"
|
|
398
|
-
const pad = `${" ".repeat(stack.length * 2)}${stack.length ? "└─ " : ""}`
|
|
399
|
-
const debug = (message, key) =>
|
|
400
|
-
logger.debug(
|
|
401
|
-
`${tag}${pad}${message}${key ? " " + breadcrumb(key) : ""}`,
|
|
402
|
-
2,
|
|
403
|
-
true,
|
|
404
|
-
)
|
|
405
|
-
const error = (message, key) =>
|
|
406
|
-
logger.error(`${tag}${pad}${message}${key ? " " + breadcrumb(key) : ""}`)
|
|
407
|
-
|
|
408
|
-
const errors = []
|
|
409
|
-
|
|
410
|
-
debug(`Keys in schema: ${Object.keys(schema).join(", ")}`)
|
|
411
|
-
debug(`Keys in definition: ${Object.keys(definition).join(", ")}`)
|
|
412
|
-
|
|
413
|
-
for(const [key, value] of Object.entries(definition)) {
|
|
414
|
-
debug(`Checking key: ${key} [required = ${value.required ?? false}]`)
|
|
415
|
-
|
|
416
|
-
if(value.required && key in schema === false) {
|
|
417
|
-
error(`❌ Required key not found in schema: ${key}`, key)
|
|
418
|
-
|
|
419
|
-
errors.push(
|
|
420
|
-
new SyntaxError(`Missing required key: ${key} ${breadcrumb(key)}`),
|
|
421
|
-
)
|
|
422
|
-
|
|
423
|
-
continue
|
|
424
|
-
} else {
|
|
425
|
-
debug(`✔️ Required key found in schema: ${key}`)
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
if(key in schema) {
|
|
429
|
-
const expectedType = value.dataType
|
|
430
|
-
const actualType = schema[key]
|
|
431
|
-
|
|
432
|
-
if(!expectedType.match(actualType))
|
|
433
|
-
errors.push(
|
|
434
|
-
new TypeError(
|
|
435
|
-
`Type mismatch for key: ${key}. Expected: ${expectedType}, got: ${actualType} ${breadcrumb(key)}`,
|
|
436
|
-
),
|
|
437
|
-
)
|
|
438
|
-
|
|
439
|
-
// Recursive validation for nested objects
|
|
440
|
-
if(value.contains) {
|
|
441
|
-
debug(`Recursing into nested object: ${key}`)
|
|
442
|
-
const nestedResult = schemaCompare(
|
|
443
|
-
schema[key]?.contains,
|
|
444
|
-
value.contains,
|
|
445
|
-
[...stack, key],
|
|
446
|
-
logger,
|
|
447
|
-
)
|
|
448
|
-
|
|
449
|
-
if(nestedResult.errors.length)
|
|
450
|
-
errors.push(...nestedResult.errors)
|
|
451
|
-
}
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
return {status: errors.length === 0 ? "success" : "error", errors}
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
/**
|
|
459
|
-
* Determine the Levenshtein distance between two string values
|
|
460
|
-
*
|
|
461
|
-
* @param {string} a The first value for comparison.
|
|
462
|
-
* @param {string} b The second value for comparison.
|
|
463
|
-
* @returns {number} The Levenshtein distance
|
|
464
|
-
*/
|
|
465
|
-
function levenshteinDistance(a, b) {
|
|
466
|
-
const matrix = Array.from({length: a.length + 1}, (_, i) =>
|
|
467
|
-
Array.from({length: b.length + 1}, (_, j) =>
|
|
468
|
-
(i === 0 ? j : j === 0 ? i : 0)
|
|
469
|
-
)
|
|
470
|
-
)
|
|
471
|
-
|
|
472
|
-
for(let i = 1; i <= a.length; i++) {
|
|
473
|
-
for(let j = 1; j <= b.length; j++) {
|
|
474
|
-
matrix[i][j] =
|
|
475
|
-
a[i - 1] === b[j - 1]
|
|
476
|
-
? matrix[i - 1][j - 1]
|
|
477
|
-
: 1 + Math.min(
|
|
478
|
-
matrix[i - 1][j], matrix[i][j - 1],
|
|
479
|
-
matrix[i - 1][j - 1]
|
|
480
|
-
)
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
return matrix[a.length][b.length]
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
/**
|
|
488
|
-
* Determine the closest match between a string and allowed values
|
|
489
|
-
* from the Levenshtein distance.
|
|
490
|
-
*
|
|
491
|
-
* @param {string} input The input string to resolve
|
|
492
|
-
* @param {Array<string>} allowedValues The values which are permitted
|
|
493
|
-
* @returns {string} Suggested, probable match.
|
|
494
|
-
*/
|
|
495
|
-
function findClosestMatch(input, allowedValues) {
|
|
496
|
-
const threshold = 2 // Max edit distance for a "close match"
|
|
497
|
-
let closestMatch = null
|
|
498
|
-
let closestDistance = Infinity
|
|
499
|
-
|
|
500
|
-
for(const value of allowedValues) {
|
|
501
|
-
const distance = levenshteinDistance(input, value)
|
|
502
|
-
if(distance < closestDistance && distance <= threshold) {
|
|
503
|
-
closestMatch = value
|
|
504
|
-
closestDistance = distance
|
|
505
|
-
}
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
return closestMatch
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
export {
|
|
513
|
-
// Classes
|
|
514
|
-
TypeSpec,
|
|
515
|
-
// Variables
|
|
516
|
-
dataTypes,
|
|
517
|
-
emptyableTypes,
|
|
518
|
-
// Functions
|
|
519
|
-
allocateObject,
|
|
520
|
-
appendString,
|
|
521
|
-
arrayIntersection,
|
|
522
|
-
arrayPad,
|
|
523
|
-
cloneObject,
|
|
524
|
-
deepFreezeObject,
|
|
525
|
-
findClosestMatch,
|
|
526
|
-
isArrayUniform,
|
|
527
|
-
isArrayUnique,
|
|
528
|
-
isBaseType,
|
|
529
|
-
isEmpty,
|
|
530
|
-
isNothing,
|
|
531
|
-
isObjectEmpty,
|
|
532
|
-
isType,
|
|
533
|
-
isValidType,
|
|
534
|
-
levenshteinDistance,
|
|
535
|
-
mapObject,
|
|
536
|
-
newTypeSpec,
|
|
537
|
-
prependString,
|
|
538
|
-
schemaCompare,
|
|
539
|
-
typeOf,
|
|
540
|
-
}
|