magic-utils-yonava 1.0.2 → 1.0.4
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/clone.d.ts +9 -0
- package/dist/clone.js +15 -0
- package/dist/clone.test.d.ts +1 -0
- package/dist/clone.test.js +42 -0
- package/dist/colors.d.ts +522 -0
- package/{src/colors.ts → dist/colors.js} +224 -274
- package/dist/ctx/index.d.ts +10 -0
- package/dist/ctx/index.js +19 -0
- package/dist/debounce.d.ts +9 -0
- package/{src/debounce.ts → dist/debounce.js} +6 -6
- package/dist/debugging.d.ts +2 -0
- package/dist/debugging.js +21 -0
- package/dist/deepDelta/delta.test.d.ts +1 -0
- package/dist/deepDelta/delta.test.js +114 -0
- package/dist/deepDelta/index.d.ts +8 -0
- package/dist/deepDelta/index.js +40 -0
- package/dist/deepMerge.d.ts +16 -0
- package/dist/deepMerge.js +32 -0
- package/dist/deepMerge.test.d.ts +1 -0
- package/dist/deepMerge.test.js +68 -0
- package/dist/fps.d.ts +6 -0
- package/dist/fps.js +51 -0
- package/dist/fracDecConverter/index.d.ts +9 -0
- package/dist/fracDecConverter/index.js +34 -0
- package/dist/hashing.d.ts +2 -0
- package/dist/hashing.js +7 -0
- package/dist/id.d.ts +5 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/localStorage.d.ts +37 -0
- package/dist/localStorage.js +22 -0
- package/dist/math.d.ts +60 -0
- package/{src/math.ts → dist/math.js} +28 -32
- package/dist/math.test.d.ts +1 -0
- package/dist/math.test.js +42 -0
- package/{src/maybeGetter/index.ts → dist/maybeGetter/index.d.ts} +3 -18
- package/dist/maybeGetter/index.js +15 -0
- package/dist/mouse.d.ts +8 -0
- package/{src/mouse.ts → dist/mouse.js} +4 -4
- package/dist/random.d.ts +19 -0
- package/dist/random.js +21 -0
- package/dist/sets.d.ts +8 -0
- package/{src/sets.ts → dist/sets.js} +2 -2
- package/dist/string.d.ts +21 -0
- package/{src/string.ts → dist/string.js} +11 -11
- package/dist/string.test.d.ts +1 -0
- package/dist/string.test.js +14 -0
- package/{src/types.ts → dist/types.d.ts} +13 -38
- package/dist/types.js +1 -0
- package/dist/utils/clone.d.ts +9 -0
- package/dist/utils/clone.js +15 -0
- package/dist/utils/clone.test.d.ts +1 -0
- package/dist/utils/clone.test.js +42 -0
- package/dist/utils/colors.d.ts +522 -0
- package/dist/utils/colors.js +517 -0
- package/dist/utils/ctx/index.d.ts +10 -0
- package/dist/utils/ctx/index.js +19 -0
- package/dist/utils/debounce.d.ts +9 -0
- package/dist/utils/debounce.js +15 -0
- package/dist/utils/debugging.d.ts +2 -0
- package/dist/utils/debugging.js +21 -0
- package/dist/utils/deepDelta/delta.test.d.ts +1 -0
- package/dist/utils/deepDelta/delta.test.js +114 -0
- package/dist/utils/deepDelta/index.d.ts +8 -0
- package/dist/utils/deepDelta/index.js +40 -0
- package/dist/utils/deepMerge.d.ts +16 -0
- package/dist/utils/deepMerge.js +32 -0
- package/dist/utils/deepMerge.test.d.ts +1 -0
- package/dist/utils/deepMerge.test.js +68 -0
- package/dist/utils/fps.d.ts +6 -0
- package/dist/utils/fps.js +51 -0
- package/dist/utils/fracDecConverter/index.d.ts +9 -0
- package/dist/utils/fracDecConverter/index.js +34 -0
- package/dist/utils/hashing.d.ts +2 -0
- package/dist/utils/hashing.js +7 -0
- package/dist/utils/id.d.ts +5 -0
- package/dist/utils/id.js +5 -0
- package/dist/utils/localStorage.d.ts +37 -0
- package/dist/utils/localStorage.js +22 -0
- package/dist/utils/math.d.ts +60 -0
- package/dist/utils/math.js +89 -0
- package/dist/utils/math.test.d.ts +1 -0
- package/dist/utils/math.test.js +42 -0
- package/dist/utils/maybeGetter/index.d.ts +26 -0
- package/dist/utils/maybeGetter/index.js +15 -0
- package/dist/utils/mouse.d.ts +8 -0
- package/dist/utils/mouse.js +8 -0
- package/dist/utils/random.d.ts +19 -0
- package/dist/utils/random.js +21 -0
- package/dist/utils/sets.d.ts +8 -0
- package/dist/utils/sets.js +10 -0
- package/dist/utils/string.d.ts +21 -0
- package/dist/utils/string.js +31 -0
- package/dist/utils/string.test.d.ts +1 -0
- package/dist/utils/string.test.js +14 -0
- package/dist/utils/types.d.ts +54 -0
- package/dist/utils/types.js +1 -0
- package/package.json +4 -1
- package/src/clone.test.ts +0 -47
- package/src/clone.ts +0 -15
- package/src/ctx/index.ts +0 -20
- package/src/debugging.ts +0 -23
- package/src/deepDelta/delta.test.ts +0 -129
- package/src/deepDelta/index.ts +0 -48
- package/src/deepMerge.test.ts +0 -89
- package/src/deepMerge.ts +0 -37
- package/src/fps.ts +0 -64
- package/src/fracDecConverter/index.ts +0 -36
- package/src/hashing.ts +0 -9
- package/src/localStorage.ts +0 -49
- package/src/math.test.ts +0 -57
- package/src/random.ts +0 -27
- package/src/string.test.ts +0 -17
- package/tsconfig.json +0 -20
- /package/{src/id.ts → dist/id.js} +0 -0
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* combine an array of sets into a single set
|
|
3
|
+
*
|
|
4
|
+
* @param sets array of sets
|
|
5
|
+
* @returns a single set containing all elements from the input sets
|
|
6
|
+
* @example mergeSetArrayIntoSet([new Set([1, 2]), new Set([2, 3])]) // Set(1, 2, 3)
|
|
7
|
+
*/
|
|
8
|
+
export declare const mergeSetArrayIntoSet: <T>(sets: Set<T>[]) => Set<T>;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* combine an array of sets into a single set
|
|
3
|
+
*
|
|
4
|
+
* @param sets array of sets
|
|
5
|
+
* @returns a single set containing all elements from the input sets
|
|
6
|
+
* @example mergeSetArrayIntoSet([new Set([1, 2]), new Set([2, 3])]) // Set(1, 2, 3)
|
|
7
|
+
*/
|
|
8
|
+
export const mergeSetArrayIntoSet = (sets) => {
|
|
9
|
+
return sets.reduce((acc, set) => new Set([...acc, ...set]), new Set());
|
|
10
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* makes the first letter of a string uppercase
|
|
3
|
+
*
|
|
4
|
+
* @param str - the string to capitalize
|
|
5
|
+
* @returns the capitalized string
|
|
6
|
+
* @example capitalize('hello') // 'Hello'
|
|
7
|
+
*/
|
|
8
|
+
export declare const capitalize: (str: string) => string;
|
|
9
|
+
/**
|
|
10
|
+
* turns a camelCase string into a title case string
|
|
11
|
+
*
|
|
12
|
+
* @param str - the string to convert
|
|
13
|
+
* @returns the title case string
|
|
14
|
+
* @example camelCaseToTitleCase('camelCase') // 'Camel Case'
|
|
15
|
+
*/
|
|
16
|
+
export declare const camelCaseToTitleCase: (str: string) => string;
|
|
17
|
+
/**
|
|
18
|
+
* turns an array of strings into a comma separated list with 'and' before the last item
|
|
19
|
+
* @example toCommaList(['a', 'b', 'c']) // 'a, b and c'
|
|
20
|
+
*/
|
|
21
|
+
export declare const getCommaList: (arr: string[]) => string;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* makes the first letter of a string uppercase
|
|
3
|
+
*
|
|
4
|
+
* @param str - the string to capitalize
|
|
5
|
+
* @returns the capitalized string
|
|
6
|
+
* @example capitalize('hello') // 'Hello'
|
|
7
|
+
*/
|
|
8
|
+
export const capitalize = (str) => str.charAt(0).toUpperCase() + str.slice(1);
|
|
9
|
+
/**
|
|
10
|
+
* turns a camelCase string into a title case string
|
|
11
|
+
*
|
|
12
|
+
* @param str - the string to convert
|
|
13
|
+
* @returns the title case string
|
|
14
|
+
* @example camelCaseToTitleCase('camelCase') // 'Camel Case'
|
|
15
|
+
*/
|
|
16
|
+
export const camelCaseToTitleCase = (str) => {
|
|
17
|
+
const result = str.replace(/([A-Z])/g, ' $1');
|
|
18
|
+
return capitalize(result);
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* turns an array of strings into a comma separated list with 'and' before the last item
|
|
22
|
+
* @example toCommaList(['a', 'b', 'c']) // 'a, b and c'
|
|
23
|
+
*/
|
|
24
|
+
export const getCommaList = (arr) => {
|
|
25
|
+
if (arr.length === 0)
|
|
26
|
+
return '';
|
|
27
|
+
if (arr.length === 1)
|
|
28
|
+
return arr[0];
|
|
29
|
+
const last = arr.pop();
|
|
30
|
+
return arr.join(', ') + ' and ' + last;
|
|
31
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { describe, expect, test } from 'vitest';
|
|
2
|
+
import { camelCaseToTitleCase, capitalize } from './string';
|
|
3
|
+
describe('capitalize', () => {
|
|
4
|
+
test('capitalizes the first letter of a string', () => {
|
|
5
|
+
expect(capitalize('hello')).toBe('Hello');
|
|
6
|
+
expect(capitalize('this is one string')).toBe('This is one string');
|
|
7
|
+
});
|
|
8
|
+
});
|
|
9
|
+
describe('camelCaseToTitleCase', () => {
|
|
10
|
+
test('converts camelCase to title case', () => {
|
|
11
|
+
expect(camelCaseToTitleCase('camelCase')).toBe('Camel Case');
|
|
12
|
+
expect(camelCaseToTitleCase('thisIsOneString')).toBe('This Is One String');
|
|
13
|
+
});
|
|
14
|
+
});
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { Prettify, UnionToIntersection } from 'ts-essentials';
|
|
2
|
+
/**
|
|
3
|
+
* makes only certain keys K in an object T optional
|
|
4
|
+
* @example
|
|
5
|
+
* type T = PartiallyPartial<{ a: number, b: string }, 'a'> // { a?: number, b: string }
|
|
6
|
+
*/
|
|
7
|
+
export type PartiallyPartial<T, K extends keyof T> = Prettify<Omit<T, K> & Partial<Pick<T, K>>>;
|
|
8
|
+
/**
|
|
9
|
+
* makes only certain keys K in an object T required
|
|
10
|
+
* @example
|
|
11
|
+
* type T = PartiallyRequired<{ a?: number, b?: string }, 'a'> // { a: number, b?: string }
|
|
12
|
+
*/
|
|
13
|
+
export type PartiallyRequired<T, K extends keyof T> = Prettify<Omit<T, K> & Required<Pick<T, K>>>;
|
|
14
|
+
/**
|
|
15
|
+
* takes `any[]` out of a union of arrays
|
|
16
|
+
* @example RemoveAnyArray<number[] | any[]> // number[]
|
|
17
|
+
*/
|
|
18
|
+
export type RemoveAnyArray<T extends unknown[]> = Exclude<T, [
|
|
19
|
+
'!!!-@-NOT-A-TYPE-@-!!!'
|
|
20
|
+
][]>;
|
|
21
|
+
type EventNames = keyof HTMLElementEventMap;
|
|
22
|
+
type FilterEventNames<T> = {
|
|
23
|
+
[K in EventNames]: HTMLElementEventMap[K] extends T ? K : never;
|
|
24
|
+
}[EventNames];
|
|
25
|
+
type MouseEventNames = FilterEventNames<MouseEvent>;
|
|
26
|
+
type KeyboardEventNames = FilterEventNames<KeyboardEvent>;
|
|
27
|
+
type EventMap<T extends EventNames, E> = Record<T, (ev: E) => void>;
|
|
28
|
+
export type MouseEventMap = EventMap<MouseEventNames, MouseEvent>;
|
|
29
|
+
export type KeyboardEventMap = EventMap<KeyboardEventNames, KeyboardEvent>;
|
|
30
|
+
export type MouseEventEntries = [
|
|
31
|
+
keyof MouseEventMap,
|
|
32
|
+
(ev: MouseEvent) => void
|
|
33
|
+
][];
|
|
34
|
+
export type KeyboardEventEntries = [
|
|
35
|
+
keyof KeyboardEventMap,
|
|
36
|
+
(ev: KeyboardEvent) => void
|
|
37
|
+
][];
|
|
38
|
+
export type TimeoutHandler = ReturnType<typeof setTimeout>;
|
|
39
|
+
export type IntervalHandler = ReturnType<typeof setInterval>;
|
|
40
|
+
type LastOf<U> = UnionToIntersection<U extends any ? () => U : never> extends () => infer R ? R : never;
|
|
41
|
+
type Push<T extends any[], V> = [...T, V];
|
|
42
|
+
type UnionToTuple<T, L = LastOf<T>> = [T] extends [never] ? [] : Push<UnionToTuple<Exclude<T, L>>, L>;
|
|
43
|
+
/**
|
|
44
|
+
* turns the keys of an object into a tuple
|
|
45
|
+
*
|
|
46
|
+
* WARNING: order is not guaranteed! for deterministic ordering use
|
|
47
|
+
* readonly runtime array instead
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ObjectKeysToTuple<{ name: string, age: number, id: string }>
|
|
51
|
+
* // ["name", "age", "id"]
|
|
52
|
+
*/
|
|
53
|
+
export type ObjectKeysToTuple<T extends object> = UnionToTuple<keyof T>;
|
|
54
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED
package/src/clone.test.ts
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import { expect, test } from "vitest";
|
|
2
|
-
|
|
3
|
-
import { clone } from "./clone";
|
|
4
|
-
import { delta } from "./deepDelta";
|
|
5
|
-
|
|
6
|
-
test("works on basic objects", () => {
|
|
7
|
-
const obj = { a: 1, b: 2 };
|
|
8
|
-
const cloned = clone(obj);
|
|
9
|
-
expect(cloned).not.toBe(obj);
|
|
10
|
-
expect(cloned).toEqual(obj);
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
test("works on nested objects", () => {
|
|
14
|
-
const obj = { a: { b: 1 } };
|
|
15
|
-
const cloned = clone(obj);
|
|
16
|
-
expect(cloned).not.toBe(obj);
|
|
17
|
-
expect(cloned).toEqual(obj);
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
test("works by preserving nested references", () => {
|
|
21
|
-
const objWithRef = { a: { b: () => {} }, c: { d: 1 } } as any;
|
|
22
|
-
const cloned = clone(objWithRef);
|
|
23
|
-
objWithRef.c = {};
|
|
24
|
-
expect(cloned.a.b).toBe(objWithRef.a.b);
|
|
25
|
-
expect(cloned.c).toEqual({ d: 1 });
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
test("works with deep delta", () => {
|
|
29
|
-
const nodeSize = (node: any) => node.size;
|
|
30
|
-
const nodeBorderWidth = (node: any) => node.borderWidth;
|
|
31
|
-
const storedTheme = clone({
|
|
32
|
-
nodeSize,
|
|
33
|
-
nodeBorderWidth,
|
|
34
|
-
nodeBorderColor: "#000",
|
|
35
|
-
edgeWidth: 1,
|
|
36
|
-
edgeColor: "#123",
|
|
37
|
-
});
|
|
38
|
-
const incomingTheme = {
|
|
39
|
-
nodeSize,
|
|
40
|
-
nodeBorderWidth,
|
|
41
|
-
nodeBorderColor: "#000",
|
|
42
|
-
edgeWidth: 1,
|
|
43
|
-
edgeColor: "#123",
|
|
44
|
-
};
|
|
45
|
-
const diff = delta(storedTheme, incomingTheme);
|
|
46
|
-
expect(diff).toEqual(null);
|
|
47
|
-
});
|
package/src/clone.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* clones an object while preserving the references of non-primitive values
|
|
3
|
-
*
|
|
4
|
-
* @param obj - the object to clone
|
|
5
|
-
* @returns the cloned object
|
|
6
|
-
*/
|
|
7
|
-
export const clone = (obj: Record<any, any>) => {
|
|
8
|
-
const cloned = { ...obj };
|
|
9
|
-
for (const key in cloned) {
|
|
10
|
-
if (typeof cloned[key] === 'object') {
|
|
11
|
-
cloned[key] = clone(cloned[key]);
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
return cloned;
|
|
15
|
-
};
|
package/src/ctx/index.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import type { MaybeRef } from 'vue';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* pulls ctx from a canvas or canvas ref (vue.js), throws if not found
|
|
5
|
-
*
|
|
6
|
-
* @returns {CanvasRenderingContext2D}
|
|
7
|
-
* @example const ctx = getCtx(canvasRef);
|
|
8
|
-
* // ctx is defined and ready to use
|
|
9
|
-
* @throws {Error} if canvas or 2d context not found
|
|
10
|
-
*/
|
|
11
|
-
export const getCtx = (
|
|
12
|
-
canvasInput: MaybeRef<HTMLCanvasElement | null | undefined>,
|
|
13
|
-
) => {
|
|
14
|
-
if (!canvasInput) throw new Error('canvas not found');
|
|
15
|
-
const canvas = 'value' in canvasInput ? canvasInput.value : canvasInput;
|
|
16
|
-
if (!canvas) throw new Error('canvas not found');
|
|
17
|
-
const ctx = canvas.getContext('2d');
|
|
18
|
-
if (!ctx) throw new Error('2d context not found');
|
|
19
|
-
return ctx;
|
|
20
|
-
};
|
package/src/debugging.ts
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
export const useLogReport = <T = string>(
|
|
2
|
-
frequencyMs = 1000,
|
|
3
|
-
resetReportAfterLogging = true,
|
|
4
|
-
) => {
|
|
5
|
-
const report = new Set<T>();
|
|
6
|
-
const logReport = () => {
|
|
7
|
-
console.log(Array.from(report));
|
|
8
|
-
if (resetReportAfterLogging) report.clear();
|
|
9
|
-
};
|
|
10
|
-
setInterval(logReport, frequencyMs);
|
|
11
|
-
return report;
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
export const useCooldownLog = (frequencyMs = 1000) => {
|
|
15
|
-
let cooldown = false;
|
|
16
|
-
const log = (...data: any[]) => {
|
|
17
|
-
if (cooldown) return;
|
|
18
|
-
console.log(...data);
|
|
19
|
-
cooldown = true;
|
|
20
|
-
};
|
|
21
|
-
setInterval(() => (cooldown = false), frequencyMs);
|
|
22
|
-
return log;
|
|
23
|
-
};
|
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
import { expect, test } from 'vitest';
|
|
2
|
-
|
|
3
|
-
import { delta } from '.';
|
|
4
|
-
|
|
5
|
-
test('deepDelta standard', () => {
|
|
6
|
-
const yona = {
|
|
7
|
-
name: 'yona',
|
|
8
|
-
sex: 'm',
|
|
9
|
-
residence: 'amherst',
|
|
10
|
-
school: {
|
|
11
|
-
name: 'umass',
|
|
12
|
-
year: 'senior',
|
|
13
|
-
info: {
|
|
14
|
-
major: ['cs'],
|
|
15
|
-
minor: [],
|
|
16
|
-
start: '2023',
|
|
17
|
-
},
|
|
18
|
-
},
|
|
19
|
-
test: {
|
|
20
|
-
hello: 'world',
|
|
21
|
-
removeMe: {
|
|
22
|
-
removeMe: 'removeMe',
|
|
23
|
-
removeMe2: {},
|
|
24
|
-
},
|
|
25
|
-
test2: {
|
|
26
|
-
test3: 'secret',
|
|
27
|
-
},
|
|
28
|
-
},
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
const dila = {
|
|
32
|
-
name: 'dila',
|
|
33
|
-
sex: 'f',
|
|
34
|
-
residence: 'amherst',
|
|
35
|
-
school: {
|
|
36
|
-
name: 'umass',
|
|
37
|
-
year: 'junior',
|
|
38
|
-
info: {
|
|
39
|
-
major: ['cs', 'japanese'],
|
|
40
|
-
minor: [],
|
|
41
|
-
start: '2022',
|
|
42
|
-
},
|
|
43
|
-
},
|
|
44
|
-
test: {
|
|
45
|
-
hello: 'world',
|
|
46
|
-
removeMe: {
|
|
47
|
-
removeMe: 'removeMe',
|
|
48
|
-
removeMe2: {},
|
|
49
|
-
},
|
|
50
|
-
test2: {
|
|
51
|
-
test3: 'secret changed',
|
|
52
|
-
},
|
|
53
|
-
},
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
const expected = {
|
|
57
|
-
name: 'dila',
|
|
58
|
-
sex: 'f',
|
|
59
|
-
school: {
|
|
60
|
-
year: 'junior',
|
|
61
|
-
info: {
|
|
62
|
-
major: ['cs', 'japanese'],
|
|
63
|
-
start: '2022',
|
|
64
|
-
},
|
|
65
|
-
},
|
|
66
|
-
test: {
|
|
67
|
-
test2: {
|
|
68
|
-
test3: 'secret changed',
|
|
69
|
-
},
|
|
70
|
-
},
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
const result = delta(yona, dila);
|
|
74
|
-
expect(result).toEqual(expected);
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
test('deepDelta works if new object has more keys', () => {
|
|
78
|
-
const yona = {
|
|
79
|
-
name: 'yona',
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
const dila = {
|
|
83
|
-
name: 'dila',
|
|
84
|
-
favoriteColor: 'blue',
|
|
85
|
-
anime: {
|
|
86
|
-
naruto: 'meh',
|
|
87
|
-
bleach: 'good',
|
|
88
|
-
},
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
const expected = {
|
|
92
|
-
name: 'dila',
|
|
93
|
-
favoriteColor: 'blue',
|
|
94
|
-
anime: {
|
|
95
|
-
naruto: 'meh',
|
|
96
|
-
bleach: 'good',
|
|
97
|
-
},
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
const result = delta(yona, dila);
|
|
101
|
-
|
|
102
|
-
expect(result).toEqual(expected);
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
test('deepDelta works on fields that are null or undefined', () => {
|
|
106
|
-
const colorGetter = () => 'blue';
|
|
107
|
-
|
|
108
|
-
const yona = {
|
|
109
|
-
name: 'yona',
|
|
110
|
-
favoriteColor: {
|
|
111
|
-
color: colorGetter,
|
|
112
|
-
},
|
|
113
|
-
sbahn: 's1',
|
|
114
|
-
};
|
|
115
|
-
|
|
116
|
-
const dila = {
|
|
117
|
-
name: 'dila',
|
|
118
|
-
favoriteColor: null,
|
|
119
|
-
sbahn: undefined,
|
|
120
|
-
};
|
|
121
|
-
|
|
122
|
-
const expected = {
|
|
123
|
-
name: 'dila',
|
|
124
|
-
sbahn: undefined,
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
const result = delta(yona, dila);
|
|
128
|
-
expect(result).toEqual(expected);
|
|
129
|
-
});
|
package/src/deepDelta/index.ts
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
// recursively compare two objects and return the delta
|
|
2
|
-
|
|
3
|
-
// -----------------
|
|
4
|
-
|
|
5
|
-
const isObj = (obj: any) =>
|
|
6
|
-
Object.prototype.toString.call(obj) === '[object Object]';
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* gets the delta between two objects
|
|
10
|
-
*
|
|
11
|
-
* @param oldObject
|
|
12
|
-
* @param newObject
|
|
13
|
-
* @returns an object with only the changes, the values are the new values
|
|
14
|
-
*/
|
|
15
|
-
export const delta = (
|
|
16
|
-
oldObject: Record<any, any>,
|
|
17
|
-
newObject: Record<any, any>,
|
|
18
|
-
) => {
|
|
19
|
-
const output: Record<any, any> = {};
|
|
20
|
-
|
|
21
|
-
if (!oldObject) return newObject;
|
|
22
|
-
if (!newObject) return null;
|
|
23
|
-
|
|
24
|
-
const oldObjectKeys = Object.keys(oldObject);
|
|
25
|
-
const newObjectKeys = Object.keys(newObject);
|
|
26
|
-
|
|
27
|
-
for (const key of newObjectKeys) {
|
|
28
|
-
if (!oldObjectKeys.includes(key)) {
|
|
29
|
-
output[key] = newObject[key];
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
for (const key of oldObjectKeys) {
|
|
34
|
-
if (isObj(oldObject[key])) {
|
|
35
|
-
const diffObj = delta(oldObject[key], newObject[key]);
|
|
36
|
-
if (diffObj) output[key] = diffObj;
|
|
37
|
-
continue;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
if (Array.isArray(oldObject[key])) {
|
|
41
|
-
if (JSON.stringify(oldObject[key]) !== JSON.stringify(newObject[key]))
|
|
42
|
-
output[key] = newObject[key];
|
|
43
|
-
continue;
|
|
44
|
-
} else if (oldObject[key] !== newObject[key]) output[key] = newObject[key];
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return Object.keys(output).length ? output : null;
|
|
48
|
-
};
|
package/src/deepMerge.test.ts
DELETED
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
|
|
3
|
-
import { deepMerge } from './deepMerge';
|
|
4
|
-
|
|
5
|
-
describe('deepMerge', () => {
|
|
6
|
-
it('merges flat objects with rightmost priority', () => {
|
|
7
|
-
const result = deepMerge({ a: 1 }, { a: 2, b: 3 });
|
|
8
|
-
expect(result).toEqual({ a: 2, b: 3 });
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
it('merges nested objects deeply', () => {
|
|
12
|
-
const result = deepMerge({ a: { b: 1, c: 2 } }, { a: { b: 3, d: 4 } });
|
|
13
|
-
expect(result).toEqual({ a: { b: 3, c: 2, d: 4 } });
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
it('overwrites arrays instead of merging them', () => {
|
|
17
|
-
const result = deepMerge(
|
|
18
|
-
{ a: [1, 2], b: { c: [3, 4] } },
|
|
19
|
-
{ a: [5], b: { c: [6] } },
|
|
20
|
-
);
|
|
21
|
-
expect(result).toEqual({ a: [5], b: { c: [6] } });
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
it('handles primitive overwrites correctly', () => {
|
|
25
|
-
const result = deepMerge({ a: { b: 1 } }, { a: 5 });
|
|
26
|
-
expect(result).toEqual({ a: 5 });
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
it('returns non-object when passed a primitive last', () => {
|
|
30
|
-
const result = deepMerge({ a: 1 }, 5);
|
|
31
|
-
expect(result).toBe(5);
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
it('handles merging with undefined and null safely', () => {
|
|
35
|
-
const result = deepMerge({ a: { b: 1 } }, undefined, null, { a: { c: 2 } });
|
|
36
|
-
expect(result).toEqual({ a: { b: 1, c: 2 } });
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
it('handles empty inputs', () => {
|
|
40
|
-
const result = deepMerge();
|
|
41
|
-
expect(result).toEqual({});
|
|
42
|
-
});
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
describe('deepMerge - special cases', () => {
|
|
46
|
-
it('overwrites Date objects', () => {
|
|
47
|
-
const date1 = new Date('2020-01-01');
|
|
48
|
-
const date2 = new Date('2021-01-01');
|
|
49
|
-
|
|
50
|
-
const result = deepMerge({ date: date1 }, { date: date2 });
|
|
51
|
-
expect(result.date).toBe(date2);
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
it('overwrites Map objects', () => {
|
|
55
|
-
const map1 = new Map([['a', 1]]);
|
|
56
|
-
const map2 = new Map([['b', 2]]);
|
|
57
|
-
|
|
58
|
-
const result = deepMerge({ map: map1 }, { map: map2 });
|
|
59
|
-
expect(result.map).toBe(map2);
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
it('overwrites Set objects', () => {
|
|
63
|
-
const set1 = new Set([1, 2]);
|
|
64
|
-
const set2 = new Set([3, 4]);
|
|
65
|
-
|
|
66
|
-
const result = deepMerge({ set: set1 }, { set: set2 });
|
|
67
|
-
expect(result.set).toBe(set2);
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
it('overwrites functions', () => {
|
|
71
|
-
const fn1 = () => 1;
|
|
72
|
-
const fn2 = () => 2;
|
|
73
|
-
|
|
74
|
-
const result = deepMerge({ fn: fn1 }, { fn: fn2 });
|
|
75
|
-
expect(result.fn).toBe(fn2);
|
|
76
|
-
expect(result.fn()).toBe(2);
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
it('overwrites class instances', () => {
|
|
80
|
-
class Example {
|
|
81
|
-
x = 1;
|
|
82
|
-
}
|
|
83
|
-
const obj1 = { instance: new Example() };
|
|
84
|
-
const obj2 = { instance: { y: 2 } };
|
|
85
|
-
|
|
86
|
-
const result = deepMerge(obj1, obj2);
|
|
87
|
-
expect(result.instance).toEqual({ y: 2 });
|
|
88
|
-
});
|
|
89
|
-
});
|
package/src/deepMerge.ts
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
export const isPlainObject = (
|
|
2
|
-
obj: any,
|
|
3
|
-
): obj is Record<string | number | symbol, unknown> =>
|
|
4
|
-
!!obj &&
|
|
5
|
-
typeof obj === 'object' &&
|
|
6
|
-
Object.getPrototypeOf(obj) === Object.prototype;
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Deeply merges multiple objects. Properties from later objects overwrite those from earlier ones.
|
|
10
|
-
* Non-object values (including arrays) are replaced, not merged.
|
|
11
|
-
*
|
|
12
|
-
* @param {...any[]} objects - Objects to merge. The rightmost object's properties take precedence.
|
|
13
|
-
* @returns {any} - A new deeply merged object.
|
|
14
|
-
*
|
|
15
|
-
* @example
|
|
16
|
-
* const result = deepMerge(
|
|
17
|
-
* { a: 1, b: { c: 2 } },
|
|
18
|
-
* { b: { d: 3 }, e: 4 }
|
|
19
|
-
* );
|
|
20
|
-
* // result: { a: 1, b: { c: 2, d: 3 }, e: 4 }
|
|
21
|
-
*/
|
|
22
|
-
export const deepMerge = (...objects: any[]): any =>
|
|
23
|
-
objects.reduce((acc, obj) => {
|
|
24
|
-
if (obj === undefined || obj === null) return acc;
|
|
25
|
-
|
|
26
|
-
if (!isPlainObject(obj)) return obj;
|
|
27
|
-
|
|
28
|
-
Object.keys(obj).forEach((key) => {
|
|
29
|
-
if (isPlainObject(obj[key]) && isPlainObject(acc[key])) {
|
|
30
|
-
acc[key] = deepMerge(acc[key], obj[key]);
|
|
31
|
-
} else {
|
|
32
|
-
acc[key] = obj[key];
|
|
33
|
-
}
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
return acc;
|
|
37
|
-
}, {});
|
package/src/fps.ts
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import { onMounted, onUnmounted, ref } from "vue";
|
|
2
|
-
|
|
3
|
-
export function useFPS() {
|
|
4
|
-
const fps = ref(0);
|
|
5
|
-
const frameTime = ref(0);
|
|
6
|
-
const slowFrameCount = ref(0);
|
|
7
|
-
const slowFrameRatio = ref(0);
|
|
8
|
-
|
|
9
|
-
let frameId: number;
|
|
10
|
-
let intervalId: NodeJS.Timeout;
|
|
11
|
-
|
|
12
|
-
let lastMeasure = performance.now();
|
|
13
|
-
let lastFrame = performance.now();
|
|
14
|
-
let frames = 0;
|
|
15
|
-
let slowFrames = 0;
|
|
16
|
-
|
|
17
|
-
const SLOW_FRAME_THRESHOLD = 33.3; // ms
|
|
18
|
-
|
|
19
|
-
const update = () => {
|
|
20
|
-
const now = performance.now();
|
|
21
|
-
const delta = now - lastFrame;
|
|
22
|
-
frameTime.value = delta;
|
|
23
|
-
lastFrame = now;
|
|
24
|
-
|
|
25
|
-
if (delta > SLOW_FRAME_THRESHOLD) {
|
|
26
|
-
slowFrames++;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
frames++;
|
|
30
|
-
frameId = requestAnimationFrame(update);
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
const measure = () => {
|
|
34
|
-
const now = performance.now();
|
|
35
|
-
const delta = now - lastMeasure;
|
|
36
|
-
|
|
37
|
-
fps.value = Math.round((frames * 1000) / delta);
|
|
38
|
-
slowFrameCount.value = slowFrames;
|
|
39
|
-
slowFrameRatio.value = frames > 0 ? slowFrames / frames : 0;
|
|
40
|
-
|
|
41
|
-
frames = 0;
|
|
42
|
-
slowFrames = 0;
|
|
43
|
-
lastMeasure = now;
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
onMounted(() => {
|
|
47
|
-
lastFrame = performance.now();
|
|
48
|
-
lastMeasure = performance.now();
|
|
49
|
-
frameId = requestAnimationFrame(update);
|
|
50
|
-
intervalId = setInterval(measure, 500);
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
onUnmounted(() => {
|
|
54
|
-
cancelAnimationFrame(frameId);
|
|
55
|
-
clearInterval(intervalId);
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
return {
|
|
59
|
-
fps,
|
|
60
|
-
frameTime,
|
|
61
|
-
slowFrameCount,
|
|
62
|
-
slowFrameRatio,
|
|
63
|
-
};
|
|
64
|
-
}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
export const gcd = (a: number, b: number): number => (b ? gcd(b, a % b) : a);
|
|
3
|
-
|
|
4
|
-
export const isNullOrUnd = (input: any) =>
|
|
5
|
-
input === null || input === undefined;
|
|
6
|
-
|
|
7
|
-
export const isFraction = (input: string) => {
|
|
8
|
-
const fraction = input.trim().split('/').filter(Boolean);
|
|
9
|
-
if (fraction.length !== 2) return false;
|
|
10
|
-
const [numerator, denominator] = fraction.map(Number);
|
|
11
|
-
if (isNullOrUnd(numerator) || isNullOrUnd(denominator)) return false;
|
|
12
|
-
return true;
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
export const decimalToFraction = (decimalInput: number) => {
|
|
16
|
-
const decimal = Math.round(decimalInput * 1e10) / 1e10;
|
|
17
|
-
const len = decimal.toString().length - 2;
|
|
18
|
-
let denominator = 10 ** len;
|
|
19
|
-
let numerator = decimal * denominator;
|
|
20
|
-
const divisor = gcd(numerator, denominator);
|
|
21
|
-
numerator /= divisor;
|
|
22
|
-
denominator /= divisor;
|
|
23
|
-
if (denominator === 1) return numerator.toString();
|
|
24
|
-
return `${numerator}/${denominator}`;
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* @param fractionInput a string representing a fraction
|
|
29
|
-
* @returns the decimal representation of the fraction or undefined if the input is not a fraction
|
|
30
|
-
*/
|
|
31
|
-
export const fractionToDecimal = (fractionInput: string) => {
|
|
32
|
-
if (!isFraction(fractionInput)) return;
|
|
33
|
-
const fraction = fractionInput.split('/');
|
|
34
|
-
const [numerator, denominator] = fraction.map(Number);
|
|
35
|
-
return numerator / denominator;
|
|
36
|
-
};
|
package/src/hashing.ts
DELETED