@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/meta.js CHANGED
@@ -4,6 +4,12 @@
4
4
  */
5
5
  'use strict';
6
6
 
7
+ const { getOwnKeys, keyName } = require('./obj/keys');
8
+ const { isIterable, isObj } = require('./types/basics');
9
+
10
+ const cp = Object.assign;
11
+ const dp = Object.defineProperty;
12
+
7
13
  /**
8
14
  * Get a stacktrace.
9
15
  *
@@ -17,13 +23,10 @@
17
23
  * @returns {string[]} An array of stack strings.
18
24
  * @alias module:@lumjs/core/meta.stacktrace
19
25
  */
20
- function stacktrace(msg)
21
- {
26
+ function stacktrace(msg) {
22
27
  return (new Error(msg)).stack.split("\n");
23
28
  }
24
29
 
25
- exports.stacktrace = stacktrace;
26
-
27
30
  /**
28
31
  * An Error that can be thrown from abstract methods.
29
32
  *
@@ -54,8 +57,7 @@ exports.stacktrace = stacktrace;
54
57
  *
55
58
  * @alias module:@lumjs/core/meta.AbstractError
56
59
  */
57
- class AbstractError extends Error
58
- {
60
+ class AbstractError extends Error {
59
61
  /**
60
62
  * Construct an AbstractError
61
63
  *
@@ -69,8 +71,7 @@ class AbstractError extends Error
69
71
  * error message to use 'getter' instead of 'method'.
70
72
  *
71
73
  */
72
- constructor(name, getter=false)
73
- {
74
+ constructor(name, getter = false) {
74
75
  let msg = "Abstract ";
75
76
  msg += (getter ? 'getter ' : 'method ');
76
77
  if (name) msg += `'${name}' `;
@@ -80,8 +81,6 @@ class AbstractError extends Error
80
81
  }
81
82
  }
82
83
 
83
- exports.AbstractError = AbstractError;
84
-
85
84
  /**
86
85
  * Function prototypes for async, generator, and async generator functions.
87
86
  * @alias module:@lumjs/core/meta.Functions
@@ -91,22 +90,20 @@ const Functions =
91
90
  /**
92
91
  * Constructor for dynamic generator functions.
93
92
  */
94
- Generator: Object.getPrototypeOf(function*(){}).constructor,
93
+ Generator: Object.getPrototypeOf(function* () { }).constructor,
95
94
 
96
95
  /**
97
96
  * Constructor for dynamic async functions.
98
97
  */
99
- Async: Object.getPrototypeOf(async function(){}).constructor,
98
+ Async: Object.getPrototypeOf(async function () { }).constructor,
100
99
 
101
100
  /**
102
101
  * Constructor for dynamic async generator functions.
103
102
  */
104
- AsyncGenerator: Object.getPrototypeOf(async function*(){}).constructor,
103
+ AsyncGenerator: Object.getPrototypeOf(async function* () { }).constructor,
105
104
 
106
105
  }
107
106
 
108
- exports.Functions = Functions;
109
-
110
107
  /**
111
108
  * A placeholder function for when something is not implemented.
112
109
  *
@@ -117,54 +114,134 @@ exports.Functions = Functions;
117
114
  * @returns {void}
118
115
  * @alias module:@lumjs/core/meta.NYI
119
116
  */
120
- function NYI(fatal=true, prefix='')
121
- {
122
- const msg = prefix+"« NOT YET IMPLEMENTED »";
117
+ function NYI(fatal = true, prefix = '') {
118
+ const msg = prefix + "« NOT YET IMPLEMENTED »";
123
119
  if (fatal)
124
120
  throw new Error(msg);
125
121
  else
126
122
  console.error(msg);
127
123
  }
128
124
 
129
- exports.NYI = NYI;
125
+ function defaultDeprecateItemHandler(item) {
126
+ let { depVar } = this.opts;
127
+ if (depVar && typeof item === 'string') {
128
+ return item.trim().replaceAll(depVar, this.dep);
129
+ }
130
+ else if (this.setOpts && isObj(item)) {
131
+ cp(this.opts, item);
132
+ return this.next = true;
133
+ }
134
+ return item;
135
+ }
136
+
137
+ const DEP_OPTS =
138
+ {
139
+ depVar: '{DEP}',
140
+ handle: defaultDeprecateItemHandler,
141
+ preDep: '[DEPRECATED]:',
142
+ }
130
143
 
131
144
  /**
132
- * Send a deprecation message to the console.
145
+ * Send a deprecation message to the console (low-level).
133
146
  *
134
- * Will default to using `console.warn()` to send the message,
135
- * unless `core.state.get('core.traceDeprecated')` evaluates to true,
136
- * in which case it will use `console.trace()` instead.
147
+ * Will default to using `console.warn()` to send the message, however
148
+ * it can be made to use `console.trace()` instead if one of the following
149
+ * evaluates to a true value:
150
+ * - `core.env.get('LUM_TRACE_DEPRECATED')`
151
+ * - `core.state.get('core.traceDeprecated')`
152
+ * As the state module is deprecated, as of 2.x only the env.get() test
153
+ * will continue to work.
137
154
  *
138
- * @param {string} dep - Name of what is deprecated
139
- * @param {(string|string[])} [rep] Replacement suggestion(s).
140
- * @param {*} [ret] Value to return
141
- * @returns {mixed} `ret`
142
- * @alias module:@lumjs/core/meta.deprecated
155
+ * @param {string} dep - Name of what is deprecated.
156
+ * @param {...mixed} [info] Additional information.
157
+ *
158
+ * This may be used to suggest replacements, or whatever other
159
+ * information you want to include in the logs.
160
+ *
161
+ * TODO: document how functions are handled.
143
162
  */
144
- function deprecated(dep, rep, ret)
145
- {
146
- const fn = require('./state').get('core.traceDeprecated')
147
- ? 'trace'
163
+ function deprecate(dep, ...info) {
164
+
165
+ let stateLib = require('./state');
166
+ let fn = stateLib.eors('LUM_TRACE_DEPRECATED', 'core.traceDeprecated')
167
+ ? 'trace'
148
168
  : 'warn';
149
- const msgs = [dep,'is deprecated'];
150
- if (rep)
151
- {
152
- msgs.push('replace with', rep);
169
+
170
+ let ctx = {
171
+ add: true,
172
+ dep,
173
+ done: false,
174
+ info,
175
+ logs: [],
176
+ next: false,
177
+ opts: cp({showDep: dep}, DEP_OPTS),
178
+ setOpts: true,
179
+ }
180
+
181
+ for (let item of info) {
182
+ if (typeof item === 'function') {
183
+ item.call(ctx, ctx);
184
+ }
185
+ else {
186
+ if (typeof ctx.opts.handle === 'function') {
187
+ ctx.next = false;
188
+ item = ctx.opts.handle.call(ctx, item, ctx);
189
+ if (ctx.next) continue; // Move on.
190
+ }
191
+
192
+ if (ctx.add) {
193
+ if (ctx.logs.length === 0 && ctx.opts.preDep) {
194
+ ctx.logs.push(ctx.opts.preDep);
195
+ }
196
+ ctx.logs.push(ctx.opts.showDep || ctx.dep || dep);
197
+ ctx.add = false;
198
+ }
199
+
200
+ if (item) {
201
+ ctx.logs.push(item);
202
+ }
203
+ }
204
+ if (ctx.done) break; // No more items, we're done.
153
205
  }
154
- console[fn](...msgs);
155
- return ret;
206
+
207
+ console[fn](...ctx.logs);
208
+ return ctx.return ?? ctx.opts.return;
156
209
  }
157
210
 
158
- exports.deprecated = deprecated;
211
+ const DED_OPTS =
212
+ {
213
+ preRep: 'replace with',
214
+ }
215
+
216
+ /**
217
+ * Send a deprecation message to the console (simple).
218
+ *
219
+ * This is now a frontend to the newer `deprecate()` function,
220
+ * which may replace it entirely one day.
221
+ *
222
+ * @param {string} dep - Name of what is deprecated.
223
+ * @param {?(string|string[])} [rep] Replacement suggestion(s).
224
+ * @param {mixed} [ret] Value to return
225
+ * @param {object} [opts] Formatting options.
226
+ *
227
+ * @returns {mixed} `ret`
228
+ * @alias module:@lumjs/core/meta.deprecated
229
+ */
230
+ function deprecated(dep, rep, ret, opts) {
231
+ opts = cp({}, DED_OPTS, opts);
232
+ rep = Array.isArray(rep) ? rep.slice(0) : [rep];
233
+ if (opts.preRep) rep.unshift(opts.preRep);
234
+ return deprecate(dep, opts, ...rep);
235
+ }
159
236
 
160
237
  /**
161
238
  * Assign a getter property that when accessed will
162
239
  * show a deprecation message via `deprecated()` function
163
240
  * before returning the deprecated property value.
164
241
  *
165
- * @param {object} obj - Target to assign property on
166
- * @param {string} prop - Property to assign
167
- * @param {(object|function)} spec - Specification
242
+ * @param {(object|function)} obj - Target to assign property on.
243
+ * @param {(string|symbol)} prop - Property to assign.
244
+ * @param {(object|function)} spec - Specification.
168
245
  *
169
246
  * If this is a `function` it will be used as the `spec.get` value.
170
247
  *
@@ -172,29 +249,154 @@ exports.deprecated = deprecated;
172
249
  * @param {string} [spec.dep=prop] Name of what is deprecated;
173
250
  * defaults to `prop` if omitted.
174
251
  * @param {(string|string[])} [spec.rep] Replacement suggestion(s).
175
- * @param {object} [spec.opts] Options for `def()` to add getter with.
176
- *
177
252
  * @returns {object} `obj`
178
253
  *
179
254
  * @alias module:@lumjs/core/meta.wrapDepr
180
255
  */
181
- function wrapDepr(obj,prop,spec)
182
- {
256
+ function wrapDepr(obj, prop, spec) {
183
257
  if (typeof spec === 'function')
184
- spec = {get: spec};
185
- if (typeof spec.get !== 'function')
258
+ spec = { get: spec };
259
+ if (typeof spec.get !== 'function')
186
260
  throw new TypeError("invalid init");
187
261
 
188
- return df(obj, prop,
189
- {
190
- get: () =>
191
- deprecated(spec.dep??prop, spec.rep, spec.get())
192
- }, spec.opts);
262
+ let getter;
263
+ if (spec.rep) { // Original style using deprecated() function.
264
+ getter = () =>
265
+ deprecated(spec.dep ?? prop, spec.rep, spec.get(), spec)
266
+ }
267
+ else {
268
+ getter = () => {
269
+ let info = Array.isArray(spec.info) ? spec.info.slice(0) : [];
270
+ return deprecate(spec.dep ?? prop, {return: spec.get()}, spec, ...info);
271
+ }
272
+ }
273
+
274
+ return dp(obj, prop, {
275
+ configurable: true,
276
+ get: getter,
277
+ });
193
278
  }
194
279
 
195
- exports.wrapDepr = wrapDepr;
280
+ wrapDepr(exports, 'AbstractClass', () => require('./old/abstractclass'));
196
281
 
197
- // This is near the bottom, but before any calls to wrapDepr.
198
- const {df} = require('./obj/df');
282
+ const FUN_RESERVED = new Set(['prototype', 'length', 'name']);
283
+ const FUN_FILTER = (key) => !FUN_RESERVED.has(key);
199
284
 
200
- wrapDepr(exports, 'AbstractClass', () => require('./old/abstractclass'));
285
+ /**
286
+ * Deprecate an entire module.
287
+ *
288
+ * This will use `getOwnKeys()` to get a list of properties in a module,
289
+ * and will create a wrapped version that will use `wrapDepr()` for each
290
+ * of the properties.
291
+ *
292
+ * @param {(object|function)} src - The exported module.
293
+ *
294
+ * If this is a function, a wrapped version that calls deprecated()
295
+ * before passing all arguments will be created. It will also use
296
+ * the name of the function as a default `basespec.dep` value.
297
+ *
298
+ * @param {(object|string)} [basespec] Base specification.
299
+ *
300
+ * If this is a string it will be used as the `basespec.dep` option.
301
+ *
302
+ * Properties in this will be used as the defaults for the `spec` for each
303
+ * property in the `src` when calling the `wrapDepr()` function, with the
304
+ * exception of `dep` (see below for how it is handled), and `get` which
305
+ * is generated automatically for each property.
306
+ *
307
+ * @param {string} [basespec.dep] The name of the module being deprecated.
308
+ *
309
+ * If this is specified as a non-empty string then the spec for each property
310
+ * will use `{dep: basespec.dep+sep+key}`; otherwise it will use `key` alone.
311
+ *
312
+ * @param {function} [basespec.filter] Filter the properties.
313
+ *
314
+ * If this is specified it will be passed to `keys.filter()` to filter
315
+ * the keys that will actually be created in the destination object.
316
+ *
317
+ * If `src` is a *function*, and this is NOT specified, then a *default*
318
+ * will be used that excludes `[prototype, length, name]` from the keys.
319
+ *
320
+ * There is NO default when `src` is an *object*.
321
+ *
322
+ * @param {string} [basespec.sep] Value to use as `sep` (see `basespec.dep`).
323
+ *
324
+ * The default is a single dot (`.`) character.
325
+ *
326
+ * @param {object} [dest] Object to add wrapped properties to.
327
+ *
328
+ * This is only applicable if `src` is an *object*.
329
+ * It will NOT be used when `src` is a *function*.
330
+ * If not specified, an empty object will be created.
331
+ *
332
+ * @returns {(object|function)} Return value depends on the type of `src`.
333
+ */
334
+ function deprecateAll(src, basespec, dest = {}) {
335
+ if (typeof basespec === 'string') {
336
+ basespec = cp({ dep: basespec }, DED_OPTS);
337
+ }
338
+ else {
339
+ basespec = cp({}, DED_OPTS, basespec);
340
+ }
341
+
342
+ let hasName = () =>
343
+ (typeof basespec.dep === 'string' && basespec.dep.trim() !== '');
344
+
345
+ let getInfo = (spec) => {
346
+ let info = Array.isArray(spec.info)
347
+ ? spec.info.slice(0)
348
+ : [];
349
+ if (spec.rep) {
350
+ info.push(spec.rep);
351
+ delete spec.rep;
352
+ }
353
+ return info;
354
+ }
355
+
356
+ let keys = getOwnKeys(src);
357
+ let sep = basespec.sep ?? '.';
358
+
359
+ if (typeof src === 'function') {
360
+ if (!hasName()) basespec.dep = src.name;
361
+ if (!basespec.filter) basespec.filter = FUN_FILTER;
362
+
363
+ dest = function () {
364
+ let fun = {src, arguments};
365
+ let spec = cp({}, basespec, {fun});
366
+ let info = getInfo(spec);
367
+ deprecate(spec.dep, spec, ...info);
368
+ return src.apply(this, arguments);
369
+ }
370
+ }
371
+
372
+ if (typeof basespec.filter === 'function') {
373
+ keys = keys.filter(basespec.filter);
374
+ }
375
+
376
+ hasName = hasName(); // finalise
377
+
378
+ for (let key of keys) {
379
+ //let dep = hasName ? basespec.dep + sep + keyName(key) : key;
380
+ let spec = cp({}, basespec, {
381
+ dep: key,
382
+ get: () => src[key],
383
+ });
384
+
385
+ if (hasName) {
386
+ spec.info = getInfo(spec);
387
+ if (basespec.preRep) spec.info.unshift(basespec.preRep);
388
+ spec.showDep = basespec.dep + sep + keyName(key);
389
+ }
390
+
391
+ wrapDepr(dest, key, spec);
392
+ }
393
+
394
+ return dest;
395
+ }
396
+
397
+ cp(deprecateAll, {FUN_FILTER, FUN_RESERVED});
398
+
399
+ exports = module.exports = {
400
+ AbstractError, deprecate, deprecated, deprecateAll, Functions,
401
+ NYI, stacktrace, wrapDepr,
402
+ }
package/lib/obj/apply.js CHANGED
@@ -1,5 +1,3 @@
1
- const {F} = require('../types');
2
-
3
1
  /**
4
2
  * ApplyInfo objects will have a property with this Symbol
5
3
  * as its key and boolean `true` as its value.
@@ -208,7 +206,7 @@ function apply(target, ...values)
208
206
 
209
207
  for (let value of values)
210
208
  {
211
- if (typeof value === F)
209
+ if (typeof value === 'function')
212
210
  {
213
211
  value.apply(opts.ctx, opts.args);
214
212
  }
@@ -0,0 +1,12 @@
1
+ 'use strict';
2
+
3
+ const getProperty = require('./getproperty.js');
4
+ const getPrototypeOf = require('./getprotos.js');
5
+ const { getOwnKeys, isEmptyObject, keyName } = require('./keys.js');
6
+ const ownCount = require('./owncount.js');
7
+ const unlocked = require('./unlocked.js');
8
+
9
+ module.exports = {
10
+ getOwnKeys, getProperty, getPrototypeOf, keyName, isEmptyObject,
11
+ ownCount, unlocked,
12
+ }
package/lib/obj/cp.js CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Object property copying utilities.
3
3
  *
4
- * NOTE: This now just re-exports the `@lumjs/cp` package.
4
+ * NOTE: This now just re-exports the `@lumjs/cp/obj` package.
5
5
  *
6
6
  * @module @lumjs/core/obj/cp
7
7
  */
package/lib/obj/df.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
 
3
- const {B,F,S,doesDescriptor} = require('../types/basics');
3
+ const {doesDescriptor} = require('../types/basics');
4
4
  const {needObj,needType} = require('../types/needs');
5
5
  const TYPES = require('../types/typelist');
6
6
 
@@ -67,8 +67,8 @@ function descriptor(desc, opts)
67
67
 
68
68
  for (const dp of dps)
69
69
  {
70
- if (typeof opts[dp] === B
71
- && (force.includes(dp) || typeof desc[dp] !== B))
70
+ if (typeof opts[dp] === 'boolean'
71
+ && (force.includes(dp) || typeof desc[dp] !== 'boolean'))
72
72
  {
73
73
  desc[dp] = opts[dp];
74
74
  }
@@ -122,11 +122,11 @@ function df(obj, name, value, opts={})
122
122
  needObj(obj, {log, fun: true});
123
123
  needType(TYPES.PROP, name, {log});
124
124
 
125
- if (typeof opts === B)
125
+ if (typeof opts === 'boolean')
126
126
  {
127
127
  opts = {useDesc: opts};
128
128
  }
129
- else if (typeof opts === F && typeof value === F)
129
+ else if (typeof opts === 'function' && typeof value === 'function')
130
130
  {
131
131
  value = {get: value, set: opts};
132
132
  opts = {useDesc: true};
@@ -220,7 +220,7 @@ function dfor(obj, opts={}, prev=null)
220
220
  {
221
221
  const fnOne = function (name, value, setter)
222
222
  {
223
- if (typeof setter === F && typeof value === F)
223
+ if (typeof setter === 'function' && typeof value === 'function')
224
224
  {
225
225
  value = {get: value, set: setter}
226
226
  opts = clone(opts, {useDesc: true});
@@ -334,7 +334,7 @@ function lazy(target, name, initfunc, opts={})
334
334
  let log = {target, name, initfunc, opts};
335
335
  needObj(target, {log, fun: true});
336
336
  needType(TYPES.PROP, name, {log});
337
- needType(F, initfunc, {log});
337
+ needType('function', initfunc, {log});
338
338
  needObj(opts, {log});
339
339
 
340
340
  // The `this` object for the functions.
@@ -367,7 +367,7 @@ function lazy(target, name, initfunc, opts={})
367
367
  }
368
368
  else if (doesDescriptor(value))
369
369
  { // A descriptor was returned, extract the real value.
370
- if (typeof value.get === F)
370
+ if (typeof value.get === 'function')
371
371
  {
372
372
  return value.get();
373
373
  }
@@ -387,7 +387,7 @@ function lazy(target, name, initfunc, opts={})
387
387
  return defval(initfunc.call(ctx,ctx));
388
388
  }
389
389
 
390
- if (typeof opts.set === F)
390
+ if (typeof opts.set === 'function')
391
391
  { // We want to assign a lazy setter as well.
392
392
  desc.set = function(value)
393
393
  {
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
 
3
- const {isComplex,isObj} = require('../types');
3
+ const {isComplex,isObj} = require('../types/basics');
4
4
 
5
5
  /**
6
6
  * Get a property descriptor.
package/lib/obj/index.js CHANGED
@@ -5,20 +5,15 @@
5
5
 
6
6
  const {apply,ApplyInfo,ApplyOpts,ApplyWith} = require('./apply');
7
7
  const assignd = require('./assignd');
8
- const {copyAll,duplicateOne,duplicateAll} = require('./copyall');
9
- const copyProps = require('./copyprops');
10
- const {CLONE,clone,addClone,cloneIfLocked} = require('./clone');
11
8
  const {df,dfa,dfor,lazy} = require('./df');
12
9
  const {flip, flipKeyVal, flipMap} = require('./flip');
13
10
  const {getMethods,signatureOf,MethodFilter} = require('./getmethods');
14
11
  const getProperty = require('./getproperty');
15
12
  const getPrototypesOf = require('./getprotos');
16
- const {lock,addLock} = require('./lock');
17
- const {mergeNested,syncNested} = require('./merge');
18
13
  const ns = require('./ns');
19
- const cp = require('./cp');
20
14
  const unlocked = require('./unlocked');
21
15
  const ownCount = require('./owncount');
16
+ const {getOwnKeys,isEmptyObject} = require('./keys');
22
17
 
23
18
  const
24
19
  {
@@ -26,6 +21,11 @@ const
26
21
  getNamespace,setNamespace,nsFactory,
27
22
  } = ns;
28
23
 
24
+ // Deprecated functions; TODO: remove in 2.0
25
+ const NSO = '@lumjs/core/obj';
26
+ const { deprecateAll } = require('../meta');
27
+ const cp = deprecateAll(require('./cp'), NSO+'/cp');
28
+
29
29
  /**
30
30
  * Create a new object with a `null` prototype.
31
31
  *
@@ -43,13 +43,17 @@ const
43
43
  const discrete = (props) => Object.create(null, props);
44
44
 
45
45
  // TODO: reorder the following alphabetically, for sanity sake...
46
- module.exports =
46
+ exports = module.exports =
47
47
  {
48
- assignd, cp, CLONE, clone, addClone, cloneIfLocked, lock, addLock,
49
- df, dfa, dfor, lazy, discrete,
50
- mergeNested, syncNested, copyProps, copyAll, ns, nsFactory,
51
- getObjectPath, setObjectPath, delObjectPath, getNamespace, setNamespace,
52
- getProperty, duplicateAll, duplicateOne, getMethods, signatureOf,
53
- MethodFilter, flip, flipKeyVal, flipMap, unlocked, getPrototypesOf,
54
- apply, ApplyInfo, ApplyOpts, ApplyWith, ownCount,
48
+ cp, // TODO: remove this line in 2.0
49
+
50
+ apply, ApplyInfo, ApplyOpts, ApplyWith, assignd,
51
+ delObjectPath, df, dfa, dfor, discrete, flip, flipKeyVal, flipMap,
52
+ getMethods, getNamespace, getObjectPath, getOwnKeys, getProperty,
53
+ getPrototypesOf, isEmptyObject, lazy, MethodFilter, ns, nsFactory,
54
+ ownCount, setNamespace, setObjectPath, signatureOf, unlocked,
55
55
  }
56
+
57
+ // Deprecated collections; TODO: remove in 2.0
58
+ deprecateAll(require('@lumjs/cp/fun'), NSO, exports);
59
+ deprecateAll(require('@lumjs/cp/util'), NSO, exports);
@@ -0,0 +1,55 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Get both string and symbol keys from an object.
5
+ * @param {object} obj - Target object.
6
+ * @returns {(string|symbol)[]}
7
+ */
8
+ const getOwnKeys = obj => [
9
+ ...(Object.getOwnPropertyNames(obj)),
10
+ ...(Object.getOwnPropertySymbols(obj)),
11
+ ];
12
+
13
+ /**
14
+ * See if an object has its own properties.
15
+ */
16
+ const isEmptyObject = obj => getOwnKeys(obj).length === 0;
17
+
18
+ /**
19
+ * Get the name of a property key.
20
+ * @param {(string|symbol)} key - Property key.
21
+ *
22
+ * If this is a string it will be returned as-is.
23
+ * If this is a symbol then how it is formatted depends
24
+ * on the options specified.
25
+ *
26
+ * @param {object} [opts] Options for handling symbol property keys.
27
+ * @param {string} [opts.prefix] Prefix character; default: `[`.
28
+ * @param {string} [opts.suffix] Suffix character; default: `]`.
29
+ * @param {boolean} [opts.toStr=false] Use toString method?
30
+ *
31
+ * If this is false (default), then the return value will be the
32
+ * `key.description` value with the prefix and suffix values applied.
33
+ *
34
+ * If this is true then the prefix/suffix options are ignored,
35
+ * and `key.toString()` will be used as the return value.
36
+ *
37
+ * @returns {string}
38
+ */
39
+ function keyName(key, opts={}) {
40
+ if (typeof key === 'string') return key;
41
+ if (typeof key === 'symbol') {
42
+ if (opts.toStr) {
43
+ return key.toString();
44
+ }
45
+
46
+ let pf = opts.prefix ?? '[';
47
+ let sf = opts.suffix ?? ']';
48
+ return pf+key.description+sf;
49
+ }
50
+
51
+ console.error({key, opts});
52
+ throw new TypeError('Invalid key');
53
+ }
54
+
55
+ module.exports = {getOwnKeys, isEmptyObject, keyName}