@lumjs/core 1.21.0 → 1.23.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/PLANS.txt +10 -0
- package/lib/index.js +10 -2
- package/lib/meta.js +59 -0
- package/lib/obj/flip.js +258 -0
- package/lib/obj/index.js +5 -3
- package/lib/objectid.js +182 -3
- package/lib/types/basics.js +23 -2
- package/lib/types/index.js +8 -3
- package/lib/types/isa.js +554 -82
- package/package.json +3 -2
package/lib/types/isa.js
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
1
3
|
const {O, F, S} = require('./js');
|
|
2
|
-
const {isObj,isArray} = require('./basics');
|
|
4
|
+
const {isObj,isArray,isIterable,notNil} = require('./basics');
|
|
3
5
|
const TYPES = require('./typelist');
|
|
6
|
+
const def = require('./def');
|
|
4
7
|
|
|
5
8
|
/**
|
|
6
9
|
* See if a value is an instance of a class.
|
|
10
|
+
* @deprecated Just use `instanceof` directly, this function is unnecessary.
|
|
7
11
|
* @param {*} v - The value we're testing.
|
|
8
12
|
* @param {function} f - The constructor/class we want.
|
|
9
13
|
* @param {boolean} [needProto=false] If true, the `v` must have a `prototype`.
|
|
@@ -23,7 +27,7 @@ function isInstance(v, f, needProto=false)
|
|
|
23
27
|
// Everything passed.
|
|
24
28
|
return true;
|
|
25
29
|
}
|
|
26
|
-
|
|
30
|
+
|
|
27
31
|
exports.isInstance = isInstance;
|
|
28
32
|
|
|
29
33
|
/**
|
|
@@ -38,6 +42,7 @@ exports.isInstance = isInstance;
|
|
|
38
42
|
* this function does not count `null` as a valid `object`.
|
|
39
43
|
*
|
|
40
44
|
* @param {*} v - The value we're testing.
|
|
45
|
+
*
|
|
41
46
|
* @returns {boolean} If the value was of the desired type.
|
|
42
47
|
* @alias module:@lumjs/core/types.isType
|
|
43
48
|
*/
|
|
@@ -52,12 +57,12 @@ function isType(type, v)
|
|
|
52
57
|
{ // A type-specific test.
|
|
53
58
|
return TYPES.tests[type](v);
|
|
54
59
|
}
|
|
55
|
-
else
|
|
60
|
+
else
|
|
56
61
|
{ // No type-specific tests.
|
|
57
62
|
return (typeof v === type);
|
|
58
63
|
}
|
|
59
64
|
}
|
|
60
|
-
|
|
65
|
+
|
|
61
66
|
exports.isType = isType;
|
|
62
67
|
|
|
63
68
|
// Default options parser.
|
|
@@ -127,7 +132,8 @@ function processOptions(type, v)
|
|
|
127
132
|
* @param {...any} types - The types the value should be one of.
|
|
128
133
|
*
|
|
129
134
|
* For each of the `types`, by default if it is a `string`
|
|
130
|
-
* we test with `isType()`, if it is a `function` we
|
|
135
|
+
* we test with `isType()`, if it is a `function` we see if `v`
|
|
136
|
+
* is either an instance of the type or a sub-class of it.
|
|
131
137
|
*
|
|
132
138
|
* If it is an `object` and has an `is()` method, use that as the test.
|
|
133
139
|
*
|
|
@@ -138,9 +144,9 @@ function processOptions(type, v)
|
|
|
138
144
|
* - `needProto: boolean`, Change the `needProto` option for `isInstance()`
|
|
139
145
|
* - `parsers: function`, Add another options parser function.
|
|
140
146
|
* - `process: function`, A one-time set-up function.
|
|
141
|
-
* - `test: function`, Pass the `v` to this
|
|
142
|
-
* - `
|
|
143
|
-
*
|
|
147
|
+
* - `test: function`, Pass the `v` to this and return `true` if it passes.
|
|
148
|
+
* - `instanceof: boolean`, If `true` use `instanceof` *instead of* `isInstance()`.
|
|
149
|
+
* As of version `1.22`, this defaults to `true`.
|
|
144
150
|
* - Anything else will be set as an option that may be used by other parsers.
|
|
145
151
|
*
|
|
146
152
|
* Any other type value will only match if `v === type`
|
|
@@ -156,8 +162,7 @@ function isa(v, ...types)
|
|
|
156
162
|
needProto: false,
|
|
157
163
|
parsers: [DEFAULT_ISA_PARSER],
|
|
158
164
|
process: processOptions,
|
|
159
|
-
|
|
160
|
-
instanceof: false,
|
|
165
|
+
instanceof: true,
|
|
161
166
|
}
|
|
162
167
|
|
|
163
168
|
for (const type of types)
|
|
@@ -168,13 +173,25 @@ function isa(v, ...types)
|
|
|
168
173
|
// With that out of the way, let's go!
|
|
169
174
|
if (typeof type === S)
|
|
170
175
|
{ // A string is passed to isType()
|
|
171
|
-
if (opts.typeof && typeof v === type) return true;
|
|
172
176
|
if (isType(type, v)) return true;
|
|
173
177
|
}
|
|
174
178
|
else if (typeof type === F)
|
|
175
179
|
{ // A function is passed to isInstance()
|
|
176
|
-
if (
|
|
177
|
-
if
|
|
180
|
+
if (typeof v === F)
|
|
181
|
+
{ // See if it is a sub-class.
|
|
182
|
+
if (type.isPrototypeOf(v)) return true;
|
|
183
|
+
}
|
|
184
|
+
else
|
|
185
|
+
{ // See if it is an instance.
|
|
186
|
+
if (opts.instanceof)
|
|
187
|
+
{ // Simple test, is default now.
|
|
188
|
+
if (v instanceof type) return true;
|
|
189
|
+
}
|
|
190
|
+
else
|
|
191
|
+
{ // Old test, will be removed in the future.
|
|
192
|
+
if (isInstance(v, type, opts.needProto)) return true;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
178
195
|
}
|
|
179
196
|
else if (isObj(type))
|
|
180
197
|
{ // Objects can be additional tests, or options.
|
|
@@ -189,128 +206,583 @@ function isa(v, ...types)
|
|
|
189
206
|
exports.isa = isa;
|
|
190
207
|
|
|
191
208
|
/**
|
|
192
|
-
* Extended return value from `
|
|
209
|
+
* Extended return value from any test powered by the `OfTest` class.
|
|
193
210
|
*
|
|
194
|
-
* Used if
|
|
211
|
+
* Used if `rules.details` was set to `true`.
|
|
195
212
|
*
|
|
196
|
-
* @typedef module:@lumjs/core/types
|
|
213
|
+
* @typedef module:@lumjs/core/types.OfTestResult
|
|
197
214
|
*
|
|
198
|
-
* @prop {boolean} pass
|
|
199
|
-
* @prop {boolean} empty - Was the
|
|
215
|
+
* @prop {boolean} pass - Did the test pass?
|
|
216
|
+
* @prop {boolean} empty - Was the list empty?
|
|
200
217
|
*
|
|
201
218
|
* @prop {object} [failed] Failure information;
|
|
202
|
-
* Only addedd if `pass` is false
|
|
219
|
+
* Only addedd if `pass` is `false`.
|
|
203
220
|
*
|
|
204
|
-
*
|
|
205
|
-
*
|
|
206
|
-
*
|
|
207
|
-
*
|
|
208
|
-
* @prop {mixed} failed.value - The item that caused the failure.
|
|
209
|
-
* Will be the `opts.value` itself if `failed.index` is `-1`.
|
|
221
|
+
* Will be the {@link module:@lumjs/core/types.OfTest.state.at}
|
|
222
|
+
* property from the underlying `OfTest` instance.
|
|
223
|
+
* See the documentation for that property for further details.
|
|
210
224
|
*
|
|
211
225
|
*/
|
|
212
226
|
|
|
213
227
|
/**
|
|
214
|
-
*
|
|
228
|
+
* A special class used for testing the contents of containers.
|
|
215
229
|
*
|
|
216
|
-
*
|
|
230
|
+
* Used by the `is[List|Array|Map]Of()` methods, this class
|
|
231
|
+
* abstracts out most of the common functionality of those tests.
|
|
232
|
+
*
|
|
233
|
+
* @alias module:@lumjs/core/types.OfTest
|
|
217
234
|
*
|
|
218
|
-
*
|
|
219
|
-
*
|
|
235
|
+
* @prop {boolean} valid - Does `rules.value` pass the validity test?
|
|
236
|
+
* @prop {object} rules - Rules passed to the constructor.
|
|
237
|
+
* @prop {object} state - Contains the current state of the tests.
|
|
238
|
+
* @prop {Array} vtype - Value type tests.
|
|
239
|
+
* @prop {?Array} ktype - Key/index type tests; `null` if unused.
|
|
220
240
|
*
|
|
221
|
-
* @
|
|
222
|
-
* @param {boolean} [opts.details=false] Return detailed test results?
|
|
223
|
-
* @param {boolean} [opts.empty=false] Does an empty array pass the test?
|
|
241
|
+
* @prop {mixed} target - An alias to the `rules.value` property.
|
|
224
242
|
*
|
|
225
|
-
* @
|
|
243
|
+
* @prop {mixed} rules.value - Should be the container we are testing.
|
|
244
|
+
* May be `null` if no valid `rules` were passed.
|
|
226
245
|
*
|
|
227
|
-
*
|
|
228
|
-
* from the `opts.value` array as the subject of the test.
|
|
246
|
+
* @prop {boolean} [rules.details=false] Use detailed result objects?
|
|
229
247
|
*
|
|
230
|
-
*
|
|
248
|
+
* If `true` the `pass` and `fail` dynamic result properties will return
|
|
249
|
+
* {@link module:@lumjs/core/types.OfTestResult} objects.
|
|
250
|
+
*
|
|
251
|
+
* If `false` (default), the results will be simple `boolean` values.
|
|
252
|
+
*
|
|
253
|
+
* @prop {boolean} [rules.empty=false] Are empty containers valid?
|
|
231
254
|
*
|
|
232
|
-
*
|
|
233
|
-
*
|
|
255
|
+
* Determines the return value of the `empty()` method.
|
|
256
|
+
*
|
|
257
|
+
* Should be used by the calling test function to determine if the
|
|
258
|
+
* test should return a `pass` or `fail` value on an empty container.
|
|
234
259
|
*
|
|
235
|
-
*
|
|
236
|
-
* if the test passed or failed.
|
|
260
|
+
* @prop {boolean} state.empty - Has the container been marked empty?
|
|
237
261
|
*
|
|
238
|
-
* @
|
|
262
|
+
* @prop {object} state.at - Info about the last value tested.
|
|
263
|
+
* Will be used as the `failed` property in the `OfTestResult` object.
|
|
264
|
+
*
|
|
265
|
+
* @prop {mixed} state.at.index - Index/Key of the last value tested.
|
|
266
|
+
*
|
|
267
|
+
* For `isArrayOf()` and `isListOf()` this will always be a `number`.
|
|
268
|
+
*
|
|
269
|
+
* For `isMapOf()` this will be set to the _key_ of each item in the map,
|
|
270
|
+
* and so may be any type of value.
|
|
271
|
+
*
|
|
272
|
+
* This will be set to `-1` if no value has been tested yet,
|
|
273
|
+
* which will always be the case for empty or invalid containers.
|
|
274
|
+
*
|
|
275
|
+
* @prop {mixed} state.at.value - The last value that was tested.
|
|
276
|
+
*
|
|
277
|
+
* Will be the `rules.value` itself if no value has been tested yet,
|
|
278
|
+
* which will always be the case for empty or invalid containers.
|
|
279
|
+
*
|
|
280
|
+
* @prop {boolean} [state.at.key] Was it the key test that failed?
|
|
281
|
+
* Only included if `ktype` is not `null`.
|
|
239
282
|
*/
|
|
240
|
-
|
|
283
|
+
class OfTest
|
|
241
284
|
{
|
|
242
|
-
|
|
285
|
+
/**
|
|
286
|
+
* Create an `OfTest` instance.
|
|
287
|
+
*
|
|
288
|
+
* Unless you are creating a custom test function similar to the built-in
|
|
289
|
+
* `is*Of()` methods you'll never need to create an instance of this.
|
|
290
|
+
* It's documented here for the potential of advanced tests.
|
|
291
|
+
*
|
|
292
|
+
* @param {function} valid - A validity test for the `rules.value`.
|
|
293
|
+
*
|
|
294
|
+
* This will be used to set the value of the `this.valid` property.
|
|
295
|
+
*
|
|
296
|
+
* @param {object} rules - Object to set as the `this.rules` property.
|
|
297
|
+
*
|
|
298
|
+
* If `valid(rules)` returns `true` then the `rules` will be
|
|
299
|
+
* changed to `{value: rules}`. This is a short-cut so that
|
|
300
|
+
* the containers can be passed directly if default rules are okay.
|
|
301
|
+
*
|
|
302
|
+
* If this is NOT an object, then the `rules` will be changed to
|
|
303
|
+
* `{value: null}`, which should ensure that `this.valid` is `false`.
|
|
304
|
+
*
|
|
305
|
+
* See the class property description for the supported optional values.
|
|
306
|
+
*
|
|
307
|
+
* @param {object} rules.value - The container being tested.
|
|
308
|
+
*
|
|
309
|
+
* @param {?Array} valueTypes - An array of type tests for the values.
|
|
310
|
+
*
|
|
311
|
+
* Will be set as the `this.vtype` property.
|
|
312
|
+
*
|
|
313
|
+
* If `null` (only applicable if `keyTypes` is NOT `null`), then the
|
|
314
|
+
* value type tests will be skipped.
|
|
315
|
+
*
|
|
316
|
+
* See {@link module:@lumjs/core/types.isa} for details on the tests.
|
|
317
|
+
*
|
|
318
|
+
* @param {?Array} [keyTypes=null] Array of type tests for the _key/index_.
|
|
319
|
+
*
|
|
320
|
+
* Will be set as the `this.ktype` property.
|
|
321
|
+
*
|
|
322
|
+
* Uses `isa()` just like `valueTypes`. Is only needed in very specific
|
|
323
|
+
* cases, such as `isMapOf()` as `Map` objects may have any kind of key.
|
|
324
|
+
* If `null` (default), no tests against the keys will be done.
|
|
325
|
+
*
|
|
326
|
+
* @throws {TypeError} If any of the parameters is not valid.
|
|
327
|
+
* @throws {RangeError} If both `valueTypes` and `keyTypes` are `null`.
|
|
328
|
+
*
|
|
329
|
+
*/
|
|
330
|
+
constructor(valid, rules, valueTypes, keyTypes=null)
|
|
331
|
+
{
|
|
332
|
+
if (typeof valid !== F)
|
|
333
|
+
{
|
|
334
|
+
throw new TypeError("Invalid OfTest validator");
|
|
335
|
+
}
|
|
243
336
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
337
|
+
if (valueTypes !== null && !isArray(valueTypes))
|
|
338
|
+
{
|
|
339
|
+
throw new TypeError("Invalid valueTypes array");
|
|
340
|
+
}
|
|
248
341
|
|
|
249
|
-
|
|
342
|
+
if (keyTypes !== null && !isArray(keyTypes))
|
|
343
|
+
{
|
|
344
|
+
throw new TypeError("Invalid keyTypes array");
|
|
345
|
+
}
|
|
250
346
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
347
|
+
if (valueTypes === null && keyTypes === null)
|
|
348
|
+
{
|
|
349
|
+
throw new RangeError("Both valueTypes and keyTypes are null");
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
if (valid(rules))
|
|
353
|
+
{ // The container was sent instead of rules.
|
|
354
|
+
rules = {value: rules};
|
|
355
|
+
this.valid = true;
|
|
356
|
+
}
|
|
357
|
+
else if (isObj(rules))
|
|
358
|
+
{
|
|
359
|
+
this.valid = valid(rules.valid);
|
|
360
|
+
}
|
|
361
|
+
else
|
|
362
|
+
{ // That's gonna be a no from me.
|
|
363
|
+
rules = {value: null};
|
|
364
|
+
this.valid = false;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
this.vtype = valueTypes;
|
|
368
|
+
this.ktype = keyTypes;
|
|
369
|
+
this.rules = rules;
|
|
370
|
+
this.state =
|
|
254
371
|
{
|
|
255
|
-
pass: false,
|
|
256
372
|
empty: false,
|
|
257
|
-
|
|
373
|
+
at:
|
|
258
374
|
{
|
|
259
375
|
index: -1,
|
|
260
|
-
value:
|
|
376
|
+
value: rules.value,
|
|
377
|
+
},
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
if (keyTypes !== null)
|
|
381
|
+
{
|
|
382
|
+
this.state.at.key = false;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// A shortcut for tests.
|
|
386
|
+
this.target = rules.value;
|
|
387
|
+
|
|
388
|
+
} // constructor()
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* Run `isa()` tests on the next item.
|
|
392
|
+
*
|
|
393
|
+
* @param {*} key - `state.at.index` will be set to this
|
|
394
|
+
*
|
|
395
|
+
* If `this.ktype` was set (via the `keyTypes` constructor argument),
|
|
396
|
+
* then the `key` will be tested for validity against those tests using
|
|
397
|
+
* the `isa()` function.
|
|
398
|
+
*
|
|
399
|
+
* @param {*} val - `state.at.value` will be set to this
|
|
400
|
+
*
|
|
401
|
+
* The `val` will be tested for validity against the `this.vtype` tests
|
|
402
|
+
* (via the `valueTests` constructor argument) using the `isa()` function.
|
|
403
|
+
*
|
|
404
|
+
* @returns {boolean} Was the `val` (and the `key` if applicable) valid?
|
|
405
|
+
*/
|
|
406
|
+
test(key, val)
|
|
407
|
+
{
|
|
408
|
+
this.state.at.index = key;
|
|
409
|
+
this.state.at.value = val;
|
|
410
|
+
|
|
411
|
+
if (this.ktype)
|
|
412
|
+
{
|
|
413
|
+
const keyOk = isa(key, ...this.ktype);
|
|
414
|
+
if (!keyOk)
|
|
415
|
+
{
|
|
416
|
+
this.state.at.key = true;
|
|
417
|
+
return false;
|
|
261
418
|
}
|
|
262
419
|
}
|
|
420
|
+
|
|
421
|
+
return isa(val, ...this.vtype);
|
|
263
422
|
}
|
|
264
|
-
|
|
423
|
+
|
|
424
|
+
/**
|
|
425
|
+
* Mark `this.state.empty` as `true`
|
|
426
|
+
*
|
|
427
|
+
* @returns {boolean} `this.rules.empty`;
|
|
428
|
+
* determines if the test should be considered a `pass` or `fail`
|
|
429
|
+
* upon being marked `empty`.
|
|
430
|
+
*/
|
|
431
|
+
empty()
|
|
265
432
|
{
|
|
266
|
-
|
|
433
|
+
this.state.empty = true;
|
|
434
|
+
return !!this.rules.empty;
|
|
267
435
|
}
|
|
268
436
|
|
|
269
|
-
|
|
437
|
+
/**
|
|
438
|
+
* A dynamic getter property that returns a _passed_ test result.
|
|
439
|
+
* @returns {(object|boolean)} Depends on `this.rules.details` value.
|
|
440
|
+
*/
|
|
441
|
+
get pass()
|
|
442
|
+
{
|
|
443
|
+
if (this.rules.details)
|
|
444
|
+
{
|
|
445
|
+
const empty = this.state.empty;
|
|
446
|
+
return {pass: true, empty};
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
return true;
|
|
450
|
+
}
|
|
270
451
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
452
|
+
/**
|
|
453
|
+
* A dynamic getter property that returns a _failed_ test result.
|
|
454
|
+
* @returns {(object|boolean)} Depends on `this.rules.details` value.
|
|
455
|
+
*/
|
|
456
|
+
get fail()
|
|
457
|
+
{
|
|
458
|
+
if (this.rules.details)
|
|
274
459
|
{
|
|
275
|
-
|
|
460
|
+
const {empty, at: failed} = this.state;
|
|
461
|
+
return {pass: false, empty, failed};
|
|
276
462
|
}
|
|
277
463
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
464
|
+
return false;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
exports.OfTest = OfTest;
|
|
470
|
+
|
|
471
|
+
/**
|
|
472
|
+
* A nested class representing an explicit set of rules for `OfTest`.
|
|
473
|
+
*
|
|
474
|
+
* Not really needed for anything except for `isPlainObjectOf()` function.
|
|
475
|
+
*
|
|
476
|
+
* @alias module:@lumjs/core/types.OfTest.Rules
|
|
477
|
+
*/
|
|
478
|
+
class OfTestRules
|
|
479
|
+
{
|
|
480
|
+
/**
|
|
481
|
+
* Define the Rules
|
|
482
|
+
*
|
|
483
|
+
* @param {mixed} value - Sets `rules.value`
|
|
484
|
+
* @param {object} [opts] Optional rules
|
|
485
|
+
* @param {boolean} [opts.empty=false] Sets `rules.empty`
|
|
486
|
+
* @param {boolean} [opts.details=false] Sets `rules.details`
|
|
487
|
+
*
|
|
488
|
+
*/
|
|
489
|
+
constructor(value, opts)
|
|
490
|
+
{
|
|
491
|
+
this.value = value;
|
|
492
|
+
this.empty = opts.empty ?? false;
|
|
493
|
+
this.details = opts.details ?? false;
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
def(OfTest, 'Rules', OfTestRules);
|
|
498
|
+
|
|
499
|
+
/**
|
|
500
|
+
* See if every item in an `Array` passes an `isa()` test.
|
|
501
|
+
*
|
|
502
|
+
* Uses the {@link module:@lumjs/core/types.OfTest} class.
|
|
503
|
+
* This implementation works explicitly with `Array` objects only.
|
|
504
|
+
*
|
|
505
|
+
* @param {(object|Array)} rules - Rules for this test function.
|
|
506
|
+
*
|
|
507
|
+
* If this argument is an `Array` it is assumed to be the
|
|
508
|
+
* `rules.value` named option.
|
|
509
|
+
*
|
|
510
|
+
* @param {Array} rules.value - The actual Array object to test.
|
|
511
|
+
*
|
|
512
|
+
* @param {...any} types - See {@link module:@lumjs/core/types.isa}.
|
|
513
|
+
*
|
|
514
|
+
* All arguments other than `rules` are passed to `isa()` with each item
|
|
515
|
+
* from the `rules.value` object as the subject of the test.
|
|
516
|
+
*
|
|
517
|
+
* @returns {(boolean|object)} Results of the test.
|
|
518
|
+
*
|
|
519
|
+
* If `rules.details` was `true` this will be a
|
|
520
|
+
* {@link module:@lumjs/core/types~OfTestResult} object.
|
|
521
|
+
*
|
|
522
|
+
* Otherwise it will be a simple `boolean` value indicating
|
|
523
|
+
* if the test passed or failed.
|
|
524
|
+
*
|
|
525
|
+
* @alias module:@lumjs/core/types.isArrayOf
|
|
526
|
+
*/
|
|
527
|
+
function isArrayOf(rules, ...types)
|
|
528
|
+
{
|
|
529
|
+
// Build an OfTest instance.
|
|
530
|
+
const test = new OfTest(isArray, rules, types);
|
|
531
|
+
|
|
532
|
+
if (!test.valid)
|
|
533
|
+
{ // Test was invalid right off the bat.
|
|
534
|
+
return test.fail;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
const target = test.target;
|
|
538
|
+
|
|
539
|
+
if (target.length === 0)
|
|
540
|
+
{ // Array is empty.
|
|
541
|
+
if (!test.empty())
|
|
542
|
+
{ // `opts.empty` was not `true`
|
|
543
|
+
return test.fail;
|
|
281
544
|
}
|
|
282
545
|
}
|
|
283
546
|
else
|
|
284
547
|
{ // Run the tests on each item.
|
|
285
|
-
for (let i=0; i <
|
|
548
|
+
for (let i=0; i < target.length; i++)
|
|
286
549
|
{
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
{
|
|
291
|
-
res.failed.index = i;
|
|
292
|
-
res.failed.value = vi;
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
if (!isa(vi, ...types))
|
|
296
|
-
{ // An item did not pass the test.
|
|
297
|
-
return res;
|
|
550
|
+
if (!test.test(i, target[i]))
|
|
551
|
+
{ // The test failed.
|
|
552
|
+
return test.fail;
|
|
298
553
|
}
|
|
299
554
|
}
|
|
300
555
|
}
|
|
301
556
|
|
|
302
557
|
// If we made it here, we passed.
|
|
303
|
-
|
|
558
|
+
return test.pass;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
exports.isArrayOf = isArrayOf;
|
|
562
|
+
|
|
563
|
+
/**
|
|
564
|
+
* See if every item in an Iterable list passes an `isa()` test.
|
|
565
|
+
*
|
|
566
|
+
* Uses the {@link module:@lumjs/core/types.OfTest} class.
|
|
567
|
+
* This implementation works with any kind of `Iterable` object.
|
|
568
|
+
*
|
|
569
|
+
* @param {(object|Iterable)} rules - Rules for this test function.
|
|
570
|
+
*
|
|
571
|
+
* If this argument is an `Iterable` object it is assumed to be the
|
|
572
|
+
* `rules.value` named option.
|
|
573
|
+
*
|
|
574
|
+
* @param {Iterable} rules.value - The actual list object for the tests.
|
|
575
|
+
*
|
|
576
|
+
* @param {...any} types - See {@link module:@lumjs/core/types.isa}.
|
|
577
|
+
*
|
|
578
|
+
* All arguments other than `rules` are passed to `isa()` with each item
|
|
579
|
+
* from the `rules.value` object as the subject of the test.
|
|
580
|
+
*
|
|
581
|
+
* @returns {(boolean|object)} Results of the test.
|
|
582
|
+
*
|
|
583
|
+
* If `rules.details` was `true` this will be a
|
|
584
|
+
* {@link module:@lumjs/core/types~OfTestResult} object.
|
|
585
|
+
*
|
|
586
|
+
* Otherwise it will be a simple `boolean` value indicating
|
|
587
|
+
* if the test passed or failed.
|
|
588
|
+
*
|
|
589
|
+
* @alias module:@lumjs/core/types.isListOf
|
|
590
|
+
*/
|
|
591
|
+
function isListOf(rules, ...types)
|
|
592
|
+
{
|
|
593
|
+
// Build an OfTest instance.
|
|
594
|
+
const test = new OfTest(isIterable, rules, types);
|
|
595
|
+
|
|
596
|
+
if (!test.valid)
|
|
597
|
+
{ // Test was invalid right off the bat.
|
|
598
|
+
return test.fail;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
let i = 0;
|
|
602
|
+
for (const val of test.target)
|
|
304
603
|
{
|
|
305
|
-
|
|
306
|
-
|
|
604
|
+
if (!test.test(i++, val))
|
|
605
|
+
{ // The test failed.
|
|
606
|
+
return test.fail;
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
if (i === 0)
|
|
611
|
+
{ // List was empty.
|
|
612
|
+
if (!test.empty())
|
|
613
|
+
{ // `opts.empty` was not `true`
|
|
614
|
+
return test.fail;
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
// If we made it here, we passed.
|
|
619
|
+
return test.pass;
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
exports.isListOf = isListOf;
|
|
623
|
+
|
|
624
|
+
const isMap = v => (v instanceof Map);
|
|
625
|
+
|
|
626
|
+
/**
|
|
627
|
+
* See if every key/value pair in a Map passes an `isa()` test.
|
|
628
|
+
*
|
|
629
|
+
* Uses the {@link module:@lumjs/core/types.OfTest} class.
|
|
630
|
+
* This implementation works with only `Map` objects.
|
|
631
|
+
*
|
|
632
|
+
* Unlike `isArrayOf()` and `isListOf()`, this function does not
|
|
633
|
+
* have a variable argument list. It has only **3** arguments,
|
|
634
|
+
* and all of them are mandatory!
|
|
635
|
+
*
|
|
636
|
+
* @param {(object|Map)} rules - Rules for this test function.
|
|
637
|
+
*
|
|
638
|
+
* If this argument is a `Map` object it is assumed to be the
|
|
639
|
+
* `rules.value` named option.
|
|
640
|
+
*
|
|
641
|
+
* @param {Map} rules.value - The actual list object for the tests.
|
|
642
|
+
*
|
|
643
|
+
* @param {?Array} keyTypes - Types the _keys_ must be one of.
|
|
644
|
+
*
|
|
645
|
+
* If anything other than an `Array` or `null` is passed here,
|
|
646
|
+
* it will be wrapped in an `Array`.
|
|
647
|
+
*
|
|
648
|
+
* @param {?Array} valTypes - Types the _values_ must be one of.
|
|
649
|
+
*
|
|
650
|
+
* If anything other than an `Array` or `null` is passed here,
|
|
651
|
+
* it will be wrapped in an `Array`.
|
|
652
|
+
*
|
|
653
|
+
* @returns {(boolean|object)} Results of the test.
|
|
654
|
+
*
|
|
655
|
+
* If `rules.details` was `true` this will be a
|
|
656
|
+
* {@link module:@lumjs/core/types~OfTestResult} object.
|
|
657
|
+
*
|
|
658
|
+
* Otherwise it will be a simple `boolean` value indicating
|
|
659
|
+
* if the test passed or failed.
|
|
660
|
+
*
|
|
661
|
+
* @alias module:@lumjs/core/types.isListOf
|
|
662
|
+
*/
|
|
663
|
+
function isMapOf(rules, keyTypes, valTypes)
|
|
664
|
+
{
|
|
665
|
+
if (notNil(keyTypes) && !isArray(keyTypes))
|
|
666
|
+
{
|
|
667
|
+
keyTypes = [keyTypes];
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
if (notNil(valTypes) && !isArray(valTypes))
|
|
671
|
+
{
|
|
672
|
+
valTypes = [valTypes];
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
const test = new OfTest(isMap, rules, valTypes, keyTypes);
|
|
676
|
+
|
|
677
|
+
if (!test.valid)
|
|
678
|
+
{ // Test was invalid right off the bat.
|
|
679
|
+
return test.fail;
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
if (test.target.size === 0)
|
|
683
|
+
{ // Map is empty.
|
|
684
|
+
if (!test.empty())
|
|
685
|
+
{ // `opts.empty` was not `true`
|
|
686
|
+
return test.fail;
|
|
687
|
+
}
|
|
307
688
|
}
|
|
308
689
|
else
|
|
309
690
|
{
|
|
310
|
-
|
|
691
|
+
for (const [key,val] of test.target)
|
|
692
|
+
{
|
|
693
|
+
if (!test.test(key,val))
|
|
694
|
+
{
|
|
695
|
+
return test.fail;
|
|
696
|
+
}
|
|
697
|
+
}
|
|
311
698
|
}
|
|
312
699
|
|
|
313
|
-
|
|
700
|
+
// We made it, yay!
|
|
701
|
+
return test.pass;
|
|
314
702
|
}
|
|
315
703
|
|
|
316
|
-
exports.
|
|
704
|
+
exports.isMapOf = isMapOf;
|
|
705
|
+
|
|
706
|
+
const isntRules = v => (isObj(v) && !(v instanceof OfTestRules));
|
|
707
|
+
|
|
708
|
+
/**
|
|
709
|
+
* See if all enumerable property values in an `object` pass an `isa()` test.
|
|
710
|
+
*
|
|
711
|
+
* Uses the {@link module:@lumjs/core/types.OfTest} class.
|
|
712
|
+
* This implementation works with any _plain_ objects.
|
|
713
|
+
*
|
|
714
|
+
* There is a special function called `rules(value, opts)` defined on this
|
|
715
|
+
* test function that will return an instance of the `OfTest.Rules` class,
|
|
716
|
+
* passing all parameters along to the constructor.
|
|
717
|
+
*
|
|
718
|
+
* @param {object} rules - Rules for this test function.
|
|
719
|
+
*
|
|
720
|
+
* If this argument is anything other than a `OfTest.Rules` instance,
|
|
721
|
+
* it is assumed to be the `rules.value` named option.
|
|
722
|
+
*
|
|
723
|
+
* If you want to set any of the optional rules, you can use the `rules()`
|
|
724
|
+
* helper function mentioned above to return a `OfTest.Rules` instance:
|
|
725
|
+
*
|
|
726
|
+
* ```js
|
|
727
|
+
* const opts = {details: true, empty: true}; // Example rules.
|
|
728
|
+
* const isValid = isObjOf(isObjOf.rules(value, opts), type1, type2, ...);
|
|
729
|
+
* ```
|
|
730
|
+
*
|
|
731
|
+
* @param {Iterable} rules.value - The actual list object for the tests.
|
|
732
|
+
*
|
|
733
|
+
* @param {...any} types - See {@link module:@lumjs/core/types.isa}.
|
|
734
|
+
*
|
|
735
|
+
* All arguments other than `rules` are passed to `isa()` with each item
|
|
736
|
+
* from the `rules.value` object as the subject of the test.
|
|
737
|
+
*
|
|
738
|
+
* @returns {(boolean|object)} Results of the test.
|
|
739
|
+
*
|
|
740
|
+
* If `rules.details` was `true` this will be a
|
|
741
|
+
* {@link module:@lumjs/core/types~OfTestResult} object.
|
|
742
|
+
*
|
|
743
|
+
* Otherwise it will be a simple `boolean` value indicating
|
|
744
|
+
* if the test passed or failed.
|
|
745
|
+
*
|
|
746
|
+
* @alias module:@lumjs/core/types.isObjOf
|
|
747
|
+
*/
|
|
748
|
+
function isPlainObjectOf(rules, ...types)
|
|
749
|
+
{
|
|
750
|
+
// Build an OfTest instance.
|
|
751
|
+
const test = new OfTest(isntRules, rules, types);
|
|
752
|
+
|
|
753
|
+
if (!test.valid)
|
|
754
|
+
{ // Test was invalid right off the bat.
|
|
755
|
+
return test.fail;
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
const target = test.target;
|
|
759
|
+
const keys = Object.keys(target);
|
|
760
|
+
|
|
761
|
+
if (keys.length === 0)
|
|
762
|
+
{ // Object had no enumerable properties.
|
|
763
|
+
if (!test.empty())
|
|
764
|
+
{ // `opts.empty` was not `true`
|
|
765
|
+
return test.fail;
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
else
|
|
769
|
+
{ // There are properties to check.
|
|
770
|
+
for (const key of keys)
|
|
771
|
+
{
|
|
772
|
+
if (!test.test(key, target[key]))
|
|
773
|
+
{ // The test failed.
|
|
774
|
+
return test.fail;
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
// If we made it here, we passed.
|
|
780
|
+
return test.pass;
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
def(isPlainObjectOf, 'rules', function()
|
|
784
|
+
{
|
|
785
|
+
return new OfTestRules(...arguments);
|
|
786
|
+
});
|
|
787
|
+
|
|
788
|
+
exports.isObjOf = isPlainObjectOf;
|