@lumjs/core 1.0.0-beta.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.
package/CHANGELOG.md ADDED
@@ -0,0 +1,17 @@
1
+ # Changelog
2
+ All notable changes to this project will be documented in this file.
3
+
4
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [Unreleased]
8
+
9
+ ## [1.0.0-beta.1] - 2022-07-07
10
+ ### Added
11
+ - Initial release.
12
+ - Pulled a bunch of the core libraries from the old Lum.js project.
13
+ - Refactored and reorganized the libraries a lot.
14
+
15
+ [Unreleased]: https://github.com/supernovus/lum.core.js/compare/v1.0.0-beta.1...HEAD
16
+ [1.0.0-beta.1]: https://github.com/supernovus/lum.core.js/releases/tag/v1.0.0-beta.1
17
+
package/README.md ADDED
@@ -0,0 +1,27 @@
1
+ # lum.core.js
2
+
3
+ Some small *core* constants, functions, classes, and objects.
4
+ Things that make working with objects in Javascript slightly nicer,
5
+ and work in CommonJS/Node.js and modern browsers without any modifications.
6
+
7
+ Used by all the rest of my *Lum.js* libraries.
8
+
9
+ ## Exports
10
+
11
+ TODO: a brief summary of the exports.
12
+
13
+ ## Official URLs
14
+
15
+ This library can be found in two places:
16
+
17
+ * [Github](https://github.com/supernovus/lum.core.js)
18
+ * [NPM](https://www.npmjs.com/package/@lumjs/core)
19
+
20
+ ## Author
21
+
22
+ Timothy Totten <2010@totten.ca>
23
+
24
+ ## License
25
+
26
+ [MIT](https://spdx.org/licenses/MIT.html)
27
+
package/TODO.md ADDED
@@ -0,0 +1,23 @@
1
+ # TODO
2
+
3
+ - Proper tests using new [@lumjs/tests](https://github.com/supernovus/lum.tests.js) library.
4
+ - [x] `types`
5
+ - [ ] `context`
6
+ - [ ] `strings`
7
+ - [ ] `flags`
8
+ - [ ] `descriptors`
9
+ - [ ] `obj/copyall`
10
+ - [ ] `obj/copyprops`
11
+ - [ ] `obj/clone`
12
+ - [ ] `obj/lock`
13
+ - [ ] `obj/merge`
14
+ - [ ] `obj/ns`
15
+ - [ ] `opt`
16
+ - [ ] `objectid`
17
+ - [ ] `meta`
18
+ - [ ] `enum`
19
+ - [ ] `prop`
20
+ - [ ] `lazy`
21
+ - [ ] `observable`
22
+ - Anything that has a TODO notice in the code.
23
+
package/index.js ADDED
@@ -0,0 +1,64 @@
1
+ // The core library is made up of a bunch of child modules,
2
+ // as well as some standalone classes and functions.
3
+
4
+ /**
5
+ * Constants and functions for basic type checking.
6
+ * @namespace types
7
+ */
8
+ const types = require('./src/types');
9
+ const def = types.def;
10
+
11
+ /**
12
+ * Information about the JS context we're running in.
13
+ * @namespace context
14
+ */
15
+ const context = require('./src/context');
16
+
17
+ /**
18
+ * Functions for working with strings and locales.
19
+ * @namespace strings
20
+ */
21
+ const strings = require('./src/strings');
22
+
23
+ /**
24
+ * Functions for working with binary flags.
25
+ * @namespace flags
26
+ */
27
+ const flags = require('./src/flags');
28
+
29
+ /**
30
+ * Functions and Enums for working with Descriptors.
31
+ * @namespace descriptors
32
+ */
33
+ const descriptors = require('./src/descriptors');
34
+ // The *primary* export from descriptors is DESC.
35
+ const DESC = descriptors.DESC;
36
+
37
+ /**
38
+ * Functions for manipulating objects.
39
+ * @namespace obj
40
+ */
41
+ const obj = require('./src/obj');
42
+
43
+ /**
44
+ * Function for getting values and properties with fallback defaults.
45
+ * @namespace opt
46
+ */
47
+ const opt = require('./src/opt');
48
+
49
+ // A few modules that we're going to expand into the main exports.
50
+ const {randomNumber, InternalObjectId} = require('./src/objectid');
51
+ const {stacktrace,AbstractClass,Functions} = require('./src/meta');
52
+
53
+ // And finally some standalone functions.
54
+ const Enum = require('./src/enum');
55
+ const prop = require('./src/prop');
56
+ const lazy = require('./src/lazy');
57
+ const observable = require('./src/observable');
58
+
59
+ module.exports =
60
+ {
61
+ types, context, strings, flags, descriptors, obj, opt,
62
+ randomNumber, InternalObjectId, Enum, prop, lazy, observable,
63
+ stacktrace, AbstractClass, Functions, def, DESC,
64
+ }
package/package.json ADDED
@@ -0,0 +1,15 @@
1
+ {
2
+ "name": "@lumjs/core",
3
+ "version": "1.0.0-beta.1",
4
+ "main": "index.js",
5
+ "license": "MIT",
6
+ "repository":
7
+ {
8
+ "type": "git",
9
+ "url": "https://github.com/supernovus/lum.core.js.git"
10
+ },
11
+ "devDependencies":
12
+ {
13
+ "@lumjs/tests": "^1.0.0"
14
+ }
15
+ }
package/src/context.js ADDED
@@ -0,0 +1,47 @@
1
+
2
+ const {root,B,F,U,O,isObj,def} = require('./types');
3
+
4
+ /**
5
+ * Context object.
6
+ *
7
+ * @namespace Lum.context
8
+ *
9
+ * Offers some insight into the current JS context.
10
+ *
11
+ */
12
+
13
+ const ctx = {root};
14
+
15
+ const rootHas = what => typeof root[what] !== U;
16
+ const cd = def(ctx, true);
17
+
18
+ cd('AMD', typeof define === F && define.amd)
19
+ ('hasExports', typeof exports !== U)
20
+ ('hasModule', typeof module === O && module !== null)
21
+ ('CJS', ctx.hasModule && isObj(module.exports))
22
+ ('isWindow', !ctx.CJS && rootHas('window'))
23
+ ('isWorker', !ctx.CJS && rootHas('WorkerGlobalScope'))
24
+ ('isServiceWorker', !ctx.CJS && rootHas('ServiceWorkerGlobalScope'))
25
+ ('isDedicatedWorker', !ctx.CJS && rootHas('DedicatedWorkerGlobalScope'))
26
+ ('isSharedWorker', !ctx.CJS && rootHas('SharedWorkerGlobalScope'))
27
+ ('isBrowser', ctx.isWindow || ctx.isWorker);
28
+
29
+ // Does the global object/property exist?
30
+ // Caches the results so we can do `context.has.Thingy` tests.
31
+ function hasRoot(ns)
32
+ {
33
+ if (typeof hasRoot[ns] === B) return hasRoot[ns];
34
+ const result = rootHas(ns);
35
+ def(hasRoot, ns, {value: result, enumerable: true});
36
+ return result;
37
+ }
38
+
39
+ // Build some common has items.
40
+ for (const what of ['Proxy','Promise','Reflect','fetch'])
41
+ {
42
+ hasRoot(what);
43
+ }
44
+
45
+ cd('has', hasRoot);
46
+
47
+ module.exports = ctx;
@@ -0,0 +1,243 @@
1
+
2
+ const {InternalObjectId} = require('./objectid');
3
+ const Enum = require('./enum');
4
+ const {doesDescriptor,isObj,isComplex,def,B,F,N} = require('./types');
5
+
6
+ /**
7
+ * Get a property descriptor.
8
+ *
9
+ * This is like `Object.getOwnPropertyDescriptor`, except that method
10
+ * fails if the property is inhereted. This method will travel through
11
+ * the entire prototype chain until it finds the descriptor.
12
+ *
13
+ * @param {object|function} obj - Object to find a property in.
14
+ * @param {string} prop - Name of the property we want the descriptor of.
15
+ * @param {mixed} [defval] The fallback value if no descriptor is found.
16
+ *
17
+ * @returns {mixed} - The descriptor if found, `defval` if not.
18
+ */
19
+ function getProperty(obj, prop, defval)
20
+ {
21
+ if (!isComplex(obj)) throw new TypeError("Target must be an object or function");
22
+ // Yeah it looks like an infinite loop, but it's not.
23
+ while (true)
24
+ {
25
+ const desc = Object.getOwnPropertyDescriptor(obj, prop);
26
+ if (isObj(desc))
27
+ { // Found it.
28
+ return desc;
29
+ }
30
+
31
+ // Didn't find it, so let's try the next object in the prototype chain.
32
+ obj = Object.getPrototypeOf(obj);
33
+ if (!isComplex(obj))
34
+ { // We've gone as far up the prototype chain as we can, bye now!
35
+ return defval;
36
+ }
37
+ }
38
+ }
39
+
40
+ exports.getProperty = getProperty;
41
+
42
+ const DESC_ID = new InternalObjectId({name: '$LumDescriptor'});
43
+ const DESC_ADD = Enum(['ONE','SHORT', 'SET'], {flags: true});
44
+
45
+ /**
46
+ * Create a magic Descriptor object.
47
+ * @param {object} desc - Descriptor template.
48
+ * @param {object} [opts] - Options (to be documented.)
49
+ * @returns {object} `desc`
50
+ */
51
+ function descriptor(desc, opts={})
52
+ {
53
+ if (!isObj(opts)) throw new TypeError("Options must be an object");
54
+
55
+ if (typeof desc === B)
56
+ { // This is a special case.
57
+ opts.accessorSafe = desc;
58
+ opts.add = DESC_ADD.ONE;
59
+ desc = {};
60
+ }
61
+
62
+ if (!isObj(desc))
63
+ throw new TypeError("First parameter (desc) must be a descriptor template object");
64
+
65
+ if (!Object.isExtensible(desc))
66
+ throw new RangeError("First parameter (desc) must not be locked, sealed, frozen, etc.");
67
+
68
+ const accessorSafe = (typeof opts.accessorSafe === B)
69
+ ? opts.accessorSafe
70
+ : (desc.writable === undefined);
71
+
72
+ DESC_ID.tag(desc);
73
+
74
+ // Add a function or other property.
75
+ const add = def(desc);
76
+
77
+ // Add a getter.
78
+ function accessor(name, getter, setter)
79
+ {
80
+ const adef = {configurable: true};
81
+ if (typeof getter === F) adef.get = getter;
82
+ if (typeof setter === F) adef.set = setter;
83
+ def(desc, name, adef);
84
+ }
85
+
86
+ add('accessorSafe', accessorSafe);
87
+
88
+ add('whenDone', function(func)
89
+ {
90
+ if (typeof func === F)
91
+ {
92
+ add('done', func);
93
+ }
94
+ return this;
95
+ });
96
+
97
+ if (typeof opts.done === F)
98
+ {
99
+ desc.whenDone(opts.done);
100
+ }
101
+
102
+ add('setValue', function (val, noClean)
103
+ {
104
+ if (this.get !== undefined || this.set !== undefined)
105
+ {
106
+ console.error("Accessor properties already defined", this);
107
+ }
108
+ else
109
+ {
110
+ this.value = val;
111
+ }
112
+
113
+ return this;
114
+ });
115
+
116
+ if (accessorSafe)
117
+ {
118
+ function validate ()
119
+ {
120
+ if (this.value !== undefined)
121
+ {
122
+ console.error("Data 'value' property defined", this);
123
+ return false;
124
+ }
125
+
126
+ for (const arg of arguments)
127
+ { // All accessor arguments must be functions.
128
+ if (typeof arg !== F) throw new TypeError("Parameter must be a function");
129
+ }
130
+
131
+ return true;
132
+ }
133
+
134
+ add('setGetter', function(func)
135
+ {
136
+ if (validate.call(this, func))
137
+ this.get = func;
138
+ return this;
139
+ });
140
+
141
+ add('setSetter', function(func)
142
+ {
143
+ if (validate.call(this, func))
144
+ this.set = func;
145
+ return this;
146
+ });
147
+
148
+ add('setAccessor', function(getter, setter)
149
+ {
150
+ if (validate.call(this, getter, setter))
151
+ {
152
+ this.get = getter;
153
+ this.set = setter;
154
+ }
155
+ return this;
156
+ });
157
+
158
+ } // accessorSafe
159
+
160
+ if (opts.add)
161
+ {
162
+ const addTypes
163
+ = (typeof opts.add === N)
164
+ ? opts.add
165
+ : DESC_ADD.SET;
166
+
167
+ function addBool(propname)
168
+ {
169
+ const setBool = function()
170
+ {
171
+ this[propname] = true;
172
+ return this;
173
+ }
174
+
175
+ if (addTypes & DESC_ADD.ONE)
176
+ {
177
+ const aname = propname[0];
178
+ accessor(aname, setBool);
179
+ }
180
+ if (addTypes & DESC_ADD.SHORT)
181
+ {
182
+ const aname = propname.substring(0,4);
183
+ accessor(aname, setBool);
184
+ }
185
+ if (addTypes & DESC_ADD.SET)
186
+ {
187
+ const aname = 'set'+ucfirst(propname);
188
+ accessor(aname, setBool);
189
+ }
190
+ }
191
+
192
+ addBool('configurable');
193
+ addBool('enumerable');
194
+ addBool('writable');
195
+
196
+ } // addBools
197
+
198
+ // Is the descriptor ready to be used?
199
+ accessor('isReady', function()
200
+ {
201
+ return doesDescriptor(this);
202
+ });
203
+
204
+ return desc;
205
+ } // descriptor()
206
+
207
+ exports.descriptor = descriptor;
208
+
209
+ /**
210
+ * Get a Descriptor object.
211
+ * @param {object} desc - Either a Descriptor, or a descriptor template.
212
+ * @returns {object}
213
+ */
214
+ function getDescriptor(desc)
215
+ {
216
+ return DESC_ID.is(desc) ? desc : descriptor(desc);
217
+ }
218
+
219
+ exports.getDescriptor = getDescriptor;
220
+
221
+ /**
222
+ * A factory for building magic Descriptor objects.
223
+ */
224
+ const DESC =
225
+ {
226
+ get RO() { return descriptor(true) },
227
+ get CONF() { return descriptor(true).c },
228
+ get ENUM() { return descriptor(true).e },
229
+ get WRITE() { return descriptor(false).w },
230
+ get RW() { return descriptor(false).c.w },
231
+ get DEF() { return descriptor(true).c.e },
232
+ get OPEN() { return descriptor(false).c.e.w },
233
+ }
234
+
235
+ def(DESC)
236
+ ('make', descriptor)
237
+ ('is', DESC_ID.isFunction())
238
+ ('get', getDescriptor)
239
+ ('does', doesDescriptor)
240
+ ('ADD', DESC_ADD);
241
+
242
+ exports.DESC = DESC;
243
+
package/src/enum.js ADDED
@@ -0,0 +1,123 @@
1
+
2
+ const {S,def,notNil,isObj} = require('./types')
3
+ const {InternalObjectId} = require('./objectid');
4
+
5
+ const ENUM_ID = new InternalObjectId({name: '$Enum'});
6
+
7
+ /**
8
+ * A function to build magic Enum objects.
9
+ *
10
+ * Like the built-in `Symbol`, this is called as a function, not a constructor.
11
+ *
12
+ * @param {*} spec - TBD
13
+ * @param {*} [opts] - TBD
14
+ * @returns {object} A magic Enum object.
15
+ */
16
+ function Enum (spec, opts={})
17
+ {
18
+ if (!isObj(spec))
19
+ {
20
+ throw new TypeError("Enum spec must be an object");
21
+ }
22
+ if (!isObj(opts))
23
+ {
24
+ throw new TypeError("Enum options must be an object")
25
+ }
26
+
27
+ const anEnum = ENUM_ID.tag({});
28
+
29
+ function getVal (name, def)
30
+ {
31
+ if (opts.symbols)
32
+ { // We want to use symbols.
33
+ if (opts.globals)
34
+ {
35
+ return Symbol.for(name);
36
+ }
37
+ else
38
+ {
39
+ return Symbol(name);
40
+ }
41
+ }
42
+ else
43
+ { // Use the default.
44
+ return def;
45
+ }
46
+ }
47
+
48
+ function addVal(pName, sName, inVal)
49
+ {
50
+ const desc = {configurable: true, enumerable: true};
51
+ desc.value = getVal(sName, inVal);
52
+ def(anEnum, pName, desc);
53
+ }
54
+
55
+ if (Array.isArray(spec))
56
+ { // An array of strings is expected.
57
+ let counter = opts.counter ?? 1;
58
+
59
+ for (let i = 0; i < spec.length; i++)
60
+ {
61
+ const name = spec[i];
62
+ if (typeof name !== S)
63
+ {
64
+ throw new TypeError("Non-string passed in Lum.Enum object");
65
+ }
66
+
67
+ const val
68
+ = opts.strings
69
+ ? name
70
+ : (opts.flags ? counter : i);
71
+
72
+ addVal(name, name, val);
73
+
74
+ if (opts.flags)
75
+ { // Increment the binary flag counter.
76
+ counter *= 2;
77
+ }
78
+ }
79
+ }
80
+ else
81
+ { // An object mapping of property name to value.
82
+ for (const pName in spec)
83
+ {
84
+ const val = spec[pName];
85
+ const sName = (typeof val === S) ? val : pName;
86
+ addVal(pName, sName, val);
87
+ }
88
+ }
89
+
90
+ if (notNil(opts.lock))
91
+ { // Use lock.
92
+ let lockOpts;
93
+ if (Array.isArray(opts.lock))
94
+ {
95
+ lockOpts = opts.lock;
96
+ }
97
+ else if (isObj(opts.lock))
98
+ {
99
+ lockOpts = [true, opts.lock, false];
100
+ }
101
+ else if (typeof opts.lock === B)
102
+ {
103
+ lockOpts = [opts.lock, null, false];
104
+ }
105
+ else
106
+ {
107
+ lockOpts = [true, null, false];
108
+ }
109
+ return lock(anEnum);
110
+ }
111
+ else if (!opts.open)
112
+ { // Use Object.freeze()
113
+ return Object.freeze(anEnum);
114
+ }
115
+
116
+ return anEnum;
117
+
118
+ } // Enum
119
+
120
+ // Add an 'is' method.
121
+ def(Enum, 'is', ENUM_ID.isFunction());
122
+
123
+ module.exports = Enum;
package/src/flags.js ADDED
@@ -0,0 +1,45 @@
1
+
2
+ const {N} = require('./types');
3
+
4
+ /**
5
+ * Add or remove a binary flag from a set of flags.
6
+ * @param {number} flags - An integer representing a set of flags.
7
+ * @param {number} flag - An integer representing the flag to add or remove.
8
+ * @param {boolean} [value=true] `true` means add, `false` means remove.
9
+ * @returns {number} The `flags` with the `flag` added or removed accordingly.
10
+ */
11
+ function setFlag(flags, flag, value=true)
12
+ {
13
+ if (typeof flags !== N) throw new TypeError("Flags must be number");
14
+ if (typeof flag !== N) throw new TypeError("Flag must be number");
15
+
16
+ if (value)
17
+ flags = flags | flag;
18
+ else
19
+ flags = flags - (flags & flag);
20
+
21
+ return flags;
22
+ }
23
+
24
+ exports.setFlag = setFlag;
25
+
26
+ /**
27
+ * Combine an entire set of flags into a single set.
28
+ * @param {...number} flag - Any number of flags you want to add.
29
+ * @returns {number} All the passed flags combined into one set.
30
+ */
31
+ function allFlags()
32
+ {
33
+ const flags = 0;
34
+ for (const arg of arguments)
35
+ {
36
+ if (typeof arg !== N)
37
+ {
38
+ throw new TypeError("Arguments must be numbers");
39
+ }
40
+ flags = flags | arg;
41
+ }
42
+ return flags;
43
+ }
44
+
45
+ exports.allFlags = allFlags
package/src/lazy.js ADDED
@@ -0,0 +1,102 @@
1
+
2
+ const {S,F,isComplex} = require('./types');
3
+ const prop = require('./prop');
4
+ const {DESC} = require('./descriptors');
5
+
6
+ /**
7
+ * Build a lazy initializer property.
8
+ *
9
+ * Basically the first time the property is accessed it's built.
10
+ * 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.
13
+ *
14
+ * @param {object} obj - The object to add the property to.
15
+ * @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.
22
+ *
23
+ * If this is `true` then the new value will be assigned directly,
24
+ * skipping the initialization process entirely.
25
+ *
26
+ * If this is `false` then any attempt at assignment will throw
27
+ * a `ReferenceError` with a message indicating the property is read-only.
28
+ *
29
+ * If this is a `function` it will take two arguments, the
30
+ * first being the value that is trying to be assigned, and
31
+ * the second being the currently assigned value.
32
+ * As with any getter or setter, `this` will be the `obj` itself.
33
+ * The function must return the value to be assigned.
34
+ * If it returns `undefined`, then the value was not valid,
35
+ * and will not be assigned.
36
+ *
37
+ * If this is anything else, assignment will do nothing at all.
38
+ *
39
+ * @param {object} [desc=DESC.CONF] The Descriptor for the property.
40
+ *
41
+ * @return {object} The object we defined the property on.
42
+ */
43
+ function lazy(obj, name, initfunc, onset, desc=DESC.CONF)
44
+ {
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
+ }
57
+
58
+ let value;
59
+ let setter = null;
60
+
61
+ function getter()
62
+ {
63
+ if (value === undefined)
64
+ {
65
+ value = initfunc.call(this, name);
66
+ }
67
+ return value;
68
+ }
69
+
70
+ if (onset === true)
71
+ { // Allow direct assignment.
72
+ setter = function(newval)
73
+ {
74
+ value = newval;
75
+ }
76
+ }
77
+ else if (onset === false)
78
+ { // Throw an error on assignment.
79
+ setter = function()
80
+ {
81
+ throw new ReferenceError("The "+name+" property is read-only");
82
+ }
83
+ }
84
+ else if (typeof onset === F)
85
+ { // A proxy method for assignment.
86
+ setter = function(newval)
87
+ {
88
+ const setval = onset.call(this, newval);
89
+ if (setval !== undefined)
90
+ {
91
+ value = setval;
92
+ }
93
+ }
94
+ }
95
+
96
+ prop(obj, name, getter, setter, desc);
97
+ }
98
+
99
+ // Gotta be one of the greatest lines...
100
+ prop(prop, 'lazy', lazy);
101
+
102
+ module.exports = lazy;