Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "tempo-monorepo",
"version": "2.4.0",
"version": "2.5.0",
"private": true,
"description": "Magma Computing Monorepo",
"repository": {
Expand Down
2 changes: 1 addition & 1 deletion packages/library/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@magmacomputing/library",
"version": "2.4.0",
"version": "2.5.0",
"description": "Shared utility library for Tempo",
"author": "Magma Computing Solutions",
"license": "MIT",
Expand Down
2 changes: 1 addition & 1 deletion packages/library/src/browser/mapper.library.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { asObject } from '#library/object.library.js';
import { CONTEXT, getContext } from '#library/utility.library.js';
import { isNullish } from '#library/type.library.js';
import { isNullish } from '#library/assertion.library.js';
import { instant } from '#library/temporal.library.js';
import { getHemisphere } from '#library/international.library.js';

Expand Down
3 changes: 2 additions & 1 deletion packages/library/src/browser/tapper.class.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { enumify } from '#library/enumerate.library.js';
import { isEmpty, isFunction, type ValueOf } from '#library/type.library.js';
import { isEmpty, isFunction } from '#library/assertion.library.js';
import type { ValueOf } from '#library/type.library.js';

/**
* A Wrapper Class around HammerJS.
Expand Down
3 changes: 2 additions & 1 deletion packages/library/src/browser/webstore.class.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { distinct, ownEntries } from '#library/primitive.library.js';
import { stringify, objectify } from '#library/serialize.library.js';
import { asType, isEmpty, isNullish, isString } from '#library/type.library.js';
import { asType } from '#library/type.library.js';
import { isEmpty, isNullish, isString } from '#library/assertion.library.js';
import type { Property, ValueOf } from '#library/type.library.js';

const STORAGE = {
Expand Down
1 change: 1 addition & 0 deletions packages/library/src/common.index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
*/

export * from './common/array.library.js';
export * from './common/assertion.library.js';
export * from './common/buffer.library.js';
export * from './common/cipher.class.js';
export * from './common/class.library.js';
Expand Down
4 changes: 2 additions & 2 deletions packages/library/src/common/array.library.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { asString } from '#library/coercion.library.js';
import { asString, nullToValue } from '#library/coercion.library.js';
import { extract, ownEntries } from '#library/primitive.library.js';
import { stringify } from '#library/serialize.library.js';
import { isNumber, isDate, isObject, isDefined, isUndefined, isFunction, nullToValue } from '#library/type.library.js';
import { isNumber, isDate, isObject, isDefined, isUndefined, isFunction } from '#library/assertion.library.js';
import type { Property } from '#library/type.library.js';

// adapted from https://jsbin.com/insert/4/edit?js,output
Expand Down
108 changes: 108 additions & 0 deletions packages/library/src/common/assertion.library.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import { sym } from '#library/symbol.library.js';
import { getType, protoType, asType } from '#library/type.library.js';
import type { Type, Primitive, Nullish, Temporals, Property, GetType } from '#library/type.library.js';

/** assert value is one of a list of Types */
export const isType = <T>(obj: unknown, ...types: Type[]): obj is T => types.includes(getType(obj));

/** Type-Guards: assert \<obj> is of \<type> */
export const isPrimitive = (obj?: unknown): obj is Primitive => isType(obj, 'String', 'Number', 'BigInt', 'Boolean', 'Symbol', 'Undefined', 'Void', 'Null', 'Empty');
export const isReference = (obj?: unknown): obj is Object => !isPrimitive(obj);
export const isIterable = <T>(obj: unknown): obj is Iterable<T> => Symbol.iterator in Object(obj) && !isString(obj);

export const isString = <T>(obj?: T): obj is Extract<T, string> => isType(obj, 'String');
export const isNumber = <T>(obj?: T): obj is Extract<T, number> => isType(obj, 'Number') && isFinite(obj as number);
Comment thread
magmacomputing marked this conversation as resolved.
Outdated

/** test if can convert String to Numeric */
export function isNumeric(str?: any): boolean {
const type = typeof str;
switch (type) {
case 'number': return isFinite(str);
case 'bigint': return true;
case 'string': {
const val = str.trim();
if (val.length === 0) return false;
return /^-?[0-9]+n$/.test(val) || (!isNaN(parseFloat(val)) && isFinite(Number(val)));
}
default: return false;
}
}
export const isInteger = <T>(obj?: T): obj is Extract<T, bigint> => isType(obj, 'BigInt');
export const isIntegerLike = <T>(obj?: T): obj is Extract<T, string> => isType(obj, 'String') && /^-?[0-9]+n$/.test(obj as string);
export const isDigit = <T>(obj?: T): obj is Extract<T, number | bigint> => isType(obj, 'Number', 'BigInt');
export const isBoolean = <T>(obj?: T): obj is Extract<T, boolean> => isType(obj, 'Boolean');
export const isArray = <T>(obj: unknown): obj is T[] => isType(obj, 'Array');
export const isArrayLike = <T>(obj: any): obj is ArrayLike<T> => protoType(obj) === 'Object' && 'length' in obj && Object.keys(obj).every(key => key === 'length' || !isNaN(Number(key)));
export const isObject = <T>(obj?: T): obj is Extract<T, object> => isType(obj, 'Object');
export const isDate = <T>(obj?: T): obj is Extract<T, Date> => isType(obj, 'Date');
export const isRegExp = <T>(obj?: T): obj is Extract<T, RegExp> => isType(obj, 'RegExp');
export const isRegExpLike = <T>(obj?: T): obj is Extract<T, string> => isType(obj, 'String') && /^\/.*\/$/.test(obj as string);
export const isSymbol = <T>(obj?: T): obj is Extract<T, symbol> => isType(obj, 'Symbol');
export const isSymbolFor = <T>(obj?: T): obj is Extract<T, symbol> => isType<symbol>(obj, 'Symbol') && Symbol.keyFor(obj) !== undefined;
export const isPropertyKey = (obj?: unknown): obj is PropertyKey => isType<PropertyKey>(obj, 'String', 'Number', 'Symbol');

export const isNull = <T>(obj?: T): obj is Extract<T, null> => isType(obj, 'Null');
export const isNullish = <T>(obj: T): obj is Extract<T, Nullish> => isType<undefined | null | void>(obj, 'Null', 'Undefined', 'Void', 'Empty');
export const isUndefined = <T>(obj?: T): obj is undefined => isType<undefined>(obj, 'Undefined', 'Void', 'Empty');
export const isDefined = <T>(obj: T): obj is NonNullable<T> => !isNullish(obj);

export const isClass = <T>(obj?: T): obj is Extract<T, Function> => isType(obj, 'Class');
export const isFunction = <T>(obj?: T): obj is Extract<T, Function> => isType(obj, 'Function', 'AsyncFunction');
export const isPromise = <T>(obj?: T): obj is Extract<T, Promise<any>> => isType(obj, 'Promise');
export const isMap = <T, K = any, V = any>(obj?: T): obj is Extract<T, Map<K, V>> => isType(obj, 'Map');
export const isSet = <T, K = any>(obj?: T): obj is Extract<T, Set<K>> => isType(obj, 'Set');
export const isError = <T>(err?: T): err is Extract<T, Error> => isType(err, 'Error');

export const isTemporal = <T>(obj: T): obj is Extract<T, Temporals> => protoType(obj).startsWith('Temporal.') || (!!(globalThis as any).Temporal && (
(obj as any) instanceof (globalThis as any).Temporal.Instant ||
(obj as any) instanceof (globalThis as any).Temporal.ZonedDateTime ||
(obj as any) instanceof (globalThis as any).Temporal.PlainDate ||
(obj as any) instanceof (globalThis as any).Temporal.PlainTime ||
(obj as any) instanceof (globalThis as any).Temporal.PlainDateTime ||
(obj as any) instanceof (globalThis as any).Temporal.Duration ||
(obj as any) instanceof (globalThis as any).Temporal.PlainYearMonth ||
(obj as any) instanceof (globalThis as any).Temporal.PlainMonthDay
));

export const isInstant = <T>(obj: T): obj is Extract<T, Temporal.Instant> => isType(obj, 'Temporal.Instant') || (!!(globalThis as any).Temporal?.Instant && (obj as any) instanceof (globalThis as any).Temporal.Instant) || (!!obj && (obj as any)[Symbol.toStringTag] === 'Temporal.Instant') || (!!obj && typeof (obj as any).toZonedDateTimeISO === 'function' && isUndefined((obj as any).timeZoneId));
export const isZonedDateTime = <T>(obj: T): obj is Extract<T, Temporal.ZonedDateTime> => isType(obj, 'Temporal.ZonedDateTime') || (!!(globalThis as any).Temporal?.ZonedDateTime && (obj as any) instanceof (globalThis as any).Temporal.ZonedDateTime) || (!!obj && (obj as any)[Symbol.toStringTag] === 'Temporal.ZonedDateTime') || (!!obj && typeof (obj as any).toInstant === 'function' && isDefined((obj as any).timeZoneId));
export const isPlainDate = <T>(obj: T): obj is Extract<T, Temporal.PlainDate> => isType(obj, 'Temporal.PlainDate') || (!!(globalThis as any).Temporal?.PlainDate && (obj as any) instanceof (globalThis as any).Temporal.PlainDate) || (!!obj && (obj as any)[Symbol.toStringTag] === 'Temporal.PlainDate') || (!!obj && typeof (obj as any).toZonedDateTime === 'function' && isUndefined((obj as any).timeZoneId) && isDefined((obj as any).daysInMonth));
export const isPlainTime = <T>(obj: T): obj is Extract<T, Temporal.PlainTime> => isType(obj, 'Temporal.PlainTime') || (!!(globalThis as any).Temporal?.PlainTime && (obj as any) instanceof (globalThis as any).Temporal.PlainTime) || (!!obj && (obj as any)[Symbol.toStringTag] === 'Temporal.PlainTime') || (!!obj && typeof (obj as any).toPlainDateTime === 'function' && isUndefined((obj as any).daysInMonth));
export const isPlainDateTime = <T>(obj: T): obj is Extract<T, Temporal.PlainDateTime> => isType(obj, 'Temporal.PlainDateTime') || (!!(globalThis as any).Temporal?.PlainDateTime && (obj as any) instanceof (globalThis as any).Temporal.PlainDateTime) || (!!obj && (obj as any)[Symbol.toStringTag] === 'Temporal.PlainDateTime') || (!!obj && typeof (obj as any).toZonedDateTime === 'function' && isUndefined((obj as any).timeZoneId) && isUndefined((obj as any).daysInMonth));
Comment thread
magmacomputing marked this conversation as resolved.
Outdated
export const isDuration = <T>(obj: T): obj is Extract<T, Temporal.Duration> => isType(obj, 'Temporal.Duration') || (!!(globalThis as any).Temporal?.Duration && (obj as any) instanceof (globalThis as any).Temporal.Duration) || (!!obj && (obj as any)[Symbol.toStringTag] === 'Temporal.Duration');
export const isDurationLike = <T>(obj: T): obj is Extract<T, Temporal.DurationLike | string | Temporal.Duration> => isString(obj) || isDuration(obj) || (isObject(obj) && (
'years' in obj || 'months' in obj || 'weeks' in obj || 'days' in obj ||
'hours' in obj || 'minutes' in obj || 'seconds' in obj ||
'milliseconds' in obj || 'microseconds' in obj || 'nanoseconds' in obj
));
export const isZonedDateTimeLike = <T>(obj: T): obj is Extract<T, Temporal.ZonedDateTimeLike | string | Temporal.ZonedDateTime> => isString(obj) || isZonedDateTime(obj) || (isObject(obj) && (
'year' in obj || 'month' in obj || 'day' in obj || 'hour' in obj || 'minute' in obj || 'second' in obj ||
'millisecond' in obj || 'microsecond' in obj || 'nanosecond' in obj || 'monthCode' in obj || 'offset' in obj || 'timeZone' in obj || 'calendar' in obj
));
export const isPlainYearMonth = <T>(obj: T): obj is Extract<T, Temporal.PlainYearMonth> => isType(obj, 'Temporal.PlainYearMonth') || (!!(globalThis as any).Temporal?.PlainYearMonth && (obj as any) instanceof (globalThis as any).Temporal.PlainYearMonth);
export const isPlainMonthDay = <T>(obj: T): obj is Extract<T, Temporal.PlainMonthDay> => isType(obj, 'Temporal.PlainMonthDay') || (!!(globalThis as any).Temporal?.PlainMonthDay && (obj as any) instanceof (globalThis as any).Temporal.PlainMonthDay);

// non-standard Objects
export const isEnum = <T, E extends Property<any>>(obj?: T): obj is Extract<T, GetType<'Enumify', E>> => isType(obj, 'Enumify');
export const isPledge = <T, P = any>(obj?: T): obj is Extract<T, GetType<'Pledge', P>> => isType(obj, 'Pledge');

/** assert value for secure() */
export const isExtensible = (obj: any): obj is any => !!(obj?.[sym.$Extensible]);
export const isTarget = (obj: any): obj is any => !!(obj?.[sym.$Target]);

/** object has no values */
export const isEmpty = <T>(obj?: T) => false
|| isNullish(obj)
|| (isObject(obj) && (Reflect.ownKeys(obj).length === 0))
|| (isString(obj) && (obj.trim().length === 0))
|| (isNumber(obj) && (isNaN(obj as any) === true))
|| (isArray(obj) && (obj.length === 0))
|| (isSet(obj) && (obj.size === 0))
|| (isMap(obj) && (obj.size === 0))
Comment thread
magmacomputing marked this conversation as resolved.

export function assertCondition(condition: boolean, message?: string): asserts condition {
if (!condition)
throw new Error(message);
}
export function assertString(str: unknown): asserts str is string { assertCondition(isString(str), `Invalid string: ${str}`) };
export function assertNever(val: never): asserts val is never { throw new Error(`Unexpected object: ${val}`) };
26 changes: 6 additions & 20 deletions packages/library/src/common/coercion.library.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { clone, stringify } from '#library/serialize.library.js';
import { isIntegerLike, isArrayLike, isDefined, isInteger, isIterable, isNullish, isString, isUndefined, asType, isNumber } from '#library/type.library.js';
import { asType } from '#library/type.library.js';
import { isIntegerLike, isArrayLike, isDefined, isInteger, isIterable, isNullish, isString, isUndefined, isNumber, isNumeric } from '#library/assertion.library.js';

/** Coerce {value} into {value[]} ( if not already ), with optional {fill} Object */
export function asArray<T>(arr: Exclude<ArrayLike<T>, string> | undefined): T[];
Expand Down Expand Up @@ -52,25 +53,6 @@ export function asInteger<T extends string | number | bigint>(str?: T) {
}
}

/** test if can convert String to Numeric */
export function isNumeric(str?: string | number | bigint) {
const arg = asType(str);

switch (arg.type) {
case 'Number':
case 'BigInt':
return true;

case 'String':
return isIntegerLike(arg.value)
? true // is Number | BigInt
: !isNaN(asNumber(str)) && isFinite(str as number) // test if Number

default:
return false;
}
}

/** return as Number if possible, else original String */
export const ifNumeric = (str: string | number | bigint, stripZero = false) => {
switch (true) {
Expand All @@ -87,3 +69,7 @@ export const ifNumeric = (str: string | number | bigint, stripZero = false) => {
return str as string; // non-numeric String → as-is
}
}

export const nullToZero = <T>(obj: T) => obj ?? 0;
export const nullToEmpty = <T>(obj: T) => obj ?? '';
export const nullToValue = <T, R>(obj: T, value: R) => obj ?? value;
14 changes: 7 additions & 7 deletions packages/library/src/common/enumerate.library.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { secure } from '#library/utility.library.js';
import { asType, isNumber } from '#library/type.library.js';
import { asType } from '#library/type.library.js';
import { isNumber } from '#library/assertion.library.js';
import { ownEntries } from '#library/primitive.library.js';
import { proxify } from '#library/proxy.library.js';
import { secure, proxify } from '#library/proxy.library.js';
import { Serializable } from '#library/class.library.js';
import { memoizeMethod } from '#library/function.library.js';
import lib from '#library/symbol.library.js';
import { sym } from '#library/symbol.library.js';
import type { Property, Index, KeyOf, ValueOf, EntryOf, Invert, LooseKey } from '#library/type.library.js';

declare module '#library/type.library.js' {
Expand Down Expand Up @@ -79,7 +79,7 @@ function value(val: any) {
* ```typescript
* const Status = enumify(['Active', 'Inactive', 'Pending']);
* console.log(Status.Active); // 0
* console.log(Status.has('Active')); // true
* console.log(Status.has('Active')); // true
* console.log(Status.keys()); // ['Active', 'Inactive', 'Pending']
* ```
*/
Expand Down Expand Up @@ -109,8 +109,8 @@ export function enumify<T>(this: any, list: T, frozen = true): any {
}

const target = Object.create(proto, Object.getOwnPropertyDescriptors(stash));
if (!frozen) Object.defineProperty(target, lib.$Extensible, { value: true, enumerable: false });
return proxify(target, true, frozen); // proxy is ALWAYS frozen (read-only), but target is only 'locked' if requested
if (!frozen) Object.defineProperty(target, sym.$Extensible, { value: true, enumerable: false });
return proxify(target, true, frozen); // proxy is ALWAYS frozen (read-only), but target is only 'locked' if requested
Comment thread
magmacomputing marked this conversation as resolved.
Outdated
}

/** create an entry in the Serialization Registry to describe how to rebuild an Enum */
Expand Down
6 changes: 3 additions & 3 deletions packages/library/src/common/function.library.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { secure } from '#library/utility.library.js';
import { isUndefined, type Property } from '#library/type.library.js';
import { secure } from '#library/proxy.library.js';
import type { Property } from '#library/type.library.js';

// https://medium.com/codex/currying-in-typescript-ca5226c85b85
type PartialTuple<T extends any[], X extends any[] = []> =
Expand Down Expand Up @@ -36,7 +36,7 @@ type Curry<Args extends any[], Res> =
* Handles BigInt, Map, Set, Function, Undefined, and Circular refs.
*/
function serialize(val: any, seen = new WeakSet()): string {
return JSON.stringify(val, function(this: any, key: string, value: any) {
return JSON.stringify(val, function (this: any, key: string, value: any) {
if (value === undefined) return '\u0000__undefined__\u0000';
if (typeof value === 'bigint') return `bigint:${value}`;
if (typeof value === 'function') return `function:${value.name || 'anonymous'}`;
Expand Down
27 changes: 20 additions & 7 deletions packages/library/src/common/logify.class.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { Immutable } from '#library/class.library.js';
import lib, { markConfig } from '#library/symbol.library.js';
import { asType, isObject, isEmpty, type ValueOf } from '#library/type.library.js';
import { sym, markConfig } from '#library/symbol.library.js';
import { asType } from '#library/type.library.js';
import { isObject, isEmpty } from '#library/assertion.library.js';
import type { ValueOf } from '#library/type.library.js';

const Method = {
Log: 'log',
Expand All @@ -10,22 +12,33 @@ const Method = {
Error: 'error',
} as const

const Level = {
[Method.Error]: 1,
[Method.Warn]: 2,
[Method.Info]: 3,
[Method.Log]: 3,
[Method.Debug]: 4,
} as const;

/**
* provide standard logging methods to the console for a class
*/
@Immutable
export class Logify {
#name: string;
#opts: Logify.Constructor = { [lib.$Logify]: true };
#opts: Logify.Constructor = { [sym.$Logify]: true };

/**
* if {catch:true} then show a warning on the console and return
* otherwise show an error on the console and re-throw the error
*/
#trap(method: Logify.Method, ...msg: any[]) {
const config = (isObject(msg[0]) && (msg[0] as any)[lib.$Logify] === true) ? msg.shift() : this.#opts;
const config = (isObject(msg[0]) && (msg[0] as any)[sym.$Logify] === true) ? msg.shift() : this.#opts;
const currentLevel = (typeof config.debug === 'number') ? config.debug : (config.debug ? Level[Method.Debug] : Level[Method.Error]);
const methodLevel = Level[method] ?? 0;
Comment on lines +47 to +50
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Doc mismatch between Logify and tempo.config.md regarding debug: true mapping.

The JSDoc here (Line 117) and the runtime mapping (Line 49) both say debug === true maps to LOG.Trace. However, packages/tempo/doc/tempo.config.md Line 118 states true maps to LOG.Debug. One of the docs is wrong — the implementation is the source of truth, so the Tempo config doc needs updating (flagged in that file).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/library/src/common/logify.class.ts` around lines 47 - 50, The
tempo.config.md doc incorrectly states that debug: true maps to LOG.Debug;
update that doc to match the implementation in Logify where config.debug ===
true maps to LOG.Trace (refer to the Logify class and the config.debug runtime
mapping that uses LOG.Trace), ensuring the doc text and any examples mention
LOG.Trace instead of LOG.Debug so docs and runtime are consistent.


if (methodLevel > currentLevel) return;

if (method === Method.Debug && !config.debug) return;

const output = msg.map(m => {
if (m instanceof Error) return m.message;
Expand Down Expand Up @@ -89,9 +102,9 @@ export namespace Logify {
export type Method = ValueOf<typeof Method>

export interface Constructor {
debug?: boolean | undefined,
debug?: boolean | number | undefined,
catch?: boolean | undefined,
silent?: boolean | undefined,
[lib.$Logify]?: boolean | undefined
[sym.$Logify]?: boolean | undefined
}
}
2 changes: 1 addition & 1 deletion packages/library/src/common/object.library.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ownKeys, ownEntries } from '#library/primitive.library.js';
import { isObject, isArray, isReference, isFunction, isDefined, isNullish } from '#library/type.library.js';
import { isObject, isArray, isReference, isFunction, isDefined, isNullish } from '#library/assertion.library.js';
import type { Extend, Property } from '#library/type.library.js';

/** remove quotes around property names */
Expand Down
4 changes: 2 additions & 2 deletions packages/library/src/common/pledge.class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import { Logify } from '#library/logify.class.js';
import { markConfig } from '#library/symbol.library.js';
import { asArray } from '#library/coercion.library.js';
import { ifDefined } from '#library/object.library.js';
import { secure } from '#library/utility.library.js';
import { secure } from '#library/proxy.library.js';
import { cleanify } from '#library/serialize.library.js';
import { Immutable } from '#library/class.library.js';
import { isEmpty, isObject } from '#library/type.library.js';
import { isEmpty, isObject } from '#library/assertion.library.js';

declare module '#library/type.library.js' {
interface TypeValueMap<T> {
Expand Down
Loading