@magmacomputing/tempo 1.0.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.
Files changed (61) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +61 -0
  3. package/dist/array.library.d.ts +25 -0
  4. package/dist/array.library.js +78 -0
  5. package/dist/buffer.library.d.ts +6 -0
  6. package/dist/buffer.library.js +192 -0
  7. package/dist/cipher.class.d.ts +18 -0
  8. package/dist/cipher.class.js +65 -0
  9. package/dist/class.library.d.ts +10 -0
  10. package/dist/class.library.js +57 -0
  11. package/dist/coercion.library.d.ts +14 -0
  12. package/dist/coercion.library.js +71 -0
  13. package/dist/enumerate.library.d.ts +64 -0
  14. package/dist/enumerate.library.js +54 -0
  15. package/dist/function.library.d.ts +9 -0
  16. package/dist/function.library.js +49 -0
  17. package/dist/index.d.ts +1 -0
  18. package/dist/index.js +3 -0
  19. package/dist/logify.class.d.ts +33 -0
  20. package/dist/logify.class.js +53 -0
  21. package/dist/number.library.d.ts +16 -0
  22. package/dist/number.library.js +36 -0
  23. package/dist/object.library.d.ts +37 -0
  24. package/dist/object.library.js +94 -0
  25. package/dist/pledge.class.d.ts +54 -0
  26. package/dist/pledge.class.js +131 -0
  27. package/dist/prototype.library.d.ts +33 -0
  28. package/dist/prototype.library.js +51 -0
  29. package/dist/reflection.library.d.ts +74 -0
  30. package/dist/reflection.library.js +97 -0
  31. package/dist/serialize.library.d.ts +25 -0
  32. package/dist/serialize.library.js +266 -0
  33. package/dist/storage.library.d.ts +8 -0
  34. package/dist/storage.library.js +57 -0
  35. package/dist/string.library.d.ts +37 -0
  36. package/dist/string.library.js +93 -0
  37. package/dist/tempo.class.d.ts +556 -0
  38. package/dist/tempo.class.js +1412 -0
  39. package/dist/tempo.config/plugins/term.import.d.ts +42 -0
  40. package/dist/tempo.config/plugins/term.import.js +44 -0
  41. package/dist/tempo.config/plugins/term.quarter.d.ts +7 -0
  42. package/dist/tempo.config/plugins/term.quarter.js +28 -0
  43. package/dist/tempo.config/plugins/term.season.d.ts +7 -0
  44. package/dist/tempo.config/plugins/term.season.js +36 -0
  45. package/dist/tempo.config/plugins/term.timeline.d.ts +7 -0
  46. package/dist/tempo.config/plugins/term.timeline.js +19 -0
  47. package/dist/tempo.config/plugins/term.utils.d.ts +17 -0
  48. package/dist/tempo.config/plugins/term.utils.js +38 -0
  49. package/dist/tempo.config/plugins/term.zodiac.d.ts +7 -0
  50. package/dist/tempo.config/plugins/term.zodiac.js +62 -0
  51. package/dist/tempo.config/tempo.default.d.ts +169 -0
  52. package/dist/tempo.config/tempo.default.js +158 -0
  53. package/dist/tempo.config/tempo.enum.d.ts +99 -0
  54. package/dist/tempo.config/tempo.enum.js +78 -0
  55. package/dist/temporal.polyfill.d.ts +9 -0
  56. package/dist/temporal.polyfill.js +18 -0
  57. package/dist/type.library.d.ts +296 -0
  58. package/dist/type.library.js +80 -0
  59. package/dist/utility.library.d.ts +32 -0
  60. package/dist/utility.library.js +54 -0
  61. package/package.json +54 -0
@@ -0,0 +1,64 @@
1
+ import type { Index, Prettify, Entry, Invert, Property, CountOf, KeyOf, ValueOf, EntryOf, LooseKey, WellKnownSymbols } from './type.library.js';
2
+ /** used to identify the Enumify type */ declare const tag: "Enumify";
3
+ /** This is the prototype signature for an Enum object. */ type Proto<T extends Property<any>> = Readonly<{
4
+ /** count of Enum keys */ count(): CountOf<KeyOf<T>>;
5
+ /** array of Enum keys */ keys(): KeyOf<T>[];
6
+ /** array of Enum values */ values(): ValueOf<T>[];
7
+ /** tuple of Enum entries */ entries(): EntryOf<T>[];
8
+ /** new Enum with inverted key-values except invert() */ invert(): Prettify<Invert<T> & Omit<Proto<T>, "invert">>;
9
+ /** serialized Enum */ toString(): string;
10
+ /** key exists in Enum */ has<K extends LooseKey<keyof T>>(key: K): boolean;
11
+ /** value exists in Enum */ includes<V extends LooseKey<T[keyof T]>>(value: V): boolean;
12
+ /** reverse lookup of Enum key by value */ keyOf<V extends ValueOf<T>>(value: V): Invert<T>[V];
13
+ /** loop over Enum entries */ forEach(fn: (entry: EntryOf<T>, index: number, enumify: Enum.wrap<T>) => void, thisArg?: any): void;
14
+ /** subset of Enum entries */ filter(fn: (entry: EntryOf<T>, index: number, enumify: Enum.wrap<T>) => boolean, thisArg?: any): Enum.wrap<T>;
15
+ /** map of Enum entries */ map<U>(fn: (entry: EntryOf<T>, index: number, enumify: Enum.wrap<T>) => [PropertyKey, U] | U, thisArg?: any): any;
16
+ /** iterator for Enum */ [Symbol.iterator](): Iterator<Entry<T extends {} ? T : never>, EntryOf<T>>;
17
+ /** string tag */ [Symbol.toStringTag]: typeof tag;
18
+ }>;
19
+ /** namespace for Enum type-helpers */
20
+ type Methods<T extends Property<any> = any> = Omit<Proto<T>, WellKnownSymbols>;
21
+ export declare namespace Enum {
22
+ /** Enum properties & methods */ type wrap<T extends Property<any>> = props<T> & Methods<T>;
23
+ /** Enum methods (filtered) */ type methods<T extends Property<any> = any> = keyof Methods<T>;
24
+ /** Enum own properties */ type props<T extends Property<any>> = Readonly<T>;
25
+ }
26
+ /** Argument can be an array of PropertyKeys */ type ArrayArg = ReadonlyArray<PropertyKey>;
27
+ /** Argument can be a JSON object */ type ObjectArg = Record<PropertyKey, any>;
28
+ /**
29
+ * # Enumify
30
+ * The intent of this function is to provide Javascript-supported syntax for an object to behave as a read-only Enum.
31
+ * It can be used instead of Typescript's Enum (which must be compiled down to plain Javascript, and is not supported in NodeJS)
32
+ *
33
+ * @example
34
+ * ```typescript
35
+ * const SEASON = enumify({ Spring: 'spring', Summer: 'summer', Autumn: 'autumn', Winter: 'winter' });
36
+ * type SEASON = ValueOf<typeof SEASON>
37
+ * ```
38
+ *
39
+ * | Method | Value |
40
+ * | :--- | :---- |
41
+ * | `SEASON.keys()` | ['Spring', 'Summer', 'Autumn', 'Winter'] |
42
+ * | `SEASON.values()` | ['spring', 'summer', 'autumn', 'winter'] |
43
+ * | `SEASON.entries()` | ['Spring','spring'], ['Summer','summer'], ['Autumn','autumn'], ['Winter','winter']] |
44
+ * | `SEASON.count()` | 4 |
45
+ * | `SEASON.keyOf(SEASON.Summer)` | 'Summer' (using the Enum value directly) |
46
+ * | `SEASON.keyOf('summer')` | 'Summer' (using a string value) |
47
+ * | `SEASON.toString()` | '{"Spring": "spring", "Summer": "summer", "Autumn": "autumn", "Winter": "winter"}' |
48
+ * | `SEASON.invert() ` | enumify({spring: 'Spring', summer: 'Summer', autumn: 'Autumn', winter: 'Winter'}) |
49
+ * | `SEASON.has('xxx')` | false (using an invalid key) |
50
+ * | `SEASON.includes('summer')` | true (using a string value) |
51
+ * | `Object.prototype.toString.call(SEASON).slice(8,-1)` | 'Enumify' |
52
+ *
53
+ * @template T - The structure of the enumeration (Array or Object)
54
+ * if Array, Values will be the array indices. if Object, keys and values are preserved.
55
+ * @param {T} list - The list of keys (Array) or key-value pairs (Object) to enumify
56
+ * @returns {Enum.wrap<T>} A frozen object enhanced with enumeration methods
57
+ */
58
+ export declare function enumify<const T extends ArrayArg>(list: T): Enum.wrap<Index<T>>;
59
+ export declare function enumify<const T extends ObjectArg>(list: T): Enum.wrap<T>;
60
+ /** create an entry in the Serialization Registry to describe how to rebuild an Enum */
61
+ export declare class Enumify {
62
+ constructor(list: Property<any>);
63
+ }
64
+ export {};
@@ -0,0 +1,54 @@
1
+ import { secure } from './utility.library.js';
2
+ import { Serializable } from './class.library.js';
3
+ import { stringify } from './serialize.library.js';
4
+ import { memoizeMethod } from './function.library.js';
5
+ import { ownKeys, ownValues, ownEntries } from './reflection.library.js';
6
+ import { asType, isArray } from './type.library.js';
7
+ /** used to identify the Enumify type */ const tag = 'Enumify';
8
+ /**
9
+ * This is the prototype implementation for an Enum object.
10
+ * It contains just the methods / symbols we need, without standard Object methods like `hasOwnProperty`, etc.
11
+ */
12
+ const ENUM = secure(Object.create(null, {
13
+ count: memoizeMethod('count', function () { return ownKeys(this).length; }),
14
+ keys: memoizeMethod('keys', function () { return ownKeys(this); }),
15
+ values: memoizeMethod('values', function () { return ownValues(this); }),
16
+ entries: memoizeMethod('entries', function () { return ownEntries(this); }),
17
+ invert: memoizeMethod('invert', function () { return enumify(this.entries().reduce((acc, [key, val]) => ({ ...acc, [val]: key }), {})); }),
18
+ toString: memoizeMethod('toString', function () { return stringify({ ...this }); }),
19
+ has: value(function (key) { return this.keys().includes(key); }),
20
+ includes: value(function (search) { return this.values().includes(search); }),
21
+ keyOf: value(function (search) { return this.invert()[search]; }),
22
+ forEach: value(function (fn, thisArg) { this.entries().forEach((entry, index) => fn.call(thisArg, entry, index, this)); }),
23
+ filter: value(function (fn, thisArg) { return enumify(this.entries().reduce((acc, entry, index) => (fn.call(thisArg, entry, index, this) ? Object.assign(acc, { [entry[0]]: entry[1] }) : acc), {})); }),
24
+ map: value(function (fn, thisArg) { return enumify(this.entries().reduce((acc, entry, index) => { const res = fn.call(thisArg, entry, index, this); return Object.assign(acc, isArray(res) && res.length === 2 ? { [res[0]]: res[1] } : { [entry[0]]: res }); }, {})); }),
25
+ [Symbol.iterator]: value(function () { return this.entries()[Symbol.iterator](); }),
26
+ [Symbol.toStringTag]: value(tag),
27
+ }));
28
+ /** define a Descriptor for an Enum's method */
29
+ function value(value) {
30
+ return Object.assign({ enumerable: false, configurable: false, writable: false, value });
31
+ }
32
+ export function enumify(list) {
33
+ const arg = asType(list);
34
+ const stash = {};
35
+ switch (arg.type) {
36
+ case 'Object':
37
+ Object.assign(stash, arg.value);
38
+ break;
39
+ case 'Array':
40
+ arg.value // reduce Array to an Object with indexes as values
41
+ .reduce((_, itm, idx) => Object.assign(stash, { [itm]: idx }), stash);
42
+ break;
43
+ default:
44
+ throw new Error(`enumify requires an Object or Array as input: received ${arg.type} instead`);
45
+ }
46
+ return secure(Object.create(ENUM, Object.getOwnPropertyDescriptors(stash)));
47
+ }
48
+ /** create an entry in the Serialization Registry to describe how to rebuild an Enum */
49
+ @Serializable
50
+ export class Enumify {
51
+ constructor(list) {
52
+ return enumify(list);
53
+ }
54
+ }
@@ -0,0 +1,9 @@
1
+ import { type Property } from './type.library.js';
2
+ type Curry<Args extends any[], Res> = Args extends [infer FirstArg, ...infer RestArgs] ? (arg: FirstArg) => Curry<RestArgs, Res> : Res;
3
+ /** curry a Function to allow partial calls */
4
+ export declare function curry<Args extends any[], Res>(fn: (...args: Args) => Res): Curry<Args, Res>;
5
+ /** generic function to memoize repeated function calls */
6
+ export declare function memoizeFunction<F extends (...args: any[]) => any>(fn: F): (...args: unknown[]) => ReturnType<F> | undefined;
7
+ /** define a Descriptor for an Object's memoized-method */
8
+ export declare function memoizeMethod<T>(name: PropertyKey, fn: (this: Property<any>, ...args: any[]) => T): PropertyDescriptor;
9
+ export {};
@@ -0,0 +1,49 @@
1
+ import { secure } from './utility.library.js';
2
+ import { isUndefined } from './type.library.js';
3
+ /** curry a Function to allow partial calls */
4
+ export function curry(fn) {
5
+ return function curried(...args) {
6
+ return (args.length >= fn.length)
7
+ ? fn(...args)
8
+ : (...nextArgs) => curried(...args, ...nextArgs);
9
+ };
10
+ }
11
+ /** generic function to memoize repeated function calls */
12
+ export function memoizeFunction(fn) {
13
+ const cache = new Map(); // using a Map for better key handling than plain objects
14
+ return function (...args) {
15
+ const key = JSON.stringify(args); // create a unique key from arguments
16
+ console.log('memoize: ', key);
17
+ if (!cache.has(key)) {
18
+ // @ts-ignore
19
+ const result = fn.apply(this, args); // call the original function with the correct context
20
+ console.log('set: ', result);
21
+ cache.set(key, Object.freeze(result)); // stash the result for subsequent calls
22
+ }
23
+ else
24
+ console.log('get: ', cache.get(key));
25
+ return cache.get(key);
26
+ };
27
+ }
28
+ const wm = new WeakMap();
29
+ /** define a Descriptor for an Object's memoized-method */
30
+ export function memoizeMethod(name, fn) {
31
+ return {
32
+ enumerable: true,
33
+ configurable: false,
34
+ writable: false,
35
+ value: function (...args) {
36
+ const key = `${String(name)},${JSON.stringify(args)}`;
37
+ let cache = wm.get(this);
38
+ if (!cache) { // add a new object into the WeakMap
39
+ cache = Object.create(null);
40
+ wm.set(this, cache);
41
+ }
42
+ if (isUndefined(cache[key])) { // first time for this method
43
+ cache[key] = fn.apply(this, args); // evaluate the method
44
+ secure(cache[key]); // freeze the returned value
45
+ }
46
+ return cache[key];
47
+ }
48
+ };
49
+ }
@@ -0,0 +1 @@
1
+ export { Tempo } from './tempo.class.js';
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export { Tempo } from './tempo.class.js';
2
+ // Add any other intended public exports here.
3
+ // For now, Tempo is the primary interface.
@@ -0,0 +1,33 @@
1
+ import { type ValueOf } from './type.library.js';
2
+ declare const Method: {
3
+ readonly Log: "log";
4
+ readonly Info: "info";
5
+ readonly Warn: "warn";
6
+ readonly Debug: "debug";
7
+ readonly Error: "error";
8
+ };
9
+ /**
10
+ * provide standard logging methods to the console for a class
11
+ */
12
+ export declare class Logify {
13
+ #private;
14
+ /**
15
+ * if {catch:true} then show a warning on the console and return
16
+ * otherwise show an error on the console and re-throw the error
17
+ */
18
+ catch(...msg: any[]): void;
19
+ /** console.log */ log: (...args: any[]) => void;
20
+ /** console.info */ info: (...args: any[]) => void;
21
+ /** console.warn */ warn: (...args: any[]) => void;
22
+ /** console.debug */ debug: (...args: any[]) => void;
23
+ /** console.error */ error: (...args: any[]) => void;
24
+ constructor(self?: Logify.Constructor | string, opts?: Logify.Constructor);
25
+ }
26
+ export declare namespace Logify {
27
+ type Method = ValueOf<typeof Method>;
28
+ interface Constructor {
29
+ debug?: boolean | undefined;
30
+ catch?: boolean | undefined;
31
+ }
32
+ }
33
+ export {};
@@ -0,0 +1,53 @@
1
+ import { Immutable } from './class.library.js';
2
+ import { asType } from './type.library.js';
3
+ const Method = {
4
+ Log: 'log',
5
+ Info: 'info',
6
+ Warn: 'warn',
7
+ Debug: 'debug',
8
+ Error: 'error',
9
+ };
10
+ /**
11
+ * provide standard logging methods to the console for a class
12
+ */
13
+ @Immutable
14
+ export class Logify {
15
+ #name;
16
+ #opts = {};
17
+ #log(method, ...msg) {
18
+ if (this.#opts.debug)
19
+ console[method](this.#name, ...msg);
20
+ }
21
+ /**
22
+ * if {catch:true} then show a warning on the console and return
23
+ * otherwise show an error on the console and re-throw the error
24
+ */
25
+ catch(...msg) {
26
+ if (this.#opts.catch) {
27
+ this.warn(...msg); // show a warning on the console
28
+ return; // safe-return
29
+ }
30
+ this.error(...msg); // this goes to the console
31
+ throw new Error(`${this.#name}${msg}`); // this goes back to the caller
32
+ }
33
+ /** console.log */ log = this.#log.bind(this, Method.Log);
34
+ /** console.info */ info = this.#log.bind(this, Method.Info);
35
+ /** console.warn */ warn = this.#log.bind(this, Method.Warn);
36
+ /** console.debug */ debug = this.#log.bind(this, Method.Debug);
37
+ /** console.error */ error = this.#log.bind(this, Method.Error);
38
+ constructor(self, opts = {}) {
39
+ const arg = asType(self);
40
+ switch (arg.type) {
41
+ case 'String':
42
+ this.#name = arg.value;
43
+ break;
44
+ // @ts-ignore
45
+ case 'Object':
46
+ Object.assign(opts, arg.value);
47
+ default:
48
+ this.#name = (self ?? this).constructor.name.concat(': ') ?? '';
49
+ }
50
+ this.#opts.debug = opts.debug ?? false; // default debug to 'false'
51
+ this.#opts.catch = opts.catch ?? false; // default catch to 'false'
52
+ }
53
+ }
@@ -0,0 +1,16 @@
1
+ import type { TValues } from './type.library.js';
2
+ /** convert String | Number | BigInt to Number */
3
+ export { asNumber, asInteger, isNumeric, ifNumeric } from './coercion.library.js';
4
+ /** show Hex value of a number */
5
+ export declare const toHex: (num?: TValues<number>, len?: number) => string;
6
+ /** apply an Ordinal suffix */
7
+ export declare const suffix: (idx: number) => string;
8
+ /** split a value into an array */
9
+ export declare function split<T extends number>(nbr: T, chr?: string, zero?: boolean): number[];
10
+ export declare function split<T extends string>(nbr: T, chr?: string, zero?: boolean): (string | number)[];
11
+ /** fix a string to set decimal precision */
12
+ export declare const fix: (nbr?: string | number, max?: number) => string;
13
+ /** remove ':' from an HH:MI string, return as number */
14
+ export declare const asTime: (hhmi: string | number) => number;
15
+ /** format a value as currency */
16
+ export declare const asCurrency: (str: string | number, scale?: number, currency?: string) => string;
@@ -0,0 +1,36 @@
1
+ import { asArray, asNumber, ifNumeric } from './coercion.library.js';
2
+ /** convert String | Number | BigInt to Number */
3
+ export { asNumber, asInteger, isNumeric, ifNumeric } from './coercion.library.js';
4
+ /** show Hex value of a number */
5
+ export const toHex = (num = [], len) => asArray(num) // ensure array
6
+ .flat(1_000_000) // flatten any arrays to arbitrary depth
7
+ .filter(Number.isInteger) // ensure integers
8
+ .map(val => (val + 0x100).toString(16).slice(-2))
9
+ .join('')
10
+ .toLowerCase()
11
+ .substring(0, len ?? Number.MAX_SAFE_INTEGER);
12
+ /** apply an Ordinal suffix */
13
+ export const suffix = (idx) => {
14
+ const str = String(idx ?? ''); // so we can use 'endsWith'
15
+ switch (true) {
16
+ case str.endsWith('1') && !str.endsWith('11'):
17
+ return str + 'st';
18
+ case str.endsWith('2') && !str.endsWith('12'):
19
+ return str + 'nd';
20
+ case str.endsWith('3') && !str.endsWith('13'):
21
+ return str + 'rd';
22
+ default:
23
+ return str + 'th';
24
+ }
25
+ };
26
+ export function split(nbr, chr = '.', zero = true) {
27
+ return nbr?.toString().split(chr).map(val => ifNumeric(val, zero))
28
+ || [];
29
+ }
30
+ ;
31
+ /** fix a string to set decimal precision */
32
+ export const fix = (nbr = 0, max = 2) => asNumber(nbr).toFixed(max);
33
+ /** remove ':' from an HH:MI string, return as number */
34
+ export const asTime = (hhmi) => Number(String(hhmi).replace(':', ''));
35
+ /** format a value as currency */
36
+ export const asCurrency = (str, scale = 2, currency = 'AUD') => str.toLocaleString(void 0, { style: 'currency', currency, maximumFractionDigits: scale });
@@ -0,0 +1,37 @@
1
+ import type { Extend, Property } from './type.library.js';
2
+ /** Get nested value */
3
+ export declare function extract<T>(obj: any, path: string | number, dflt?: T): T;
4
+ /** remove quotes around property names */
5
+ export declare const unQuoteObj: (obj: any) => string;
6
+ /** copy enumerable properties to a new Object */
7
+ export declare const asObject: <T>(obj?: Record<PropertyKey, any>) => T;
8
+ /** deep-compare object values for equality */
9
+ export declare const isEqual: (obj1?: any, obj2?: any) => boolean;
10
+ /** find all methods on an Object */
11
+ export declare const getMethods: (obj: any, all?: boolean) => PropertyKey[];
12
+ /** extract only defined values from Object */
13
+ export declare function ifDefined<T extends Property<any>>(obj: T): T;
14
+ /** extract a subset of keys from an object */
15
+ export declare const pick: <T extends Property<T>, K extends string>(obj: T, ...keys: K[]) => Partial<T>;
16
+ /** extract a named key from an array of objects */
17
+ export declare const pluck: <T, K extends keyof T>(objs: T[], key: K) => T[K][];
18
+ /** extend an object with the properties of another */
19
+ export declare const extend: <T extends {}, U>(obj: T, ...objs: U[]) => T;
20
+ export declare const countProperties: (obj?: {}) => number;
21
+ /**
22
+ * helper to define objects with fixed literal properties
23
+ * and a loose index signature for further extensions.
24
+ * @example
25
+ * ```
26
+ * const obj = looseIndex<string,string>()({ foo: 'bar', bar: 'foo' });
27
+ * type obj = typeof obj
28
+ * ```
29
+ */
30
+ export declare function looseIndex<K extends PropertyKey = string, V = any>(): <const T extends object>(obj: T | (() => T)) => Extend<T, K, V>;
31
+ export declare function looseIndex<const T extends object>(obj: T | (() => T)): Extend<T, string, any>;
32
+ export declare namespace looseIndex {
33
+ var stringSymbol: <const T extends object>(obj: T | (() => T)) => Extend<T, string, symbol>;
34
+ var symbolRegExp: <const T extends object>(obj: T | (() => T)) => Extend<T, symbol, RegExp>;
35
+ var symbolString: <const T extends object>(obj: T | (() => T)) => Extend<T, symbol, string>;
36
+ var stringString: <const T extends object>(obj: T | (() => T)) => Extend<T, string, string>;
37
+ }
@@ -0,0 +1,94 @@
1
+ import { ownKeys, ownEntries } from './reflection.library.js';
2
+ import { isObject, isArray, isReference, isFunction, isDefined, isEmpty, isNullish } from './type.library.js';
3
+ /** Get nested value */
4
+ export function extract(obj, path, dflt) {
5
+ if (isEmpty(path))
6
+ return obj; // finished searching
7
+ if (!isObject(obj) && !isArray(obj))
8
+ return obj;
9
+ return path
10
+ .toString()
11
+ .replace(/\[([^\[\]]*)\]/g, '.$1.') // convert [indexes] to properties
12
+ .split('.')
13
+ .filter(field => !isEmpty(field)) // remove empty fields
14
+ .reduce((acc, field) => acc?.[field] ?? null, obj) ?? dflt;
15
+ }
16
+ /** remove quotes around property names */
17
+ export const unQuoteObj = (obj) => {
18
+ return JSON.stringify(obj)
19
+ ?.replace(/"([^"]+)":/g, '$1: ')
20
+ ?.replace(/,/g, ', ');
21
+ };
22
+ /** copy enumerable properties to a new Object */
23
+ export const asObject = (obj) => {
24
+ if (isNullish(obj) || !isObject(obj))
25
+ return obj;
26
+ const temp = isArray(obj) ? [] : {};
27
+ ownKeys(obj)
28
+ .forEach(key => temp[key] = asObject(obj[key]));
29
+ return temp;
30
+ };
31
+ /** deep-compare object values for equality */
32
+ export const isEqual = (obj1 = {}, obj2 = {}) => {
33
+ const keys = new Set(); // union of unique keys from both Objects
34
+ const keys1 = isFunction(obj1.keys) ? Array.from(obj1.keys()) : ownKeys(obj1);
35
+ const keys2 = isFunction(obj2.keys) ? Array.from(obj2.keys()) : ownKeys(obj2);
36
+ keys1.forEach(key => keys.add(key));
37
+ keys2.forEach(key => keys.add(key));
38
+ return [...keys] // cast as Array
39
+ .every(key => {
40
+ const val1 = obj1[key];
41
+ const val2 = obj2[key];
42
+ return isReference(val1) && isReference(val2)
43
+ ? isEqual(val1, val2) // recurse into object
44
+ : val1 === val2;
45
+ });
46
+ };
47
+ /** find all methods on an Object */
48
+ export const getMethods = (obj, all = false) => {
49
+ const properties = new Set();
50
+ let currentObj = obj;
51
+ do {
52
+ Object
53
+ .getOwnPropertyNames(currentObj)
54
+ .map(key => properties.add(key));
55
+ } while (all && (currentObj = Object.getPrototypeOf(currentObj)));
56
+ return [...properties.keys()]
57
+ .filter(key => isFunction(obj[key]));
58
+ };
59
+ /** extract only defined values from Object */
60
+ export function ifDefined(obj) {
61
+ return ownEntries(obj)
62
+ .reduce((acc, [key, val]) => {
63
+ if (isDefined(val))
64
+ acc[key] = val;
65
+ return acc;
66
+ }, {});
67
+ }
68
+ /** extract a subset of keys from an object */
69
+ export const pick = (obj, ...keys) => {
70
+ const ownKeys = Object.getOwnPropertyNames(obj);
71
+ return keys.reduce((acc, key) => {
72
+ if (ownKeys.includes(key))
73
+ acc[key] = obj[key];
74
+ return acc;
75
+ }, {});
76
+ };
77
+ /** extract a named key from an array of objects */
78
+ export const pluck = (objs, key) => objs.map(obj => obj[key]);
79
+ /** extend an object with the properties of another */
80
+ export const extend = (obj, ...objs) => Object.assign(obj, ...objs);
81
+ export const countProperties = (obj = {}) => ownKeys(obj).length;
82
+ export function looseIndex(arg) {
83
+ if (isDefined(arg))
84
+ return isFunction(arg) ? arg() : arg;
85
+ return (obj) => isFunction(obj) ? obj() : obj;
86
+ }
87
+ /** loose object with symbols values */
88
+ looseIndex.stringSymbol = looseIndex();
89
+ /** loose object with symbol keys and RegExp values */
90
+ looseIndex.symbolRegExp = looseIndex();
91
+ /** loose object with symbol keys and string values */
92
+ looseIndex.symbolString = looseIndex();
93
+ /** loose object with string keys and string values */
94
+ looseIndex.stringString = looseIndex();
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Wrap a Promise's resolve/reject/finally methods for later fulfilment.
3
+ * with useful methods for tracking the state of the Promise, chaining fulfilment, etc.
4
+ ```
5
+ new Pledge<T>({tag: string, onResolve?: () => void, onReject?: () => void, onSettle?: () => void})
6
+ new Pledge<T>(tag?: string)
7
+ ```
8
+ */
9
+ export declare class Pledge<T> {
10
+ #private;
11
+ static STATE: import("./type.library.js").SecureObject<{
12
+ readonly Pending: symbol;
13
+ readonly Resolved: symbol;
14
+ readonly Rejected: symbol;
15
+ }>;
16
+ /** initialize future Pledge instances */
17
+ static init(arg?: Pledge.Constructor | string): Pledge.Status<typeof Pledge>;
18
+ static get status(): Pledge.Status<typeof Pledge>;
19
+ constructor(arg?: Pledge.Constructor | string);
20
+ get [Symbol.toStringTag](): string;
21
+ [Symbol.dispose](): void;
22
+ get status(): Pledge.Status<T>;
23
+ get promise(): Promise<T>;
24
+ get state(): string | undefined;
25
+ get isPending(): boolean;
26
+ get isResolved(): boolean;
27
+ get isRejected(): boolean;
28
+ get isSettled(): boolean;
29
+ toString(): string;
30
+ resolve(value: T): Promise<T>;
31
+ reject(error: any): Promise<T>;
32
+ then(fn: Function): void;
33
+ }
34
+ export declare namespace Pledge {
35
+ type Resolve = (val?: any) => any;
36
+ type Reject = (err: Error) => any;
37
+ type Settle = () => void;
38
+ type Constructor = {
39
+ tag?: string;
40
+ onResolve?: Pledge.Resolve | Pledge.Resolve[];
41
+ onReject?: Pledge.Reject | Pledge.Reject[];
42
+ onSettle?: Pledge.Settle | Pledge.Settle[];
43
+ debug?: boolean | undefined;
44
+ catch?: boolean | undefined;
45
+ };
46
+ interface Status<T> {
47
+ tag?: string;
48
+ debug?: boolean | undefined;
49
+ catch?: boolean | undefined;
50
+ state: symbol;
51
+ settled?: T;
52
+ error?: Error;
53
+ }
54
+ }
@@ -0,0 +1,131 @@
1
+ import { Logify } from './logify.class.js';
2
+ import { asArray } from './array.library.js';
3
+ import { sprintf } from './string.library.js';
4
+ import { ifDefined } from './object.library.js';
5
+ import { secure } from './utility.library.js';
6
+ import { cleanify } from './serialize.library.js';
7
+ import { Immutable } from './class.library.js';
8
+ import { isEmpty, isObject } from './type.library.js';
9
+ /**
10
+ * Wrap a Promise's resolve/reject/finally methods for later fulfilment.
11
+ * with useful methods for tracking the state of the Promise, chaining fulfilment, etc.
12
+ ```
13
+ new Pledge<T>({tag: string, onResolve?: () => void, onReject?: () => void, onSettle?: () => void})
14
+ new Pledge<T>(tag?: string)
15
+ ```
16
+ */
17
+ @Immutable
18
+ export class Pledge {
19
+ #pledge;
20
+ #status = {};
21
+ #dbg;
22
+ static #static = {};
23
+ static STATE = secure({
24
+ Pending: Symbol('pending'),
25
+ Resolved: Symbol('resolved'),
26
+ Rejected: Symbol('rejected')
27
+ });
28
+ /** initialize future Pledge instances */
29
+ static init(arg) {
30
+ if (isObject(arg)) {
31
+ if (isEmpty(arg))
32
+ Pledge.#static = {}; // reset static values
33
+ Object.assign(Pledge.#static, ifDefined({ tag: arg.tag, debug: arg.debug, catch: arg.catch, }), ifDefined({ onResolve: arg.onResolve, onReject: arg.onReject, onSettle: arg.onSettle, }));
34
+ }
35
+ else {
36
+ Object.assign(Pledge.#static, ifDefined({ tag: arg, }));
37
+ }
38
+ if (Pledge.#static.debug)
39
+ console.log('Pledge: ', Pledge.#static); // debug
40
+ return Pledge.status;
41
+ }
42
+ static get status() {
43
+ return Pledge.#static;
44
+ }
45
+ /** use catch:boolean to determine whether to throw or return */
46
+ #catch(...msg) {
47
+ if (this.status.catch) {
48
+ this.#dbg.warn(...msg); // catch, but warn {error}
49
+ return;
50
+ }
51
+ this.#dbg.error(...msg); // assume {error}
52
+ throw new Error(sprintf('pledge: ', ...msg));
53
+ }
54
+ constructor(arg) {
55
+ this.#pledge = Promise.withResolvers();
56
+ this.#status = { state: Pledge.STATE.Pending, ...Pledge.#static };
57
+ if (isObject(arg)) {
58
+ this.#dbg = new Logify({ debug: arg.debug, catch: arg.catch });
59
+ Object.assign(this.#status, ifDefined({ tag: Pledge.#static.tag, debug: Pledge.#static.debug, catch: Pledge.#static.catch }), ifDefined({ tag: arg.tag, debug: arg.debug, catch: arg.catch, }));
60
+ asArray(Pledge.#static.onResolve) // stack any static onResolve actions
61
+ .concat(asArray(arg.onResolve)) // stack any instance onResolve actions
62
+ .forEach(resolve => this.#pledge.promise.then(resolve));
63
+ asArray(Pledge.#static.onReject) // stack any static onReject actions
64
+ .concat(asArray(arg.onReject)) // stack any instance onReject actions
65
+ .forEach(reject => this.#pledge.promise.catch(reject));
66
+ asArray(Pledge.#static.onSettle) // stack any static onSettle actions
67
+ .concat(asArray(arg.onSettle)) // stack any instance onSettle actions
68
+ .forEach(settle => this.#pledge.promise.finally(settle));
69
+ if (this.#status.catch) // stack a 'catch-all'
70
+ this.#pledge.promise.catch(_ => this.#catch(this.#status, this.#status.error));
71
+ }
72
+ else {
73
+ this.#dbg = new Logify();
74
+ Object.assign(this.#status, ifDefined({ tag: arg ?? Pledge.#static.tag, }));
75
+ }
76
+ Object.freeze(this); // make this instance immutable
77
+ }
78
+ get [Symbol.toStringTag]() {
79
+ return 'Pledge';
80
+ }
81
+ [Symbol.dispose]() {
82
+ if (this.isPending)
83
+ this.reject(new Error(`Pledge disposed`)); // discard pending Pledge (to notify wait-ers)
84
+ }
85
+ get status() {
86
+ return cleanify(this.#status);
87
+ }
88
+ get promise() {
89
+ return this.#pledge.promise;
90
+ }
91
+ get state() {
92
+ return this.#status.state.description;
93
+ }
94
+ get isPending() {
95
+ return this.#status.state === Pledge.STATE.Pending;
96
+ }
97
+ get isResolved() {
98
+ return this.#status.state === Pledge.STATE.Resolved;
99
+ }
100
+ get isRejected() {
101
+ return this.#status.state === Pledge.STATE.Rejected;
102
+ }
103
+ get isSettled() {
104
+ return this.#status.state !== Pledge.STATE.Pending;
105
+ }
106
+ toString() {
107
+ return JSON.stringify(this.status);
108
+ }
109
+ resolve(value) {
110
+ if (this.isPending) {
111
+ this.#status.settled = value;
112
+ this.#status.state = Pledge.STATE.Resolved;
113
+ this.#pledge.resolve(value); // resolve, then trigger any Pledge.onResolve, then Pledge.onSettle
114
+ }
115
+ else
116
+ this.#dbg.warn(this.#status, `Pledge was already ${this.state}`);
117
+ return this.#pledge.promise;
118
+ }
119
+ reject(error) {
120
+ if (this.isPending) {
121
+ this.#status.error = error;
122
+ this.#status.state = Pledge.STATE.Rejected;
123
+ this.#pledge.reject(error); // reject, then trigger any Pledge.onReject, then Pledge.onSettle
124
+ }
125
+ else
126
+ this.#dbg.warn(this.#status, `Pledge was already ${this.state}`);
127
+ return this.#pledge.promise;
128
+ }
129
+ then(fn) {
130
+ }
131
+ }