@lumjs/core 1.38.7 → 1.39.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/add.js DELETED
@@ -1,233 +0,0 @@
1
-
2
- const {N,def} = require('../types');
3
-
4
- /**
5
- * Functions for adding values to arrays in different ways.
6
- * @alias module:@lumjs/core/arrays.add
7
- */
8
- const ArrayAdd = module.exports = exports =
9
- {
10
- prepend, before, append, after, insert,
11
- }
12
-
13
- function _addWith(array, oldValue, newValue, ifMissing, fn)
14
- {
15
- let pos = array.indexOf(oldValue);
16
-
17
- if (pos === -1)
18
- {
19
- if (typeof ifMissing === N)
20
- {
21
- pos = ifMissing;
22
- }
23
- else
24
- {
25
- return false;
26
- }
27
- }
28
-
29
- return fn(array, newValue, pos);
30
- }
31
-
32
- /**
33
- * Prepend a value to an array.
34
- *
35
- * @param {Array} array - Array to add the value to.
36
- * @param {*} value - Value to add to the array.
37
- * @param {number} [pos=0] Position to prepend at.
38
- *
39
- * If `pos` is `0` then `array.unshift(value)` is used.
40
- * Otherwise `array.splice(pos, 0, value)` is used.
41
- *
42
- * @returns {Array} `array`
43
- * @alias module:@lumjs/core/arrays.add.prepend
44
- */
45
- function prepend(array, value, pos=0)
46
- {
47
- if (pos === 0)
48
- {
49
- array.unshift(value);
50
- }
51
- else
52
- {
53
- array.splice(pos, 0, value);
54
- }
55
-
56
- return array;
57
- }
58
-
59
- /**
60
- * Append a value to an array.
61
- *
62
- * @param {Array} array - Array to add the value to.
63
- * @param {*} value - Value to add to the array.
64
- * @param {number} [pos=-1] Position to append at.
65
- *
66
- * If `pos` is `-1` then `array.push(value)` is used.
67
- * Otherwise `array.splice(pos+1, 0, value)` is used.
68
- *
69
- * @returns {Array} `array`
70
- * @alias module:@lumjs/core/arrays.add.append
71
- */
72
- function append(array, value, pos=-1)
73
- {
74
- if (pos === -1)
75
- {
76
- array.push(value);
77
- }
78
- else
79
- {
80
- array.splice(pos+1, 0, value);
81
- }
82
-
83
- return array;
84
- }
85
-
86
- /**
87
- * Add a value to an array _before_ an existing value.
88
- *
89
- * This uses `prepend()` to add the value.
90
- *
91
- * @param {Array} array - Array to add the value to.
92
- * @param {*} oldValue - Existing value to find.
93
- * @param {*} newValue - Value to add to the array.
94
- * @param {(number|false)} [ifMissing=0] If `oldValue` is not found.
95
- *
96
- * If this is a `number` it will be used as the `pos` argument.
97
- *
98
- * If this is `false` the function will not add the value at all,
99
- * but will return `false` instead.
100
- *
101
- * @returns {(Array|false)} Usually the input `array`; but will return
102
- * `false` if `ifMissing` was `false` and the `oldValue` was not found.
103
- *
104
- * @alias module:@lumjs/core/arrays.add.before
105
- */
106
- function before(array, oldValue, newValue, ifMissing=0)
107
- {
108
- return _addWith(array, oldValue, newValue, ifMissing, prepend);
109
- }
110
-
111
- /**
112
- * Add a value to an array _after_ an existing value.
113
- *
114
- * This uses `append()` to add the value.
115
- *
116
- * @param {Array} array - Array to add the value to.
117
- * @param {*} oldValue - Existing value to find.
118
- * @param {*} newValue - Value to add to the array.
119
- * @param {(number|false)} [ifMissing=-1] If `oldValue` is not found.
120
- *
121
- * If this is a `number` it will be used as the `pos` argument.
122
- *
123
- * If this is `false` the function will not add the value at all,
124
- * but will return `false` instead.
125
- *
126
- * @returns {(Array|false)} Usually the input `array`; but will return
127
- * `false` if `ifMissing` was `false` and the `oldValue` was not found.
128
- *
129
- * @alias module:@lumjs/core/arrays.add.after
130
- */
131
- function after(array, oldValue, newValue, ifMissing=-1)
132
- {
133
- return _addWith(array, oldValue, newValue, ifMissing, append);
134
- }
135
-
136
- /**
137
- * Insert a value to an array using special logic.
138
- *
139
- * See the `pos` argument for the positioning logic used.
140
- *
141
- * @param {Array} array - Array to add the value to.
142
- * @param {*} value - Value to add to the array.
143
- * @param {number} [pos=-1] Position to insert at.
144
- *
145
- * If `pos` is less than `0` this uses `append()`;
146
- * Otherwise it uses `prepend()`.
147
- *
148
- * @returns {Array} `array`
149
- * @alias module:@lumjs/core/arrays.add.insert
150
- */
151
- function insert(array, value, pos=-1)
152
- {
153
- if (pos < 0)
154
- {
155
- return append(array, value, pos);
156
- }
157
- else
158
- {
159
- return prepend(array, value, pos);
160
- }
161
- }
162
-
163
- /**
164
- * A wrapper class to provide helper methods to Arrays.
165
- *
166
- * Provides wrapped versions of `prepend()`, `append()`,
167
- * `before()`, `after()`, and `insert()`.
168
- *
169
- * The methods obviously pass the array argument, so remove it from
170
- * the argument signature when using the wrapper method. Other than that,
171
- * the rest of the arguments are the same as the wrapped functions.
172
- *
173
- * @alias module:@lumjs/core/arrays.add.At
174
- */
175
- class ArrayAt
176
- {
177
- /**
178
- * Build a wrapper around an array.
179
- *
180
- * @param {Array} array - Array to wrap.
181
- */
182
- constructor(array)
183
- {
184
- this.array = array;
185
- }
186
-
187
- /**
188
- * Add wrappers for the helper functions to the Array directly.
189
- *
190
- * WARNING: This is monkey patching the array object instance.
191
- * If you're okay with that, cool.
192
- *
193
- * Otherwise, use a new `ArrayAt` object instance instead.
194
- *
195
- * @param {Array} array - Array to add methods to.
196
- *
197
- * @returns {Array} The input `array`
198
- */
199
- static extend(array)
200
- {
201
- for (const methName in ArrayAdd)
202
- {
203
- const methFunc = ArrayAdd[methName];
204
- def(array, methName, methFunc.bind(array, array));
205
- }
206
- return array;
207
- }
208
-
209
- /**
210
- * A static method that calls `new ArrayAt(array)`;
211
- *
212
- * @param {Array} array - Array to wrap
213
- * @returns {module:@lumjs/core/arrays.ArrayAt} A new instance.
214
- */
215
- static new(array)
216
- {
217
- return new this(array);
218
- }
219
-
220
- } // ArrayAt class
221
-
222
- // Set up ArrayAt methods, and export the functions.
223
- for (const methName in ArrayAdd)
224
- {
225
- const methFunc = ArrayAdd[methName];
226
-
227
- def(ArrayAt.prototype, methName, function()
228
- {
229
- methFunc(this.array, ...arguments);
230
- });
231
- }
232
-
233
- def(ArrayAdd, 'At', ArrayAt);
@@ -1,314 +0,0 @@
1
- /**
2
- * A wrapper class to abstract functionality of various kinds of lists.
3
- *
4
- * Supports `Array`, `Set`, and `Map` explicitly.
5
- * Other classes may be supported. YMMV.
6
- *
7
- * @alias module:@lumjs/core/arrays.List
8
- */
9
- class List
10
- {
11
- /**
12
- * A list of known closure properties.
13
- *
14
- * Corresponding `wrap_${closure}` methods will be used to generate
15
- * the closure properties.
16
- */
17
- static get KNOWN_CLOSURES()
18
- {
19
- return (Object.getOwnPropertyNames(this.prototype)
20
- .filter(name => name.startsWith('wrap_')));
21
- }
22
-
23
- /**
24
- * Build a new List wrapper.
25
- *
26
- * @param {object} obj - The list object.
27
- *
28
- * Typically an `Array`, `Set`, `Map`, or something similar.
29
- * Not all of types will support all features. YMMV.
30
- *
31
- * @param {object} [opts] Options for advanced use.
32
- *
33
- * @param {bool} [opts.allowRaw=false] Attempt to support raw objects?
34
- *
35
- * Treat regular Javascript objects as lists, as they are a kind of
36
- * map after all. This may lead to unusual results. YMMV.
37
- *
38
- * @param {mixed} [opts.closures=true] Create closure methods?
39
- *
40
- * If `true` or `"*"` we will create all known closure properties.
41
- *
42
- * If an `Array` it's the list of closure names we want to create.
43
- * Possible values can be found in `List.KNOWN_CLOSURES`.
44
- *
45
- * If it's a `string` it's treated as a whitespace separated list
46
- * of closure names and split into an `Array`.
47
- *
48
- * If `false` we won't create any closure properties.
49
- *
50
- */
51
- constructor(obj, opts={})
52
- {
53
- this.obj = obj;
54
- this.opts = opts;
55
- this.allowRaw = opts.allowRaw ?? false;
56
- this.setupClosures(opts.closures ?? true);
57
- }
58
-
59
- setupClosures(closures)
60
- {
61
- if (!closures)
62
- { // Nothing to do.
63
- return;
64
- }
65
-
66
- if (closures === true || closures === '*')
67
- { // Setup all closures.
68
- closures = this.constructor.KNOWN_CLOSURES;
69
- }
70
- else if (typeof closures === S)
71
- {
72
- closures = closures.trim().split(/\s+/);
73
- }
74
-
75
- if (Array.isArray(closures))
76
- {
77
- for (const closure of closures)
78
- {
79
- const meth = 'wrap_'+closure;
80
- if (typeof this[meth] === F)
81
- { // Create the detail property.
82
- this[closure] = this[meth]();
83
- }
84
- else
85
- {
86
- console.error({closure, closures, list: this});
87
- throw new Error("Unsupported closure");
88
- }
89
- }
90
- }
91
- else
92
- {
93
- console.error({closures, list: this});
94
- throw new TypeError("Invalid closures value");
95
- }
96
- }
97
-
98
- unsupported(obj, forClosure, info, retVal=false)
99
- {
100
- console.error("Unsupported object", {obj, forClosure, info, list: this});
101
- return () => retVal;
102
- }
103
-
104
- /**
105
- * Return a closure that returns if an item is in a list object.
106
- *
107
- * @param {object} obj - The list object the closure will be for.
108
- *
109
- * May be an `Array`, `Set`, `Map`, or `TypedArray` or any object
110
- * that has either an `includes()` or `has()` method.
111
- *
112
- * @returns {function}
113
- * @throws {TypeError}
114
- */
115
- wrap_contains(obj=this.obj, allowRaw=this.allowRaw)
116
- {
117
- if (typeof obj.includes === F)
118
- { // Array and TypedArray have includes()
119
- return item => obj.includes(item);
120
- }
121
- else if (typeof obj.has === F)
122
- { // Set and Map have has()
123
- return item => obj.has(item);
124
- }
125
- else if (allowRaw)
126
- { // A fallback to raw object search.
127
- return item => (typeof obj[item] !== undefined);
128
- }
129
- else
130
- { // Nope.
131
- return this.unsupported(obj, 'contains', {allowRaw});
132
- }
133
- }
134
-
135
- /**
136
- * Return a closure that removes an item from a list.
137
- *
138
- * @param {object} obj - The list object the closure will be for.
139
- *
140
- * May be an `Array`, `Set`, `Map`, or anything with a `delete()` method.
141
- *
142
- * @returns {function}
143
- * @throws {TypeError}
144
- */
145
- wrap_remove(obj=this.obj, allowRaw=this.allowRaw)
146
- {
147
- let closure;
148
- if (Array.isArray(obj))
149
- { // Arrays have no easy method to do this.
150
- closure = function(item)
151
- {
152
- let removed = 0;
153
- let index = obj.indexOf(item);
154
- while (index !== -1)
155
- {
156
- obj.splice(index, 1);
157
- removed++;
158
- index = obj.indexOf(item, index);
159
- }
160
- return removed;
161
- }
162
- }
163
- else if (typeof obj.delete === F)
164
- { // Use the delete() method.
165
- closure = item => (obj.delete(item) ? 1 : 0);
166
- }
167
- else if (allowRaw)
168
- { // Fallback to removing the property.
169
- closure = function(item)
170
- {
171
- if (obj[item] === undefined)
172
- {
173
- return 0;
174
- }
175
- else
176
- {
177
- return (delete obj[item] ? 1 : 0);
178
- }
179
- }
180
- }
181
- else
182
- { // Nada.
183
- closure = this.unsupported(obj, 'remove', {allowRaw}, 0);
184
- }
185
- return closure;
186
- }
187
-
188
- /**
189
- * See if our list object contains *any* of the specified items.
190
- *
191
- * @param {...any} items
192
- * @returns {boolean}
193
- */
194
- containsAny(...items)
195
- {
196
- for (const item of items)
197
- {
198
- if (this.contains(item))
199
- { // Item found.
200
- return true;
201
- }
202
- }
203
-
204
- // No items found.
205
- return false;
206
- }
207
-
208
- /**
209
- * See if our list contains *all* of the specified items.
210
- *
211
- * @param {...any} items
212
- * @returns {boolean}
213
- */
214
- containsAll(...items)
215
- {
216
- for (const item of items)
217
- {
218
- if (!this.contains(item))
219
- { // An item was missing.
220
- return false;
221
- }
222
- }
223
- // All items found.
224
- return true;
225
- }
226
-
227
- /**
228
- * Remove items from our list object.
229
- *
230
- * Passed any number of items, it will see if any of those items are
231
- * found in the array, and if they are, will remove them from the array.
232
- *
233
- * @param {...any} items
234
- * @returns {number} Number of items actually removed.
235
- */
236
- removeAll(...items)
237
- {
238
- let removed = 0;
239
- for (const item of items)
240
- {
241
- removed += this.remove(item);
242
- }
243
- return removed;
244
- }
245
-
246
- /**
247
- * Return a List instance for the passed object.
248
- *
249
- * If the object is already a `List` it is returned as is.
250
- *
251
- * @param {object} obj - The list object.
252
- * @param {object} [opts] - Passed to constructor.
253
- */
254
- static for(obj, opts)
255
- {
256
- return (obj instanceof this) ? obj : new this(obj, opts);
257
- }
258
- }
259
-
260
- const CONTAINS_OPTS = {closures: ['contains']};
261
-
262
- /**
263
- * See if an list object contains *any* of the specified items.
264
- *
265
- * @param {object} list - A `List` or object for `new List(list)`;
266
- * @param {...any} items
267
- * @returns {boolean}
268
- * @alias module:@lumjs/core/arrays.containsAny
269
- */
270
- function containsAny(list, ...items)
271
- {
272
- return List.for(list, CONTAINS_OPTS).containsAny(...items);
273
- }
274
-
275
- List.containsAny = containsAny;
276
-
277
- /**
278
- * See if a list contains *all* of the specified items.
279
- *
280
- * @param {object} list - A `List` or object for `new List(list)`;
281
- * @param {...any} items
282
- * @returns {boolean}
283
- * @alias module:@lumjs/core/arrays.containsAll
284
- */
285
- function containsAll(list, ...items)
286
- {
287
- return List.for(list, CONTAINS_OPTS).containsAll(...items);
288
- }
289
-
290
- List.containsAll = containsAll;
291
-
292
- const REMOVE_OPTS = {closures: ['remove']};
293
-
294
- /**
295
- * Remove items from a list object.
296
- *
297
- * Passed any number of items, it will see if any of those items are
298
- * found in the array, and if they are, will remove them from the array.
299
- *
300
- * @param {object} list - A `List` or object for `new List(list)`;
301
- * @param {...any} items
302
- * @returns {number} Number of items actually removed.
303
- * @alias module:@lumjs/core/arrays.removeItems
304
- */
305
- function removeItems(list, ...items)
306
- {
307
- return List.for(list, REMOVE_OPTS).removeAll(...items);
308
- }
309
-
310
- List.removeItems = removeItems;
311
-
312
- module.exports = exports = List;
313
-
314
- const {F,S} = require('../types');
@@ -1,139 +0,0 @@
1
- 'use strict';
2
-
3
- const {F, isObj, isPlainObject} = require('../types/basics');
4
- const Enum = require('../enum');
5
- const TypedArray = Object.getPrototypeOf(Int8Array);
6
- const CT = Enum(['GT','LT','ALL'], {flags: true});
7
-
8
- /**
9
- * An Enum object with bitwise flags for the `opts.convert` argument
10
- * of the makeType() function.
11
- *
12
- * @name module:@lumjs/core/arrays.ConvertType
13
- * @type {object}
14
- * @prop {number} GT - Convert any values which have a larger bytes per
15
- * element size than the `opts.type` TypedArray class.
16
- * @prop {number} LT - Convert any values which have a smaller bytes per
17
- * element size than the `opts.type` TypedArray class.
18
- * @prop {number} ALL - Convert all values that aren't already instances
19
- * of the `opts.type` class.
20
- * Note: if you use this flag, then the other flags are ignored!
21
- *
22
- */
23
-
24
- /**
25
- * Build a new TypedArray with multiple input values.
26
- *
27
- * @alias module:@lumjs/core/arrays.makeTyped
28
- *
29
- * @param {(object|function)} [opts] - Options.
30
- *
31
- * If this is a `function` then it is assumed to be the `opts.type` value.
32
- *
33
- * If this is anything other than a _plain object_ (i.e. it was created
34
- * with a class constructor rather than `{}` literal syntax), then it will
35
- * be considered an element of the `inValues` parameter instead of options.
36
- *
37
- * @param {function} [opts.type=Uint8Array] TypedArray class to return.
38
- * @param {number} [opts.convert=0] Convert TypedArrays before merge?
39
- *
40
- * By default all TypedArray objects passed (or created by the TextEncoder)
41
- * will be added to the new TypedArray as-is, which may result in data
42
- * corruption if the unit byte-size of the source TypeArray is larger
43
- * than the newly created TypeArray. If the unit byte-size of the requested
44
- * type is the same or larger than any input sources, then it doesn't matter.
45
- *
46
- * You can however convert all source TypedArray objects to the `opts.type`
47
- * class using an intermediary ArrayBuffer, and choose the criteria for
48
- * the conversion by specifying various bitwise flags in this option.
49
- *
50
- * Use the properties of the `ConvertType` Enum as the flag values.
51
- *
52
- * e.g.: `makeTyped({convert: ConvertType.GT | ConvertType.LT}, ...values);`
53
- *
54
- * WARNING: The type conversion methodology is not the greatest and doesn't
55
- * cover every possible issue, so don't be surprised if you end up with
56
- * corrupted data (or you have an Exception thrown) when mixing different
57
- * TypeArray classes. If at all possible, only merge values of the same type.
58
- *
59
- * @param {...mixed} inValues - Values to be added to the new TypeArray.
60
- *
61
- * If the value is considered a _View_ (i.e. a TypedArray or DataView),
62
- * it will be used directly.
63
- *
64
- * Any other kind of `object` will be serialized using JSON and then
65
- * encoded into a Uint8Array using TextEncoder.
66
- *
67
- * Any other type of value will be encoded using TextEncoder, which will
68
- * call `value.toString()` on any non-string values before encoding.
69
- *
70
- * @returns {TypedArray}
71
- *
72
- * @throws {TypeError} If `opts.type` is not a TypedArray constructor.
73
- * @throws {RangeError} If the byte length of any of the `inValues`
74
- * is not an integral multiple of the `opts.type` bytes per element size.
75
- *
76
- */
77
- function makeTyped(opts, ...inValues)
78
- {
79
- if (typeof opts === F)
80
- {
81
- opts = {type: opts};
82
- }
83
- else if (isPlainObject(opts))
84
- {
85
- inValues.unshift(opts);
86
- opts = {};
87
- }
88
-
89
- let wantType = opts.type ?? Uint8Array;
90
- let convert = opts.convert ?? 0;
91
-
92
- if (typeof wantType !== F || !TypedArray.isPrototypeOf(wantType))
93
- {
94
- console.error({wantType, opts, inValues});
95
- throw new TypeError("Invalid TypedArray class");
96
- }
97
-
98
- let fullLen = 0;
99
- let tenc = new TextEncoder();
100
- let wantSize = wantType.BYTES_PER_ELEMENT;
101
-
102
- let mergeValues = inValues.map(value =>
103
- {
104
- if (!ArrayBuffer.isView(value))
105
- {
106
- if (isObj(value))
107
- {
108
- value = JSON.stringify(value);
109
- }
110
- value = tenc.encode(value);
111
- }
112
-
113
- let valSize = value.constructor.BYTES_PER_ELEMENT;
114
-
115
- if ( ((convert & CT.ALL) && !(value instanceof wantType))
116
- || ((convert & CT.GT) && valSize > wantSize)
117
- || ((convert & CT.LT) && valSize < wantSize))
118
- { // Convert value to our wanted type
119
- value = new wantType(value.buffer);
120
- }
121
-
122
- fullLen += value.byteLength;
123
-
124
- return value;
125
- });
126
-
127
- let output = new wantType(new ArrayBuffer(fullLen));
128
- let offset = 0;
129
-
130
- for (let value of mergeValues)
131
- {
132
- output.set(value, offset);
133
- offset += value.length;
134
- }
135
-
136
- return output;
137
- }
138
-
139
- module.exports = {makeTyped, ConvertType: CT, TypedArray}