@lumjs/core 1.31.1 → 1.33.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/index.js CHANGED
@@ -10,100 +10,7 @@
10
10
  * @module @lumjs/core
11
11
  */
12
12
 
13
- /**
14
- * Constants and functions for type checks
15
- * and other fundamental functions
16
- *
17
- * @alias module:@lumjs/core.types
18
- * @see module:@lumjs/core/types
19
- */
20
- const types = require('./types');
21
-
22
- /**
23
- * Define properties on an object or function
24
- * @name module:@lumjs/core.def
25
- * @function
26
- * @see module:@lumjs/core/types.def
27
- */
28
- const def = types.def;
29
-
30
- /**
31
- * Define *lazy* properties on an object or function
32
- * @alias module:@lumjs/core.lazy
33
- * @function
34
- * @see module:@lumjs/core/types.lazy
35
- */
36
- const lazy = types.lazy;
37
-
38
- def(exports, 'types', types);
39
- def(exports, 'def', def);
40
- def(exports, 'lazy', lazy);
41
-
42
- // TODO: document
43
- def(exports, 'state', require('./state'));
44
-
45
- /**
46
- * Array utility functions «Lazy»
47
- * @name module:@lumjs/core.arrays
48
- * @type {module:@lumjs/core/arrays}
49
- */
50
- lazy(exports, 'arrays', () => require('./arrays'));
51
-
52
- /**
53
- * Map utility functions «Lazy»
54
- * @name module:@lumjs/core.maps
55
- * @type {module:@lumjs/core/maps}
56
- */
57
- lazy(exports, 'maps', () => require('./maps'));
58
-
59
- /**
60
- * Information about the JS context we're running in
61
- * @name module:@lumjs/core.context
62
- * @type {module:@lumjs/core/context}
63
- */
64
- def(exports, 'context', require('./context'));
65
-
66
- /**
67
- * Functions for working with strings and locales «Lazy»
68
- * @name module:@lumjs/core.strings
69
- * @type {module:@lumjs/core/strings}
70
- */
71
- lazy(exports, 'strings', () => require('./strings'));
72
-
73
- /**
74
- * Functions for working with binary flags «Lazy»
75
- * @name module:@lumjs/core.flags
76
- * @type {module:@lumjs/core/flags}
77
- */
78
- lazy(exports, 'flags', () => require('./flags'));
79
-
80
- /**
81
- * Functions for manipulating objects «Lazy»
82
- * @name module:@lumjs/core.obj
83
- * @type {module:@lumjs/core/obj}
84
- */
85
- lazy(exports, 'obj', () => require('./obj'));
86
-
87
- /**
88
- * A wrapper around the Javascript console «Lazy»
89
- * @name module:@lumjs/core.console
90
- * @type {module:@lumjs/core/console}
91
- */
92
- lazy(exports, 'console', () => require('./console'));
93
-
94
- /**
95
- * A simplistic implementation of class/object traits «Lazy»
96
- * @name module:@lumjs/core.traits
97
- * @type {module:@lumjs/core/traits}
98
- */
99
- lazy(exports, 'traits', () => require('./traits'));
100
-
101
- /**
102
- * Functions for getting values and properties with fallback defaults «Lazy»
103
- * @name module:@lumjs/core.opt
104
- * @type {module:@lumjs/core/opt}
105
- */
106
- lazy(exports, 'opt', () => require('./opt'), {def:{autoDesc: false}});
13
+ /// see also: `docs/src/index.js`
107
14
 
108
15
  // Get a bunch of properties from a submodule.
109
16
  function from(submod)
@@ -114,49 +21,18 @@ function from(submod)
114
21
  }
115
22
  }
116
23
 
117
- // ObjectID stuff is imported directly without registering a sub-module.
118
- from(require('./objectid'));
119
-
120
- /**
121
- * Get a simplistic debugging stacktrace
122
- * @name module:@lumjs/core.stacktrace
123
- * @function
124
- * @see module:@lumjs/core/meta.stacktrace
125
- */
126
-
127
- /**
128
- * A simple base class for making *abstract* classes
129
- * @name module:@lumjs/core.AbstractClass
130
- * @see module:@lumjs/core/meta.AbstractClass
131
- */
132
-
133
- /**
134
- * A *factory* for special types of JS `function` constructors
135
- * @name module:@lumjs/core.Functions
136
- * @see module:@lumjs/core/meta.Functions
137
- */
24
+ const types = require('./types');
25
+ const {def,lazy} = types;
138
26
 
139
- /**
140
- * Throw an `Error` saying that a feature is not yet implemented
141
- * @name module:@lumjs/core.NYI
142
- * @function
143
- * @see module:@lumjs/core/meta.NYI
144
- */
27
+ def(exports, 'types', types);
28
+ def(exports, 'def', def);
29
+ def(exports, 'lazy', lazy);
145
30
 
146
- /**
147
- * A function indicating that something is deprecated
148
- * @name module:@lumjs/core.deprecated
149
- * @function
150
- * @see module:@lumjs/core/meta.deprecated
151
- */
31
+ def(exports, 'context', require('./context'));
32
+ def(exports, 'state', {value: require('./state')});
152
33
 
153
- /**
154
- * Assign a getter property to an object that calls the `deprecated`
155
- * function with a specific message before returning the proxied value.
156
- * @name module:@lumjs/core.wrapDepr
157
- * @function
158
- * @see module:@lumjs/core/meta.wrapDepre
159
- */
34
+ // ObjectID stuff is imported directly without registering a sub-module.
35
+ from(require('./objectid'));
160
36
 
161
37
  // These are exported directly, but a meta sub-module also exists.
162
38
  // Unlike most sub-modules there is no `meta` property in the main library.
@@ -167,25 +43,14 @@ def(exports, 'AbstractClass',
167
43
  get() { return meta.AbstractClass; }
168
44
  });
169
45
 
170
- /**
171
- * Create a magic *Enum* object «Lazy»
172
- * @name module:@lumjs/core.Enum
173
- * @function
174
- * @see module:@lumjs/core/enum
175
- */
46
+ lazy(exports, 'arrays', () => require('./arrays'));
47
+ lazy(exports, 'maps', () => require('./maps'));
48
+ lazy(exports, 'strings', () => require('./strings'));
49
+ lazy(exports, 'flags', () => require('./flags'));
50
+ lazy(exports, 'obj', () => require('./obj'));
51
+ lazy(exports, 'console', () => require('./console'));
52
+ lazy(exports, 'traits', () => require('./traits'));
53
+ lazy(exports, 'opt', () => require('./opt'), {def:{autoDesc: false}});
176
54
  lazy(exports, 'Enum', () => require('./enum'));
177
-
178
- /**
179
- * Make an object support the *Observable* API «Lazy»
180
- * @name module:@lumjs/core.observable
181
- * @function
182
- * @see module:@lumjs/core/observable
183
- */
184
55
  lazy(exports, 'observable', () => require('./observable'));
185
-
186
- /**
187
- * The Events module «Lazy»
188
- * @name module:@lumjs/core.events
189
- * @see module:@lumjs/core/events
190
- */
191
56
  lazy(exports, 'events', () => require('./events'));
package/lib/meta.js CHANGED
@@ -135,7 +135,11 @@ function NYI(fatal=true, prefix='')
135
135
  exports.NYI = NYI;
136
136
 
137
137
  /**
138
- * Send a deprecation message with a stack trace.
138
+ * Send a deprecation message to the console.
139
+ *
140
+ * Will default to using `console.warn()` to send the message,
141
+ * unless `core.state.get('core.traceDeprecated')` evaluates to true,
142
+ * in which case it will use `console.trace()` instead.
139
143
  *
140
144
  * @param {string} dep - Name of what is deprecated
141
145
  * @param {(string|string[])} [rep] Replacement suggestion(s).
@@ -145,12 +149,15 @@ exports.NYI = NYI;
145
149
  */
146
150
  function deprecated(dep, rep, ret)
147
151
  {
152
+ const fn = require('./state').get('core.traceDeprecated')
153
+ ? 'trace'
154
+ : 'warn';
148
155
  const msgs = [dep,'is deprecated'];
149
156
  if (rep)
150
157
  {
151
158
  msgs.push('replace with', rep);
152
159
  }
153
- console.trace(...msgs);
160
+ console[fn](...msgs);
154
161
  return ret;
155
162
  }
156
163
 
package/lib/obj/cp.js CHANGED
@@ -440,7 +440,7 @@ function cpArgs(subject, ...sources)
440
440
  } // cpArgs()
441
441
 
442
442
  /**
443
- * Copy enumerable properties into a subject.
443
+ * Copy enumerable properties into a subject.
444
444
  *
445
445
  * This version overwrites any existing properties, with latter sources
446
446
  * taking precedence over earlier ones.
@@ -448,6 +448,18 @@ function cpArgs(subject, ...sources)
448
448
  * See {@link module:@lumjs/core/obj/cp.safe cp.safe()} for a version that
449
449
  * only copies non-existent properties.
450
450
  *
451
+ * **Note**: this is the actual `obj.cp` default export value!
452
+ *
453
+ * To keep JSDoc happy it is listed as a _named export_ inside
454
+ * the `cp` module, but in reality, it *is* the module.
455
+ *
456
+ * So when you do: `const cp = require('@lumjs/core/obj/cp');`
457
+ * or `const core = require('@lumjs/core), {cp} = core.obj;`
458
+ * the `cp` variable will be this function. All the other named
459
+ * exports are properties attached to the function object itself.
460
+ *
461
+ * The named export *does* exist as an alias to itself (`cp.cp = cp`).
462
+ *
451
463
  * @param {object} subject - Subject of the copy operation
452
464
  *
453
465
  * If this is a {@link module:@lumjs/core/obj/cp.HandlerSet HandlerSet},
@@ -475,20 +487,7 @@ function cpArgs(subject, ...sources)
475
487
  *
476
488
  * @returns {object} Either `subject` or a clone of `subject`.
477
489
  *
478
- * @alias module:@lumjs/core/obj/cp.cp
479
- *
480
- * **Note**: this is the actual `obj.cp` default export value!
481
- *
482
- * To keep JSDoc happy it is listed as a _named export_ inside
483
- * the `cp` module, but in reality, it *is* the module.
484
- *
485
- * So when you do: `const cp = require('@lumjs/core/obj/cp');`
486
- * or `const core = require('@lumjs/core), {cp} = core.obj;`
487
- * the `cp` variable will be this function. All the other named
488
- * exports are properties attached to the function object itself.
489
- *
490
- * The named export *does* exist as an alias to itself (`cp.cp = cp`).
491
- *
490
+ * @alias module:@lumjs/core/obj/cp.cp
492
491
  */
493
492
  function cp()
494
493
  {
package/lib/obj/ns.js CHANGED
@@ -2,7 +2,7 @@
2
2
  const
3
3
  {
4
4
  B, S,
5
- root, isObj, needObj, def, nonEmptyArray, notNil,
5
+ root, isObj, needObj, def, nonEmptyArray, isNil, notNil,
6
6
  doesDescriptorTemplate,
7
7
  } = require('../types');
8
8
 
@@ -152,20 +152,20 @@ function setObjectPath(obj, proppath, opts={})
152
152
  proppath = nsArray(proppath);
153
153
 
154
154
  let assign;
155
- if (doesDescriptorTemplate(opts.desc))
155
+ if (opts.assign)
156
+ { // Use direct property assignment.
157
+ assign = (o,p,v={}) => o[p] = v;
158
+ }
159
+ else if (doesDescriptorTemplate(opts.desc))
156
160
  { // An explicit descriptor.
157
161
  assign = (o,p,v={}) =>
158
162
  {
159
- const desc = clone(opts.desc);
163
+ const desc = Object.assign({}, opts.desc);
160
164
  desc.value = v;
161
165
  def(o,p,desc);
162
166
  }
163
167
  }
164
- else if (opts.assign)
165
- { // Use direct property assignment.
166
- assign = (o,p,v={}) => o[p] = v;
167
- }
168
- else
168
+ else
169
169
  { // Use def with default descriptor.
170
170
  assign = (o,p,v={}) => def(o,p,v);
171
171
  }
@@ -216,6 +216,41 @@ function setObjectPath(obj, proppath, opts={})
216
216
 
217
217
  exports.setObjectPath = setObjectPath;
218
218
 
219
+ /**
220
+ * Delete a nested property from an object.
221
+ *
222
+ * @param {(object|function)} obj - Object we're operating on.
223
+ * @param {(string|Array)} proppath - Property path we want to remove.
224
+ *
225
+ * Generally a string of dot (`.`) separated nested property names.
226
+ *
227
+ * The last property name in the path will be the one that is deleted
228
+ * from its parent object.
229
+ *
230
+ * @param {object} [opts] Options for `getObjectPath()`
231
+ * @returns {*} The value that was removed; or `undefined` if the
232
+ * proppath didn't resolve to a defined value.
233
+ */
234
+ function delObjectPath(obj, proppath, opts={})
235
+ {
236
+ proppath = nsArray(proppath);
237
+ const rmProp = proppath.pop();
238
+ const rmFrom = proppath.length
239
+ ? getObjectPath(obj, proppath, opts)
240
+ : obj;
241
+
242
+ if (isNil(rmFrom))
243
+ { // Nothing to remove from
244
+ return undefined;
245
+ }
246
+
247
+ const removed = rmFrom[rmProp];
248
+ delete rmFrom[rmProp];
249
+ return removed;
250
+ }
251
+
252
+ exports.delObjectPath = delObjectPath;
253
+
219
254
  /**
220
255
  * Get a global namespace path if it exists.
221
256
  *
package/lib/state.js CHANGED
@@ -2,15 +2,47 @@
2
2
 
3
3
  const {S,isObj} = require('./types')
4
4
  const ctx = require('./context')
5
+ const ns = require('./obj/ns');
6
+ const cp = require('./obj/cp');
5
7
  const stateSym = Symbol('@lumjs/core/state')
6
8
  const stateData = {[stateSym]: false}
7
9
  const stateKey = 'LUM_JS_STATE'
8
10
  const stateOpts = {}
9
11
 
10
- module.exports =
12
+ function getOpts(localOpts)
11
13
  {
12
- get(opts={})
14
+ return Object.assign({}, stateOpts, localOpts);
15
+ }
16
+
17
+ /**
18
+ * A simple persistent state helper object.
19
+ *
20
+ * Will use `localStorage` in a browser environment,
21
+ * and `process.env` if running in Node.js.
22
+ *
23
+ * In both cases the key `LUM_JS_STATE` is used,
24
+ * and the value must be valid JSON data.
25
+ *
26
+ * @exports module:@lumjs/core/state
27
+ */
28
+ exports = module.exports =
29
+ {
30
+ /**
31
+ * Load (retrieve) state data
32
+ * @param {object} [opts] Options
33
+ * @param {boolean} [opts.refresh=false] Refresh data from storage?
34
+ *
35
+ * If `true` this will force the data to be reloaded from storage.
36
+ * If `false` previously loaded data will be returned as is.
37
+ *
38
+ * @param {function} [opts.jsonRevive] JSON.parse() reviver
39
+ *
40
+ * @returns {object}
41
+ */
42
+ load(opts)
13
43
  {
44
+ opts = getOpts(opts);
45
+
14
46
  if (opts.refresh || !stateData[stateSym])
15
47
  {
16
48
  let json;
@@ -25,24 +57,44 @@ module.exports =
25
57
 
26
58
  if (typeof json === S)
27
59
  {
28
- const revive = opts.jsonRevive ?? stateOpts.jsonRevive;
60
+ const revive = opts.jsonRevive;
29
61
  const storedData = JSON.parse(json, revive);
30
62
  if (isObj(storedData))
31
63
  {
32
64
  Object.assign(stateData, storedData);
33
- stateData[stateSym] = true;
34
65
  }
35
66
  }
67
+
68
+ stateData[stateSym] = true;
36
69
  }
70
+
37
71
  return stateData;
38
72
  },
39
73
 
40
- save(opts={})
74
+ /**
75
+ * Save current state data into persistent storage.
76
+ *
77
+ * In a web browser environment, this will save the data
78
+ * into the `localStorage` global object directly.
79
+ *
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.)
85
+ *
86
+ * @param {object} [opts] Options
87
+ * @param {function} [opts.jsonReplace] JSON.stringify() replacer
88
+ * @returns {string} The JSON data
89
+ */
90
+ save(opts)
41
91
  {
42
92
  if (!stateData[stateSym]) return false;
93
+
94
+ opts = getOpts(opts);
43
95
 
44
- const replace = opts.jsonReplace ?? stateOpts.jsonReplace;
45
- const json = JSON.stringify(stateData, replace);
96
+ const replace = opts.jsonReplace;
97
+ let json = JSON.stringify(stateData, replace);
46
98
 
47
99
  if (ctx.isBrowser)
48
100
  {
@@ -50,10 +102,71 @@ module.exports =
50
102
  }
51
103
  else if (ctx.isNode)
52
104
  {
53
- console.log("export ", stateKey, "=", json);
105
+ process.env[stateKey] = json;
106
+ console.log(`${stateKey}='${json.replaceAll("'", "'\"'\"'")}'`);
54
107
  }
108
+
109
+ return json;
110
+ },
111
+
112
+ /**
113
+ * Get a property value from the state data via a namespace path
114
+ * @param {(string|string[])} path - Namespace path to get
115
+ * @param {object} [opts] Options for `load()` and `getObjectPath()`
116
+ * @returns {*} The property value (or `undefined` if not found)
117
+ * @see module:@lumjs/core/obj.getObjectPath
118
+ */
119
+ get(path, opts)
120
+ {
121
+ const data = exports.load(opts);
122
+ return ns.getObjectPath(data, path, opts);
123
+ },
124
+
125
+ /**
126
+ * Set a (nested) property value in the state data
127
+ * @param {(string|string[])} path - Namespace path to set
128
+ * @param {*} value - Value to set the property to
129
+ * @param {object} [opts] Options for `load()` and `setObjectPath()`
130
+ *
131
+ * The call to `setObjectPath()` has a few different defaults here:
132
+ * - `opts.assign` defaults to `true`
133
+ * - `opts.overwrite` defaults to `true`
134
+ * - `opts.value` is **forced** to the `value` argument
135
+ *
136
+ * @returns {*}
137
+ * @see module:@lumjs/core/obj.setObjectPath
138
+ */
139
+ set(path, value, opts)
140
+ {
141
+ const loadOpts = cp({refresh: false}, opts);
142
+ const setOpts = cp({assign: true, overwrite: true}, opts, {value});
143
+ const data = exports.load(loadOpts);
144
+ return ns.setObjectPath(data, path, setOpts);
145
+ },
146
+
147
+ /**
148
+ * Get a property value from the state data via a namespace path
149
+ * @param {(string|string[])} path - Namespace path to delete
150
+ * @param {object} [opts] Options for `load()` and `delObjectPath()`
151
+ * @returns {*} The property value (or `undefined` if not found)
152
+ * @see module:@lumjs/core/obj.delObjectPath
153
+ */
154
+ del(path, opts)
155
+ {
156
+ const data = exports.load(cp({refresh: false}, opts));
157
+ return ns.delObjectPath(data, path, opts);
55
158
  },
56
159
 
160
+ /**
161
+ * Default options for `load()` and `save()` methods.
162
+ *
163
+ * Any of the options supported by those methods can be set here.
164
+ *
165
+ * The `refresh` option will be ignored by the `set()` and `del()`
166
+ * methods if it's specified here.
167
+ *
168
+ * @type {object}
169
+ */
57
170
  opts: stateOpts,
58
171
 
59
172
  [stateSym]: stateData,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lumjs/core",
3
- "version": "1.31.1",
3
+ "version": "1.33.0",
4
4
  "main": "lib/index.js",
5
5
  "exports":
6
6
  {
@@ -19,6 +19,7 @@
19
19
  "./observable": "./lib/observable.js",
20
20
  "./opt": "./lib/opt/index.js",
21
21
  "./opt/args": "./lib/opt/args.js",
22
+ "./state": "./lib/state.js",
22
23
  "./strings": "./lib/strings.js",
23
24
  "./traits": "./lib/traits.js",
24
25
  "./types": "./lib/types/index.js",
@@ -40,7 +41,8 @@
40
41
  "test": "lumtest.js",
41
42
  "build-meta": "lum-build",
42
43
  "build-jsdoc": "jsdoc -c ./jsdoc.js",
43
- "build-docs": "npm run build-meta && npm run build-jsdoc"
44
+ "build-docs": "npm run build-meta && npm run build-jsdoc",
45
+ "build": "npm run build-docs"
44
46
  },
45
47
  "repository":
46
48
  {