@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.
- package/LICENSE +21 -0
- package/README.md +61 -0
- package/dist/array.library.d.ts +25 -0
- package/dist/array.library.js +78 -0
- package/dist/buffer.library.d.ts +6 -0
- package/dist/buffer.library.js +192 -0
- package/dist/cipher.class.d.ts +18 -0
- package/dist/cipher.class.js +65 -0
- package/dist/class.library.d.ts +10 -0
- package/dist/class.library.js +57 -0
- package/dist/coercion.library.d.ts +14 -0
- package/dist/coercion.library.js +71 -0
- package/dist/enumerate.library.d.ts +64 -0
- package/dist/enumerate.library.js +54 -0
- package/dist/function.library.d.ts +9 -0
- package/dist/function.library.js +49 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +3 -0
- package/dist/logify.class.d.ts +33 -0
- package/dist/logify.class.js +53 -0
- package/dist/number.library.d.ts +16 -0
- package/dist/number.library.js +36 -0
- package/dist/object.library.d.ts +37 -0
- package/dist/object.library.js +94 -0
- package/dist/pledge.class.d.ts +54 -0
- package/dist/pledge.class.js +131 -0
- package/dist/prototype.library.d.ts +33 -0
- package/dist/prototype.library.js +51 -0
- package/dist/reflection.library.d.ts +74 -0
- package/dist/reflection.library.js +97 -0
- package/dist/serialize.library.d.ts +25 -0
- package/dist/serialize.library.js +266 -0
- package/dist/storage.library.d.ts +8 -0
- package/dist/storage.library.js +57 -0
- package/dist/string.library.d.ts +37 -0
- package/dist/string.library.js +93 -0
- package/dist/tempo.class.d.ts +556 -0
- package/dist/tempo.class.js +1412 -0
- package/dist/tempo.config/plugins/term.import.d.ts +42 -0
- package/dist/tempo.config/plugins/term.import.js +44 -0
- package/dist/tempo.config/plugins/term.quarter.d.ts +7 -0
- package/dist/tempo.config/plugins/term.quarter.js +28 -0
- package/dist/tempo.config/plugins/term.season.d.ts +7 -0
- package/dist/tempo.config/plugins/term.season.js +36 -0
- package/dist/tempo.config/plugins/term.timeline.d.ts +7 -0
- package/dist/tempo.config/plugins/term.timeline.js +19 -0
- package/dist/tempo.config/plugins/term.utils.d.ts +17 -0
- package/dist/tempo.config/plugins/term.utils.js +38 -0
- package/dist/tempo.config/plugins/term.zodiac.d.ts +7 -0
- package/dist/tempo.config/plugins/term.zodiac.js +62 -0
- package/dist/tempo.config/tempo.default.d.ts +169 -0
- package/dist/tempo.config/tempo.default.js +158 -0
- package/dist/tempo.config/tempo.enum.d.ts +99 -0
- package/dist/tempo.config/tempo.enum.js +78 -0
- package/dist/temporal.polyfill.d.ts +9 -0
- package/dist/temporal.polyfill.js +18 -0
- package/dist/type.library.d.ts +296 -0
- package/dist/type.library.js +80 -0
- package/dist/utility.library.d.ts +32 -0
- package/dist/utility.library.js +54 -0
- 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
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Tempo } from './tempo.class.js';
|
package/dist/index.js
ADDED
|
@@ -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
|
+
}
|