@lumjs/core 1.0.0-beta.1 → 1.0.0-beta.6

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.
Files changed (45) hide show
  1. package/README.md +10 -2
  2. package/TODO.md +12 -4
  3. package/docs/changelogs/1.0-beta.md +99 -0
  4. package/{CHANGELOG.md → docs/changelogs/1.x.md} +7 -7
  5. package/jsdoc.json +33 -0
  6. package/lib/arrays.js +78 -0
  7. package/lib/context.js +86 -0
  8. package/{src → lib}/enum.js +30 -13
  9. package/{src → lib}/flags.js +6 -0
  10. package/lib/index.js +131 -0
  11. package/{src → lib}/lazy.js +33 -35
  12. package/{src → lib}/meta.js +38 -1
  13. package/lib/modules.js +113 -0
  14. package/lib/obj/clone.js +220 -0
  15. package/{src → lib}/obj/copyall.js +3 -0
  16. package/{src → lib}/obj/copyprops.js +1 -0
  17. package/lib/obj/index.js +23 -0
  18. package/{src → lib}/obj/lock.js +16 -13
  19. package/{src → lib}/obj/merge.js +2 -0
  20. package/{src → lib}/obj/ns.js +45 -8
  21. package/{src → lib}/objectid.js +2 -3
  22. package/{src → lib}/observable.js +23 -8
  23. package/{src → lib}/opt.js +6 -1
  24. package/lib/strings.js +192 -0
  25. package/lib/types/basics.js +154 -0
  26. package/lib/types/def.js +183 -0
  27. package/lib/types/index.js +55 -0
  28. package/lib/types/isa.js +186 -0
  29. package/lib/types/js.js +12 -0
  30. package/lib/types/needs.js +117 -0
  31. package/lib/types/root.js +92 -0
  32. package/lib/types/stringify.js +98 -0
  33. package/lib/types/typelist.js +168 -0
  34. package/package.json +26 -3
  35. package/test/arrays.js +19 -0
  36. package/test/meta.js +17 -0
  37. package/test/types.js +106 -23
  38. package/index.js +0 -64
  39. package/src/context.js +0 -47
  40. package/src/descriptors.js +0 -243
  41. package/src/obj/clone.js +0 -201
  42. package/src/obj/index.js +0 -18
  43. package/src/prop.js +0 -170
  44. package/src/strings.js +0 -76
  45. package/src/types.js +0 -545
@@ -1,24 +1,25 @@
1
1
 
2
- const {S,F,isComplex} = require('./types');
3
- const prop = require('./prop');
4
- const {DESC} = require('./descriptors');
2
+ const {S,F,TYPES:{COMPLEX},needType,needObj,def} = require('./types');
3
+
4
+ /**
5
+ * @callback module:@lumjs/core~InitFunc
6
+ * @param {string} name - The `name` parameter passed to `lazy()`
7
+ * @this {object} - The `obj` parameter passed to `lazy()`
8
+ */
5
9
 
6
10
  /**
7
11
  * Build a lazy initializer property.
8
12
  *
9
13
  * Basically the first time the property is accessed it's built.
10
14
  * Subsequent accesses will use the already built property.
11
- * This is an extension of the {@link prop} method, and indeed an
12
- * alias called `prop.lazy()` is also available.
15
+ * This is an extension of the {@link def} method, and indeed an
16
+ * alias called `def.lazy()` is also available.
13
17
  *
14
- * @param {object} obj - The object to add the property to.
18
+ * @param {Object} obj - The object to add the property to.
15
19
  * @param {string} name - The name of the property to add.
16
- * @param {function} initfunc - The function to initialize the property.
17
- *
18
- * This function will have `this` set to the `obj` parameter.
19
- * It will also be passed `name` as the sole parameter.
20
- *
21
- * @param {mixed} [onset] How to handle assignment.
20
+ * @param {module:@lumjs/core~InitFunc} initfunc
21
+ * The function to initialize the property.
22
+ * @param {*} [onset] How to handle assignment.
22
23
  *
23
24
  * If this is `true` then the new value will be assigned directly,
24
25
  * skipping the initialization process entirely.
@@ -36,29 +37,26 @@ const {DESC} = require('./descriptors');
36
37
  *
37
38
  * If this is anything else, assignment will do nothing at all.
38
39
  *
39
- * @param {object} [desc=DESC.CONF] The Descriptor for the property.
40
+ * @param {Object} [desc={}] Descriptor rules for the property.
41
+ * We only support two descriptor rules with this function, and
42
+ * their default values are the same as the `def()` function.
43
+ * - `configurable` → `true`
44
+ * - `enumerable` → `false`
45
+ * Any other descriptor properties are invalid here.
40
46
  *
41
- * @return {object} The object we defined the property on.
47
+ * @return {Object} The object we defined the property on.
48
+ * @alias module:@lumjs/core.lazy
42
49
  */
43
- function lazy(obj, name, initfunc, onset, desc=DESC.CONF)
50
+ function lazy(obj, name, initfunc, onset, desc={})
44
51
  {
45
- if (!isComplex(obj))
46
- {
47
- throw new TypeError("obj parameter was not an object");
48
- }
49
- if (typeof name !== S)
50
- {
51
- throw new TypeError("name parameter was not a string");
52
- }
53
- if (typeof initfunc !== F)
54
- {
55
- throw new TypeError("initfunc parameter was not a function");
56
- }
52
+ needType(COMPLEX, obj, 'obj must be an object');
53
+ needType(S, name, 'name must be a string');
54
+ needType(F, initfunc, 'initfunc must be a function');
55
+ needObj(desc, 'desc parameter was not an object');
57
56
 
58
57
  let value;
59
- let setter = null;
60
58
 
61
- function getter()
59
+ desc.get = function()
62
60
  {
63
61
  if (value === undefined)
64
62
  {
@@ -69,23 +67,23 @@ function lazy(obj, name, initfunc, onset, desc=DESC.CONF)
69
67
 
70
68
  if (onset === true)
71
69
  { // Allow direct assignment.
72
- setter = function(newval)
70
+ desc.set = function(newval)
73
71
  {
74
72
  value = newval;
75
73
  }
76
74
  }
77
75
  else if (onset === false)
78
76
  { // Throw an error on assignment.
79
- setter = function()
77
+ desc.set = function()
80
78
  {
81
79
  throw new ReferenceError("The "+name+" property is read-only");
82
80
  }
83
81
  }
84
82
  else if (typeof onset === F)
85
83
  { // A proxy method for assignment.
86
- setter = function(newval)
84
+ desc.set = function(newval)
87
85
  {
88
- const setval = onset.call(this, newval);
86
+ const setval = onset.call(this, newval, value);
89
87
  if (setval !== undefined)
90
88
  {
91
89
  value = setval;
@@ -93,10 +91,10 @@ function lazy(obj, name, initfunc, onset, desc=DESC.CONF)
93
91
  }
94
92
  }
95
93
 
96
- prop(obj, name, getter, setter, desc);
94
+ def(obj, name, desc);
97
95
  }
98
96
 
99
97
  // Gotta be one of the greatest lines...
100
- prop(prop, 'lazy', lazy);
98
+ def(def, 'lazy', lazy);
101
99
 
102
100
  module.exports = lazy;
@@ -1,7 +1,20 @@
1
-
1
+ /**
2
+ * Meta-programming helpers.
3
+ * @module @lumjs/core/meta
4
+ */
2
5
 
3
6
  /**
4
7
  * Get a stacktrace. Differs from browser to browser.
8
+ *
9
+ * Uses the `stack` property of an `Error` object as the source.
10
+ * This is a super simplistic hack. For a more complete solution, try
11
+ * the `stacktrace-js` library, which will be used in the new `@lumjs/debug`
12
+ * library as a dependency.
13
+ *
14
+ * @param {string} [msg] - A message for the Error object.
15
+ *
16
+ * @returns {string[]} An array of stack strings.
17
+ * @alias module:@lumjs/core/meta.stacktrace
5
18
  */
6
19
  function stacktrace(msg)
7
20
  {
@@ -12,6 +25,7 @@ exports.stacktrace = stacktrace;
12
25
 
13
26
  /**
14
27
  * Abstract classes for Javascript.
28
+ * @alias module:@lumjs/core/meta.AbstractClass
15
29
  */
16
30
  class AbstractClass
17
31
  {
@@ -38,6 +52,8 @@ exports.AbstractClass = AbstractClass;
38
52
 
39
53
  /**
40
54
  * Function prototypes for async, generator, and async generator functions.
55
+ * @namespace
56
+ * @alias module:@lumjs/core/meta.Functions
41
57
  */
42
58
  const Functions =
43
59
  {
@@ -59,3 +75,24 @@ const Functions =
59
75
  }
60
76
 
61
77
  exports.Functions = Functions;
78
+
79
+ /**
80
+ * A placeholder function for when something is not implemented.
81
+ *
82
+ * @param {boolean} [fatal=true] If `true` throw Error.
83
+ * If `false` use `console.error()` instead.
84
+ * @param {string} [prefix=''] A prefix for the error message.
85
+ *
86
+ * @returns {void}
87
+ * @alias module:@lumjs/core/meta.NYI
88
+ */
89
+ function NYI(fatal=true, prefix='')
90
+ {
91
+ const msg = prefix+"« NOT YET IMPLEMENTED »";
92
+ if (fatal)
93
+ throw new Error(msg);
94
+ else
95
+ console.error(msg);
96
+ }
97
+
98
+ exports.NYI = NYI;
package/lib/modules.js ADDED
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Module helpers.
3
+ * @module @lumjs/core/modules
4
+ */
5
+
6
+ const path = require('path');
7
+ const {S,isObj} = require('./types');
8
+ const replace = require('./strings').replaceItems;
9
+
10
+ /**
11
+ * Get the name of a module.
12
+ *
13
+ * @param {(object|string)} module - Either a module object, or filename.
14
+ * If it is an `object`, it should be a CommonJS `module` object.
15
+ * If it is a `string`, it should be the module filename.
16
+ * @param {object} [opts] Options.
17
+ *
18
+ * @param {boolean} [opts.useAuto=true] Enable automatic name cleaning.
19
+ * If *basename* mode was **not** used, then once all other rules have been
20
+ * applied, strip any leading `.` and `/` characters, and the file extension.
21
+ *
22
+ * @param {boolean} [opts.basename=false] Use `path.basename()`
23
+ * This will strip all parent directories, and the file extension.
24
+ * If no other rules are specified in the `opts`, then this will
25
+ * be applied automatically as a fallback method. If it is set to
26
+ * `true` explicitly, then it will be applied *before* any other options.
27
+ *
28
+ * @param {object} [opts.replace] Call {@link module:@lumjs/core/strings.replaceItems}
29
+ * This uses the default `useAll` values based on the `object` format.
30
+ * @param {object} [opts.replaceOne] `replace` but `useAll` set to `false`.
31
+ * @param {object} [opts.replaceAll] `replace` but `useAll` set to `true`.
32
+ * @param {(string|string[])} [opts.strip] Sub-strings to remove entirely.
33
+ * @returns {string} The *name* of a module as per the options set.
34
+ * @alias module:@lumjs/core/modules.name
35
+ */
36
+ function name(module, opts={})
37
+ {
38
+ let filename;
39
+
40
+ if (typeof module === S)
41
+ { // Going to assume a string is the filename.
42
+ filename = module;
43
+ }
44
+ else if (isObj(module) && typeof module.filename === S)
45
+ { // It's a CommonJS module context object.
46
+ filename = module.filename;
47
+ }
48
+ else
49
+ { // Sorry, we don't support that.
50
+ throw new TypeError("Unsupported module parameter");
51
+ }
52
+
53
+ const ext = path.extname(filename);
54
+
55
+ let useFallback = true;
56
+ let useAuto = opts.useAuto ?? true;
57
+
58
+ if (opts.basename)
59
+ { // We want to run the basename sequence first.
60
+ filename = path.basename(filename, ext);
61
+ useFallback = false;
62
+ useAuto = false;
63
+ }
64
+
65
+ if (isObj(opts.replace))
66
+ { // Replacements using replace() or replaceAll() based on parameter format.
67
+ useFallback = false;
68
+ filename = replace(filename, opts.replace);
69
+ }
70
+
71
+ if (isObj(opts.replaceOne))
72
+ { // Replacements using replace() regardless of parameter format.
73
+ useFallback = false;
74
+ filename = replace(filename, opts.replaceOne, false);
75
+ }
76
+
77
+ if (isObj(opts.replaceAll))
78
+ { // Replacements using replaceAll() regardless of parameter format.
79
+ useFallback = false;
80
+ filename = replace(filename, opts.replaceAll, true);
81
+ }
82
+
83
+ if (typeof opts.strip === S)
84
+ { // A prefix. This always uses replace(), never replaceAll().
85
+ filename = filename.replace(opts.strip, '');
86
+ useFallback = false;
87
+ }
88
+ else if (Array.isArray(opts.strip))
89
+ { // A list of strings or regexps to strip. Ditto on the use of replace().
90
+ for (const strip of opts.strip)
91
+ {
92
+ filename = filename.replace(strip, '');
93
+ }
94
+ useFallback = false;
95
+ }
96
+
97
+ if (useFallback)
98
+ { // We're going to use the basename, either as a fallback
99
+ filename = path.basename(filename, ext);
100
+ useAuto = false;
101
+ }
102
+
103
+ if (useAuto)
104
+ { // A few automatic rules that normally apply if the fallback was not used.
105
+ filename = filename
106
+ .replace(/^[./]+/, '')
107
+ .replace(RegExp(ext+'$'), '');
108
+ }
109
+
110
+ return filename;
111
+ }
112
+
113
+ exports.name = name;
@@ -0,0 +1,220 @@
1
+ // Import *most* required bits here.
2
+ const {B,N,F, isObj, isComplex, def} = require('../types');
3
+ const Enum = require('../enum');
4
+ const copyProps = require('./copyprops');
5
+
6
+ /**
7
+ * An enum of supported modes for the `clone` method.
8
+ *
9
+ * - **P** = All properties. If unchecked, enumerable properties only.
10
+ * - **A** = Uses `Array.slice()` shortcut for shallow Array cloning.
11
+ * - **R** = Recursive (deep) cloning of nested objects.
12
+ *
13
+ * | Mode | P | A | R | Notes |
14
+ * | ---- | - | - | - | ----- |
15
+ * | `CLONE.DEF` | × | ✓ | × | Default mode for cloning functions. |
16
+ * | `CLONE.DEEP` | × | × | ✓ | |
17
+ * | `CLONE.FULL` | ✓ | ✓ | × | |
18
+ * | `CLONE.ALL` | ✓ | × | × | |
19
+ * | `CLONE.ENTIRE` | ✓ | × | ✓ | |
20
+ * | `CLONE.JSON` | × | × | ✓ | Uses JSON, so no `function` or `symbol` support. |
21
+ *
22
+ * @alias module:@lumjs/core/obj.CLONE
23
+ */
24
+ const CLONE = Enum(['DEF','FULL','ALL','DEEP','ENTIRE','JSON']);
25
+
26
+ exports.CLONE = CLONE;
27
+
28
+ // A list of modes that should use the array.slice shallow shortcut.
29
+ const SLICE_ARRAYS = [CLONE.DEF, CLONE.FULL];
30
+
31
+ // A list of modes that should get *all* properties.
32
+ const ALL_PROPS = [CLONE.FULL, CLONE.ALL, CLONE.DEEP];
33
+
34
+ // A list of modes that should do recursive cloning of objects.
35
+ const RECURSIVE = [CLONE.DEEP, CLONE.ENTIRE];
36
+
37
+ /**
38
+ * Clone an object or function.
39
+ *
40
+ * @param {object|function} obj - The object we want to clone.
41
+ * @param {object} [opts={}] - Options for the cloning process.
42
+ *
43
+ * @param {number} [opts.mode=CLONE.DEF] - One of the `CLONE.*` enum values.
44
+ *
45
+ * Note: The `CLONE` enum is also aliased as `clone.MODE` as an alternative.
46
+ *
47
+ * @param {boolean} [opts.addClone=false] - Call `addClone()` on the cloned object.
48
+ *
49
+ * The options sent to this function will be used as the defaults in
50
+ * the `clone()` method added to the object.
51
+ *
52
+ * @param {boolean} [opts.addLock=false] - Call `addLock()` on the cloned object.
53
+ *
54
+ * No further options for this, just add a `lock()` method to the clone.
55
+ *
56
+ * @param {?object} [opts.copy] Call `copyProps()` on the cloned object.
57
+ *
58
+ * Will pass the original `obj` as the source to copy from.
59
+ * Will pass `opts.copy` as the options.
60
+ *
61
+ * @return {object} - The clone of the object.
62
+ * @alias module:@lumjs/core/obj.clone
63
+ */
64
+ function clone(obj, opts={})
65
+ {
66
+ //console.debug("clone()", obj, opts);
67
+
68
+ if (!isComplex(obj))
69
+ { // Doesn't need cloning.
70
+ //console.debug("no cloning required");
71
+ return obj;
72
+ }
73
+
74
+ if (!isObj(opts))
75
+ { // Opts has to be a valid object.
76
+ opts = {};
77
+ }
78
+
79
+ const mode = typeof opts.mode === N ? opts.mode : CLONE.DEF;
80
+ const reclone = typeof opts.addClone === B ? opts.addClone : false;
81
+ const relock = typeof opts.addLock === B ? opts.addLock : false;
82
+
83
+ let copy;
84
+
85
+ //console.debug("::clone", {mode, reclone, relock});
86
+
87
+ if (mode === CLONE.JSON)
88
+ { // Deep clone enumerable properties using JSON trickery.
89
+ //console.debug("::clone using JSON cloning");
90
+ copy = JSON.parse(JSON.stringify(obj));
91
+ }
92
+ else if (Array.isArray(obj) && SLICE_ARRAYS.includes(mode))
93
+ { // Make a shallow copy using slice.
94
+ //console.debug("::clone using Array.slice()");
95
+ copy = obj.slice();
96
+ }
97
+ else
98
+ { // Build a clone using a simple loop.
99
+ //console.debug("::clone using simple loop");
100
+ copy = {};
101
+
102
+ let props;
103
+ if (ALL_PROPS.includes(mode))
104
+ { // All object properties.
105
+ //console.debug("::clone getting all properties");
106
+ props = Object.getOwnPropertyNames(obj);
107
+ }
108
+ else
109
+ { // Enumerable properties.
110
+ //console.debug("::clone getting enumerable properties");
111
+ props = Object.keys(obj);
112
+ }
113
+
114
+ //console.debug("::clone[props]", props);
115
+
116
+ for (const prop of props)
117
+ {
118
+ let val = obj[prop];
119
+ if (isObj(val) && RECURSIVE.includes(mode))
120
+ { // Deep cloning.
121
+ val = clone(val, {mode});
122
+ }
123
+ copy[prop] = val;
124
+ }
125
+ }
126
+
127
+ if (reclone)
128
+ { // Add the clone() method to the clone, with the passed opts as defaults.
129
+ addClone(copy, opts);
130
+ }
131
+
132
+ if (opts.copy)
133
+ { // Pass the clone through the copyProps() function as well.
134
+ copyProps(obj, copy, opts.copy);
135
+ }
136
+
137
+ if (relock)
138
+ { // Add the lock() method to the clone.
139
+ addLock(copy, opts);
140
+ }
141
+
142
+ return copy;
143
+ }
144
+
145
+ // Alias the CLONE enum as clone.MODE
146
+ def(clone, 'MODE', CLONE);
147
+
148
+ // Export the clone here.
149
+ exports.clone = clone;
150
+
151
+ /**
152
+ * Add a clone() method to an object.
153
+ *
154
+ * @param {object|function} obj - The object to add clone() to.
155
+ * @param {object} [defOpts=null] Default options for the clone() method.
156
+ *
157
+ * If `null` or anything other than an object, the defaults will be:
158
+ *
159
+ * ```{mode: CLONE.DEF, addClone: true, addLock: false}```
160
+ *
161
+ * @alias module:@lumjs/core/obj.addClone
162
+ */
163
+ function addClone(obj, defOpts=null)
164
+ {
165
+ if (!isObj(defOpts))
166
+ { // Assign a default set of defaults.
167
+ defOpts = {mode: CLONE.DEF, addClone: true, addLock: false};
168
+ }
169
+
170
+ const defDesc = defOpts.cloneDesc ?? {};
171
+
172
+ defDesc.value = function (opts)
173
+ {
174
+ if (!isObj(opts))
175
+ opts = defOpts;
176
+ return clone(obj, opts);
177
+ }
178
+
179
+ return def(obj, 'clone', defDesc);
180
+ }
181
+
182
+ exports.addClone = addClone;
183
+
184
+ /**
185
+ * Clone an object if it's not extensible (locked, sealed, frozen, etc.)
186
+ *
187
+ * If the object is extensible, it's returned as is.
188
+ *
189
+ * If not, if the object has a `clone()` method it will be used.
190
+ * Otherwise use our `clone()` function.
191
+ *
192
+ * @param {object} obj - The object to clone if needed.
193
+ * @param {object} [opts] - Options to pass to `clone()` method.
194
+ *
195
+ * @return {object} - Either the original object, or an extensible clone.
196
+ *
197
+ * @alias module:@lumjs/core/obj.cloneIfLocked
198
+ */
199
+ function cloneIfLocked(obj, opts)
200
+ {
201
+ if (!Object.isExtensible(obj))
202
+ {
203
+ if (typeof obj.clone === F)
204
+ { // Use the object's clone() method.
205
+ return obj.clone(opts);
206
+ }
207
+ else
208
+ { // Use our own clone method.
209
+ return clone(obj, opts);
210
+ }
211
+ }
212
+
213
+ // Return the object itself, it's fine.
214
+ return obj;
215
+ }
216
+
217
+ exports.cloneIfLocked = cloneIfLocked;
218
+
219
+ // Import `addLock()` here *after* assigning the clone methods.
220
+ const {addLock} = require('./lock');
@@ -3,6 +3,8 @@
3
3
  *
4
4
  * It does no type checking, and has no qualms about overwriting properties.
5
5
  * You probably want something like `copyProps` instead.
6
+ *
7
+ * @alias module:@lumjs/core/obj.copyAll
6
8
  */
7
9
  function copyAll(target, ...sources)
8
10
  {
@@ -13,6 +15,7 @@ function copyAll(target, ...sources)
13
15
  target[name] = source[name];
14
16
  }
15
17
  }
18
+ return target;
16
19
  }
17
20
 
18
21
  module.exports = copyAll;
@@ -23,6 +23,7 @@ const {B,isObj,isComplex,isArray,def: defProp} = require('../types');
23
23
  * if that property can be overwritten or not.
24
24
  *
25
25
  * @returns {object} The `target` object.
26
+ * @alias module:@lumjs/core/obj.copyProps
26
27
  */
27
28
  function copyProps(source, target, propOpts)
28
29
  {
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Object helpers sub-module.
3
+ * @module @lumjs/core/obj
4
+ */
5
+
6
+ const copyAll = require('./copyall');
7
+ const copyProps = require('./copyprops');
8
+ const {CLONE,clone,addClone,cloneIfLocked} = require('./clone');
9
+ const {lock,addLock} = require('./lock');
10
+ const {mergeNested,syncNested} = require('./merge');
11
+ const ns = require('./ns');
12
+ const
13
+ {
14
+ getObjectPath,setObjectPath,
15
+ getNamespace,setNamespace,
16
+ } = ns;
17
+
18
+ module.exports =
19
+ {
20
+ CLONE, clone, addClone, cloneIfLocked, lock, addLock,
21
+ mergeNested, syncNested, copyProps, copyAll, ns,
22
+ getObjectPath, setObjectPath, getNamespace, setNamespace,
23
+ }
@@ -1,13 +1,12 @@
1
1
  // Import *most* required bits here.
2
2
  const {B, isObj, def} = require('../types');
3
- const {getDescriptor, DESC} = require('../descriptors');
4
3
 
5
4
  /**
6
5
  * Lock an object using Object.freeze()
7
6
  *
8
7
  * @param {object} obj - The object we want to lock.
9
- * @param {boolean} [clonable=true] Pass to {@link Lum._.addClone} first?
10
- * @param {object} [cloneOpts=null] Options for addClone.
8
+ * @param {boolean} [clonable=true] Pass to `addClone()` first?
9
+ * @param {object} [cloneOpts=null] Options for `addClone()`.
11
10
  * @param {boolean} [useSeal=false] Use Object.seal() instead of freeze.
12
11
  *
13
12
  * If cloneOpts is `null` then we will use the following:
@@ -15,8 +14,7 @@ const {getDescriptor, DESC} = require('../descriptors');
15
14
  * ```{mode: CLONE.DEF, addClone: true, addLock: true}```
16
15
  *
17
16
  * @return {object} The locked object.
18
- *
19
- * @method Lum._.lock
17
+ * @alias module:@lumjs/core/obj.lock
20
18
  */
21
19
  function lock(obj, clonable=true, cloneOpts=null, useSeal=false)
22
20
  {
@@ -38,23 +36,28 @@ const {getDescriptor, DESC} = require('../descriptors');
38
36
  /**
39
37
  * Add a lock() method to an object.
40
38
  *
41
- * Adds a wrapper version of {@link Lum._.lock} to the object as a method.
42
- *
43
- * @param {object} obj - The object we're adding lock() to.
44
- * @param {object} opts - Options (TODO: document this).
39
+ * Adds a wrapper version of `lock()` to the object as a method.
45
40
  *
46
- * @method Lum._.addLock
41
+ * @param {object} obj - The object we're adding `lock()` to.
42
+ * @param {object} [opts] - Options and defaults.
43
+ * In addition to options specific to this function, any options
44
+ * supported by `addClone()` may be specified here as well.
45
+ * @param {object} [opts.lockDesc] Descriptor rules the `lock()` method.
46
+ * @param {boolean} [opts.addClone=true] Default for `clonable` parameter.
47
+ * @param {object} [opts.useSeal=false] Default for `useSeal` parameter.
48
+ * @returns {object} `obj`
49
+ * @alias module:@lumjs/core/obj.addLock
47
50
  */
48
51
  function addLock(obj, opts)
49
52
  {
50
- const defDesc = getDescriptor(opts.lockDesc ?? DESC.CONF);
51
- defDesc.setValue(function(obj, cloneable, cloneOpts, useSeal)
53
+ const defDesc = opts.lockDesc ?? {};
54
+ defDesc.value = function(obj, cloneable, cloneOpts, useSeal)
52
55
  {
53
56
  if (typeof cloneable !== B) clonable = opts.addClone ?? true;
54
57
  if (!isObj(cloneOpts)) cloneOpts = opts; // Yup, just a raw copy.
55
58
  if (typeof useSeal !== B) useSeal = opts.useSeal ?? false;
56
59
  return lock(obj, cloneable, cloneOpts, useSeal);
57
- });
60
+ }
58
61
  return def(obj, 'lock', defDesc);
59
62
  }
60
63
 
@@ -20,6 +20,7 @@ const {B, isObj} = require('../types');
20
20
  * as the `opts` argument directly will set this option.
21
21
  *
22
22
  * @returns {object} The `target` object.
23
+ * @alias module:@lumjs/core/obj.mergeNested
23
24
  */
24
25
  function mergeNested(source, target, opts={})
25
26
  {
@@ -64,6 +65,7 @@ const {B, isObj} = require('../types');
64
65
  * @param {object} [opts2=opts1] Options for the second merge operation.
65
66
  * If this is not specified, `opts2` will be the same as `opts1`.
66
67
  *
68
+ * @alias module:@lumjs/core/obj.syncNested
67
69
  */
68
70
  function syncNested(obj1, obj2, opts1={}, opts2=opts1)
69
71
  {