@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/PLANS.txt
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Future Plans
|
|
2
|
+
|
|
3
|
+
## 2.x
|
|
4
|
+
|
|
5
|
+
I've done a lot of updates to this vanity library since writing it, and it's built up a
|
|
6
|
+
bit of cruft over time.
|
|
7
|
+
|
|
8
|
+
While it likely won't happen for a while yet, I do plan to have a clean-up version where
|
|
9
|
+
anything marked deprecated is removed, and any other cleanup that may break stuff can be
|
|
10
|
+
done at that time as well.
|
package/lib/index.js
CHANGED
|
@@ -105,7 +105,10 @@ function from(submod, ...libs)
|
|
|
105
105
|
|
|
106
106
|
// ObjectID stuff is imported directly without registering a sub-module.
|
|
107
107
|
const objectid = require('./objectid');
|
|
108
|
-
from(objectid,
|
|
108
|
+
from(objectid,
|
|
109
|
+
'randomNumber',
|
|
110
|
+
'UniqueObjectIds',
|
|
111
|
+
'InternalObjectId');
|
|
109
112
|
|
|
110
113
|
/**
|
|
111
114
|
* Get a simplistic debugging stacktrace
|
|
@@ -136,7 +139,12 @@ from(objectid, 'randomNumber', 'InternalObjectId');
|
|
|
136
139
|
// These are exported directly, but a meta sub-module also exists.
|
|
137
140
|
// Unlike most sub-modules there is no `meta` property in the main library.
|
|
138
141
|
const meta = require('./meta');
|
|
139
|
-
from(meta,
|
|
142
|
+
from(meta,
|
|
143
|
+
'stacktrace',
|
|
144
|
+
'AbstractClass',
|
|
145
|
+
'AbstractError',
|
|
146
|
+
'Functions',
|
|
147
|
+
'NYI');
|
|
140
148
|
|
|
141
149
|
/**
|
|
142
150
|
* Create a magic *Enum* object «Lazy»
|
package/lib/meta.js
CHANGED
|
@@ -27,6 +27,7 @@ exports.stacktrace = stacktrace;
|
|
|
27
27
|
|
|
28
28
|
/**
|
|
29
29
|
* Abstract classes for Javascript.
|
|
30
|
+
* @deprecated Just use `throw new AbstractError()` instead.
|
|
30
31
|
* @alias module:@lumjs/core/meta.AbstractClass
|
|
31
32
|
*/
|
|
32
33
|
class AbstractClass
|
|
@@ -103,6 +104,64 @@ class AbstractClass
|
|
|
103
104
|
|
|
104
105
|
exports.AbstractClass = AbstractClass;
|
|
105
106
|
|
|
107
|
+
/**
|
|
108
|
+
* An Error that can be thrown from abstract methods.
|
|
109
|
+
*
|
|
110
|
+
* Example usage:
|
|
111
|
+
*
|
|
112
|
+
* ```js
|
|
113
|
+
* class MyAbstractClass
|
|
114
|
+
* {
|
|
115
|
+
* abstractMethod()
|
|
116
|
+
* {
|
|
117
|
+
* throw new AbstractError();
|
|
118
|
+
* // msg = "Abstract method not implemented"
|
|
119
|
+
* }
|
|
120
|
+
*
|
|
121
|
+
* namedMethod()
|
|
122
|
+
* {
|
|
123
|
+
* throw new AbstractError("namedMethod");
|
|
124
|
+
* // msg = "Abstract method 'namedMethod' not implemented"
|
|
125
|
+
* }
|
|
126
|
+
*
|
|
127
|
+
* get absProp()
|
|
128
|
+
* {
|
|
129
|
+
* throw new AbstractError("absProp", true);
|
|
130
|
+
* // msg = "Abstract getter 'absProp' not implemented"
|
|
131
|
+
* }
|
|
132
|
+
* }
|
|
133
|
+
* ```
|
|
134
|
+
*
|
|
135
|
+
* @alias module:@lumjs/core/meta.AbstractError
|
|
136
|
+
*/
|
|
137
|
+
class AbstractError extends Error
|
|
138
|
+
{
|
|
139
|
+
/**
|
|
140
|
+
* Construct an AbstractError
|
|
141
|
+
*
|
|
142
|
+
* @param {string} [name] Name of abstract method/getter.
|
|
143
|
+
*
|
|
144
|
+
* If included will be included in error message.
|
|
145
|
+
*
|
|
146
|
+
* @param {boolean} [getter=false] Is a getter?
|
|
147
|
+
*
|
|
148
|
+
* This option literally just changes the phrasing of the
|
|
149
|
+
* error message to use 'getter' instead of 'method'.
|
|
150
|
+
*
|
|
151
|
+
*/
|
|
152
|
+
constructor(name, getter=false)
|
|
153
|
+
{
|
|
154
|
+
let msg = "Abstract ";
|
|
155
|
+
msg += (getter ? 'getter ' : 'method ');
|
|
156
|
+
if (name) msg += `'${name}' `;
|
|
157
|
+
msg += 'not implemented';
|
|
158
|
+
super(msg);
|
|
159
|
+
this.name = 'AbstractError';
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
exports.AbstractError = AbstractError;
|
|
164
|
+
|
|
106
165
|
/**
|
|
107
166
|
* Function prototypes for async, generator, and async generator functions.
|
|
108
167
|
* @namespace
|
package/lib/obj/flip.js
ADDED
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const {B,S,isObj,needObj,isMapOf,isa,isArray} = require('../types');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Flip an object's keys and values.
|
|
7
|
+
*
|
|
8
|
+
* Is a wrapper around the other `flip*` functions.
|
|
9
|
+
*
|
|
10
|
+
* @alias module:@lumjs/core/obj.flip
|
|
11
|
+
*
|
|
12
|
+
* @param {object} obj - Object to flip.
|
|
13
|
+
*
|
|
14
|
+
* If `obj` is a `Map`, passes it to `flipMap()`.
|
|
15
|
+
* Anything else will be passed to `flipKeyVal()`.
|
|
16
|
+
*
|
|
17
|
+
* Other supported types may be added in the future.
|
|
18
|
+
*
|
|
19
|
+
* @param {object} [opts] Any options supported by the
|
|
20
|
+
* underlying `flip*` functions that may be called.
|
|
21
|
+
*
|
|
22
|
+
* @returns {object} Object with flipped keys and values.
|
|
23
|
+
*
|
|
24
|
+
* @throws {Error} See the underlying functions for exact error types.
|
|
25
|
+
*
|
|
26
|
+
* @see {@link module:@lumjs/core/obj.flipKeyVal}
|
|
27
|
+
* @see {@link module:@lumjs/core/obj.flipMap}
|
|
28
|
+
*/
|
|
29
|
+
function flip(obj, opts={})
|
|
30
|
+
{
|
|
31
|
+
if (obj instanceof Map)
|
|
32
|
+
{
|
|
33
|
+
return flipMap(obj, opts);
|
|
34
|
+
}
|
|
35
|
+
else
|
|
36
|
+
{
|
|
37
|
+
return flipKeyVal(obj, opts);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Flip a plain object's keys and values.
|
|
43
|
+
*
|
|
44
|
+
* Works for _enumerable_ properties with _non-duplicate_ `string` values.
|
|
45
|
+
*
|
|
46
|
+
* @param {object} inObj - The input object to flip.
|
|
47
|
+
*
|
|
48
|
+
* @param {object} [opts] Options
|
|
49
|
+
* @param {boolean} [opts.warnDup=true] Use `console` on duplicate values?
|
|
50
|
+
* @param {boolean} [opts.fatalDup=false] Throw Error on duplicate values?
|
|
51
|
+
* @param {boolean} [opts.warnStr=true] Use `console` on non-string values?
|
|
52
|
+
* @param {boolean} [opts.fatalStr=false] Throw Error on non-string values?
|
|
53
|
+
*
|
|
54
|
+
* @returns {object} A new `object` with flipped keys and values.
|
|
55
|
+
*
|
|
56
|
+
* @throws {TypeError} If `obj` was not a valid `object`,
|
|
57
|
+
* or if `fatalStr` is `true` and a non-string value is found.
|
|
58
|
+
*
|
|
59
|
+
* @throws {ReferenceError} If `fatalDup` is `true`
|
|
60
|
+
* and a duplicate value is found.
|
|
61
|
+
*
|
|
62
|
+
*/
|
|
63
|
+
function flipKeyVal(inObj, opts={})
|
|
64
|
+
{
|
|
65
|
+
needObj(inObj);
|
|
66
|
+
|
|
67
|
+
const wdup = opts.warnDup ?? true,
|
|
68
|
+
fdup = opts.fatalDup ?? false,
|
|
69
|
+
wstr = opts.warnStr ?? true,
|
|
70
|
+
fstr = opts.fatalStr ?? false;
|
|
71
|
+
|
|
72
|
+
const outObj = {};
|
|
73
|
+
|
|
74
|
+
for (const key in inObj)
|
|
75
|
+
{
|
|
76
|
+
const val = inObj[key];
|
|
77
|
+
if (typeof val === S)
|
|
78
|
+
{
|
|
79
|
+
if (outObj[val] === undefined)
|
|
80
|
+
{
|
|
81
|
+
outObj[val] = key;
|
|
82
|
+
}
|
|
83
|
+
else
|
|
84
|
+
{
|
|
85
|
+
if (wdup) console.error({key, val, obj: inObj});
|
|
86
|
+
if (fdup) throw new ReferenceError("Duplicate value");
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
else
|
|
90
|
+
{
|
|
91
|
+
if (wstr) console.error({key, val, obj: inObj});
|
|
92
|
+
if (fstr) throw new TypeError("Non-string value");
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return outObj;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Flip the keys and values of a `Map`.
|
|
101
|
+
*
|
|
102
|
+
* @param {Map} inMap - The input Map to flip.
|
|
103
|
+
*
|
|
104
|
+
* @param {object} [opts] Options
|
|
105
|
+
*
|
|
106
|
+
* @param {boolean} [opts.warn=true] Log warnings with `console`?
|
|
107
|
+
* @param {boolean} [opts.fatal=false] Throw Errors for invalid parameters?
|
|
108
|
+
*
|
|
109
|
+
* @param {object} [opts.valid] Validate the `inMap` keys/values?
|
|
110
|
+
* @param {?Array} [opts.valid.keys=null] Valid key type tests.
|
|
111
|
+
* @param {?Array} [opts.valid.values=null] Validate value type tests.
|
|
112
|
+
*
|
|
113
|
+
* @param {(boolean|object)} [opts.valid.map=false] Validate entire map?
|
|
114
|
+
*
|
|
115
|
+
* If this is `true` or an `object` then the entire `inMap` will be tested
|
|
116
|
+
* using `isMapOf()`. If an `object`, it will be used as the `rules`
|
|
117
|
+
* to pass to `isMapOf()` (the `inMap` will be assigned as `rules.value`).
|
|
118
|
+
* If the validation fails when this is `true`, no attempt to flip the
|
|
119
|
+
* map will be done.
|
|
120
|
+
*
|
|
121
|
+
* If this is `false`, but at least one of `.keys` or
|
|
122
|
+
* `.values` is set, then each key and/or value will be tested using
|
|
123
|
+
* the `isa()` function, and any invalid key-val pair will be skipped.
|
|
124
|
+
*
|
|
125
|
+
* If not specified, this will default to the value of the `.fatal` option.
|
|
126
|
+
*
|
|
127
|
+
* @param {boolean} [opts.valid.warn] Use `console` if validation failed?
|
|
128
|
+
*
|
|
129
|
+
* If `.map` is `true`, the console will only have one log entry added
|
|
130
|
+
* with the results of the `isMapOf()` test.
|
|
131
|
+
*
|
|
132
|
+
* If `.map` is `false`, the console will have an entry for every
|
|
133
|
+
* invalid key-val pair in the `inMap`.
|
|
134
|
+
*
|
|
135
|
+
* If not specified, this will default to the value of the `..warn` option.
|
|
136
|
+
*
|
|
137
|
+
* @param {boolean} [opts.valid.fatal] Throw Error on invalid `inMap`?
|
|
138
|
+
*
|
|
139
|
+
* Only applicable if `.map` is `true`.
|
|
140
|
+
* If not specified, this will default to the value of the `..fatal` option.
|
|
141
|
+
*
|
|
142
|
+
* @returns {?Map} A new Map with flipped keys and values.
|
|
143
|
+
*
|
|
144
|
+
* @throws {TypeError} Thrown in specific situations:
|
|
145
|
+
*
|
|
146
|
+
* - If `inMap` is not a valid `Map` and `opts.fatal` is `true`.
|
|
147
|
+
*
|
|
148
|
+
* - If both `opts.valid.map` and `opts.valid.fatal` are `true`,
|
|
149
|
+
* and the validation tests against the `inMap` object fail.
|
|
150
|
+
*
|
|
151
|
+
*/
|
|
152
|
+
function flipMap(inMap, opts={})
|
|
153
|
+
{
|
|
154
|
+
const warn = opts.warn ?? true;
|
|
155
|
+
const fatal = opts.fatal ?? false;
|
|
156
|
+
|
|
157
|
+
if (!(inMap instanceof Map))
|
|
158
|
+
{
|
|
159
|
+
const errMsg = "Invalid Map instance";
|
|
160
|
+
|
|
161
|
+
if (warn)
|
|
162
|
+
{
|
|
163
|
+
const log = [{inMap, opts}];
|
|
164
|
+
if (!fatal) log.unshift(errMsg);
|
|
165
|
+
console.error(log);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (fatal)
|
|
169
|
+
{
|
|
170
|
+
throw new TypeError(errMsg);
|
|
171
|
+
}
|
|
172
|
+
else
|
|
173
|
+
{
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const vopts = opts.valid ?? {};
|
|
179
|
+
const vwarn = vopts.warn ?? warn;
|
|
180
|
+
const vfatal = vopts.fatal ?? fatal;
|
|
181
|
+
const vmap = vopts.map ?? vfatal;
|
|
182
|
+
|
|
183
|
+
const vkeys = isArray(vopts.keys) ? vopts.keys : null;
|
|
184
|
+
const vvals = isArray(vopts.values) ? vopts.values : null;
|
|
185
|
+
|
|
186
|
+
function validate(key, val)
|
|
187
|
+
{
|
|
188
|
+
if (vkeys && !isa(key, ...vkeys))
|
|
189
|
+
{
|
|
190
|
+
if (vwarn)
|
|
191
|
+
{
|
|
192
|
+
console.error("Invalid Key", {key, val});
|
|
193
|
+
}
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (vvals && !isa(val, ...vvals))
|
|
198
|
+
{
|
|
199
|
+
if (vwarn)
|
|
200
|
+
{
|
|
201
|
+
console.error("Invalid Value", {key, val});
|
|
202
|
+
}
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return true;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (vmap)
|
|
210
|
+
{ // Validating the entire input Map.
|
|
211
|
+
const rules = isObj(vmap) ? vmap : {};
|
|
212
|
+
const valid = isMapOf(rules, vkeys, vvals);
|
|
213
|
+
|
|
214
|
+
let passed = false;
|
|
215
|
+
if (typeof valid === B)
|
|
216
|
+
{
|
|
217
|
+
passed = valid;
|
|
218
|
+
}
|
|
219
|
+
else if (isObj(valid) && typeof valid.pass === B)
|
|
220
|
+
{
|
|
221
|
+
passed = valid.pass;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (!passed)
|
|
225
|
+
{
|
|
226
|
+
if (vwarn)
|
|
227
|
+
{ // Log to the console.
|
|
228
|
+
console.error("Map validation failed", {inMap, opts, valid});
|
|
229
|
+
}
|
|
230
|
+
if (vfatal)
|
|
231
|
+
{
|
|
232
|
+
throw new TypeError("Invalid Map content");
|
|
233
|
+
}
|
|
234
|
+
else
|
|
235
|
+
{ // We're done here.
|
|
236
|
+
return null;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const outMap = new Map();
|
|
242
|
+
|
|
243
|
+
for (const [key, val] of inMap)
|
|
244
|
+
{
|
|
245
|
+
if (!vmap && !validate(key, val))
|
|
246
|
+
{ // Skip invalid key-val pair.
|
|
247
|
+
continue;
|
|
248
|
+
}
|
|
249
|
+
outMap.set(val, key);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return outMap;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
module.exports =
|
|
256
|
+
{
|
|
257
|
+
flip, flipKeyVal, flipMap,
|
|
258
|
+
}
|
package/lib/obj/index.js
CHANGED
|
@@ -7,11 +7,13 @@ const apply = require('./apply');
|
|
|
7
7
|
const {copyAll,duplicateOne,duplicateAll} = require('./copyall');
|
|
8
8
|
const copyProps = require('./copyprops');
|
|
9
9
|
const {CLONE,clone,addClone,cloneIfLocked} = require('./clone');
|
|
10
|
+
const {flip, flipKeyVal, flipMap} = require('./flip');
|
|
11
|
+
const {getMethods,signatureOf,MethodFilter} = require('./getmethods');
|
|
12
|
+
const getProperty = require('./getproperty');
|
|
10
13
|
const {lock,addLock} = require('./lock');
|
|
11
14
|
const {mergeNested,syncNested} = require('./merge');
|
|
12
15
|
const ns = require('./ns');
|
|
13
|
-
|
|
14
|
-
const {getMethods,signatureOf,MethodFilter} = require('./getmethods');
|
|
16
|
+
|
|
15
17
|
const
|
|
16
18
|
{
|
|
17
19
|
getObjectPath,setObjectPath,
|
|
@@ -24,5 +26,5 @@ module.exports =
|
|
|
24
26
|
mergeNested, syncNested, copyProps, copyAll, ns,
|
|
25
27
|
getObjectPath, setObjectPath, getNamespace, setNamespace,
|
|
26
28
|
getProperty, duplicateAll, duplicateOne, getMethods, signatureOf,
|
|
27
|
-
MethodFilter, apply,
|
|
29
|
+
MethodFilter, apply, flip, flipKeyVal, flipMap,
|
|
28
30
|
}
|
package/lib/objectid.js
CHANGED
|
@@ -1,19 +1,198 @@
|
|
|
1
1
|
|
|
2
|
-
const {notNil,def,isNil} = require('./types');
|
|
2
|
+
const {notNil,def,isNil,F,N,S,SY} = require('./types');
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Generate a large random number.
|
|
6
6
|
*
|
|
7
|
+
* @param {number} [seed] A base number to use.
|
|
8
|
+
*
|
|
9
|
+
* The default is to use `Date.now()` as the `seed`.
|
|
10
|
+
*
|
|
7
11
|
* @returns {number}
|
|
8
12
|
* @alias module:@lumjs/core.randomNumber
|
|
9
13
|
*/
|
|
10
|
-
function randomNumber()
|
|
14
|
+
function randomNumber(seed)
|
|
11
15
|
{
|
|
12
|
-
|
|
16
|
+
if (typeof seed !== N) seed = Date.now();
|
|
17
|
+
return Math.floor(Math.random() * seed);
|
|
13
18
|
}
|
|
14
19
|
|
|
15
20
|
exports.randomNumber = randomNumber;
|
|
16
21
|
|
|
22
|
+
const validBase = base => (typeof base === N && base > 1 && base < 37);
|
|
23
|
+
|
|
24
|
+
class UniqueObjectIds
|
|
25
|
+
{
|
|
26
|
+
constructor(opts={})
|
|
27
|
+
{
|
|
28
|
+
def(this, '$options', {value: opts});
|
|
29
|
+
|
|
30
|
+
if (validBase(opts.random))
|
|
31
|
+
{ // Use random ids.
|
|
32
|
+
def(this, '$randIds', {value: {}});
|
|
33
|
+
}
|
|
34
|
+
else if (validBase(opts.timestamp))
|
|
35
|
+
{ // Use timestamp-based ids.
|
|
36
|
+
def(this, '$timeIds', {value: {}});
|
|
37
|
+
}
|
|
38
|
+
else
|
|
39
|
+
{ // Use incremental ids.
|
|
40
|
+
def(this, '$incIds', {value: {}});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const propType = (typeof opts.idProperty);
|
|
44
|
+
const hasProp = (propType === S || propType === SY);
|
|
45
|
+
const useRegistry = opts.useRegistry ?? !hasProp;
|
|
46
|
+
|
|
47
|
+
if (useRegistry)
|
|
48
|
+
{ // Using an internal registry.
|
|
49
|
+
def(this, '$registry', {value: new Map()});
|
|
50
|
+
}
|
|
51
|
+
else if (!hasProp)
|
|
52
|
+
{ // At least ONE of them MUST be used!
|
|
53
|
+
throw new RangeError("Need one of 'useRegistry' or 'idProperty'");
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (hasProp)
|
|
57
|
+
{ // Add a direct reference to the id property key.
|
|
58
|
+
def(this, '$idProp', {value: opts.idProperty});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
id(obj)
|
|
64
|
+
{
|
|
65
|
+
const hasRegistry = (this.$registry instanceof Map);
|
|
66
|
+
if (hasRegistry && this.$registry.has(obj))
|
|
67
|
+
{ // The object was found in the registry.
|
|
68
|
+
return this.$registry.get(obj);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const idProp = this.$idProp;
|
|
72
|
+
|
|
73
|
+
if (idProp && obj[idProp])
|
|
74
|
+
{ // An existing object id was found in the object's id property.
|
|
75
|
+
return obj[idProp];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
let cno = this.$options.className ?? {};
|
|
79
|
+
if (typeof cno === F) cno = {setup: cno};
|
|
80
|
+
else if (cno === 'lc') cno = {lowercase: true};
|
|
81
|
+
else if (cno === 'uc') cno = {uppercase: true};
|
|
82
|
+
|
|
83
|
+
let id = '', idNum = null;
|
|
84
|
+
|
|
85
|
+
if (typeof this.$options.prefix === S)
|
|
86
|
+
{
|
|
87
|
+
id += this.$options.prefix;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
let className = obj.constructor.name;
|
|
91
|
+
|
|
92
|
+
if (typeof cno.setup === F)
|
|
93
|
+
{ // Perform a transformation before any other changes.
|
|
94
|
+
className = cno.setup(className);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (cno.lowercase)
|
|
98
|
+
{ // Force to lowercase.
|
|
99
|
+
className = className.toLowerCase();
|
|
100
|
+
}
|
|
101
|
+
else if (cno.uppercase)
|
|
102
|
+
{ // Force to uppercase.
|
|
103
|
+
className = className.toUpperCase();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
id += className;
|
|
107
|
+
|
|
108
|
+
if (this.$incIds)
|
|
109
|
+
{ // Auto-incrementing ids.
|
|
110
|
+
const ids = this.$incIds;
|
|
111
|
+
if (ids[className])
|
|
112
|
+
{ // An existing value was found.
|
|
113
|
+
idNum = (++ids[className]).toString();
|
|
114
|
+
}
|
|
115
|
+
else
|
|
116
|
+
{ // No existing values yet.
|
|
117
|
+
const start = this.$options.startAt ?? 1;
|
|
118
|
+
ids[className] = start;
|
|
119
|
+
if (!this.$options.skipFirst)
|
|
120
|
+
{
|
|
121
|
+
idNum = start;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
else
|
|
126
|
+
{ // Something other than auto-increment.
|
|
127
|
+
let ids, radix;
|
|
128
|
+
if (this.$randIds)
|
|
129
|
+
{ // Using a random id.
|
|
130
|
+
ids = this.$randIds;
|
|
131
|
+
radix = this.$options.random;
|
|
132
|
+
}
|
|
133
|
+
else if (this.$timeIds)
|
|
134
|
+
{ // Using timestamp ids.
|
|
135
|
+
ids = this.$timeIds;
|
|
136
|
+
radix = this.$options.timestamp;
|
|
137
|
+
}
|
|
138
|
+
else
|
|
139
|
+
{ // Something went horribly wrong.
|
|
140
|
+
throw new Error("No id storage vars found");
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const getId = () => (this.$randIds
|
|
144
|
+
? randomNumber()
|
|
145
|
+
: Date.now())
|
|
146
|
+
.toString(radix);
|
|
147
|
+
|
|
148
|
+
if (ids[className])
|
|
149
|
+
{ // Existing ids for this className have been set.
|
|
150
|
+
while (!idNum)
|
|
151
|
+
{
|
|
152
|
+
idNum = getId();
|
|
153
|
+
if (ids[className][idNum])
|
|
154
|
+
{ // That id has already been used.
|
|
155
|
+
idNum = null;
|
|
156
|
+
}
|
|
157
|
+
else
|
|
158
|
+
{ // Hasn't been used yet, yay!
|
|
159
|
+
ids[className][idNum] = true;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
else
|
|
164
|
+
{
|
|
165
|
+
idNum = getId();
|
|
166
|
+
ids[className] = {};
|
|
167
|
+
ids[className][idNum] = true;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (typeof idNum === S)
|
|
172
|
+
{
|
|
173
|
+
if (typeof this.$options.infix === S)
|
|
174
|
+
{
|
|
175
|
+
id += this.$options.infix;
|
|
176
|
+
}
|
|
177
|
+
id += idNum;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (idProp)
|
|
181
|
+
{
|
|
182
|
+
def(obj, idProp, {value: id});
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (hasRegistry)
|
|
186
|
+
{
|
|
187
|
+
this.$registry.set(obj, id);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return id;
|
|
191
|
+
} // id()
|
|
192
|
+
} // UniqueObjectIds class
|
|
193
|
+
|
|
194
|
+
exports.UniqueObjectIds = UniqueObjectIds;
|
|
195
|
+
|
|
17
196
|
/**
|
|
18
197
|
* A class for creating unique identifier objects.
|
|
19
198
|
* Generally only used by my own inernal libraries, thus the name.
|
package/lib/types/basics.js
CHANGED
|
@@ -103,6 +103,27 @@ function isProperty(v)
|
|
|
103
103
|
return (t === S || t === SY);
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
+
/**
|
|
107
|
+
* See if a value is an Iterable object.
|
|
108
|
+
*
|
|
109
|
+
* Objects with `[Symbol.iterator]` can be used in
|
|
110
|
+
* `for (foo of bar)` statements. This tests for that.
|
|
111
|
+
*
|
|
112
|
+
* @param {*} v - Value we're testing
|
|
113
|
+
*
|
|
114
|
+
* While `string` values implement the `Iterable` interface,
|
|
115
|
+
* they are not considered an `object` in this and thus will fail.
|
|
116
|
+
* Only actual `object` values that implement the `[Symbol.iterator]`
|
|
117
|
+
* function will pass the test.
|
|
118
|
+
*
|
|
119
|
+
* @returns {boolean}
|
|
120
|
+
* @alias module:@lumjs/core/types.isIterable
|
|
121
|
+
*/
|
|
122
|
+
function isIterable(v)
|
|
123
|
+
{
|
|
124
|
+
return (isObj(v) && typeof v[Symbol.iterator] === F);
|
|
125
|
+
}
|
|
126
|
+
|
|
106
127
|
/**
|
|
107
128
|
* See if an object can be used as a valid *descriptor*.
|
|
108
129
|
*
|
|
@@ -192,9 +213,9 @@ function doesDescriptorTemplate(obj, accessor=false, strict=true)
|
|
|
192
213
|
return true;
|
|
193
214
|
}
|
|
194
215
|
|
|
195
|
-
// Now export those.
|
|
196
216
|
module.exports =
|
|
197
217
|
{
|
|
198
218
|
isObj, isComplex, isNil, notNil, isScalar, isArray, isTypedArray,
|
|
199
|
-
nonEmptyArray, isArguments, isProperty,
|
|
219
|
+
isIterable, nonEmptyArray, isArguments, isProperty,
|
|
220
|
+
doesDescriptor, doesDescriptorTemplate,
|
|
200
221
|
}
|
package/lib/types/index.js
CHANGED
|
@@ -23,7 +23,7 @@ const {O, F, S, B, N, U, SY, BI} = require('./js');
|
|
|
23
23
|
const
|
|
24
24
|
{
|
|
25
25
|
isObj, isComplex, isNil, notNil, isScalar, isArray, isTypedArray,
|
|
26
|
-
nonEmptyArray, isArguments, isProperty, doesDescriptor,
|
|
26
|
+
nonEmptyArray, isArguments, isProperty, doesDescriptor, isIterable,
|
|
27
27
|
doesDescriptorTemplate,
|
|
28
28
|
} = require('./basics');
|
|
29
29
|
|
|
@@ -31,7 +31,11 @@ const
|
|
|
31
31
|
const {root, unbound} = require('./root');
|
|
32
32
|
|
|
33
33
|
// Advanced type checks.
|
|
34
|
-
const
|
|
34
|
+
const
|
|
35
|
+
{
|
|
36
|
+
isInstance, isType, isa,
|
|
37
|
+
isArrayOf, isListOf, isMapOf, isObjOf, OfTest,
|
|
38
|
+
} = require('./isa');
|
|
35
39
|
|
|
36
40
|
// Error-throwing type checks.
|
|
37
41
|
const {needObj, needType, needs} = require('./needs');
|
|
@@ -55,7 +59,8 @@ module.exports =
|
|
|
55
59
|
isObj, isComplex, isNil, notNil, isScalar, isArray, isTypedArray,
|
|
56
60
|
nonEmptyArray, isArguments, isProperty, doesDescriptor,
|
|
57
61
|
isInstance, isType, isa, needObj, needType, needs, stringify,
|
|
58
|
-
doesDescriptorTemplate, ownCount,
|
|
62
|
+
doesDescriptorTemplate, ownCount, isIterable,
|
|
63
|
+
isArrayOf, isListOf, isMapOf, isObjOf, OfTest,
|
|
59
64
|
console,
|
|
60
65
|
}
|
|
61
66
|
|