@lumjs/core 1.36.0 → 1.37.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.
@@ -1,13 +1,16 @@
1
1
  "use strict";
2
2
 
3
- const {F,isObj} = require('../types');
3
+ const {SY,F,isObj} = require('../types');
4
4
 
5
5
  /**
6
6
  * An Event object to emit to handler callbacks.
7
7
  *
8
8
  * @prop {module:@lumjs/core/events.Listener} eventListener
9
9
  * The event Listener instance this event was emitted from.
10
- * @prop {string} name - The event name that was triggered.
10
+ * @prop {(string|Symbol)} type - The event type that was triggered.
11
+ * @prop {string} name - The event name that was triggered;
12
+ * if `type` is a string this will be the same value,
13
+ * for a Symbol type this will be the `type.description` value.
11
14
  * @prop {object} target - Target object for this event.
12
15
  * @prop {Array} args - Arguments passed to `emit()`
13
16
  * @prop {object} options - Composes options from the
@@ -38,17 +41,18 @@ class LumEvent
38
41
  * @protected
39
42
  * @param {module:@lumjs/core/events.Listener} listener
40
43
  * @param {object} target
41
- * @param {string} name
42
- * @param {Array} args
44
+ * @param {(string|Symbol)} type
45
+ * @param {Array} args
43
46
  * @param {object} status
44
47
  */
45
- constructor(listener, target, name, args, status)
48
+ constructor(listener, target, type, args, status)
46
49
  {
47
50
  const reg = listener.registry;
48
51
  this.eventListener = listener;
49
52
  this.args = args;
50
53
  this.target = target;
51
- this.name = name;
54
+ this.type = type;
55
+ this.name = (typeof type === SY) ? type.description : type;
52
56
  this.emitStatus = status;
53
57
  this.options = Object.assign({},
54
58
  reg.options,
@@ -3,7 +3,7 @@
3
3
  const {F,isObj} = require('../types');
4
4
  const Event = require('./event');
5
5
 
6
- const REMOVE_OPTS = ['listener','handler','eventNames'];
6
+ const REMOVE_OPTS = ['listener','handler','eventNames','eventTypes'];
7
7
 
8
8
  function makeOpts(spec)
9
9
  {
@@ -41,7 +41,8 @@ function isListener(v)
41
41
  * @prop {module:@lumjs/core/events.Registry} registry
42
42
  * The Registry instance this Listener belongs to.
43
43
  * @prop {(function|object)} handler - Event handler callback
44
- * @prop {Set} eventNames - A set of all event names handled by this
44
+ * @prop {Set} eventTypes - A set of all event types handled by this
45
+ * @prop {Set} eventNames - Alias to `eventTypes`
45
46
  * @prop {object} options - Options specific to this listener.
46
47
  *
47
48
  * See {@link module:@lumjs/core/events.Registry#makeListener makeListener()}
@@ -76,7 +77,8 @@ class LumEventListener
76
77
  // Assign the rest here.
77
78
  this.registry = registry;
78
79
  this.options = makeOpts(spec);
79
- this.eventNames = registry.getEventNames(spec.eventNames);
80
+ const events = spec.eventTypes ?? spec.eventNames;
81
+ this.eventTypes = this.eventNames = registry.getEventNames(events);
80
82
 
81
83
  const setup = this.options.setupListener ?? registry.options.setupListener;
82
84
  if (typeof setup === F)
@@ -86,12 +88,12 @@ class LumEventListener
86
88
  }
87
89
 
88
90
  /**
89
- * See if there is at least one item in `this.eventNames`
91
+ * See if there is at least one item in `this.eventTypes`
90
92
  * @type {boolean}
91
93
  */
92
94
  get hasEvents()
93
95
  {
94
- return this.eventNames.size > 0;
96
+ return this.eventTypes.size > 0;
95
97
  }
96
98
 
97
99
  /**
@@ -100,15 +102,15 @@ class LumEventListener
100
102
  *
101
103
  * This is a *protected method* and should not be called directly.
102
104
  * @protected
103
- * @param {string} eventName - A single event name that was triggered
105
+ * @param {string} type - A single event type/name that was triggered
104
106
  * @param {object} target - A single target object
105
107
  * @param {Array} args - Arguments passed to `emit()`
106
108
  * @param {module:@lumjs/core/events~Status} status - Emit status info
107
109
  * @returns {module:@lumjs/core/events.Event} The new Event that was emitted
108
110
  */
109
- emitEvent(eventName, target, args, status)
111
+ emitEvent(type, target, args, status)
110
112
  {
111
- const event = new Event(this, target, eventName, args, status);
113
+ const event = new Event(this, target, type, args, status);
112
114
 
113
115
  if (typeof this.handler === F)
114
116
  { // The simplest is the good old function
@@ -127,6 +129,10 @@ class LumEventListener
127
129
  return event;
128
130
  }
129
131
 
132
+ static get classProps()
133
+ {
134
+ return Object.getOwnPropertyNames(this.prototype);
135
+ }
130
136
  }
131
137
 
132
138
  LumEventListener.isListener = isListener;
@@ -1,8 +1,9 @@
1
1
  "use strict";
2
2
 
3
- const {S,F,isObj,def,isIterable} = require('../types');
3
+ const {S,F,SY,isObj,def,isIterable} = require('../types');
4
4
  const Listener = require('./listener');
5
5
  const RegSym = Symbol('@lumjs/core/events:registry');
6
+ const cp = Object.assign;
6
7
 
7
8
  const DEF_EXTENDS =
8
9
  {
@@ -13,6 +14,13 @@ const DEF_EXTENDS =
13
14
  once: null,
14
15
  }
15
16
 
17
+ const INT_EXTENDS =
18
+ {
19
+ listeners: true,
20
+ results: true,
21
+ onDemand: false,
22
+ }
23
+
16
24
  const DEF_OPTIONS =
17
25
  {
18
26
  delimiter: /\s+/,
@@ -20,6 +28,12 @@ const DEF_OPTIONS =
20
28
  wildcard: '*',
21
29
  }
22
30
 
31
+ const RES_PROPS =
32
+ [
33
+ 'eventNames', 'eventTypes', 'targets', 'multiMatch', 'onceRemoved',
34
+ 'stopEmitting', 'emitted', 'targetListeners', 'registry',
35
+ ]
36
+
23
37
  /**
24
38
  * Has a target object been registered with an event registry?
25
39
  * @param {object} target
@@ -33,7 +47,7 @@ const isRegistered = target => isObj(target[RegSym]);
33
47
  * @private
34
48
  * @param {object} target - Object to get metadata for
35
49
  * @param {boolean} [create=false] Create metadata if it's not found?
36
- * @returns {object} metadata (TODO: schema docs)
50
+ * @returns {(object|undefined)} metadata (TODO: schema docs)
37
51
  * @alias module:@lumjs/core/events.Registry.getMetadata
38
52
  */
39
53
  function getMetadata(target, create=false)
@@ -44,10 +58,16 @@ function getMetadata(target, create=false)
44
58
  }
45
59
  else if (create)
46
60
  { // Create new metadata
61
+ const exts = Object.keys(DEF_EXTENDS);
47
62
  const tpm =
48
63
  {
49
64
  r: new Map(),
50
65
  p: {},
66
+ x: {},
67
+ }
68
+ for (const ext of exts)
69
+ {
70
+ tpm.x[ext] = [];
51
71
  }
52
72
  def(target, RegSym, tpm);
53
73
  return tpm;
@@ -98,29 +118,53 @@ class LumEventRegistry
98
118
  * If you want to use a `function` as an actual target, you'll need to
99
119
  * wrap it in an array or other iterable object.
100
120
  *
101
- * @param {object} [opts] Options (saved to `options` property).
121
+ * @param {object} [opts] Options
122
+ *
123
+ * A _compiled_ version is saved to the `options` property.
124
+ * The compiled version includes a bunch of defaults, and various
125
+ * compose rules (mostly for the `.extend` nested options).
102
126
  *
103
127
  * @param {(RegExp|string)} [opts.delimiter=/\s+/] Used to split event names
104
128
  *
105
- * @param {(object|boolean)} [opts.extend]
106
- * This option determines the rules for adding wrapper methods and
107
- * other extension properties to the target objects.
129
+ * @param {object} [opts.extend] Options for wrapper methods/properties
130
+ *
131
+ * The `boolean` options determine if extension methods will be added to
132
+ * certain types of objects (and in some cases, when to do so).
108
133
  *
109
- * If this is `true` (default when `targets` is an `object`), then
110
- * the target objects will be extended using the default property names.
134
+ * The `?string` options are each the names of properties/methods in the
135
+ * Registry class. If they are set to a `string` then that will be the
136
+ * name used for the wrapper property/method added to objects. If it
137
+ * is explicitly set to `null` it means skip adding a wrapper for that
138
+ * method/property. If it is omitted entirely the default will be used.
111
139
  *
112
- * If this is set to `false` (default when `targets` is a `function`),
113
- * it disables adding extension properties entirely.
140
+ * @param {boolean} [opts.extend.targets] Extend target objects?
114
141
  *
115
- * If it is an `object` then each nested property may be set to a string
116
- * to override the default, or `null` to skip adding that property.
142
+ * The default will be `true` when `targets` is an `object`, or `false`
143
+ * when `targets` is a `function`.
144
+ *
145
+ * @param {boolean} [opts.extend.listeners=true] Extend Listener instances?
146
+ * As returned by `makeListener()`, `listen()`, and `once()`
147
+ * @param {boolean} [opts.extend.results=true] Extend `emit()` results?
148
+ * @param {boolean} [opts.extend.onDemand=false] On-demand target setup
149
+ *
150
+ * If `targets` was a `function` and this is set to `true`, then
151
+ * we'll perform the target setup on every emit() call. The setup process
152
+ * is skipped on any targets that have already been set up, so this
153
+ * is meant for dynamic targets that may change on every call.
154
+ *
155
+ * @param {?string} [opts.extend.registry="events"] Registry property;
156
+ * only added to `targets`, never to listeners or results which have
157
+ * their own inherent `registry` property already.
117
158
  *
118
- * @param {?string} [opts.extend.registry="events"] Registry property
119
159
  * @param {?string} [opts.extend.emit="emit"] `emit()` proxy method
120
160
  * @param {?string} [opts.extend.listen="on"] `listen()` proxy method
121
161
  * @param {?string} [opts.extend.once=null] `once()` proxy method
122
162
  * @param {?string} [opts.extend.remove=null] `remove()` proxy method
123
163
  *
164
+ * The `remove` wrapper method added to Listener instances is slightly
165
+ * different than the one added to other objects, as if you call it
166
+ * with no arguments, it will pass the Listener itself as the argument.
167
+ *
124
168
  * @param {boolean} [opts.multiMatch=false]
125
169
  * If a registered listener has multiple event names, and a call
126
170
  * to `emit()` also has multiple event names, the value of this
@@ -159,7 +203,7 @@ class LumEventRegistry
159
203
  */
160
204
  constructor(targets, opts={})
161
205
  {
162
- let defExt; // Default opts.extend value
206
+ let defExt; // Default opts.extend.targets value
163
207
  if (typeof targets === F)
164
208
  { // A dynamic getter method
165
209
  this.funTargets = true;
@@ -181,47 +225,54 @@ class LumEventRegistry
181
225
  defExt = true;
182
226
  }
183
227
 
184
- this.options = Object.assign({extend: defExt}, DEF_OPTIONS, opts);
228
+ // Build composite extend rules
229
+ const extend = cp(
230
+ {targets: defExt},
231
+ INT_EXTENDS,
232
+ DEF_EXTENDS,
233
+ opts.extend);
234
+
235
+ // Now compile the final options
236
+ this.options = cp({}, DEF_OPTIONS, opts, {extend});
185
237
 
186
238
  this.allListeners = new Set();
187
239
  this.listenersFor = new Map();
188
240
 
189
- this.extend(targets);
241
+ this.setupTargets(targets);
190
242
  } // constructor()
191
243
 
192
244
  /**
193
- * Add extension methods to target objects;
194
- * used by `constructor` and `register()`,
195
- * not meant to be called from outside code.
245
+ * Set up target objects
246
+ *
247
+ * Always sets the necessary metadata on each target.
248
+ * May also extend the targets with wrapper properties and methods
249
+ * depending on the `options.extend` values set.
250
+ *
251
+ * Not meant to be called from outside code.
196
252
  * @private
197
253
  * @param {Iterable} targets - Targets to extend
198
254
  * @returns {module:@lumjs/core/events.Registry} `this`
199
255
  */
200
- extend(targets)
256
+ setupTargets(targets)
201
257
  {
202
258
  const opts = this.options;
203
259
  const extOpts = opts.extend;
204
-
205
- let intNames = null, extNames = null;
206
-
207
- if (extOpts)
208
- {
209
- intNames = Object.keys(DEF_EXTENDS);
210
- extNames = Object.assign({}, DEF_EXTENDS, extOpts);
211
- }
260
+ const intNames = extOpts.targets ? Object.keys(DEF_EXTENDS) : null;
212
261
 
213
262
  for (const target of targets)
214
263
  {
215
264
  const tps = {}, tpm = getMetadata(target, true);
265
+ if (tpm.r.has(this)) continue; // Already set up with this registry.
216
266
  tpm.r.set(this, tps);
217
267
 
218
- if (extOpts)
268
+ if (extOpts.targets)
219
269
  {
220
270
  for (const iname of intNames)
221
271
  {
222
- if (typeof extNames[iname] === S && extNames[iname].trim() !== '')
272
+ if ((typeof extOpts[iname] === S && extOpts[iname].trim() !== '')
273
+ || typeof extOpts[iname] === SY)
223
274
  {
224
- const ename = extNames[iname];
275
+ const ename = extOpts[iname];
225
276
  const value = iname === 'registry'
226
277
  ? this // The registry instance itself
227
278
  : (...args) => this[iname](...args) // A proxy method
@@ -230,6 +281,7 @@ class LumEventRegistry
230
281
  def(target, ename, {value});
231
282
  tps[ename] = iname;
232
283
  tpm.p[ename] = this;
284
+ tpm.x[iname].push([this, ename]);
233
285
  }
234
286
  else
235
287
  {
@@ -240,34 +292,79 @@ class LumEventRegistry
240
292
  }
241
293
  }
242
294
  }
295
+
296
+ return this;
297
+ }
298
+
299
+ /**
300
+ * Add extension methods to internal objects;
301
+ * currently supports Listener instances and result/status objects.
302
+ * Not meant to be called from outside code.
303
+ * @private
304
+ * @param {object} obj - Internal object to extend
305
+ * @returns {module:@lumjs/core/events.Registry} `this`
306
+ */
307
+ extendInternal(obj)
308
+ {
309
+ const opts = this.options;
310
+ const extOpts = opts.extend;
311
+ const intNames = Object.keys(DEF_EXTENDS);
312
+ const isLs = (obj instanceof Listener);
313
+ const reserved = isLs ? Listener.classProps : RES_PROPS;
314
+
315
+ for (const iname of intNames)
316
+ {
317
+ if (iname === 'registry') continue; // skip the registry property
318
+ if ((typeof extOpts[iname] === S && extOpts[iname].trim() !== '')
319
+ || typeof extOpts[iname] === SY)
320
+ {
321
+ const ename = extOpts[iname];
322
+ if (reserved.includes(ename))
323
+ { // Skip reserved names
324
+ console.warn("reserved property", {ename, obj, registry: this});
325
+ continue;
326
+ }
327
+
328
+ const value = (isLs && iname === 'remove')
329
+ ? (what=obj) => this.remove(what) // special remove wrapper
330
+ : (...args) => this[iname](...args) // regular wrapper method
331
+ if (opts.overwrite || obj[ename] === undefined)
332
+ {
333
+ def(obj, ename, {value});
334
+ }
335
+ }
336
+ }
337
+
243
338
  return this;
244
339
  }
245
340
 
246
341
  /**
247
342
  * Build a new Listener instance; used by `listen()` method.
248
343
  *
249
- * @param {(string|object)} eventNames
344
+ * @param {(string|symbol|object)} eventTypes
250
345
  * What this does depends on the type, and the number of arguments passed.
251
346
  *
252
347
  * If this is an `object` **AND** is *the only argument* passed,
253
- * it will be used as the `spec`, and the `spec.eventNames`
348
+ * it will be used as the `spec`, and the `spec.eventTypes`
254
349
  * and `spec.listener` properties will become mandatory.
255
350
  *
256
- * If it's a string *OR* there is more than one argument, this
257
- * will be used as the `spec.eventNames` property.
351
+ * If it's NOT an object *OR* there is more than one argument, this
352
+ * will be used as the `spec.eventTypes` property.
258
353
  *
259
354
  * @param {module:@lumjs/core/events~Handler} [handler]
260
355
  * Used as the `spec.handler` property if specified.
261
356
  *
262
- * This is mandatory if `eventNames` argument is a `string`!
357
+ * This is mandatory if `eventTypes` argument is a string or Symbol!
263
358
  *
264
359
  * @param {object} [spec] The listener specification rules
265
360
  *
266
- * @param {(string|Iterable)} [spec.eventNames] Event names to listen for
361
+ * @param {(string|symbol|Iterable)} [spec.eventTypes] Event type(s)
267
362
  *
268
363
  * See {@link module:@lumjs/core/events.Registry#getEventNames} for details.
269
364
  *
270
- * @param {module:@lumjs/core/events~Handler} [spec.handler] Event handler.
365
+ * @param {(string|symbol|Iterable)} [spec.eventNames] Alias of `eventTypes`
366
+ *
367
+ * @param {module:@lumjs/core/events~Handler} [spec.handler] Event handler
271
368
  *
272
369
  * @param {(function|object)} [spec.listener] An alias for `handler`
273
370
  *
@@ -279,8 +376,8 @@ class LumEventRegistry
279
376
  *
280
377
  * If `spec.options` is used, the properties in it take precedence over
281
378
  * those directly in the `spec` object. Note that you cannot use the
282
- * names `listener`, `handler` or `eventNames` as option properties,
283
- * and if found, they will be removed.
379
+ * names `listener`, `handler`, `eventTypes`, or `eventNames` as option
380
+ * properties, and if found, they will be removed.
284
381
  *
285
382
  * You may also override the `setupEvent` and `setupListener` registry
286
383
  * options here if needed.
@@ -305,16 +402,21 @@ class LumEventRegistry
305
402
  }
306
403
  else if (args.length === 1 && isObj(args[0]))
307
404
  { // listen(spec)
308
- spec = Object.assign({}, args[0]);
405
+ spec = cp({}, args[0]);
309
406
  }
310
407
  else
311
- { // listen(eventNames, listener, [spec])
312
- spec = Object.assign({}, args[2]);
313
- spec.eventNames = args[0];
408
+ { // listen(eventTypes, listener, [spec])
409
+ spec = cp({}, args[2]);
410
+ spec.eventTypes = args[0];
314
411
  spec.handler = args[1];
315
412
  }
316
413
 
317
- return new Listener(this, spec);
414
+ const lsnr = new Listener(this, spec);
415
+ if (this.options.extend.listeners)
416
+ {
417
+ this.extendInternal(lsnr);
418
+ }
419
+ return lsnr;
318
420
  }
319
421
 
320
422
  /**
@@ -373,7 +475,7 @@ class LumEventRegistry
373
475
 
374
476
  this.allListeners.add(listener);
375
477
 
376
- for (const ename of listener.eventNames)
478
+ for (const ename of listener.eventTypes)
377
479
  {
378
480
  let lset;
379
481
  if (this.listenersFor.has(ename))
@@ -431,7 +533,7 @@ class LumEventRegistry
431
533
  const eventListeners = this.listenersFor.get(name);
432
534
  for (const lsnr of eventListeners)
433
535
  {
434
- lsnr.eventNames.delete(name);
536
+ lsnr.eventTypes.delete(name);
435
537
  if (!lsnr.hasEvents)
436
538
  { // The last event name was removed.
437
539
  this.removeListeners(lsnr);
@@ -456,7 +558,7 @@ class LumEventRegistry
456
558
  { // First remove it from allListeners
457
559
  this.allListeners.delete(listener);
458
560
 
459
- for (const ename of listener.eventNames)
561
+ for (const ename of listener.eventTypes)
460
562
  {
461
563
  if (this.listenersFor.has(ename))
462
564
  {
@@ -477,6 +579,7 @@ class LumEventRegistry
477
579
  * - If this is the `wildcard` string, then this will call `removeAll()`.
478
580
  * - If this is any other `string` it will be split using `splitNames()`,
479
581
  * and the resulting strings passed as arguments to `removeEvents()`.
582
+ * - If this is a `Symbol` it will be passed to `removeEvents()`.
480
583
  * - If this is a `Listener` instance, its passed to `removeListeners()`.
481
584
  *
482
585
  * @returns {module:@lumjs/core/events.Registry} `this`
@@ -493,6 +596,10 @@ class LumEventRegistry
493
596
  const events = this.splitNames(what);
494
597
  return this.removeEvents(...events);
495
598
  }
599
+ else if (typeof what === SY)
600
+ {
601
+ return this.removeEvents(what);
602
+ }
496
603
  else if (what instanceof Listener)
497
604
  {
498
605
  return this.removeListeners(what);
@@ -507,9 +614,8 @@ class LumEventRegistry
507
614
  /**
508
615
  * Emit (trigger) one or more events.
509
616
  *
510
- * @param {(string|string[])} eventNames - Events to emit.
511
- *
512
- * If this is a single `string` it will be split via `splitNames()`.
617
+ * @param {(string|symbol|Array)} eventTypes - Events to emit;
618
+ * see {@link module:@lumjs/core/events#getEventTypes} for details.
513
619
  *
514
620
  * @param {object} [data] A data object (highly recommended);
515
621
  * will be assigned to `event.data` if specified.
@@ -522,20 +628,32 @@ class LumEventRegistry
522
628
  *
523
629
  * @returns {module:@lumjs/core/events~Status}
524
630
  */
525
- emit(eventNames, ...args)
631
+ emit(eventTypes, ...args)
526
632
  {
633
+ const extOpts = this.options.extend;
527
634
  const sti =
528
635
  {
529
- eventNames: this.getEventNames(eventNames),
636
+ eventTypes: this.getEventTypes(eventTypes),
530
637
  multiMatch: this.options.multiMatch,
531
638
  onceRemoved: new Set(),
532
639
  stopEmitting: false,
533
640
  emitted: [],
641
+ registry: this,
642
+ }
643
+ sti.eventNames = sti.eventTypes;
644
+
645
+ if (extOpts.results)
646
+ {
647
+ this.extendInternal(sti);
534
648
  }
535
649
 
536
650
  { // Get the targets.
537
651
  const tgs = this.getTargets(sti);
538
652
  sti.targets = (tgs instanceof Set) ? tgs : new Set(tgs);
653
+ if (this.funTargets && extOpts.onDemand)
654
+ {
655
+ this.setupTargets(sti.targets);
656
+ }
539
657
  }
540
658
 
541
659
  const wilds = this.listenersFor.get(this.options.wildcard);
@@ -543,7 +661,7 @@ class LumEventRegistry
543
661
  emitting: for (const tg of sti.targets)
544
662
  {
545
663
  const called = sti.targetListeners = new Set();
546
- for (const ename of sti.eventNames)
664
+ for (const ename of sti.eventTypes)
547
665
  {
548
666
  if (!this.listenersFor.has(ename)) continue;
549
667
 
@@ -579,8 +697,6 @@ class LumEventRegistry
579
697
  return sti;
580
698
  }
581
699
 
582
-
583
-
584
700
  /**
585
701
  * Register additional target objects
586
702
  * @param {...object} addTargets - Target objects to register
@@ -617,7 +733,7 @@ class LumEventRegistry
617
733
  }
618
734
  }
619
735
 
620
- this.extend(addTargets);
736
+ this.setupTargets(addTargets);
621
737
  return this;
622
738
  }
623
739
 
@@ -693,32 +809,37 @@ class LumEventRegistry
693
809
  }
694
810
 
695
811
  /**
696
- * Get a Set of event names from various kinds of values
697
- * @param {(string|Iterable)} names - Event names source
812
+ * Get a Set of event types/names from various kinds of values
813
+ * @param {(string|symbol|Iterable)} types - Event types source
698
814
  *
699
815
  * If this is a string, it'll be passed to `splitNames()`.
700
- * If it's any kind of `Iterable`, it'll be converted to a `Set`.
816
+ * If it's a Symbol, it'll be wrapped in a Set.
817
+ * If it's any kind of Iterable value, it'll be converted to a Set.
701
818
  *
702
819
  * @returns {Set}
703
820
  * @throws {TypeError} If `names` is not a valid value
704
821
  */
705
- getEventNames(names)
822
+ getEventTypes(types)
706
823
  {
707
- if (typeof names === S)
824
+ if (typeof types === S)
825
+ {
826
+ return this.splitNames(types);
827
+ }
828
+ else if (typeof types === SY)
708
829
  {
709
- return this.splitNames(names);
830
+ return new Set([types]);
710
831
  }
711
- else if (names instanceof Set)
832
+ else if (types instanceof Set)
712
833
  {
713
- return names;
834
+ return types;
714
835
  }
715
- else if (isIterable(names))
836
+ else if (isIterable(types))
716
837
  {
717
- return new Set(names);
838
+ return new Set(types);
718
839
  }
719
840
  else
720
841
  {
721
- console.error({names, registry: this});
842
+ console.error({names: types, registry: this});
722
843
  throw new TypeError("Invalid event names");
723
844
  }
724
845
  }
@@ -735,9 +856,20 @@ class LumEventRegistry
735
856
 
736
857
  }
737
858
 
738
- Object.assign(LumEventRegistry,
859
+ const LERP = LumEventRegistry.prototype;
860
+ def(LERP, 'getEventNames', LERP.getEventTypes);
861
+
862
+ cp(LumEventRegistry,
739
863
  {
740
864
  isRegistered, getMetadata, targetsAre,
741
865
  });
742
866
 
743
867
  module.exports = LumEventRegistry;
868
+
869
+ /**
870
+ * An alias to `getEventTypes`
871
+ * @function module:@lumjs/core/events.Registry#getEventNames
872
+ * @param {(string|symbol|Iterable)} names - Event names source
873
+ * @returns {Set}
874
+ * @see module:@lumjs/core/events.Registry#getEventTypes
875
+ */
package/lib/obj/clone.js CHANGED
@@ -296,6 +296,9 @@ exports.addClone = addClone;
296
296
  *
297
297
  * If not, if the object has a `clone()` method it will be used.
298
298
  * Otherwise use our `clone()` function.
299
+ *
300
+ * **NOTE**: A newer replacement for this function exists, see
301
+ * {@link module:@lumjs/core/obj.unlocked} for details.
299
302
  *
300
303
  * @param {object} obj - The object to clone if needed.
301
304
  * @param {object} [opts] - Options to pass to `clone()` method.
package/lib/obj/index.js CHANGED
@@ -14,6 +14,7 @@ const {lock,addLock} = require('./lock');
14
14
  const {mergeNested,syncNested} = require('./merge');
15
15
  const ns = require('./ns');
16
16
  const cp = require('./cp');
17
+ const unlocked = require('./unlocked');
17
18
 
18
19
  const
19
20
  {
@@ -27,5 +28,5 @@ module.exports =
27
28
  mergeNested, syncNested, copyProps, copyAll, ns,
28
29
  getObjectPath, setObjectPath, getNamespace, setNamespace,
29
30
  getProperty, duplicateAll, duplicateOne, getMethods, signatureOf,
30
- MethodFilter, apply, flip, flipKeyVal, flipMap,
31
+ MethodFilter, apply, flip, flipKeyVal, flipMap, unlocked,
31
32
  }
@@ -0,0 +1,87 @@
1
+ "use strict";
2
+
3
+ const {F,S,isObj,needObj} = require('../types');
4
+
5
+ const CLONE = 'clone';
6
+ const clone = obj => Object.assign({}, obj);
7
+
8
+ /**
9
+ * Get an unlocked object
10
+ *
11
+ * @param {object} obj - The target object;
12
+ *
13
+ * If the object is extensible it will be returned _as is_.
14
+ *
15
+ * If the object is frozen, sealed, or otherwise non-extensible,
16
+ * a cloning function will be used to make an unlocked copy.
17
+ *
18
+ * @param {(object|function|string)} [opts] Options to customize the behaviour
19
+ *
20
+ * - If this is a `function` it will be used as the `opts.fn` value.
21
+ * - If this is a `string` it will be used as the `opts.method` value.
22
+ *
23
+ * @param {string} [opts.method='clone'] Object method to use
24
+ *
25
+ * Set to an empty string `""` to skip checking for an object method.
26
+ *
27
+ * If the method exists on the target object, it will be called to
28
+ * perform the cloning procedure, otherwise `opts.fn` will be used.
29
+ *
30
+ * @param {Array} [opts.args] Arguments for `opts.method` method
31
+ *
32
+ * If not specified, the default value is: `[opts]`
33
+ *
34
+ * @param {function} [opts.fn] A function to perform the clone operation
35
+ *
36
+ * Must take only a single parameter, which is the object to clone.
37
+ * Must return an extensible clone of the object.
38
+ *
39
+ * If for whatever reason you need the `opts` in the function,
40
+ * then use a unbound function, and `opts` will be available
41
+ * as `this` in the function body. Arrow functions (closures) are
42
+ * always considered bound and cannot have a `this` value assigned.
43
+ *
44
+ * The default value is a closure: `obj => Object.assign({}, obj)`;
45
+ *
46
+ * @return {object}
47
+ *
48
+ * @alias module:@lumjs/core/obj.unlocked
49
+ */
50
+ function unlocked(obj, opts={})
51
+ {
52
+ needObj(obj, true, "invalid obj value");
53
+
54
+ if (Object.isExtensible(obj))
55
+ { // We're done here.
56
+ return obj;
57
+ }
58
+
59
+ if (typeof opts === F)
60
+ {
61
+ opts = {fn: opts}
62
+ }
63
+ else if (typeof opts === S)
64
+ {
65
+ opts = {method: opts}
66
+ }
67
+ else if (!isObj(opts))
68
+ {
69
+ console.error({obj, opts});
70
+ throw new TypeError("invalid opts value");
71
+ }
72
+
73
+ const fn = (typeof opts.fn === F) ? opts.fn : clone;
74
+ const meth = (typeof opts.method === S) ? opts.method.trim() : CLONE;
75
+ const args = (Array.isArray(opts.args)) ? opts.args : [opts];
76
+
77
+ if (meth && typeof obj[meth] === F)
78
+ { // Use a clone method
79
+ return obj[meth](...args);
80
+ }
81
+ else
82
+ { // Use a clone function
83
+ return fn.call(opts, obj);
84
+ }
85
+ }
86
+
87
+ module.exports = unlocked;
package/lib/types/isa.js CHANGED
@@ -461,7 +461,7 @@ class OfTest
461
461
  }
462
462
  else if (isObj(rules))
463
463
  {
464
- this.valid = valid(rules.valid);
464
+ this.valid = valid(rules.value);
465
465
  }
466
466
  else
467
467
  { // That's gonna be a no from me.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lumjs/core",
3
- "version": "1.36.0",
3
+ "version": "1.37.1",
4
4
  "main": "lib/index.js",
5
5
  "exports":
6
6
  {