utilium 3.1.1 → 3.2.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/dist/diff.d.ts ADDED
@@ -0,0 +1,208 @@
1
+ import type { CoercibleToString } from './string.js';
2
+ import type { Subtract, SubtractBigInt } from './type-math.js';
3
+ import type { ExpandRecursively, Falsy, UnionToTuple } from './types.js';
4
+ /** @internal @hidden */
5
+ export interface _EQ {
6
+ result: 'equal';
7
+ }
8
+ /** @internal @hidden */
9
+ export interface _NEQ {
10
+ result: 'not-equal';
11
+ }
12
+ interface GenericResult {
13
+ type?: string;
14
+ result: string;
15
+ }
16
+ /** @internal @hidden */
17
+ export interface _StringUnknownSubstring {
18
+ result: 'substring';
19
+ before: string;
20
+ after: string;
21
+ add: boolean;
22
+ }
23
+ /** @internal @hidden */
24
+ export type _StringUnknown = _StringUnknownSubstring | _EQ | _NEQ;
25
+ export type String<From extends string, To extends string> = string extends To ? _StringUnknown : string extends From ? _StringUnknown : To extends From ? _EQ : To extends `${infer Before}${From}${infer After}` ? {
26
+ result: 'substring';
27
+ before: Before;
28
+ after: After;
29
+ add: true;
30
+ } : From extends `${infer Before}${To}${infer After}` ? {
31
+ result: 'substring';
32
+ before: Before;
33
+ after: After;
34
+ add: false;
35
+ } : {
36
+ result: 'not-equal';
37
+ };
38
+ export declare function string<From extends string, To extends string>(from: From, to: To): String<From, To>;
39
+ /** @internal @hidden */
40
+ export interface _NumberUnknownDiff {
41
+ type: 'number';
42
+ result: 'diff';
43
+ value: number;
44
+ }
45
+ /** @internal @hidden */
46
+ export type _NumberUnknown = _NumberUnknownDiff | (_NEQ & {
47
+ type: 'number';
48
+ });
49
+ /**
50
+ * Diff two numbers.
51
+ * `not-equal` is only returned when encountering `NaN` or infinite values
52
+ */
53
+ export type Number<From extends number, To extends number> = number extends To ? _NumberUnknown : number extends From ? _NumberUnknown : To extends From ? _EQ & {
54
+ type: 'number';
55
+ } : {
56
+ result: 'diff';
57
+ type: 'number';
58
+ value: Subtract<To, From>;
59
+ };
60
+ export declare function number<From extends number, To extends number>(from: From, to: To): Number<From, To>;
61
+ /** @internal @hidden */
62
+ export interface _BigIntUnknown {
63
+ type: 'bigint';
64
+ result: 'diff';
65
+ value: bigint;
66
+ }
67
+ export type BigInt<From extends bigint, To extends bigint> = bigint extends From ? _BigIntUnknown : bigint extends To ? _BigIntUnknown : From extends To ? _EQ & {
68
+ type: 'bigint';
69
+ } : {
70
+ type: 'bigint';
71
+ result: 'diff';
72
+ value: SubtractBigInt<To, From>;
73
+ };
74
+ export declare function bigint<From extends bigint, To extends bigint>(from: From, to: To): BigInt<To, From>;
75
+ /** @internal @hidden */
76
+ export interface _BooleanUnknown {
77
+ type: 'boolean';
78
+ result: 'equal' | 'not-equal';
79
+ }
80
+ export type Boolean<From extends boolean, To extends boolean> = boolean extends From ? _BooleanUnknown : boolean extends To ? _BooleanUnknown : {
81
+ type: 'boolean';
82
+ } & (From extends To ? _EQ : _NEQ);
83
+ export declare function boolean<From extends boolean, To extends boolean>(from: From, to: To): Boolean<From, To>;
84
+ type fn = (...args: unknown[]) => unknown;
85
+ /** @internal @hidden */
86
+ export interface _FunctionUnknownDiff {
87
+ type: 'function';
88
+ result: 'diff';
89
+ return: GenericResult;
90
+ args: _ArrayUnknown;
91
+ }
92
+ /** @internal @hidden */
93
+ type _FunctionUnknown = _FunctionUnknownDiff | (_NEQ & {
94
+ type: 'function';
95
+ }) | (_EQ & {
96
+ type: 'function';
97
+ });
98
+ /** @internal @hidden */
99
+ export interface _FunctionType<From extends fn, To extends fn> {
100
+ type: 'function';
101
+ result: 'diff';
102
+ return: Deep<ReturnType<From>, ReturnType<To>>;
103
+ args: Array<Parameters<From>, Parameters<To>>;
104
+ }
105
+ export type FunctionType<From extends fn, To extends fn> = fn extends From ? _FunctionUnknown : fn extends To ? _FunctionUnknown : From extends To ? To extends From ? _EQ & {
106
+ type: 'function';
107
+ } : _FunctionType<From, To> : _FunctionType<From, To>;
108
+ /** @internal @hidden */
109
+ export interface _FunctionRTArgDiff {
110
+ type: 'function';
111
+ result: 'diff-arg';
112
+ argCount: number;
113
+ }
114
+ type _FunctionRTUnknown = _FunctionRTArgDiff | (_EQ & {
115
+ type: 'function';
116
+ });
117
+ export type FunctionRT<From extends fn, To extends fn> = fn extends From ? _FunctionRTUnknown : fn extends To ? _FunctionRTUnknown : Parameters<From>['length'] extends Parameters<To>['length'] ? _EQ & {
118
+ type: 'function';
119
+ } : {
120
+ type: 'function';
121
+ result: 'diff-arg';
122
+ argCount: Subtract<Parameters<From>['length'], Parameters<To>['length']>;
123
+ };
124
+ export declare function functionRT<From extends fn, To extends fn>(from: From, to: To): FunctionRT<From, To>;
125
+ interface _ArrayUnknownDiff {
126
+ type: 'array';
127
+ result: 'diff';
128
+ elements: number;
129
+ changed: GenericResult[];
130
+ }
131
+ type _ArrayUnknown = _ArrayUnknownDiff | {
132
+ type: 'array';
133
+ result: 'equal';
134
+ };
135
+ interface _ArrayDiff<From extends ArrayLike<unknown>, To extends ArrayLike<unknown>> {
136
+ type: 'array';
137
+ result: 'diff';
138
+ elements: Subtract<From['length'], To['length']>;
139
+ changed: {
140
+ [K in keyof From & keyof To & number]: Deep<From[K], To[K]>;
141
+ }[keyof From & keyof To & number];
142
+ }
143
+ export type Array<From extends ArrayLike<unknown>, To extends ArrayLike<unknown>> = ArrayLike<unknown> extends From ? _ArrayUnknown : ArrayLike<unknown> extends To ? _ArrayUnknown : number extends From['length'] ? _ArrayUnknown : number extends To['length'] ? _ArrayUnknown : From['length'] extends To['length'] ? {
144
+ [K in keyof From & keyof To & number]: Deep<From[K], To[K]>['result'];
145
+ }[keyof From & keyof To & number] extends 'equal' ? {
146
+ type: 'array';
147
+ result: 'equal';
148
+ } : _ArrayDiff<From, To> : _ArrayDiff<From, To>;
149
+ export declare function array<From extends ArrayLike<unknown>, To extends ArrayLike<unknown>>(from: From, to: To): Array<From, To>;
150
+ interface _ObjectUnknownDiff {
151
+ type: 'object';
152
+ result: 'diff';
153
+ removed: string[];
154
+ added: string[];
155
+ changed: Record<string, GenericResult>;
156
+ }
157
+ type _ObjectUnknown = _ObjectUnknownDiff | ({
158
+ type: 'object';
159
+ } & _EQ);
160
+ interface _ObjectDiff<From extends object, To extends object> {
161
+ type: 'object';
162
+ result: 'diff';
163
+ removed: Exclude<keyof From, keyof To> extends never ? [] : UnionToTuple<Exclude<keyof From, keyof To>>;
164
+ added: Exclude<keyof To, keyof From> extends never ? [] : UnionToTuple<Exclude<keyof To, keyof From>>;
165
+ changed: {
166
+ [K in keyof From & keyof To]: Deep<From[K], To[K]>;
167
+ };
168
+ }
169
+ export type Object<From extends object, To extends object> = object extends From ? _ObjectUnknown : object extends To ? _ObjectUnknown : From extends To ? To extends From ? {
170
+ type: 'object';
171
+ result: 'equal';
172
+ } : _ObjectDiff<From, To> : _ObjectDiff<From, To>;
173
+ export declare function object<From extends object, To extends object>(from: From, to: To): Object<From, To>;
174
+ interface NotComparable {
175
+ result: 'not-comparable';
176
+ }
177
+ type DeepCommon<From, To> = unknown extends From ? GenericResult : unknown extends To ? GenericResult : From extends string ? To extends CoercibleToString ? String<From, `${To}`> & {
178
+ type: 'string';
179
+ coerced: To extends string ? false : true;
180
+ } : NotComparable : From extends number ? To extends number ? Number<From, To> & {
181
+ coerced: false;
182
+ } : To extends bigint ? `${To}` extends `${infer V extends number}` ? Number<From, V> & {
183
+ coerced: true;
184
+ } : never & 'error: bigint -> number' : NotComparable : From extends bigint ? To extends bigint ? BigInt<To, From> & {
185
+ coerced: false;
186
+ } : To extends number ? `${To}` extends `${infer V extends bigint}` ? BigInt<From, V> & {
187
+ coerced: true;
188
+ } : never & 'error: number -> bigint' : NotComparable : From extends boolean ? To extends boolean | Falsy ? Boolean<From, To extends boolean ? To : false> & {
189
+ coerced: To extends boolean ? false : true;
190
+ } : NotComparable : From extends symbol ? To extends symbol ? {
191
+ type: 'symbol';
192
+ result: symbol extends To ? 'equal' | 'not-equal' : symbol extends From ? 'equal' | 'not-equal' : To extends From ? 'equal' : 'not-equal';
193
+ } : NotComparable : From extends undefined ? To extends undefined | Falsy ? {
194
+ result: 'equal';
195
+ type: 'undefined';
196
+ coerced: To extends undefined ? false : true;
197
+ } : NotComparable : From extends null ? To extends null | Falsy ? {
198
+ result: 'equal';
199
+ type: 'null';
200
+ coerced: To extends null ? false : true;
201
+ } : NotComparable : From extends ArrayLike<unknown> ? To extends ArrayLike<unknown> ? Array<From, To> : NotComparable : From extends object ? To extends undefined | null ? NotComparable : To extends object ? Object<From, To> : NotComparable : NotComparable;
202
+ export type DeepRT<From, To> = From extends fn ? To extends fn ? FunctionRT<From, To> : NotComparable : DeepCommon<From, To>;
203
+ export type Deep<From, To> = From extends fn ? To extends fn ? FunctionType<From, To> : NotComparable : DeepCommon<From, To>;
204
+ interface GenericDeepResult extends GenericResult {
205
+ coerced?: boolean;
206
+ }
207
+ export declare function deep<From, To>(from: From, to: To): ExpandRecursively<DeepRT<From, To>> & GenericDeepResult;
208
+ export {};
package/dist/diff.js ADDED
@@ -0,0 +1,138 @@
1
+ export function string(from, to) {
2
+ const _to = to;
3
+ if (from === _to)
4
+ return { result: 'equal' };
5
+ const to_at = _to.indexOf(from);
6
+ if (to_at !== -1)
7
+ return {
8
+ result: 'substring',
9
+ before: _to.slice(0, to_at),
10
+ after: _to.slice(to_at + from.length),
11
+ add: true,
12
+ };
13
+ const from_at = from.indexOf(_to);
14
+ if (from_at !== -1)
15
+ return {
16
+ result: 'substring',
17
+ before: from.slice(0, from_at),
18
+ after: from.slice(from_at + _to.length),
19
+ add: false,
20
+ };
21
+ return { result: 'not-equal' };
22
+ }
23
+ export function number(from, to) {
24
+ const _to = to;
25
+ if (Number.isNaN(to) || Number.isNaN(from)) {
26
+ return { type: 'number', result: 'not-equal' };
27
+ }
28
+ if (!Number.isFinite(to) || !Number.isFinite(from)) {
29
+ return { type: 'number', result: _to === from ? 'equal' : 'not-equal' };
30
+ }
31
+ if (_to === from)
32
+ return { type: 'number', result: 'equal' };
33
+ return { type: 'number', result: 'diff', value: from - to };
34
+ }
35
+ export function bigint(from, to) {
36
+ const _to = to;
37
+ if (_to === from)
38
+ return { type: 'bigint', result: 'equal' };
39
+ return { type: 'bigint', result: 'diff', value: from - to };
40
+ }
41
+ export function boolean(from, to) {
42
+ return { type: 'boolean', result: !!to === !!from ? 'equal' : 'not-equal' };
43
+ }
44
+ export function functionRT(from, to) {
45
+ if (from.length === to.length) {
46
+ return { type: 'function', result: 'equal' };
47
+ }
48
+ return { type: 'function', result: 'diff-arg', argCount: from.length - to.length };
49
+ }
50
+ export function array(from, to) {
51
+ const lengthDiff = from.length - to.length, changed = [];
52
+ let neq = false;
53
+ for (let i = 0; i < Math.min(from.length, to.length); i++) {
54
+ const diff = deep(from[i], to[i]);
55
+ changed.push(diff);
56
+ if (diff.result !== 'equal')
57
+ neq = true;
58
+ }
59
+ return (!neq && !lengthDiff
60
+ ? { type: 'array', result: 'equal' }
61
+ : { type: 'array', result: 'diff', elements: lengthDiff, changed });
62
+ }
63
+ export function object(from, to) {
64
+ const fromKeys = new Set(Object.keys(from)), toKeys = new Set(Object.keys(to));
65
+ const removed = fromKeys.difference(toKeys), added = toKeys.difference(fromKeys);
66
+ const changed = Object.create(null);
67
+ let neq = false;
68
+ for (const key of fromKeys.intersection(toKeys)) {
69
+ changed[key] = deep(from[key], to[key]);
70
+ if (changed[key].result !== 'equal')
71
+ neq = true;
72
+ }
73
+ return (neq || removed.size || added.size
74
+ ? { type: 'object', result: 'diff', removed: Array.from(removed), added: Array.from(added), changed }
75
+ : { type: 'object', result: 'equal' });
76
+ }
77
+ export function deep(from, to) {
78
+ const rt = (value) => value;
79
+ switch (typeof from) {
80
+ case 'string':
81
+ if (['symbol', 'function', 'object'].includes(typeof to))
82
+ break;
83
+ return rt({
84
+ type: 'string',
85
+ coerced: typeof to !== 'string',
86
+ ...string(from, `${to}`),
87
+ });
88
+ case 'number':
89
+ if (typeof to == 'number')
90
+ return rt({ ...number(from, to), coerced: false });
91
+ if (typeof to == 'bigint')
92
+ return rt({ ...number(from, Number(to)), coerced: true });
93
+ break;
94
+ case 'bigint':
95
+ if (typeof to == 'bigint')
96
+ return rt({ ...bigint(from, to), coerced: false });
97
+ if (typeof to == 'number')
98
+ return rt({ ...bigint(from, BigInt(to)), coerced: true });
99
+ break;
100
+ case 'boolean':
101
+ if (typeof to == 'boolean')
102
+ return rt({ ...boolean(from, to), coerced: false });
103
+ if (!to)
104
+ return rt({ ...boolean(from, false), coerced: true });
105
+ break;
106
+ case 'symbol':
107
+ if (typeof to !== 'symbol')
108
+ break;
109
+ return rt({ type: 'symbol', result: from == to ? 'equal' : 'not-equal' });
110
+ case 'undefined':
111
+ if (to === undefined)
112
+ return rt({ type: 'undefined', result: 'equal', coerced: false });
113
+ if (!to)
114
+ return rt({ type: 'undefined', result: 'equal', coerced: true });
115
+ break;
116
+ case 'function':
117
+ if (typeof to !== 'function')
118
+ break;
119
+ return rt({ ...functionRT(from, to) });
120
+ case 'object':
121
+ if (from === null) {
122
+ if (to === null)
123
+ return rt({ type: 'null', result: 'equal', coerced: false });
124
+ if (!to)
125
+ return rt({ type: 'null', result: 'equal', coerced: true });
126
+ break;
127
+ }
128
+ if (typeof to != 'object' || !to)
129
+ break;
130
+ if (Array.isArray(from)) {
131
+ if (!Array.isArray(to))
132
+ break;
133
+ return rt(array(from, to));
134
+ }
135
+ return rt(object(from, to));
136
+ }
137
+ return rt({ result: 'not-comparable' });
138
+ }
package/dist/index.d.ts CHANGED
@@ -3,6 +3,7 @@ export * from './buffer.js';
3
3
  export * as cache from './cache.js';
4
4
  export * from './checksum.js';
5
5
  export * from './color.js';
6
+ export * as diff from './diff.js';
6
7
  export * from './list.js';
7
8
  export * from './misc.js';
8
9
  export * from './numbers.js';
package/dist/index.js CHANGED
@@ -6,6 +6,7 @@ export * from './buffer.js';
6
6
  export * as cache from './cache.js';
7
7
  export * from './checksum.js';
8
8
  export * from './color.js';
9
+ export * as diff from './diff.js';
9
10
  export * from './list.js';
10
11
  export * from './misc.js';
11
12
  export * from './numbers.js';
package/dist/numbers.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import type { CoercibleToString } from './string.js';
1
2
  export declare function range(min: number, max: number): number[];
2
3
  export declare function toDegrees(radians: number): number;
3
4
  export declare function toRadians(degrees: number): number;
@@ -6,3 +7,5 @@ export declare const formatCompact: {
6
7
  (value: number | bigint): string;
7
8
  (value: number | bigint | Intl.StringNumericLiteral): string;
8
9
  };
10
+ export type ToNumber<T extends CoercibleToString> = `${T}` extends `${infer U extends number}` ? U : never;
11
+ export type ToBigInt<T extends CoercibleToString> = `${T}` extends `${infer U extends bigint}` ? U : never;
package/dist/objects.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { FilterOut } from './array.js';
2
+ import * as diff from './diff.js';
2
3
  import type { Split } from './string.js';
3
4
  import type { $drain, Expand, UnionToTuple } from './types.js';
4
5
  export declare function filterObject<O extends object, R extends object>(object: O, predicate: (key: keyof O, value: O[keyof O]) => boolean): R;
@@ -22,6 +23,8 @@ export type DeepAssign<To extends object, From extends object> = {
22
23
  [K in keyof To | keyof From]: K extends keyof To ? K extends keyof From ? To[K] extends object ? From[K] extends object ? Expand<DeepAssign<To[K], From[K]>> : never : From[K] extends object ? never : From[K] : To[K] : From[K & keyof From];
23
24
  };
24
25
  export declare function deepAssign<To extends object, From extends object>(to: To, from: From, treatArraysAsPrimitives?: boolean): DeepAssign<To, From>;
26
+ export type StructurallyEqual<To, From> = diff.DeepRT<To, From>['result'] extends 'equal' ? true : false;
27
+ export declare function structurallyEqual<To, From>(to: To, from: From): StructurallyEqual<To, From>;
25
28
  /**
26
29
  * Entries of T
27
30
  */
package/dist/objects.js CHANGED
@@ -1,6 +1,7 @@
1
1
  // SPDX-License-Identifier: LGPL-3.0-or-later
2
2
  // Copyright (c) 2025 James Prevett
3
3
  /* eslint-disable @typescript-eslint/no-explicit-any */
4
+ import * as diff from './diff.js';
4
5
  import { _throw } from './misc.js';
5
6
  export function filterObject(object, predicate) {
6
7
  const entries = Object.entries(object);
@@ -64,6 +65,9 @@ export function deepAssign(to, from, treatArraysAsPrimitives = false) {
64
65
  }
65
66
  return to;
66
67
  }
68
+ export function structurallyEqual(to, from) {
69
+ return (diff.deep(from, to).result === 'equal');
70
+ }
67
71
  export function isJSON(str) {
68
72
  try {
69
73
  JSON.parse(str);
package/dist/string.d.ts CHANGED
@@ -39,4 +39,5 @@ export type Repeat<T extends string, N extends number, Init extends string = ''>
39
39
  export type PadRight<Init extends string, R extends string, N extends number> = Repeat<R, $Max<0, w_subtract<N, StringLength<Init>>>, Init>;
40
40
  export type PadLeft<Init extends string, R extends string, N extends number> = `${Repeat<R, $Max<0, w_subtract<N, StringLength<Init>>>>}${Init}`;
41
41
  export type NonEmptyString = `${any}${string}`;
42
+ export type CoercibleToString = string | number | bigint | boolean | null | undefined;
42
43
  export {};
@@ -12,6 +12,7 @@
12
12
  *
13
13
  */
14
14
  import type { Length } from './array.js';
15
+ import type { ToNumber } from './numbers.js';
15
16
  import type { Repeat, StringLength } from './string.js';
16
17
  import type { $drain } from './types.js';
17
18
  /**
@@ -79,6 +80,10 @@ export type $Subtract<A extends number, B extends number> = _are_ints<A, B> exte
79
80
  * @internal
80
81
  */
81
82
  type i_less<A extends number, B extends number> = $drain<_either_zero<A, B> extends true ? Equal<A, B> extends true ? false : A extends 0 ? true : false : i_less<i_decrement<A>, i_decrement<B>>>;
83
+ /**
84
+ * Is A less than B?
85
+ * @internal
86
+ */
82
87
  export type $LessThan<A extends number, B extends number> = _are_ints<A, B> extends true ? i_less<A, B> : never;
83
88
  type i_max<A extends number, B extends number> = i_less<A, B> extends true ? B : A;
84
89
  export type $Max<A extends number, B extends number> = _are_ints<A, B> extends true ? i_max<A, B> : never;
@@ -116,7 +121,9 @@ type _f_sum_str<A_s extends string, B_s extends string, _A_neg extends boolean,
116
121
  type f_sum<A extends number, B extends number, _A_neg extends boolean, _B_neg extends boolean> = `${A}` extends `0.${infer A_s extends string}` ? `${B}` extends `0.${infer B_s extends string}` ? `0.${_f_sum_str<A_s, B_s, _A_neg, _B_neg>}` extends `${infer F extends number}` ? F : never : never : never;
117
122
  type _sum_with_f<A extends number, B extends number, F extends number> = `${F}` extends `${infer C extends number}.${infer S}` ? `${i_sum<i_sum<Integer<A>, Integer<B>>, C>}.${S}` extends `${infer V extends number}` ? V : never : never;
118
123
  export type Add<A extends number, B extends number, __fA extends number = Fraction<A>, __fB extends number = Fraction<B>> = __fA extends 0 ? __fB extends 0 ? i_sum<A, B> : _sum_with_f<A, B, __fB> : __fB extends 0 ? _sum_with_f<A, B, __fA> : _sum_with_f<A, B, f_sum<__fA, __fB, Is_Negative<A>, Is_Negative<B>>>;
124
+ export type AddBigInt<A extends bigint, B extends bigint> = i_sum<ToNumber<A>, ToNumber<B>>;
119
125
  export type Subtract<A extends number, B extends number> = Add<A, Negate<B>>;
126
+ export type SubtractBigInt<A extends bigint, B extends bigint> = i_sum<ToNumber<A>, Negate<ToNumber<B>>>;
120
127
  /**
121
128
  * Accumulative addition:
122
129
  * N: increment
package/dist/types.d.ts CHANGED
@@ -48,4 +48,5 @@ export type UnionToTuple<T, L = LastOfUnion<T>, N = [T] extends [never] ? true :
48
48
  * It improves performance for the math types by an order of magnitude.
49
49
  */
50
50
  export type $drain<T> = [T] extends [unknown] ? T : never;
51
+ export type Falsy = null | undefined | false | 0 | 0n | '';
51
52
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "utilium",
3
- "version": "3.1.1",
3
+ "version": "3.2.0",
4
4
  "description": "Typescript utilities",
5
5
  "funding": {
6
6
  "type": "individual",