@lumjs/core 1.23.0 → 1.24.1

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/TODO.md CHANGED
@@ -1,6 +1,35 @@
1
1
  # TODO
2
2
 
3
- - Proper tests using new [@lumjs/tests](https://github.com/supernovus/lum.tests.js) library.
3
+ ## v2.x
4
+
5
+ I'm thinking it's getting close to time to do a big cleanup of the codebase,
6
+ and refactor a few things that have gotten rather crufty. While there may be
7
+ a few more releases in the `1.x` series, I think focusing on a clean break
8
+ for the future would be a good idea. A few of the things I want to do:
9
+
10
+ - Remove all deprecated code:
11
+ - `obj.{copyAll,duplicateOne,duplicateAll}`
12
+ - `types.instanceOf` and related options in `types.isa`
13
+ - `<meta>.AbstractClass`
14
+ - Implement a new `obj.copy` API function/class
15
+ - Will completely replace `obj.{clone,copyProps,mergeNested}`
16
+ - Offer an extended version of the declarative API from `copyProps`
17
+ - Cut anything that seems superfluous or rarely used
18
+ - Add ability to copy `Symbol` properties
19
+ - Replace `obj.syncNested` with `obj.sync` using the new `obj.copy` API
20
+
21
+ I will likely update this list a bit before I get around to starting the
22
+ new branch that will eventually become the `2.0.0` release.
23
+
24
+ I also want to make a new version of the [@lumjs/compat] package that
25
+ will be more helpful for major updates in the future, so it will have
26
+ a corresponding `v2.x` release at the same time as this package.
27
+
28
+ ## Tests
29
+
30
+ Write proper tests using [@lumjs/tests] library.
31
+ This list of files is out of date and needs updating.
32
+
4
33
  - [x] `types/js`
5
34
  - [x] `types/basics`
6
35
  - [x] `types/root`
@@ -29,3 +58,17 @@
29
58
  - [ ] `observable`
30
59
  - [ ] `index`
31
60
 
61
+ Would be nice to have the tests finished for the planned `2.x` release.
62
+
63
+ ## Documentation
64
+
65
+ Go through all the DocBlocks and ensure the documentation is up-to-date and
66
+ good enough to actually be useful.
67
+
68
+ This would also be nice to have finished for the planned `2.x` release.
69
+
70
+ ---
71
+
72
+ [@lumjs/tests]: https://github.com/supernovus/lum.tests.js
73
+ [@lumjs/compat]: https://github.com/supernovus/lum.compat.js
74
+
package/lib/context.js CHANGED
@@ -112,7 +112,7 @@ cd('CJS', CJS)
112
112
  ('Node', Node)
113
113
  ('Electron', Elec)
114
114
  ('isNode', Node.ok)
115
- ('isWindow', typeof Window === F && rootHas(window))
115
+ ('isWindow', root.window === root)
116
116
  ('isWorker', rootHas('WorkerGlobalScope'))
117
117
  ('isServiceWorker', rootHas('ServiceWorkerGlobalScope'))
118
118
  ('isDedicatedWorker', rootHas('DedicatedWorkerGlobalScope'))
package/lib/maps.js CHANGED
@@ -2,7 +2,7 @@
2
2
  * Map object helper functions.
3
3
  * @module @lumjs/core/maps
4
4
  */
5
- const {F,isObj} = require('./types/js');
5
+ const {F,S,SY,isObj,def,isComplex} = require('./types/js');
6
6
 
7
7
  /**
8
8
  * Build a `Map` instance out of various types of objects.
@@ -61,7 +61,119 @@ function mapOf(input)
61
61
  return map;
62
62
  }
63
63
 
64
+ /**
65
+ * Get a `Map` stored inside any `object` or `function` target,
66
+ * using a `Symbol` property for more protected/private storage.
67
+ *
68
+ * It will build the `Map` the first time this is called on a given target;
69
+ * then it will use the existing instance for any subsequent calls.
70
+ *
71
+ * @alias module:@lumjs/core/maps.getSymbolMap
72
+ *
73
+ * @param {(object|function)} target - The target for the Map storage.
74
+ *
75
+ * A Symbol property with a new Map instance will be added to the `target`
76
+ * the first time this function is called.
77
+ *
78
+ * Then every subsequent call with the same `target` and `symbol` will
79
+ * return the Map instance stored in the Symbol property.
80
+ *
81
+ * @param {(Symbol|string)} symbol - Symbol used to store the Map.
82
+ *
83
+ * If this is a `string`, it will be passed to `Symbol.for()` to get
84
+ * a globally registered Symbol to use for the Map storage property.
85
+ *
86
+ * If you don't want a globally registerd Symbol, simply use `Symbol(name)`
87
+ * in your own class and pass the Symbol itself instead of a string.
88
+ *
89
+ * @returns {Map}
90
+ *
91
+ * @throws {TypeError} If any of the parameters are invalid.
92
+ */
93
+ function getSymbolMap(target, symbol)
94
+ {
95
+ if (typeof symbol === S)
96
+ { // Use a global symbol.
97
+ symbol = Symbol.for(symbol);
98
+ }
99
+ else if (typeof symbol !== SY)
100
+ {
101
+ console.error({symbol});
102
+ throw new TypeError("Invalid symbol");
103
+ }
104
+
105
+ if (!isComplex(target))
106
+ {
107
+ console.error({target});
108
+ throw new TypeError("Invalid target object or function");
109
+ }
110
+
111
+ if (target[symbol] === undefined)
112
+ { // Have not created the Map yet.
113
+ def(target, symbol, new Map());
114
+ }
115
+
116
+ return target[symbol];
117
+ }
118
+
119
+ exports.getSymbolMap = getSymbolMap;
120
+
121
+ /**
122
+ * A Key-Value Cache using a _Symbol Map_ for storage.
123
+ *
124
+ * See {@link module:@lumjs/core/maps.getSymbolMap getSymbolMap()}
125
+ * for the more information about Symbol Map storage and usage.
126
+ *
127
+ * @alias module:@lumjs/core/maps.getSymbolCache
128
+ *
129
+ * @param {(object|function)} target - For `getSymbolMap()`
130
+ * @param {(Symbol|string)} symbol - For `getSymbolMap()`
131
+ *
132
+ * @param {mixed} key - The key you want from the cache.
133
+ *
134
+ * If the `key` exists in the cache, and `reset` is `false`,
135
+ * then the value will be returned from the cache.
136
+ *
137
+ * @param {function} generator - A value generator function.
138
+ *
139
+ * If the `key` was not found in the cache, or `reset` is `true`,
140
+ * this function must return the value to be cached and returned.
141
+ *
142
+ * The generator function is called directly, with no `this` assignment,
143
+ * and no arguments. Just `value = generator()`; nothing else!
144
+ *
145
+ * @param {boolean} [reset=false] Reset the value using the `generator`?
146
+ *
147
+ * Set this to `true` to force the value to be reset/regenerated,
148
+ * regardless as to if it was already cached or not.
149
+ *
150
+ * This is the only optional parameter; default is `false`.
151
+ *
152
+ * @returns {mixed} The cached value, or the value returned by the
153
+ * `generator` function, depending on cache state and the `reset` value.
154
+ *
155
+ * @throws {TypeError} If any of the parameters are invalid.
156
+ */
157
+ function getSymbolCache(obj, symbol, key, generator, reset=false)
158
+ {
159
+ const cache = getSymbolMap(obj, symbol);
160
+ if (!reset && cache.has(key))
161
+ { // Existing value found.
162
+ return cache.get(key);
163
+ }
164
+
165
+ if (typeof generator !== F)
166
+ {
167
+ throw new TypeError("Invalid generator function");
168
+ }
169
+
170
+ // Get/generate the value, cache it, and return it.
171
+ const value = generator();
172
+ cache.set(key, value);
173
+ return value;
174
+ }
175
+
64
176
  module.exports =
65
177
  {
66
- mapOf,
178
+ mapOf, getSymbolMap, getSymbolCache,
67
179
  }
@@ -7,6 +7,7 @@
7
7
  * Use `copyProps`, or `mergeNested` for more robust versions.
8
8
  *
9
9
  * @alias module:@lumjs/core/obj.copyAll
10
+ * @deprecated Use `Object.assign()` instead.
10
11
  */
11
12
  function copyAll(target, ...sources)
12
13
  {
@@ -22,7 +23,7 @@ function copyAll(target, ...sources)
22
23
  exports.copyAll = copyAll;
23
24
 
24
25
  /**
25
- * Make a copy of a single object using `copyAll`.
26
+ * Make a (shallow) copy of a single object using `copyAll`.
26
27
  *
27
28
  * Use `clone` for a more robust version.
28
29
  *
@@ -31,6 +32,7 @@ exports.copyAll = copyAll;
31
32
  * @alias module:@lumjs/core/obj.duplicateOne
32
33
  * @param {object} obj - The object to duplicate.
33
34
  * @returns {object} A clone of the object.
35
+ * @deprecated Use `clone()` or `Object.assign()` depending on needs.
34
36
  */
35
37
  exports.duplicateOne = copyAll.clone = obj => copyAll({}, obj);
36
38
 
@@ -44,5 +46,6 @@ exports.duplicateOne = copyAll.clone = obj => copyAll({}, obj);
44
46
  * @alias module:@lumjs/core/obj.duplicateOne
45
47
  * @param {object} obj - The object to duplicate.
46
48
  * @returns {object} A clone of the object.
49
+ * @deprecated Use `Object.assign()`
47
50
  */
48
51
  exports.duplicateAll = copyAll.duplicate = () => copyAll({}, ...arguments);
package/lib/types/isa.js CHANGED
@@ -69,38 +69,38 @@ exports.isType = isType;
69
69
  const DEFAULT_ISA_PARSER = function(type, v)
70
70
  { // `this` is the options object itself.
71
71
  if (typeof type.is === F)
72
- { // We assume `is()` methods are the type check.
72
+ { // A simple custom type test.
73
73
  if (type.is(v)) return true;
74
74
  }
75
75
 
76
76
  if (typeof type.isa === O)
77
- { // Process our known options.
78
- const opts = type.isa;
77
+ { // Process our known rules.
78
+ const rules = type.isa;
79
79
 
80
- if (typeof opts.process === F)
80
+ if (typeof rules.process === F)
81
81
  { // Run a method to extend the options further.
82
- opts.process(this, v, type);
82
+ rules.process(this, v, type);
83
83
  }
84
84
 
85
- if (typeof opts.parsers === F)
86
- { // Assign another parser.
87
- this.parsers.push(opts.parsers);
85
+ if (typeof rules.parsers === F)
86
+ { // Add another parser.
87
+ this.parsers.push(rules.parsers);
88
88
  }
89
89
 
90
- if (typeof opts.test === F)
91
- { // A custom test, will be passed `v` and the current `opts`.
92
- if (opts.test(v, this))
90
+ if (typeof rules.test === F)
91
+ { // A more advanced custom type test.
92
+ if (rules.test(v, this, type))
93
93
  { // Mark this as having passed.
94
94
  return true;
95
95
  }
96
96
  }
97
97
 
98
- // Okay, now anything else gets set.
98
+ // Okay, now anything else gets set as an option.
99
99
  const RESERVED = ['parsers','process','test'];
100
- for (const opt in opts)
100
+ for (const opt in rules)
101
101
  {
102
102
  if (RESERVED.includes(opt)) continue; // Skip it.
103
- this[opt] = opts[opt];
103
+ this[opt] = rules[opt];
104
104
  }
105
105
 
106
106
  }
@@ -131,27 +131,22 @@ function processOptions(type, v)
131
131
  * @param {*} v - The value we're testing.
132
132
  * @param {...any} types - The types the value should be one of.
133
133
  *
134
- * For each of the `types`, by default if it is a `string`
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.
134
+ * Each of the `type` tests in `types` may be one of:
137
135
  *
138
- * If it is an `object` and has an `is()` method, use that as the test.
139
- *
140
- * If it is an `object` it will be passed to the current options parsers.
141
- * The default options parser looks for an `object` property called `isa`,
142
- * which supports the following child properties:
143
- *
144
- * - `needProto: boolean`, Change the `needProto` option for `isInstance()`
145
- * - `parsers: function`, Add another options parser function.
146
- * - `process: function`, A one-time set-up function.
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`.
150
- * - Anything else will be set as an option that may be used by other parsers.
136
+ * - `string` Use `isType()` for the test.
137
+ * - `function` → Assumed to be a class constructor. Will return `true` if:
138
+ * - `v` is an an `object` instance of the class.
139
+ * - `v` is a `function` that is in the prototype tree of the class.
140
+ * - `object` Use `parsers` in {@link module:@lumjs/core/types~IsaOptions}
141
+ * to attempt to handle the value. These may define custom tests,
142
+ * or be used to set advanced options.
151
143
  *
152
144
  * Any other type value will only match if `v === type`
153
145
  *
154
146
  * @returns {boolean} Was the value one of the desired types?
147
+ *
148
+ * Will be `true` if _any one_ of the tests pass, or `false` if none passed.
149
+ *
155
150
  * @alias module:@lumjs/core/types.isa
156
151
  */
157
152
  function isa(v, ...types)
@@ -159,10 +154,10 @@ function isa(v, ...types)
159
154
  // A special options object.
160
155
  const opts =
161
156
  {
162
- needProto: false,
163
157
  parsers: [DEFAULT_ISA_PARSER],
164
158
  process: processOptions,
165
159
  instanceof: true,
160
+ needProto: false,
166
161
  }
167
162
 
168
163
  for (const type of types)
@@ -206,22 +201,131 @@ function isa(v, ...types)
206
201
  exports.isa = isa;
207
202
 
208
203
  /**
209
- * Extended return value from any test powered by the `OfTest` class.
204
+ * Options for the `isa()` function.
210
205
  *
211
- * Used if `rules.details` was set to `true`.
206
+ * Only the default options that are supported without using
207
+ * custom `object` parsers are listed below.
212
208
  *
213
- * @typedef module:@lumjs/core/types.OfTestResult
209
+ * @typedef {object} module:@lumjs/core/types~IsaOptions
214
210
  *
215
- * @prop {boolean} pass - Did the test pass?
216
- * @prop {boolean} empty - Was the list empty?
211
+ * @prop {module:@lumjs/core/types~IsaParser[]} parsers - Object parsers
217
212
  *
218
- * @prop {object} [failed] Failure information;
219
- * Only addedd if `pass` is `false`.
213
+ * These parser functions will be used to handle `object` _type_ values
214
+ * passed to the `isa()` function. The objects can be used to specify
215
+ * custom tests, to modify any of the currently set options, or to
216
+ * extend the functionality of the `isa()` function.
220
217
  *
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.
218
+ * There is one _default_ parser which supports two kinds of objects:
219
+ *
220
+ * - {@link module:@lumjs/core/types~IsaTest} Custom type test.
221
+ * - {@link module:@lumjs/core/types~IsaRule} → Custom rules to apply.
222
+ * Using a rule object is the default way to set options, and the
223
+ * only supported way to add new parsers.
224
+ *
225
+ * @prop {function} process - Private/internal method to call parsers
226
+ *
227
+ * Not meant for external use, this is the method which calls all the
228
+ * current `parsers` whenever an `object` value is passed as a _type_.
229
+ * It uses the same arguments as the parsers themselves.
230
+ *
231
+ * @prop {boolean} instanceof - Use `instanceof` instead of `isInstance()`
232
+ *
233
+ * As the `isInstance()` function is DEPRECATED, so is this option.
234
+ * Default: `true` since `v1.22` (was `false` prior to that.)
235
+ *
236
+ * @prop {boolean} needProto - `needProto` argument for `isInstance()`
237
+ *
238
+ * Obviously only applicable if `instanceof` option is `false`.
239
+ * As the `isInstance()` function is DEPRECATED, so is this option.
240
+ * Default: `false`
241
+ *
242
+ */
243
+
244
+ /**
245
+ * `object` parsers for the `isa()` function.
246
+ *
247
+ * May be used to support more types of `object` type
248
+ * values passed to the `isa()` function.
249
+ *
250
+ * @callback module:@lumjs/core/types~IsaParser
251
+ * @param {object} type - The type value passed to `isa()`
252
+ * @param {mixed} v - The value being tested by `isa()`
253
+ * @this {module:@lumjs/core/types~IsaOptions}
254
+ */
255
+
256
+ /**
257
+ * A simple custom `isa()` type test supported by the default parser.
258
+ *
259
+ * @typedef {object} module:@lumjs/core/types~IsaTest
260
+ * @prop {function} is - The test function to run.
261
+ *
262
+ * Will be passed the value being tested as the only argument.
263
+ *
264
+ * If this returns a value that evaluates to `true`, then the
265
+ * test has passed, and `isa()` will immediately return `true`.
266
+ *
267
+ * If this returns a value that evaluates to `false`, then the test
268
+ * has failed, and `isa()` will move onto the next type test,
269
+ * continuing until either one of the tests passes, or all fail.
270
+ *
271
+ */
272
+
273
+ /**
274
+ * Rule definition `object` supported by the default `isa()` parser.
224
275
  *
276
+ * @typedef {object} module:@lumjs/core/types~IsaRule
277
+ * @prop {module:@lumjs/core/types~IsaRules} isa - The rules to apply.
278
+ */
279
+
280
+ /**
281
+ * Supported properties for `isa()` Rules.
282
+ *
283
+ * Any property not _explicitly_ listed in this description will set
284
+ * the {@link module:@lumjs/core/types~IsaOptions option} of that name
285
+ * to the specified value. This can be used to set both default options,
286
+ * or any custom options supported by extra parsers you may add.
287
+ *
288
+ * @typedef {object} module:@lumjs/core/types~IsaRules
289
+ *
290
+ * @prop {module:@lumjs/core/types~IsaParser} [parsers] Add a parser.
291
+ *
292
+ * This rule will append the parser function to the array of `parsers`
293
+ * in the options for the current `isa()` function call.
294
+ *
295
+ * @prop {module:@lumjs/core/types~IsaRuleTest} [test] A type test function.
296
+ * @prop {module:@lumjs/core/types~IsaRuleProcess} [process] A setup function.
297
+ *
298
+ * @see {@link module:@lumjs/core/types~IsaRule}
299
+ */
300
+
301
+ /**
302
+ * An advanced custom `isa()` test declared in a Rule.
303
+ *
304
+ * @callback module:@lumjs/core/types~IsaRuleTest
305
+ *
306
+ * @param {mixed} v - The value being tested by `isa()`
307
+ * @param {module:@lumjs/core/types~IsaOptions} opts - `isa()` options
308
+ * @param {object} type - The type value passed to `isa()`
309
+ * @this {module:@lumjs/core/types~IsaRules}
310
+ * @returns {boolean} The return values from this are handled the same way
311
+ * as the `is()` method of {@link module:@lumjs/core/types~IsaTest} objects.
312
+ *
313
+ */
314
+
315
+ /**
316
+ * A custom `isa()` Rule setup function.
317
+ *
318
+ * Can be used to customize the options in ways not normally
319
+ * allowed, or do any other setup logic that is beyond the
320
+ * capabilities of the default options parser.
321
+ *
322
+ * @callback module:@lumjs/core/types~IsaRuleProcess
323
+ *
324
+ * @param {module:@lumjs/core/types~IsaOptions} opts - Options
325
+ * @param {mixed} v - The value being tested.
326
+ * @param {object} type - The type value passed to `isa()`.
327
+ * @this {module:@lumjs/core/types~IsaRules}
328
+ * @returns {void}
225
329
  */
226
330
 
227
331
  /**
@@ -468,6 +572,25 @@ class OfTest
468
572
 
469
573
  exports.OfTest = OfTest;
470
574
 
575
+ /**
576
+ * Extended return value from any test powered by the `OfTest` class.
577
+ *
578
+ * Used if `rules.details` was set to `true`.
579
+ *
580
+ * @typedef module:@lumjs/core/types.OfTestResult
581
+ *
582
+ * @prop {boolean} pass - Did the test pass?
583
+ * @prop {boolean} empty - Was the list empty?
584
+ *
585
+ * @prop {object} [failed] Failure information;
586
+ * Only addedd if `pass` is `false`.
587
+ *
588
+ * Will be the {@link module:@lumjs/core/types.OfTest.state.at}
589
+ * property from the underlying `OfTest` instance.
590
+ * See the documentation for that property for further details.
591
+ *
592
+ */
593
+
471
594
  /**
472
595
  * A nested class representing an explicit set of rules for `OfTest`.
473
596
  *
package/lib/types/root.js CHANGED
@@ -1,5 +1,6 @@
1
1
  const {U} = require('./js');
2
2
  const {isNil,isArray} = require('./basics');
3
+ const removeFromArray = require('../arrays/list').removeItems;
3
4
 
4
5
  // «private»
5
6
  function no_root()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lumjs/core",
3
- "version": "1.23.0",
3
+ "version": "1.24.1",
4
4
  "main": "lib/index.js",
5
5
  "exports":
6
6
  {
package/PLANS.txt DELETED
@@ -1,10 +0,0 @@
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.