@lumjs/core 1.38.4 → 1.38.5

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/objectid.js CHANGED
@@ -1,5 +1,6 @@
1
+ 'use strict';
1
2
 
2
- const {notNil,def,isNil,isObj,F,N,S,SY} = require('./types');
3
+ const {isObj,isComplex,isProperty,F,N,S,SY} = require('./types');
3
4
  const argOpts = require('./opt/args');
4
5
 
5
6
  /**
@@ -45,14 +46,52 @@ function randomNumber(seed=0, mode=1)
45
46
  }
46
47
  }
47
48
 
48
- exports.randomNumber = randomNumber;
49
+ //exports.randomNumber = randomNumber;
49
50
 
50
- const validBase = base => (typeof base === N && base > 1 && base < 37);
51
+ const validBase = (base) => (typeof base === N && base > 1 && base < 37);
51
52
 
52
53
  /**
53
54
  * A class for generating unique ids for objects.
54
55
  *
55
- * TODO: document all the methods, etc.
56
+ * @param {object} [opts] Named options;
57
+ * Will be assigned to `this.options` property.
58
+ *
59
+ * There are three mutually-exclusive options that will determine
60
+ * how ids are generated. They are checked for in a specific order:
61
+ *
62
+ * - `opts.random`
63
+ * - `opts.timestamp`
64
+ * - `opts.incremental`
65
+ *
66
+ * The first one that has a truthful value will be used and the
67
+ * others won't be checked at all. If none of them are specified,
68
+ * then `incremental` mode will be used as the default.
69
+ *
70
+ * @param {number} [opts.radix=16] Base for id numbers.
71
+ * We use `16` (hexadecimal) as the default.
72
+ *
73
+ * @param {(number|boolean)} [opts.incremental] Use incremental ids.
74
+ *
75
+ * - A number will override `opts.radix`.
76
+ * - Boolean true may be used for consistency with the other modes,
77
+ * but is not required at all, as this is the default mode.
78
+ * - Boolean false will show a warning message if none of the other
79
+ * modes were enabled, as in that case incremental mode will be
80
+ * forced as the default.
81
+ *
82
+ * @param {(object|number|boolean)} [opts.random] Use random ids.
83
+ *
84
+ * The numeric portion of ids will be generated via randomNumber().
85
+ *
86
+ * - An object will be used as named options for randomNumber();
87
+ * for any value other than an object, default options are used.
88
+ * - A number will override `opts.radix`.
89
+ * - Boolean true enables random ids without overriding anything.
90
+ *
91
+ * @param {(number|boolean)} [opts.timestamp] Use timestamps as ids.
92
+ *
93
+ * - A number will override `opts.radix`.
94
+ * - Boolean true enables timestamps without overriding anything.
56
95
  *
57
96
  * @alias module:@lumjs/core.UniqueObjectIds
58
97
  */
@@ -60,31 +99,43 @@ class UniqueObjectIds
60
99
  {
61
100
  constructor(opts={})
62
101
  {
63
- def(this, '$options', {value: opts});
102
+ df(this, 'options', {value: opts});
64
103
 
65
- let radix = null;
104
+ let radix = opts.radix ?? 16;
66
105
 
67
- if (isObj(opts.random))
68
- { // Random ids with custom options.
69
- def(this, '$randOpts', {value: opts.random});
70
- if (validBase(opts.random.radix))
106
+ if (opts.random)
107
+ {
108
+ let randOpts;
109
+ if (isObj(opts.random))
71
110
  {
72
- radix = opts.random.radix;
111
+ randOpts = opts.random;
73
112
  }
113
+ else if (typeof opts.random === N)
114
+ {
115
+ radix = opts.random;
116
+ randOpts = {};
117
+ }
118
+ df(this, '$randOpts', {value: randOpts});
74
119
  }
75
- else if (validBase(opts.random))
76
- { // Use random ids.
77
- def(this, '$randOpts', {value: {}});
78
- radix = opts.random;
79
- }
80
- else if (validBase(opts.timestamp))
120
+ else if (opts.timestamp)
81
121
  { // Use timestamp-based ids.
82
- def(this, '$timeIds', {value: {}});
83
- radix = opts.timestamp;
122
+ df(this, '$timeIds', {value: {}});
123
+ if (validBase(opts.timestamp))
124
+ {
125
+ radix = opts.timestamp;
126
+ }
84
127
  }
85
128
  else
86
129
  { // Use incremental ids.
87
- def(this, '$incIds', {value: {}});
130
+ df(this, '$incIds', {value: {}});
131
+ if (validBase(opts.incremental))
132
+ {
133
+ radix = opts.incremental;
134
+ }
135
+ else if (opts.incremental === false)
136
+ {
137
+ console.error("no modes enabled; forcing incremental ids");
138
+ }
88
139
  }
89
140
 
90
141
  if (!radix)
@@ -92,39 +143,49 @@ class UniqueObjectIds
92
143
  radix = validBase(opts.radix) ? opts.radix : 16;
93
144
  }
94
145
 
95
- def(this, '$radix', radix);
146
+ df(this, '$radix', radix);
96
147
 
97
148
  if (this.$randOpts)
98
149
  {
99
- def(this, '$randIds', {value: {}});
150
+ df(this, '$randIds', {value: {}});
100
151
  }
101
152
 
102
- const propType = (typeof opts.idProperty);
103
- const hasProp = (propType === S || propType === SY);
104
- const useRegistry = opts.useRegistry ?? !hasProp;
153
+ let propType = (typeof opts.idProperty),
154
+ hasProp = (propType === S || propType === SY),
155
+ useRegistry = (opts.useRegistry ?? !hasProp),
156
+ reg = null;
105
157
 
106
158
  if (useRegistry)
107
- { // Using an internal registry.
108
- def(this, '$registry', {value: new Map()});
159
+ { // Using an internal registry to store ids.
160
+ if (useRegistry instanceof TaggedObjects)
161
+ {
162
+ reg = useRegistry;
163
+ }
164
+ else
165
+ { // ↓ TODO: `s/property/tagProperty/` in v2.x
166
+ let ro = Object.assign({property: false}, opts, {useRegistry});
167
+ reg = new TaggedObjects(ro);
168
+ }
109
169
  }
110
170
  else if (!hasProp)
111
171
  { // At least ONE of them MUST be used!
112
172
  throw new RangeError("Need one of 'useRegistry' or 'idProperty'");
113
173
  }
114
174
 
175
+ df(this, 'metadata', {value: reg});
176
+
115
177
  if (hasProp)
116
178
  { // Add a direct reference to the id property key.
117
- def(this, '$idProp', {value: opts.idProperty});
179
+ df(this, '$idProp', {value: opts.idProperty});
118
180
  }
119
-
120
181
  }
121
182
 
122
183
  id(obj)
123
184
  {
124
- const hasRegistry = (this.$registry instanceof Map);
125
- if (hasRegistry && this.$registry.has(obj))
126
- { // The object was found in the registry.
127
- return this.$registry.get(obj);
185
+ let metadata = this.metadata?.for(obj);
186
+ if (metadata && metadata.uid)
187
+ { // Found the id in the registry metadata.
188
+ return metadata.uid;
128
189
  }
129
190
 
130
191
  const idProp = this.$idProp;
@@ -136,16 +197,16 @@ class UniqueObjectIds
136
197
 
137
198
  let radix = this.$radix;
138
199
 
139
- let cno = this.$options.className ?? {};
200
+ let cno = this.options.className ?? {};
140
201
  if (typeof cno === F) cno = {setup: cno};
141
202
  else if (cno === 'lc') cno = {lowercase: true};
142
203
  else if (cno === 'uc') cno = {uppercase: true};
143
204
 
144
205
  let id = '', idNum = null;
145
206
 
146
- if (typeof this.$options.prefix === S)
207
+ if (typeof this.options.prefix === S)
147
208
  {
148
- id += this.$options.prefix;
209
+ id += this.options.prefix;
149
210
  }
150
211
 
151
212
  let className = (typeof obj === F ? obj : obj.constructor).name;
@@ -175,9 +236,9 @@ class UniqueObjectIds
175
236
  }
176
237
  else
177
238
  { // No existing values yet.
178
- const start = this.$options.startAt ?? 1;
239
+ const start = this.options.startAt ?? 1;
179
240
  ids[className] = start;
180
- if (!this.$options.skipFirst)
241
+ if (!this.options.skipFirst)
181
242
  {
182
243
  idNum = start;
183
244
  }
@@ -229,101 +290,196 @@ class UniqueObjectIds
229
290
 
230
291
  if (typeof idNum === S)
231
292
  {
232
- if (typeof this.$options.infix === S)
293
+ if (typeof this.options.infix === S)
233
294
  {
234
- id += this.$options.infix;
295
+ id += this.options.infix;
235
296
  }
236
297
  id += idNum;
237
298
  }
238
299
 
239
300
  if (idProp)
240
301
  {
241
- def(obj, idProp, {value: id});
302
+ df(obj, idProp, {value: id});
242
303
  }
243
304
 
244
- if (hasRegistry)
245
- {
246
- this.$registry.set(obj, id);
305
+ if (metadata)
306
+ { // Set the uid.
307
+ metadata.uid = id;
247
308
  }
248
309
 
249
310
  return id;
250
311
  } // id()
312
+
251
313
  } // UniqueObjectIds class
252
314
 
253
- exports.UniqueObjectIds = UniqueObjectIds;
315
+ //exports.UniqueObjectIds = UniqueObjectIds;
254
316
 
255
317
  /**
256
- * A class for creating unique identifier objects.
257
- * Generally only used by my own inernal libraries, thus the name.
258
- * @alias module:@lumjs/core.InternalObjectId
318
+ * A class that allows tagging objects for specific purposes.
319
+ *
320
+ * This was originally used solely as a way to identify internal objects,
321
+ * and its classname was InternalObjectId (an alias to which will continue
322
+ * to exist for the duration of v1.x).
323
+ *
324
+ * In v1.38.5 however it was redesigned to support using an internal registry
325
+ * instead of a unique property on the object itself, and with this came the
326
+ * ability to define custom metadata for any tagged objects. So I renamed it,
327
+ * and updated the UniqueObjectIds class to support using an instance of this
328
+ * for its own internal registry (replacing the Map it used previously).
329
+ *
330
+ * @alias module:@lumjs/core.TaggedObjects
259
331
  */
260
- class InternalObjectId
332
+ class TaggedObjects
261
333
  {
262
334
  /**
263
- * Build a unique InternalObjectId instance.
335
+ * Build a unique TaggedObjects instance.
264
336
  *
265
- * @param {object} opts - Named options to change default behaviours.
337
+ * @param {object} [opts] Named options to change default behaviours.
266
338
  * @param {string} [opts.name] A friendly name for diagnostics.
267
339
  * @param {(string|number)} [opts.id] An internal id value.
268
- * A reasonable default will be generated if it's not specified.
269
- * @param {(string|Symbol)} [opts.property] The property name or Symbol.
270
- * This is the property that will be set on objects that are tagged
271
- * with this InternalObjectId. Default is a unique (non-global) `Symbol`.
340
+ *
341
+ * A reasonable default will be generated if it's not specified.
342
+ *
343
+ * @param {(string|symbol|false)} [opts.property] **DEPRECATED**
344
+ *
345
+ * Use `opts.tagProperty` instead of this which is the older name.
346
+ * This is currently supported as an alias, but will be removed in v2.x.
347
+ *
272
348
  * @param {boolean} [opts.useInstance=true] Store object or id value?
273
- * If this is `true` (now the default), we store the instance itself.
274
- * If this is `false` (the old default), we store just the `id` value.
349
+ *
350
+ * - If this is true (now the default), we use `this` instance as the tag.
351
+ * - If this is false (the old default), we use `this.id` as the tag.
352
+ *
353
+ * @param {boolean} [opts.useRegistry=false] Register tagged items?
354
+ *
355
+ * If this is true, then `this.registry` will be created as a Map instance,
356
+ * and every object/function passed to tag() will be added as a key, with
357
+ * a plain object that can be used for arbitrary metadata as the value.
358
+ *
359
+ * If both this and `opts.tagProperty` are false, an error will be thrown.
360
+ *
361
+ * @param {(string|symbol|false)} [opts.tagProperty] Tag property.
362
+ *
363
+ * This is the property that will be set on objects that are tagged
364
+ * with this TaggedObjects. Default is a unique (non-global) `Symbol`.
365
+ *
366
+ * If explicitly set to false, no property will be assigned.
367
+ *
368
+ * If both this and `opts.useRegistry` are false, an error will be thrown.
369
+ *
370
+ * @throws {RangeError} If `useRegistry` and `tagProperty` are both false.
275
371
  */
276
372
  constructor(opts={})
277
373
  {
278
- const ui = this.useInstance = opts.useInstance ?? true;
374
+ let ui = this.useInstance = opts.useInstance ?? true;
279
375
  this.name = opts.name;
280
376
  this.id = opts.id ?? (ui ? Date.now() : randomNumber());
281
- this.property = opts.property ?? Symbol(this.name ?? this.id);
377
+ this.property = opts.tagProperty
378
+ ?? opts.property
379
+ ?? Symbol(this.name ?? this.id);
380
+ this.options = opts;
381
+
382
+ if (opts.useRegistry)
383
+ {
384
+ this.registry = new Map();
385
+ }
386
+ else if (!isProperty(this.property))
387
+ {
388
+ throw new RangeError("Need one of 'useRegistry' or 'tagProperty'");
389
+ }
390
+ }
391
+
392
+ /**
393
+ * Get metadata for an object.
394
+ *
395
+ * If the object
396
+ *
397
+ * This is ONLY able to be used if `opts.useRegistry` was true
398
+ * when creating the intance. An error will be thrown otherwise.
399
+ *
400
+ * @param {(object|function)} obj - Target to get metadata for.
401
+ * @returns {object} Metadata.
402
+ */
403
+ for(obj)
404
+ {
405
+ if (!this.registry) throw new Error("No registry");
406
+ if (!this.registry.has(obj))
407
+ { // Tag the object.
408
+ this.tag(obj);
409
+ }
410
+ return this.registry.get(obj);
282
411
  }
283
412
 
284
413
  /**
285
414
  * Tag an object with the ObjectId.
286
415
  *
287
- * @param {*} obj - The object to tag with the unique object id.
288
- * @returns {*} `obj`
416
+ * @param {(object|function)} obj - The target to tag.
417
+ * @returns {(object|function)} `obj`
289
418
  */
290
419
  tag(obj)
291
420
  {
292
- if (isNil(obj)) throw new TypeError("Cannot tag a null or undefined value");
421
+ if (!isComplex(obj)) throw new TypeError("Invalid tag target");
422
+
423
+ if (this.registry)
424
+ {
425
+ if (!this.registry.has(obj))
426
+ { // Initialize the metadata.
427
+ this.registry.set(obj, {});
428
+ }
429
+ if (this.property === false)
430
+ {
431
+ return obj;
432
+ }
433
+ }
434
+
293
435
  const val = this.useInstance ? this : this.id;
294
- return def(obj, this.property, val);
436
+ return df(obj, this.property, val);
295
437
  }
296
438
 
297
439
  /**
298
440
  * Remove the tag from a tagged object.
299
441
  *
300
- * @param {*} obj
442
+ * @param {(object|function)} obj - The target to untag.
443
+ * @returns {(object|function)} `obj`
301
444
  */
302
445
  untag(obj)
303
446
  {
304
- if (notNil(obj))
447
+ if (isComplex(obj))
305
448
  {
306
- delete(obj[this.property]);
449
+ if (this.registry)
450
+ {
451
+ this.registry.delete(obj);
452
+ }
453
+
454
+ if (this.property !== false)
455
+ {
456
+ delete(obj[this.property]);
457
+ }
307
458
  }
459
+
308
460
  return obj;
309
461
  }
310
462
 
311
463
  /**
312
464
  * Is the specified object tagged with this object id?
313
465
  *
314
- * @param {*} obj - The object to test.
466
+ * @param {(object|function)} obj - The object to test.
315
467
  * @returns {boolean} If it's tagged or not.
316
468
  */
317
469
  is(obj)
318
470
  {
471
+ if (this.registry)
472
+ {
473
+ return this.registry.has(obj);
474
+ }
319
475
  const want = this.useInstance ? this : this.id;
320
- return (notNil(obj) && obj[this.property] === want);
476
+ return (isComplex(obj) && obj[this.property] === want);
321
477
  }
322
478
 
323
479
  /**
324
- * Generate a function that calls `instance.is()`
480
+ * Generate a closure that calls `instance.is()`
325
481
  *
326
- * @returns {function} The wrapper function.
482
+ * @returns {function} The closure function.
327
483
  */
328
484
  isFunction()
329
485
  {
@@ -332,4 +488,13 @@ class InternalObjectId
332
488
 
333
489
  }
334
490
 
335
- exports.InternalObjectId = InternalObjectId;
491
+ module.exports =
492
+ {
493
+ InternalObjectId: TaggedObjects,
494
+ randomNumber,
495
+ TaggedObjects,
496
+ UniqueObjectIds,
497
+ validBase,
498
+ }
499
+
500
+ const {df} = require('./obj/df');
package/lib/state.js CHANGED
@@ -1,13 +1,13 @@
1
1
  "use strict";
2
2
 
3
- const {S,isObj} = require('./types')
4
- const ctx = require('./context')
3
+ const {isObj} = require('./types')
5
4
  const ns = require('./obj/ns');
6
5
  const cp = require('./obj/cp');
6
+ const env = require('./env');
7
7
  const stateSym = Symbol('@lumjs/core/state')
8
8
  const stateData = {[stateSym]: false}
9
9
  const stateKey = 'LUM_JS_STATE'
10
- const stateOpts = {}
10
+ const stateOpts = env.getOpts(); // Some default opts.
11
11
 
12
12
  function getOpts(localOpts)
13
13
  {
@@ -17,16 +17,18 @@ function getOpts(localOpts)
17
17
  /**
18
18
  * A simple persistent state helper object.
19
19
  *
20
- * Will use `localStorage` in a browser environment,
21
- * and `process.env` if running in Node.js.
20
+ * Uses the `env` module as an abstraction layer for where
21
+ * the state data is stored.
22
22
  *
23
- * In both cases the key `LUM_JS_STATE` is used,
24
- * and the value must be valid JSON data.
23
+ * The key `LUM_JS_STATE` is used, and the value must be valid JSON data.
25
24
  *
25
+ * @deprecated use the more flexible `env` module instead.
26
26
  * @exports module:@lumjs/core/state
27
27
  */
28
28
  exports = module.exports =
29
29
  {
30
+ env,
31
+
30
32
  /**
31
33
  * Load (retrieve) state data
32
34
  * @param {object} [opts] Options
@@ -45,26 +47,12 @@ exports = module.exports =
45
47
 
46
48
  if (opts.refresh || !stateData[stateSym])
47
49
  {
48
- let json;
49
- if (ctx.isNode)
50
- {
51
- json = process.env[stateKey];
52
- }
53
- else if (ctx.isBrowser)
54
- {
55
- json = localStorage.getItem(stateKey);
56
- }
57
-
58
- if (typeof json === S)
50
+ delete opts.default;
51
+ let storedData = env.get(stateKey, opts);
52
+ if (isObj(storedData))
59
53
  {
60
- const revive = opts.jsonRevive;
61
- const storedData = JSON.parse(json, revive);
62
- if (isObj(storedData))
63
- {
64
- Object.assign(stateData, storedData);
65
- }
54
+ Object.assign(stateData, storedData);
66
55
  }
67
-
68
56
  stateData[stateSym] = true;
69
57
  }
70
58
 
@@ -77,36 +65,33 @@ exports = module.exports =
77
65
  * In a web browser environment, this will save the data
78
66
  * into the `localStorage` global object directly.
79
67
  *
80
- * In Node.js while this updates the `process.env` object,
81
- * there's no way to export to the environment in a truly
82
- * persistent way. So this also will output a shell statement
83
- * via `console.log()` that can be exported into the environment
84
- * (or added to a `.env` file, etc.)
68
+ * In Node.js it is saved to `process.env` but in order to make
69
+ * those changes persist you'd have to save them to an .env file
70
+ * or something similar.
85
71
  *
86
72
  * @param {object} [opts] Options
87
73
  * @param {function} [opts.jsonReplace] JSON.stringify() replacer
88
- * @returns {string} The JSON data
74
+ * @param {function} [opts.onSave] Callback to pass results to.
75
+ *
76
+ * This exists so that a hook may be added that can save the
77
+ * environment data back into an .env file or something similar
78
+ * on Node.js runtimes where the environment is not persistent.
79
+ *
80
+ * It will be passed the result object from env.set() and can
81
+ * do with it whatever is required.
82
+ *
83
+ * @returns {module:@lumjs/core/env~SetResult}
89
84
  */
90
85
  save(opts)
91
86
  {
92
87
  if (!stateData[stateSym]) return false;
93
-
94
88
  opts = getOpts(opts);
95
-
96
- const replace = opts.jsonReplace;
97
- let json = JSON.stringify(stateData, replace);
98
-
99
- if (ctx.isBrowser)
100
- {
101
- localStorage.setItem(stateKey, json);
102
- }
103
- else if (ctx.isNode)
89
+ let res = env.set(stateKey, stateData);
90
+ if (typeof opts.onSave === 'function')
104
91
  {
105
- process.env[stateKey] = json;
106
- console.log(`${stateKey}='${json.replaceAll("'", "'\"'\"'")}'`);
92
+ opts.onSave.call(opts, res);
107
93
  }
108
-
109
- return json;
94
+ return res;
110
95
  },
111
96
 
112
97
  /**
@@ -21,7 +21,7 @@ Object.assign(exports,
21
21
  { // A few standalone exports.
22
22
  def,
23
23
  TYPES: require('./typelist'),
24
- ownCount: require('./owncount'),
24
+ ownCount: require('../obj/owncount'), // ← TODO: nix in 2.x
25
25
  },
26
26
  );
27
27
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lumjs/core",
3
- "version": "1.38.4",
3
+ "version": "1.38.5",
4
4
  "main": "lib/index.js",
5
5
  "exports":
6
6
  {
@@ -9,13 +9,17 @@
9
9
  "./console": "./lib/console.js",
10
10
  "./context": "./lib/context.js",
11
11
  "./enum": "./lib/enum.js",
12
+ "./env": "./lib/env.js",
12
13
  "./events": "./lib/events/index.js",
13
14
  "./events/observable": "./lib/events/observable.js",
14
15
  "./flags": "./lib/flags.js",
15
16
  "./maps": "./lib/maps.js",
16
17
  "./meta": "./lib/meta.js",
17
- "./modules": "./lib/modules.js",
18
+ "./modules": "./lib/node/modules.js",
19
+ "./node": "./lib/node/index.js",
20
+ "./node/modules": "./lib/node/modules.js",
18
21
  "./obj": "./lib/obj/index.js",
22
+ "./obj/apply": "./lib/obj/apply.js",
19
23
  "./obj/cp": "./lib/obj/cp.js",
20
24
  "./obj/df": "./lib/obj/df.js",
21
25
  "./observable": "./lib/observable.js",
@@ -32,7 +36,8 @@
32
36
  "dependencies":
33
37
  {
34
38
  "@lumjs/opts": "^1.0.0",
35
- "@lumjs/events-observable": "^1.0.1"
39
+ "@lumjs/events-observable": "^1.0.1",
40
+ "semver": "^7.7.3"
36
41
  },
37
42
  "devDependencies":
38
43
  {
File without changes