@gershy/clearing 0.0.6 → 0.0.7

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/cmp/mjs/main.js CHANGED
@@ -1,149 +1,113 @@
1
1
  const applyClearing = (() => {
2
2
  const global = globalThis;
3
3
  // Prevent multiple installations...
4
- const memSym = Symbol.for('clearing:mem');
4
+ const pfx = '@gershy/clearing';
5
+ const memSym = Symbol.for(`${pfx}:mem`);
5
6
  if (global[memSym])
6
7
  return;
7
8
  global[memSym] = true;
8
- const symNames = ['add', 'at', 'apart', 'assert', 'bind', 'bits', 'built', 'char', 'code', 'count', 'cut', 'dive', 'empty', 'fire', 'group', 'has', 'hasHead', 'hasTail', 'indent', 'isInt', 'limn', 'lower', 'mod', 'map', 'mapk', 'merge', 'padHead', 'padTail', 'rem', 'find', 'slash', 'slice', 'suppress', 'toArr', 'toNum', 'toObj', 'toStr', 'upper'];
9
- const syms = Object.fromEntries(symNames.map(term => [term, Symbol(`clearing:${term}`)]));
10
- const sovereign = v => v;
11
- Object.assign(global, {
12
- // Reference native constructors
13
- AsyncFunction: (async () => { }).constructor,
14
- GeneratorFunction: (function* () { })().constructor,
15
- AsyncGeneratorFunction: (async function* () { })().constructor,
16
- // Symbols
17
- ...syms,
18
- // Forms
19
- getFormName: f => {
20
- if (f === null)
21
- return 'Null';
22
- if (f === undefined)
23
- return 'Undef';
24
- if (f !== f)
25
- return 'UndefNum';
26
- return Object.getPrototypeOf(f)?.constructor.name ?? 'Prototypeless'; // e.g. `getFormName(Object.plain()) === 'Prototypeless'`
27
- },
28
- getForm: f => Object.getPrototypeOf(f)?.constructor ?? null,
29
- isForm: (fact, Form) => {
30
- // NaN only matches against the NaN primitive (not the Number Form)
31
- if (fact !== fact)
32
- return Form !== Form;
33
- if (fact == null)
34
- return false;
35
- return Object.getPrototypeOf(fact).constructor === Form;
36
- },
37
- hasForm: (fact, Form) => fact instanceof Form,
38
- // Utility
39
- skip: undefined,
40
- sovereign,
41
- then: (val, rsv = Function.stub, rjc) => {
42
- // Act on `val` regardless of whether it's a Promise or an immediate
43
- // value; return `rsv(val)` either immediately or as a Promise;
44
- // Promises are returned with `then`/`fail` handling
45
- if (hasForm(val, Promise))
46
- return val.then(rsv).catch(rjc);
47
- // No `rjc` means no `try`/`catch` handling
48
- if (!rjc)
49
- return rsv(val);
50
- try {
51
- return rsv(val);
52
- }
53
- catch (err) {
54
- return rjc(err);
55
- }
56
- },
57
- safe: (val, acc = Function.stub, rjc) => {
58
- try {
59
- return then(val(), acc, rjc);
9
+ const symNames = [
10
+ // <SYMBOLS>
11
+ 'add',
12
+ 'allArr',
13
+ 'allObj',
14
+ 'at',
15
+ 'assert',
16
+ 'base32',
17
+ 'base36',
18
+ 'base62',
19
+ 'base64Std',
20
+ 'base64Url',
21
+ 'baseline',
22
+ 'bind',
23
+ 'bits',
24
+ 'char',
25
+ 'charset',
26
+ 'code',
27
+ 'count',
28
+ 'cut',
29
+ 'dive',
30
+ 'empty',
31
+ 'find',
32
+ 'fire',
33
+ 'group',
34
+ 'has',
35
+ 'hasHead',
36
+ 'hasTail',
37
+ 'indent',
38
+ 'int32',
39
+ 'int64',
40
+ 'isInt',
41
+ 'later',
42
+ 'limn',
43
+ 'lower',
44
+ 'map',
45
+ 'mapk',
46
+ 'merge',
47
+ 'mod',
48
+ 'padHead',
49
+ 'padTail',
50
+ 'rem',
51
+ 'slash',
52
+ 'slice',
53
+ 'suppress',
54
+ 'toArr',
55
+ 'toNum',
56
+ 'toObj',
57
+ 'toStr',
58
+ 'upper'
59
+ // </SYMBOLS>
60
+ ];
61
+ const syms = Object.fromEntries(symNames.map(term => [term, Symbol(`${pfx}:${term}`)]));
62
+ const getClsName = i => {
63
+ if (i === null)
64
+ return 'Null';
65
+ if (i === undefined)
66
+ return 'Undef';
67
+ if (i !== i)
68
+ return 'Nan';
69
+ return Object.getPrototypeOf(i)?.constructor.name ?? 'Prototypeless';
70
+ };
71
+ const getCls = i => Object.getPrototypeOf(i)?.constructor ?? null;
72
+ const isCls = (i, C) => {
73
+ // NaN only matches against the NaN primitive (not the Number Form)
74
+ if (i !== i)
75
+ return C !== C;
76
+ // `null` and `undefined` only match to themselves
77
+ if (i == null)
78
+ return i === C;
79
+ // Otherwise strictly check the constructor
80
+ return Object.getPrototypeOf(i).constructor === C;
81
+ };
82
+ const inCls = (i, C) => i instanceof C;
83
+ const skip = undefined;
84
+ Object.assign(global, { ...syms, clearing: { getClsName, getCls, isCls, inCls } });
85
+ const protoDefs = (Cls, def) => {
86
+ let protoVals = [];
87
+ let classVals = [];
88
+ for (const key of Reflect.ownKeys(def)) {
89
+ if (key !== '$' && !isCls(key, Symbol))
90
+ throw Object.assign(Error('invalid proto key'), { Cls, keyClsName: getClsName(key), key });
91
+ if (key !== '$') {
92
+ protoVals.push([key, def[key]]);
60
93
  }
61
- catch (err) {
62
- if (!rjc)
63
- throw err;
64
- return rjc(err);
94
+ else {
95
+ for (const k of Reflect.ownKeys(def[key])) {
96
+ if (!isCls(k, Symbol))
97
+ throw Object.assign(Error('invalid class key'), { Cls, keyClsName: getClsName(k), key: k });
98
+ classVals.push([k, def[key][k]]);
99
+ }
65
100
  }
66
- },
67
- // Jsfn
68
- jsfn: {
69
- encode: sovereign((dec /* `val` is still not allowed to have cycles! */, opts) => {
70
- const encodeFn = opts?.encodeFn ?? (fn => fn.toString());
71
- const hoists = [];
72
- const serializeObjKey = (key) => {
73
- if (/^[$_a-zA-Z][$_a-zA-Z]+$/.test(key))
74
- return key;
75
- return `'${key.replaceAll(`'`, `\\'`)}'`;
76
- };
77
- const serialize = (val) => {
78
- if (isForm(val, Object) && Object.keys(val).sort().join(',') === 'args,form,hoist' && hasForm(val.form, Function)) {
79
- const { args, form, hoist } = val;
80
- hoists.push(hoist);
81
- return `new ${hoist.split('::').at(-1)}(${args.map(a => serialize(a)).join(', ')})`;
82
- }
83
- if (isForm(val, Array))
84
- return '[' + val.map((v) => serialize(v)).join(',') + ']';
85
- if (isForm(val, Object))
86
- return '{' + val[toArr]((v, k) => `${serializeObjKey(k)}:${serialize(v)}`).join(',') + '}';
87
- if (hasForm(val, Function))
88
- return encodeFn(val);
89
- return JSON.stringify(val);
90
- };
91
- return {
92
- // Note `str` is stringified, but it isn't json - it's js, in string representation!!
93
- // This representation is actually very simple - in order to run the js represented by
94
- // `str`, all hoists need to be imported in the context
95
- hoists,
96
- str: serialize(dec)
97
- };
98
- })
99
- },
100
- });
101
- const protoDefs = (Cls, def) => {
102
- const protoVals = [];
103
- const classVals = [];
104
- for (const key of Reflect.ownKeys(def) /* includes symbols!! */) {
105
- if (!isForm(key, String))
106
- protoVals.push({ key, val: def[key] });
107
- else if (key.startsWith('$'))
108
- classVals.push({ key: key.slice(1), val: def[key] });
109
- else if (key.endsWith('->'))
110
- protoVals.push({ key: def[key], val: Cls.prototype[key.slice(0, -2)] });
111
101
  }
112
102
  // Assign class properties
113
- Object.assign(Cls, Object.fromEntries(classVals.map(({ key, val }) => [key, val])));
114
- // Avoid making more properties available on `global` - if a typo winds up referring to a
115
- // global property, the bug which results can be highly unexpected!
116
- // TODO: Note, we don't want to populate the global namespace, *but* we still populate all the
117
- // symbols keyed by string name on `global` (note the *symbol* is keyed by its *string name* on
118
- // `global` - i.e., `global['toArr']` results in `Symbol('clearing.toArr')`! This results in
119
- // many dozens of keys being populated in global namespace... should probably move these
120
- // symbols to a module import instead :(
121
- if (Cls === global.constructor)
122
- for (const entry of protoVals)
123
- if (isForm(entry.key, String))
124
- global[entry.key] = skip;
125
- // Assign proto properties
126
- Object.defineProperties(Cls.prototype, Object.fromEntries(protoVals
127
- .map(({ key, val }) => [key, { enumerable: false, writable: true, value: val }])));
103
+ for (const [target, props] of [[Cls, classVals], [Cls.prototype, protoVals]])
104
+ for (const [sym, value] of props)
105
+ Object.defineProperty(target, sym, { enumerable: false, value });
128
106
  };
129
107
  protoDefs(Object, {
130
- $stub: Object.freeze({}),
131
- $plain: obj => obj ? Object.assign(Object.create(null), obj) : Object.create(null),
132
- 'hasOwnProperty->': has,
133
- *[apart](dive = []) {
134
- // Breaks apart a "structured" object into a flat list of dive keys paired with values; every
135
- // value in the original object appears alongside the "dive" (chain of keys to recursively
136
- // apply) required to dereference it
137
- for (const [k, val] of this) {
138
- if (isForm(val, Object))
139
- yield* val[apart]([...dive, k]);
140
- else
141
- yield { dive: [...dive, k], val };
142
- }
143
- },
144
108
  [at](cmps, def = skip) {
145
109
  let ptr = this;
146
- if (!isForm(cmps, Array))
110
+ if (!isCls(cmps, Array))
147
111
  cmps = [cmps];
148
112
  for (const c of cmps) {
149
113
  if (ptr[has](c))
@@ -153,23 +117,29 @@ const applyClearing = (() => {
153
117
  }
154
118
  return ptr;
155
119
  },
156
- [built](diveFromKey = (key) => key.split('.')) {
157
- // The reverse operation of `apart` - takes a flat list of (dive, value) pairs and produces a
158
- // structured object
159
- const result = {};
120
+ [count]() { let c = 0; for (const k in this)
121
+ c++; return c; },
122
+ [empty]() { for (const k in this)
123
+ return false; return true; },
124
+ [group](fn) {
125
+ // { a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8, i: 9, j: 10 }.group(n => {
126
+ // if (n < 4) return 'small';
127
+ // if (n < 8) return 'medium';
128
+ // return 'big';
129
+ // });
130
+ // >> { small: { a: 1, b: 2, c: 3 }, medium: { d: 4, e: 5, f: 6, g: 7 }, big: { h: 8, i: 9, j: 10 } }
131
+ const ret = {};
160
132
  for (const [k, v] of this) {
161
- const dive = diveFromKey(k);
162
- const last = dive.pop();
163
- let ptr = result;
164
- for (const cmp of dive)
165
- ptr = ptr[at](cmp) ?? (ptr[cmp] = {});
166
- ptr[last] = isForm(v, Object) ? v[built]() : v;
133
+ const g = fn(v, k);
134
+ if (g === skip)
135
+ continue;
136
+ if (!ret[has](g))
137
+ ret[g] = {};
138
+ ret[g][k] = v;
167
139
  }
168
- return result;
140
+ return ret;
169
141
  },
170
- [count]() { let c = 0; for (const k in this)
171
- c++; return c; },
172
- [dive](cmps, def = skip) { return this[at](cmps, def); },
142
+ [has]: Object.prototype.hasOwnProperty,
173
143
  [map](fn) {
174
144
  const ret = Object.assign({}, this);
175
145
  for (const k in ret) {
@@ -190,44 +160,6 @@ const applyClearing = (() => {
190
160
  }
191
161
  return Object.fromEntries(arr);
192
162
  },
193
- [toArr](fn) {
194
- const ret = [];
195
- for (const k in this) {
196
- const r = fn(this[k], k);
197
- if (r !== skip)
198
- ret.push(r);
199
- }
200
- return ret;
201
- },
202
- [slash](p) {
203
- const obj = { ...this };
204
- for (const k of p)
205
- delete obj[k];
206
- return obj;
207
- },
208
- [slice](p) {
209
- // >> { a: 1, b: 2, c: 3, d: 4 }.slice([ 'b', 'd' ]);
210
- // { b: 2, d: 4 }
211
- return p[toObj](p => this[has](p) ? [p, this[p]] : skip);
212
- },
213
- [empty]() { for (const k in this)
214
- return false; return true; },
215
- [group](fn) {
216
- // { a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8, i: 9, j: 10 }.group(n => {
217
- // if (n < 4) return 'small';
218
- // if (n < 8) return 'medium';
219
- // return 'big';
220
- // });
221
- // >> { small: { a: 1, b: 2, c: 3 }, medium: { d: 4, e: 5, f: 6, g: 7 }, big: { h: 8, i: 9, j: 10 } }
222
- const ret = {};
223
- for (const [k, v] of this) {
224
- const t = fn(v, k);
225
- if (!ret[has](t))
226
- ret[t] = {};
227
- ret[t][k] = v;
228
- }
229
- return ret;
230
- },
231
163
  [merge](o) {
232
164
  for (const [k, v] of o) {
233
165
  // `skip` can be passed to remove properties
@@ -236,46 +168,45 @@ const applyClearing = (() => {
236
168
  continue;
237
169
  }
238
170
  // Incoming non-Object properties are simple
239
- if (!isForm(v, Object)) {
171
+ if (!isCls(v, Object)) {
240
172
  this[k] = v;
241
173
  continue;
242
174
  }
243
175
  // `v` is an Object; existing non-Object replaced with `{}`
244
- if (!this[has](k) || !isForm(this[k], Object))
176
+ if (!this[has](k) || !isCls(this[k], Object))
245
177
  this[k] = {};
246
178
  // And simply recurse!
247
179
  this[k][merge](v);
248
180
  }
249
181
  return this;
250
182
  },
251
- *[Symbol.iterator]() { for (const k in this)
252
- yield [k, this[k]]; }
253
- });
254
- protoDefs(Array, {
255
- $stub: Object.freeze([]),
256
- 'includes->': has,
257
- [has](v) { return this.includes(v); },
258
- [map](it) {
259
- const ret = [];
260
- const len = this.length;
261
- for (let i = 0; i < len; i++) {
262
- const r = it(this[i], i);
263
- if (r !== skip)
264
- ret.push(r);
265
- }
266
- return ret;
183
+ [slash](p) {
184
+ const obj = { ...this };
185
+ for (const k of p)
186
+ delete obj[k];
187
+ return obj;
267
188
  },
268
- [toArr](it) { return this[map](it); },
269
- [toObj](it) {
189
+ [slice](p) {
190
+ // >> { a: 1, b: 2, c: 3, d: 4 }.slice([ 'b', 'd' ]);
191
+ // { b: 2, d: 4 }
192
+ return p[toObj](p => this[has](p) ? [p, this[p]] : skip);
193
+ },
194
+ [toArr](fn) {
270
195
  const ret = [];
271
- const len = this.length;
272
- for (let i = 0; i < len; i++) {
273
- const r = it(this[i], i);
196
+ for (const k in this) {
197
+ const r = fn(this[k], k);
274
198
  if (r !== skip)
275
199
  ret.push(r);
276
200
  }
277
- return Object.fromEntries(ret);
201
+ return ret;
278
202
  },
203
+ *[Symbol.iterator]() { for (const k in this)
204
+ yield [k, this[k]]; }
205
+ });
206
+ protoDefs(Array, {
207
+ [add](...args) { this.push(...args); return args[0]; },
208
+ [count]() { return this.length; },
209
+ [empty]() { return !this.length; },
279
210
  [find](f) {
280
211
  const n = this.length;
281
212
  for (let i = 0; i < n; i++)
@@ -283,11 +214,6 @@ const applyClearing = (() => {
283
214
  return { found: true, val: this[i], ind: i };
284
215
  return { found: false, val: null, ind: null };
285
216
  },
286
- [empty]() { return !this.length; },
287
- [add](...args) { this.push(...args); return args[0]; },
288
- [rem](val) { const ind = this.indexOf(val); if (ind > -1)
289
- this.splice(ind, 1); },
290
- [count]() { return this.length; },
291
217
  [group](fn) {
292
218
  // [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ].group(n => {
293
219
  // if (n < 4) return 'small';
@@ -297,56 +223,78 @@ const applyClearing = (() => {
297
223
  // >> { small: [ 1, 2, 3 ], medium: [ 4, 5, 6, 7 ], big: [ 8, 9, 10 ] }
298
224
  const ret = {};
299
225
  for (const elem of this) {
300
- const t = fn(elem);
301
- if (!ret[has](t))
302
- ret[t] = [];
303
- ret[t].push(elem);
226
+ const g = fn(elem);
227
+ if (g === skip)
228
+ continue;
229
+ if (!ret[has](g))
230
+ ret[g] = [];
231
+ ret[g].push(elem);
232
+ }
233
+ return ret;
234
+ },
235
+ [has]: Array.prototype.includes,
236
+ [map](it) {
237
+ const ret = [];
238
+ const len = this.length;
239
+ for (let i = 0; i < len; i++) {
240
+ const r = it(this[i], i);
241
+ if (r !== skip)
242
+ ret.push(r);
304
243
  }
305
244
  return ret;
245
+ },
246
+ [rem](val) { const ind = this.indexOf(val); if (ind > -1)
247
+ this.splice(ind, 1); },
248
+ [toObj](it) {
249
+ const ret = [];
250
+ const len = this.length;
251
+ for (let i = 0; i < len; i++) {
252
+ const r = it(this[i], i);
253
+ if (r !== skip)
254
+ ret.push(r);
255
+ }
256
+ return Object.fromEntries(ret);
306
257
  }
307
258
  });
308
259
  protoDefs(String, {
309
- $baseline: (str, seq = '| ') => {
310
- return str.split('\n')[map](ln => {
311
- const ind = ln.indexOf(seq);
312
- if (ind === -1)
313
- return skip;
314
- return ln.slice(ind + seq.length);
315
- }).join('\n');
316
- },
317
- $charset: str => {
318
- const cache = new Map();
319
- return {
320
- str,
321
- size: BigInt(str.length),
322
- charVal: (c) => {
323
- if (!cache.has(c)) {
324
- const ind = str.indexOf(c);
325
- if (ind < 0)
326
- throw Error('char outside charset')[mod]({ char: c });
327
- cache.set(c, BigInt(ind));
260
+ $: {
261
+ [baseline]: (str, seq = '| ') => {
262
+ return str.split('\n')[map](ln => {
263
+ const ind = ln.indexOf(seq);
264
+ if (ind === -1)
265
+ return skip;
266
+ return ln.slice(ind + seq.length);
267
+ }).join('\n');
268
+ },
269
+ [base32]: '0123456789abcdefghijklmnopqrstuv',
270
+ [base36]: '0123456789abcdefghijklmnopqrstuvwxyz',
271
+ [base62]: '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',
272
+ [base64Url]: '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_',
273
+ [base64Std]: '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/',
274
+ [charset]: str => {
275
+ const cache = new Map();
276
+ return {
277
+ str,
278
+ size: BigInt(str.length),
279
+ charVal: (c) => {
280
+ if (!cache.has(c)) {
281
+ const ind = str.indexOf(c);
282
+ if (ind < 0)
283
+ throw Error('char outside charset')[mod]({ char: c });
284
+ cache.set(c, BigInt(ind));
285
+ }
286
+ return cache.get(c);
287
+ },
288
+ valChar: (n) => {
289
+ if (n < 0 || n >= str.length)
290
+ throw Error('val outside charset');
291
+ return str[n];
328
292
  }
329
- return cache.get(c);
330
- },
331
- valChar: (n) => {
332
- if (n < 0 || n >= str.length)
333
- throw Error('val outside charset');
334
- return str[n];
335
- }
336
- };
293
+ };
294
+ },
337
295
  },
338
- $base32: '0123456789abcdefghijklmnopqrstuv',
339
- $base36: '0123456789abcdefghijklmnopqrstuvwxyz',
340
- $base62: '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',
341
- $base64: '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_',
342
- $base64Std: '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/',
343
- 'includes->': has,
344
- 'startsWith->': hasHead,
345
- 'endsWith->': hasTail,
346
- 'padStart->': padHead,
347
- 'padEnd->': padTail,
348
- 'toUpperCase->': upper,
349
- 'toLowerCase->': lower,
296
+ [code](ind = 0) { return this.charCodeAt(ind); },
297
+ [count]() { return this.length; },
350
298
  [cut](delim, cuts = 1) {
351
299
  // `cuts` defines # of cuts (resulting array length is `num + 1`)
352
300
  const split = this.split(delim, cuts < Infinity ? cuts : skip);
@@ -358,13 +306,14 @@ const applyClearing = (() => {
358
306
  ? [...split, this.slice(lenConsumed + delim.length)]
359
307
  : split;
360
308
  },
361
- [code](ind = 0) { return this.charCodeAt(ind); },
362
- [count]() { return this.length; },
309
+ [has]: String.prototype.includes,
310
+ [hasHead]: String.prototype.startsWith,
311
+ [hasTail]: String.prototype.endsWith,
363
312
  [indent](...args /* amt=2, char=' ' | indentStr=' '.repeat(2) */) {
364
313
  if (!this)
365
314
  return this; // No-op on empty String (otherwise it would transform a 0-line string to a 1-line string)
366
315
  let indentStr;
367
- if (isForm(args[0], String)) {
316
+ if (isCls(args[0], String)) {
368
317
  indentStr = args[0];
369
318
  }
370
319
  else {
@@ -373,10 +322,13 @@ const applyClearing = (() => {
373
322
  }
374
323
  return this.split('\n')[map](ln => `${indentStr}${ln}`).join('\n');
375
324
  },
376
- [toNum](charset = String.base62) {
377
- if (isForm(charset, String))
378
- charset = String.charset(charset);
379
- const base = charset.size;
325
+ [lower]: String.prototype.toLowerCase,
326
+ [padHead]: String.prototype.padStart,
327
+ [padTail]: String.prototype.padEnd,
328
+ [toNum](cs = String[base62]) {
329
+ if (isCls(cs, String))
330
+ cs = String[charset](cs);
331
+ const base = cs.size;
380
332
  if (base === 1n)
381
333
  return this.count();
382
334
  let sum = 0n;
@@ -385,14 +337,15 @@ const applyClearing = (() => {
385
337
  // Earlier values of `i` represent higher places same as with written numbers further left
386
338
  // digits are more significant
387
339
  // The value of the place `i` is `ind - 1`
388
- sum += (base ** BigInt(n - ind - 1)) * charset.charVal(this[ind]);
340
+ sum += (base ** BigInt(n - ind - 1)) * cs.charVal(this[ind]);
389
341
  return sum;
390
- }
342
+ },
343
+ [upper]: String.prototype.toUpperCase,
391
344
  });
392
345
  protoDefs(Number, {
393
- $int32: Math.pow(2, 32),
394
- $int64: Math.pow(2, 64),
346
+ $: { [int32]: 2 ** 32, [int64]: 2 ** 64 },
395
347
  [char]() { return String.fromCharCode(this); },
348
+ [isInt]() { return this === Math.round(this); }, // No bitwise shortcut - it disrupts Infinity
396
349
  [toArr](fn) { const arr = new Array(this || 0); for (let i = 0; i < this; i++)
397
350
  arr[i] = fn(i); return arr; },
398
351
  [toObj](fn) {
@@ -404,55 +357,67 @@ const applyClearing = (() => {
404
357
  }
405
358
  return Object.fromEntries(ret);
406
359
  },
407
- [toStr](charset, padLen = 0) {
360
+ [toStr](cs, padLen = 0) {
408
361
  // Note that base-1 requires 0 to map to the empty string. This also
409
362
  // means that, for `n >= 1`:
410
363
  // | (n).encodeStr(singleChr)
411
364
  // is always equivalent to
412
365
  // | singleChr.repeat(n - 1)
413
- if (isForm(charset, String))
414
- charset = String.charset(charset);
415
- const base = charset.size;
366
+ if (isCls(cs, String))
367
+ cs = String[charset](cs);
368
+ const base = cs.size;
416
369
  if (base === 1n && padLen)
417
370
  throw Error(`pad with base-1 encoding`);
418
371
  if (this !== this)
419
- return (base === 1n) ? '' : charset[0].repeat(Math.max(padLen, 1));
372
+ return (base === 1n) ? '' : cs[0].repeat(Math.max(padLen, 1));
420
373
  let num = this.constructor === BigInt ? this : BigInt(Math.floor(this));
421
374
  const digits = [];
422
375
  while (num) {
423
- digits.push(charset.valChar(num % base));
376
+ digits.push(cs.valChar(num % base));
424
377
  num /= base;
425
378
  }
426
- return digits.reverse().join('')[padHead](padLen, charset.str[0]);
379
+ return digits.reverse().join('')[padHead](padLen, cs.str[0]);
427
380
  },
428
- [isInt]() { return this === Math.round(this); }, // No bitwise shortcut - it disrupts Infinity
429
- *[Symbol.iterator]() { Error.assert({ n: this }, args => args.n.isInteger()); for (let i = 0; i < this; i++)
381
+ *[Symbol.iterator]() { for (let i = 0; i < this; i++)
430
382
  yield i; },
431
383
  *[bits]() { let n = this >= 0 ? this : -this; while (n) {
432
384
  yield n & 1;
433
385
  n = n >> 1;
434
- } },
435
- [map]: undefined // Prevent `Number(...).map`
386
+ } }
436
387
  });
437
388
  protoDefs(BigInt, { [toStr]: Number.prototype[toStr] });
438
389
  protoDefs(Function, {
439
- $stub: v => v,
440
390
  [bind](...args) { return this.bind(null, ...args); }
441
391
  });
442
392
  protoDefs(Error, {
443
- $stackTraceLimit: 150,
444
- $assert: (args, fn) => {
445
- if (fn(args))
446
- return;
447
- throw Error('assert failed')[mod]({
448
- fn: `false === (${fn.toString().replace(/[\s]+/, ' ')})(args)`,
449
- args
450
- });
393
+ $: {
394
+ [assert]: (args, fn) => {
395
+ if (fn(args))
396
+ return;
397
+ throw Error('assert failed')[mod]({
398
+ fn: `false === (${fn.toString().replace(/[\s]+/, ' ')})(args)`,
399
+ args
400
+ });
401
+ },
402
+ },
403
+ [fire](props /* { cause, msg, message, ...more } */) { throw this[mod](props); },
404
+ [limn](seen = new Map()) {
405
+ if (seen.has(this))
406
+ return seen.get(this);
407
+ seen.set(this, 'cycle(Error)');
408
+ const { message, stack, cause, ...props } = this;
409
+ return {
410
+ form: getClsName(this),
411
+ msg: message,
412
+ trace: stack?.split('\n').slice(1)[map](v => v.trim() ?? skip) ?? [],
413
+ ...props,
414
+ cause: !cause ? null : cause[limn](seen)
415
+ };
451
416
  },
452
417
  [mod](props = {} /* { cause, msg, message, ...more } */) {
453
- if (isForm(props, Function))
418
+ if (isCls(props, Function))
454
419
  props = props(this.message, this);
455
- if (isForm(props, String))
420
+ if (isCls(props, String))
456
421
  props = { message: props };
457
422
  const { cause = null, msg = null, message = msg ?? this.message, ...moreProps } = props;
458
423
  // - Assign `cause` to transfer props like fs "code" props, etc. - watch out, `cause` may be
@@ -460,56 +425,47 @@ const applyClearing = (() => {
460
425
  // - Assign `moreProps` to transfer any other properties
461
426
  // - Add `message` prop
462
427
  // - Only add `cause` prop if `cause` is non-null
463
- return Object.assign(this, hasForm(cause, Error) ? cause : {}, moreProps, cause ? { message, cause } : { message });
428
+ return Object.assign(this, inCls(cause, Error) ? cause : {}, moreProps, cause ? { message, cause } : { message });
464
429
  },
465
- [fire](props /* { cause, msg, message, ...more } */) { throw this[mod](props); },
466
430
  [suppress]() {
467
431
  this[Symbol.for('clearing.err.suppressed')] = true;
468
432
  if (this.cause) {
469
- const causes = hasForm(this.cause, Error) ? [this.cause] : this.cause;
433
+ const causes = inCls(this.cause, Error) ? [this.cause] : this.cause;
470
434
  for (const err of causes)
471
435
  err[suppress]();
472
436
  }
473
437
  return this;
474
- },
475
- [limn](seen = new Map()) {
476
- if (seen.has(this))
477
- return seen.get(this);
478
- seen.set(this, 'cycle(Error)');
479
- const { message, stack, cause, ...props } = this;
480
- return {
481
- form: getFormName(this),
482
- msg: message,
483
- trace: stack?.split('\n').slice(1)[map](v => v.trim() ?? skip) ?? [],
484
- ...props,
485
- cause: !cause ? null : cause[limn](seen)
486
- };
487
438
  }
488
439
  });
489
440
  protoDefs(Promise, {
490
- $resolve: Promise.resolve,
491
- $reject: Promise.reject,
492
- $all: Promise.all,
493
- $allArr: Promise.all,
494
- $later: (resolve, reject) => {
495
- const p = new Promise((...a) => [resolve, reject] = a);
496
- return Object.assign(p, { resolve, reject });
497
- },
498
- $allObj: (obj) => {
499
- // Need to get `keys` here in case `obj` mutates before resolution
500
- const keys = Object.keys(obj);
501
- return Promise.allArr(Object.values(obj)).then(vals => {
502
- const ret = {};
503
- for (const [i, k] of keys.entries())
504
- if (vals[i] !== skip)
505
- ret[k] = vals[i];
506
- return ret;
507
- });
441
+ $: {
442
+ [allArr]: Promise.all,
443
+ [allObj]: (obj) => {
444
+ // Need to get `keys` immediately, in case `obj` mutates before resolution
445
+ const keys = Object.keys(obj);
446
+ return Promise.all(Object.values(obj)).then(vals => {
447
+ const ret = {};
448
+ for (const [i, k] of keys.entries())
449
+ if (vals[i] !== skip)
450
+ ret[k] = vals[i];
451
+ return ret;
452
+ });
453
+ },
454
+ [later]: (resolve, reject) => {
455
+ const p = new Promise((...a) => [resolve, reject] = a);
456
+ return Object.assign(p, { resolve, reject });
457
+ }
508
458
  }
509
459
  });
510
460
  protoDefs(Set, {
511
- $stub: { count: () => 0, add: Function.stub, rem: Function.stub, has: () => false, values: () => Array.stub },
512
- 'delete->': rem,
461
+ [count]() { return this.size; },
462
+ [empty]() { return !this.size; },
463
+ [find](fn) {
464
+ for (const val of this)
465
+ if (fn(val))
466
+ return { found: true, val };
467
+ return { found: false, val: null };
468
+ },
513
469
  [map](fn) {
514
470
  const ret = [];
515
471
  let ind = 0;
@@ -520,14 +476,7 @@ const applyClearing = (() => {
520
476
  }
521
477
  return ret;
522
478
  },
523
- [find](fn) {
524
- for (const val of this)
525
- if (fn(val))
526
- return { found: true, val };
527
- return { found: false, val: null };
528
- },
529
- [count]() { return this.size; },
530
- [empty]() { return !this.size; },
479
+ [rem]: Set.prototype.delete,
531
480
  [toArr](fn) {
532
481
  const ret = [];
533
482
  let ind = 0;
@@ -549,27 +498,16 @@ const applyClearing = (() => {
549
498
  }
550
499
  });
551
500
  protoDefs(Map, {
552
- $stub: { count: () => 0, set: Function.stub, rem: Function.stub, has: () => false, values: () => Array.stub },
553
- 'set->': add,
554
- 'delete->': rem,
555
- [map](fn) {
556
- const ret = [];
557
- for (const [k, v] of this) {
558
- const r = fn(v, k);
559
- if (r !== skip)
560
- ret.push(r);
561
- }
562
- return Object.fromEntries(ret);
563
- },
501
+ [add]: Map.prototype.set,
502
+ [count]() { return this.size; },
503
+ [empty]() { return !this.size; },
564
504
  [find](fn) {
565
505
  for (const [k, v] of this)
566
506
  if (fn(v, k))
567
507
  return { found: true, val: v, key: k };
568
508
  return { found: false, val: null, key: null };
569
509
  },
570
- [count]() { return this.size; },
571
- [empty]() { return !this.size; },
572
- [toObj](fn) {
510
+ [map](fn) {
573
511
  const ret = [];
574
512
  for (const [k, v] of this) {
575
513
  const r = fn(v, k);
@@ -578,6 +516,7 @@ const applyClearing = (() => {
578
516
  }
579
517
  return Object.fromEntries(ret);
580
518
  },
519
+ [rem]: Map.prototype.delete,
581
520
  [toArr](fn) {
582
521
  const ret = [];
583
522
  for (const [k, v] of this) {
@@ -586,18 +525,15 @@ const applyClearing = (() => {
586
525
  ret.push(r);
587
526
  }
588
527
  return ret;
589
- }
590
- });
591
- protoDefs(GeneratorFunction, {
592
- [toArr](fn) { return [...this][map](fn); },
528
+ },
593
529
  [toObj](fn) {
594
- const ret = {};
595
- for (const v of this) {
596
- const r = fn(v);
530
+ const ret = [];
531
+ for (const [k, v] of this) {
532
+ const r = fn(v, k);
597
533
  if (r !== skip)
598
- ret[r[0]] = r[1];
534
+ ret.push(r);
599
535
  }
600
- return ret;
536
+ return Object.fromEntries(ret);
601
537
  }
602
538
  });
603
539
  });