@lumjs/core 1.13.0 → 1.15.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/lib/arrays.js CHANGED
@@ -1,82 +1,314 @@
1
1
  /**
2
- * Array helper functions.
2
+ * Array (and other list objects) helper functions.
3
3
  * @module @lumjs/core/arrays
4
4
  */
5
+ const {F,S} = require('./types/js');
6
+
7
+ /**
8
+ * A wrapper class to abstract functionality of various kinds of lists.
9
+ *
10
+ * Supports `Array`, `Set`, and `Map` explicitly.
11
+ * Other classes may be supported. YMMV.
12
+ *
13
+ * @alias module:@lumjs/core/arrays.List
14
+ */
15
+ class List
16
+ {
17
+ /**
18
+ * A list of known closure properties.
19
+ *
20
+ * Corresponding `wrap_${closure}` methods will be used to generate
21
+ * the closure properties.
22
+ */
23
+ static get KNOWN_CLOSURES()
24
+ {
25
+ return (Object.getOwnPropertyNames(this.prototype)
26
+ .filter(name => name.startsWith('wrap_')));
27
+ }
28
+
29
+ /**
30
+ * Build a new List wrapper.
31
+ *
32
+ * @param {object} obj - The list object.
33
+ *
34
+ * Typically an `Array`, `Set`, `Map`, or something similar.
35
+ * Not all of types will support all features. YMMV.
36
+ *
37
+ * @param {object} [opts] Options for advanced use.
38
+ *
39
+ * @param {bool} [opts.allowRaw=false] Attempt to support raw objects?
40
+ *
41
+ * Treat regular Javascript objects as lists, as they are a kind of
42
+ * map after all. This may lead to unusual results. YMMV.
43
+ *
44
+ * @param {mixed} [opts.closures=true] Create closure methods?
45
+ *
46
+ * If `true` or `"*"` we will create all known closure properties.
47
+ *
48
+ * If an `Array` it's the list of closure names we want to create.
49
+ * Possible values can be found in `List.KNOWN_CLOSURES`.
50
+ *
51
+ * If it's a `string` it's treated as a whitespace separated list
52
+ * of closure names and split into an `Array`.
53
+ *
54
+ * If `false` we won't create any closure properties.
55
+ *
56
+ */
57
+ constructor(obj, opts={})
58
+ {
59
+ this.obj = obj;
60
+ this.opts = opts;
61
+ this.allowRaw = opts.allowRaw ?? false;
62
+ this.setupClosures(opts.closures ?? true);
63
+ }
64
+
65
+ setupClosures(closures)
66
+ {
67
+ if (!closures)
68
+ { // Nothing to do.
69
+ return;
70
+ }
71
+
72
+ if (closures === true || closures === '*')
73
+ { // Setup all closures.
74
+ closures = this.constructor.KNOWN_CLOSURES;
75
+ }
76
+ else if (typeof closures === S)
77
+ {
78
+ closures = closures.trim().split(/\s+/);
79
+ }
80
+
81
+ if (Array.isArray(closures))
82
+ {
83
+ for (const closure of closures)
84
+ {
85
+ const meth = 'wrap_'+closure;
86
+ if (typeof this[meth] === F)
87
+ { // Create the detail property.
88
+ this[closure] = this[meth]();
89
+ }
90
+ else
91
+ {
92
+ console.error({closure, closures, list: this});
93
+ throw new Error("Unsupported closure");
94
+ }
95
+ }
96
+ }
97
+ else
98
+ {
99
+ console.error({closures, list: this});
100
+ throw new TypeError("Invalid closures value");
101
+ }
102
+ }
103
+
104
+ unsupported(obj, forClosure, info, retVal=false)
105
+ {
106
+ console.error("Unsupported object", {obj, forClosure, info, list: this});
107
+ return () => retVal;
108
+ }
109
+
110
+ /**
111
+ * Return a closure that returns if an item is in a list object.
112
+ *
113
+ * @param {object} obj - The list object the closure will be for.
114
+ *
115
+ * May be an `Array`, `Set`, `Map`, or `TypedArray` or any object
116
+ * that has either an `includes()` or `has()` method.
117
+ *
118
+ * @returns {function}
119
+ * @throws {TypeError}
120
+ */
121
+ wrap_contains(obj=this.obj, allowRaw=this.allowRaw)
122
+ {
123
+ if (typeof obj.includes === F)
124
+ { // Array and TypedArray have includes()
125
+ return item => obj.includes(item);
126
+ }
127
+ else if (typeof obj.has === F)
128
+ { // Set and Map have has()
129
+ return item => obj.has(item);
130
+ }
131
+ else if (allowRaw)
132
+ { // A fallback to raw object search.
133
+ return item => (typeof obj[item] !== undefined);
134
+ }
135
+ else
136
+ { // Nope.
137
+ return this.unsupported(obj, 'contains', {allowRaw});
138
+ }
139
+ }
140
+
141
+ /**
142
+ * Return a closure that removes an item from a list.
143
+ *
144
+ * @param {object} obj - The list object the closure will be for.
145
+ *
146
+ * May be an `Array`, `Set`, `Map`, or anything with a `delete()` method.
147
+ *
148
+ * @returns {function}
149
+ * @throws {TypeError}
150
+ */
151
+ wrap_remove(obj=this.obj, allowRaw=this.allowRaw)
152
+ {
153
+ let closure;
154
+ if (Array.isArray(obj))
155
+ { // Arrays have no easy method to do this.
156
+ closure = function(item)
157
+ {
158
+ let removed = 0;
159
+ let index = obj.indexOf(item);
160
+ while (index !== -1)
161
+ {
162
+ obj.splice(index, 1);
163
+ removed++;
164
+ index = obj.indexOf(item, index);
165
+ }
166
+ return removed;
167
+ }
168
+ }
169
+ else if (typeof obj.delete === F)
170
+ { // Use the delete() method.
171
+ closure = item => (obj.delete(item) ? 1 : 0);
172
+ }
173
+ else if (allowRaw)
174
+ { // Fallback to removing the property.
175
+ closure = function(item)
176
+ {
177
+ if (obj[item] === undefined)
178
+ {
179
+ return 0;
180
+ }
181
+ else
182
+ {
183
+ return (delete obj[item] ? 1 : 0);
184
+ }
185
+ }
186
+ }
187
+ else
188
+ { // Nada.
189
+ closure = this.unsupported(obj, 'remove', {allowRaw}, 0);
190
+ }
191
+ return closure;
192
+ }
193
+
194
+ /**
195
+ * See if our list object contains *any* of the specified items.
196
+ *
197
+ * @param {...any} items
198
+ * @returns {boolean}
199
+ */
200
+ containsAny(...items)
201
+ {
202
+ for (const item of items)
203
+ {
204
+ if (this.contains(item))
205
+ { // Item found.
206
+ return true;
207
+ }
208
+ }
209
+
210
+ // No items found.
211
+ return false;
212
+ }
213
+
214
+ /**
215
+ * See if our list contains *all* of the specified items.
216
+ *
217
+ * @param {...any} items
218
+ * @returns {boolean}
219
+ */
220
+ containsAll(...items)
221
+ {
222
+ for (const item of items)
223
+ {
224
+ if (!this.contains(item))
225
+ { // An item was missing.
226
+ return false;
227
+ }
228
+ }
229
+ // All items found.
230
+ return true;
231
+ }
232
+
233
+ /**
234
+ * Remove items from our list object.
235
+ *
236
+ * Passed any number of items, it will see if any of those items are
237
+ * found in the array, and if they are, will remove them from the array.
238
+ *
239
+ * @param {...any} items
240
+ * @returns {number} Number of items actually removed.
241
+ */
242
+ removeAll(...items)
243
+ {
244
+ let removed = 0;
245
+ for (const item of items)
246
+ {
247
+ removed += this.remove(item);
248
+ }
249
+ return removed;
250
+ }
251
+
252
+ /**
253
+ * Return a List instance for the passed object.
254
+ *
255
+ * If the object is already a `List` it is returned as is.
256
+ *
257
+ * @param {object} obj - The list object.
258
+ * @param {object} [opts] - Passed to constructor.
259
+ */
260
+ static for(obj, opts)
261
+ {
262
+ return (obj instanceof this) ? obj : new this(obj, opts);
263
+ }
264
+ }
265
+
266
+ const CONTAINS_OPTS = {closures: ['contains']};
5
267
 
6
268
  /**
7
- * See if an array contains *any* of the specified items.
8
- * @param {Array} array
269
+ * See if an list object contains *any* of the specified items.
270
+ *
271
+ * @param {object} list - A `List` or object for `new List(list)`;
9
272
  * @param {...any} items
10
273
  * @returns {boolean}
11
274
  * @alias module:@lumjs/core/arrays.containsAny
12
275
  */
13
- function containsAny(array, ...items)
14
- {
15
- for (const item of items)
16
- {
17
- if (array.includes(item))
18
- { // Item found.
19
- return true;
20
- }
21
- }
22
-
23
- // No items found.
24
- return false;
25
- }
26
-
27
- exports.containsAny = containsAny;
276
+ function containsAny(list, ...items)
277
+ {
278
+ return List.for(list, CONTAINS_OPTS).containsAny(...items);
279
+ }
28
280
 
29
281
  /**
30
- * See if an array contains *all* of the specified items.
31
- * @param {Array} array
282
+ * See if a list contains *all* of the specified items.
283
+ *
284
+ * @param {object} list - A `List` or object for `new List(list)`;
32
285
  * @param {...any} items
33
286
  * @returns {boolean}
34
287
  * @alias module:@lumjs/core/arrays.containsAll
35
288
  */
36
- function containsAll(array, ...items)
37
- {
38
- for (const item of items)
39
- {
40
- if (!array.includes(item))
41
- { // An item was missing.
42
- return false;
43
- }
44
- }
45
-
46
- // All items found.
47
- return true;
48
- }
49
-
50
- exports.containsAll = containsAll;
51
-
289
+ function containsAll(list, ...items)
290
+ {
291
+ return List.for(list, CONTAINS_OPTS).containsAll(...items);
292
+ }
293
+
294
+ const REMOVE_OPTS = {closures: ['remove']};
295
+
52
296
  /**
53
- * Remove items from an array.
297
+ * Remove items from a list object.
54
298
  *
55
299
  * Passed any number of items, it will see if any of those items are
56
300
  * found in the array, and if they are, will remove them from the array.
57
301
  *
58
- * @param {Array} array
302
+ * @param {object} list - A `List` or object for `new List(list)`;
59
303
  * @param {...any} items
60
304
  * @returns {number} Number of items actually removed.
61
305
  * @alias module:@lumjs/core/arrays.removeItems
62
306
  */
63
- function removeItems(array, ...items)
307
+ function removeItems(list, ...items)
64
308
  {
65
- let removed = 0;
66
- for (const item of items)
67
- {
68
- const index = array.indexOf(item);
69
- if (index !== -1)
70
- {
71
- array.splice(index, 1);
72
- removed++;
73
- }
74
- }
75
- return removed;
309
+ return List.for(list, REMOVE_OPTS).removeAll(...items);
76
310
  }
77
311
 
78
- exports.removeItems = removeItems;
79
-
80
312
  /**
81
313
  * Return a Powerset of values in the array.
82
314
  * @param {Array} array The array to make the powerset from.
@@ -99,8 +331,6 @@ function powerset(array)
99
331
  return ps;
100
332
  }
101
333
 
102
- exports.powerset = powerset;
103
-
104
334
  /**
105
335
  * Get a random element from an array.
106
336
  * @param {Array} array The array to get an item from.
@@ -112,4 +342,9 @@ function random(array)
112
342
  return array[Math.floor(Math.random()*array.length)];
113
343
  }
114
344
 
115
- exports.random = random;
345
+ module.exports = exports =
346
+ {
347
+ List, containsAny, containsAll,
348
+ removeItems, powerset, random,
349
+ }
350
+
package/lib/context.js CHANGED
@@ -6,18 +6,42 @@
6
6
  * describing the current JS environment and execution context.
7
7
  *
8
8
  * @module @lumjs/core/context
9
- * @property {boolean} AMD - AMD (*RequireJS*) module loading detected.
10
- * @property {boolean} CJS - CommonJS environment detected.
9
+ * @property {boolean} hasDefine - A global `define()` function was found.
11
10
  * @property {boolean} hasRequire - A global `require()` function was found.
12
- * @property {boolean} hasExports - A global `exports` object was found.
11
+ * @property {boolean} hasExports - A global `exports` variable was found.
13
12
  * @property {boolean} hasModule - A global `module` object was found.
14
- * @property {boolean} isNode - Is likely *Node.js*, *Electron*, etc.
13
+ * @property {boolean} hasModuleExports - A `module.exports` exists.
14
+ * @property {boolean} hasSyncedExports - `exports === module.exports`
15
+ * @property {boolean} isNode - An alias to `Node.ok`.
15
16
  * @property {boolean} isBrowser - A web-browser environment detected.
16
17
  * @property {boolean} isWindow - Is a browser `Window` context.
17
18
  * @property {boolean} isWorker - Is a browser `Worker` (sub-types below.)
18
19
  * @property {boolean} isServiceWorker - Is a `ServiceWorker` context.
19
20
  * @property {boolean} isDedicatedWorker - Is a `DedicatedWorker` context.
20
21
  * @property {boolean} isSharedWorker - Is a `SharedWorker` context.
22
+ * @property {object} AMD - Asynchronous Module Definition detection.
23
+ * @property {boolean} AMD.isSupported - The `define.amd` property is set.
24
+ * @property {boolean} AMD.isRequireJS - A global `requirejs` was found.
25
+ * @property {object} CJS - CommonJS detection.
26
+ * @property {boolean} CJS.standard - `hasDefine && hasRequire`
27
+ * @property {boolean} CJS.nodeLike - `CJS.standard && hasExports`
28
+ * @property {boolean} CJS.isLumV5 - Lum.js browser bundler detected.
29
+ * @property {boolean} CJS.isWebpack - Webpack bundler detected.
30
+ * @property {boolean} CJS.inBrowser - `CJS.isLumV5 || CJS.isWebpack`
31
+ * @property {object} Node - Node.js detection.
32
+ * @property {boolean} Node.isSane - `CJS.nodeLike && !CJS.inBrowser`
33
+ * @property {boolean} Node.hasProcess - A global `process` object exists.
34
+ * @property {boolean} Node.ok - `Node.isSane && Node.hasProcess`
35
+ * @property {?string} Node.ver - `process.versions.node ?? null`
36
+ * @property {?object} Node.versions - `process.versions ?? null`
37
+ * @property {Array} Node.args - Command line arguments.
38
+ * @property {string} Node.script - Command line script name.
39
+ * @property {object} Electron - Electron detection.
40
+ * @property {boolean} ok - Electron environment detected.
41
+ * @property {boolean} isDefault - Is this the default app?
42
+ * @property {boolean} isBundled - Is this a bundled app?
43
+ * @property {boolean} isMain - Is this the main renderer frame?
44
+ * @property {?string} type - `process.type`
21
45
  * @property {object} root - See {@link module:@lumjs/core/types.root}
22
46
  */
23
47
 
@@ -28,37 +52,72 @@ const rootHas = what => typeof root[what] !== U;
28
52
  const cd = def(ctx, true);
29
53
  const CJS = {};
30
54
  const AMD = {};
55
+ const Node = {};
56
+ const Elec = {};
31
57
  const cjs = def(CJS, true);
32
58
  const amd = def(AMD, true);
59
+ const node = def(Node, true);
60
+ const elec = def(Elec, true);
33
61
  const isLumV5 = typeof module.$lib === O && module.$lib.$cached === module;
34
62
 
35
63
  cd('hasDefine', typeof define === F)
36
64
  ('hasRequire', typeof require === F)
37
- ('hasExports', typeof exports !== U)
65
+ ('hasExports', typeof exports !== U && isComplex(exports))
38
66
  ('hasModule', typeof module === O && module !== null)
39
- ('hasModuleExports', isComplex(module.exports))
40
- ;
67
+ ('hasModuleExports', ctx.hasModule && isComplex(module.exports))
68
+ ('hasSyncedExports', ctx.hasExports && ctx.hasModuleExports
69
+ && exports === module.exports)
41
70
 
42
71
  cjs('standard', ctx.hasModule && ctx.hasRequire)
43
72
  ('nodeLike', CJS.standard && ctx.hasExports)
44
73
  ('isLumV5', CJS.nodeLike && isLumV5)
45
74
  ('isWebpack', CJS.nodeLike && require.resolve === require)
46
75
  ('inBrowser', CJS.isLumV5 || CJS.isWebpack)
47
- ;
76
+
77
+ node('isSane', CJS.nodeLike && !CJS.inBrowser)
78
+ ('hasProcess', typeof process === O && process !== null)
79
+ ('ok', Node.isSane && Node.hasProcess)
80
+ ('versions', Node.hasProcess ? process.versions : null)
81
+ ('ver', Node.hasProcess ? process.versions.node : null)
82
+ ('args', {get: function()
83
+ { // Inspired by yargs hideBin() function.
84
+ if (Node.ok)
85
+ {
86
+ const o = (Elec.isBundled) ? 1 : 2;
87
+ return process.argv.slice(o);
88
+ }
89
+ return [];
90
+ }})
91
+ ('script', {get: function()
92
+ { // Inspired by yargs getProcessArgvBin() function.
93
+ if (Node.ok)
94
+ {
95
+ const i = (Elec.isBundled) ? 0 : 1;
96
+ return process.argv[i];
97
+ }
98
+ return '';
99
+ }})
100
+
101
+ elec('ok', Node.ok && !!Node.versions.electron)
102
+ ('isDefault', Elec.ok && !!process.defaultApp)
103
+ ('isBundled', Elec.ok && !process.defaultApp)
104
+ ('isMain', Elec.ok && !!process.isMainFrame)
105
+ ('type', Elec.ok && process.type)
48
106
 
49
107
  amd('isSupported', ctx.hasDefine && isObj(define.amd))
50
108
  ('isRequireJS', AMD.isSupported && typeof requirejs === F)
51
109
 
52
110
  cd('CJS', CJS)
53
111
  ('AMD', AMD)
54
- ('isNode', CJS.nodeLike && !CJS.inBrowser)
55
- ('isWindow', rootHas('window'))
112
+ ('Node', Node)
113
+ ('Electron', Elec)
114
+ ('isNode', Node.ok)
115
+ ('isWindow', typeof Window === F && rootHas(window))
56
116
  ('isWorker', rootHas('WorkerGlobalScope'))
57
117
  ('isServiceWorker', rootHas('ServiceWorkerGlobalScope'))
58
118
  ('isDedicatedWorker', rootHas('DedicatedWorkerGlobalScope'))
59
119
  ('isSharedWorker', rootHas('SharedWorkerGlobalScope'))
60
120
  ('isBrowser', !ctx.isNode && (ctx.isWindow || ctx.isWorker))
61
- ;
62
121
 
63
122
  /**
64
123
  * See if a root-level name is defined.
package/lib/index.js CHANGED
@@ -33,47 +33,44 @@ const def = types.def;
33
33
  */
34
34
  const lazy = types.lazy;
35
35
 
36
- // A descriptor template for def/lazy.
37
- const E = def.e;
38
-
39
- def(exports, 'types', types, E);
40
- def(exports, 'def', def, E);
41
- def(exports, 'lazy', lazy, E);
36
+ def(exports, 'types', types);
37
+ def(exports, 'def', def);
38
+ def(exports, 'lazy', lazy);
42
39
 
43
40
  /**
44
41
  * Array utility functions «Lazy»
45
42
  * @name module:@lumjs/core.arrays
46
43
  * @type {module:@lumjs/core/arrays}
47
44
  */
48
- lazy(exports, 'arrays', () => require('./arrays'), E);
45
+ lazy(exports, 'arrays', () => require('./arrays'));
49
46
 
50
47
  /**
51
48
  * Information about the JS context we're running in
52
49
  * @name module:@lumjs/core.context
53
50
  * @type {module:@lumjs/core/context}
54
51
  */
55
- def(exports, 'context', require('./context'), E);
52
+ def(exports, 'context', require('./context'));
56
53
 
57
54
  /**
58
55
  * Functions for working with strings and locales «Lazy»
59
56
  * @name module:@lumjs/core.strings
60
57
  * @type {module:@lumjs/core/strings}
61
58
  */
62
- lazy(exports, 'strings', () => require('./strings'), E);
59
+ lazy(exports, 'strings', () => require('./strings'));
63
60
 
64
61
  /**
65
62
  * Functions for working with binary flags «Lazy»
66
63
  * @name module:@lumjs/core.flags
67
64
  * @type {module:@lumjs/core/flags}
68
65
  */
69
- lazy(exports, 'flags', () => require('./flags'), E);
66
+ lazy(exports, 'flags', () => require('./flags'));
70
67
 
71
68
  /**
72
69
  * Functions for manipulating objects «Lazy»
73
70
  * @name module:@lumjs/core.obj
74
71
  * @type {module:@lumjs/core/obj}
75
72
  */
76
- lazy(exports, 'obj', () => require('./obj'), E);
73
+ lazy(exports, 'obj', () => require('./obj'));
77
74
 
78
75
  /**
79
76
  * Functions for getting values and properties with fallback defaults «Lazy»
@@ -88,7 +85,7 @@ function from(submod, ...libs)
88
85
  {
89
86
  for (const lib of libs)
90
87
  {
91
- def(exports, lib, submod[lib], E);
88
+ def(exports, lib, submod[lib]);
92
89
  }
93
90
  }
94
91
 
@@ -133,7 +130,7 @@ from(meta, 'stacktrace', 'AbstractClass', 'Functions', 'NYI');
133
130
  * @function
134
131
  * @see module:@lumjs/core/enum
135
132
  */
136
- lazy(exports, 'Enum', () => require('./enum'), E);
133
+ lazy(exports, 'Enum', () => require('./enum'));
137
134
 
138
135
  /**
139
136
  * Make an object support the *Observable* API «Lazy»
@@ -141,4 +138,5 @@ lazy(exports, 'Enum', () => require('./enum'), E);
141
138
  * @function
142
139
  * @see module:@lumjs/core/observable
143
140
  */
144
- lazy(exports, 'observable', () => require('./observable'), E);
141
+ lazy(exports, 'observable', () => require('./observable'));
142
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lumjs/core",
3
- "version": "1.13.0",
3
+ "version": "1.15.0",
4
4
  "main": "lib/index.js",
5
5
  "exports":
6
6
  {