magic-utils-yonava 1.0.2 → 1.0.3
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/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/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
|
@@ -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
package/src/localStorage.ts
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
type LocalStorageGetter = (...args: any[]) => string;
|
|
4
|
-
type LocalStorageRecord = Record<string, string | LocalStorageGetter>;
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* a registry for all localStorage keys this application uses
|
|
8
|
-
*/
|
|
9
|
-
export const localKeys = {
|
|
10
|
-
/** nodes in graph product */
|
|
11
|
-
nodes: (key: string) => `nodes-${key}` as const,
|
|
12
|
-
/** edges in graph product */
|
|
13
|
-
edges: (key: string) => `edges-${key}` as const,
|
|
14
|
-
/** graph product simulation speed */
|
|
15
|
-
simulationPlaybackSpeed: 'simulation-playback-speed',
|
|
16
|
-
/** graph theme set by user - {@link Graph.preferredTheme} */
|
|
17
|
-
preferredTheme: 'preferred-theme',
|
|
18
|
-
} as const satisfies LocalStorageRecord;
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* all return values of localStorage are, by default, string.
|
|
22
|
-
* this type allows string to be narrowed to types such as 'true' | 'false'
|
|
23
|
-
*/
|
|
24
|
-
type TypeOverride = {};
|
|
25
|
-
|
|
26
|
-
type LocalObj = typeof localKeys;
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* @example
|
|
30
|
-
* type T = TypeOrReturnType<number> // number
|
|
31
|
-
* type TFunc = TypeOrReturnType<() => number> // number
|
|
32
|
-
*/
|
|
33
|
-
type TypeOrReturnType<T> = T extends (...args: any[]) => infer U ? U : T;
|
|
34
|
-
|
|
35
|
-
type LocalKeys = TypeOrReturnType<LocalObj[keyof LocalObj]>;
|
|
36
|
-
type LocalType<T extends LocalKeys> = T extends keyof TypeOverride
|
|
37
|
-
? TypeOverride[T]
|
|
38
|
-
: string;
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* perform **type safe** localStorage actions
|
|
42
|
-
*/
|
|
43
|
-
export const local = {
|
|
44
|
-
get: <T extends LocalKeys>(key: T) => localStorage.getItem(key),
|
|
45
|
-
set: <T extends LocalKeys, K extends LocalType<T>>(key: T, value: K) =>
|
|
46
|
-
localStorage.setItem(key, value),
|
|
47
|
-
remove: <T extends LocalKeys>(key: T) => localStorage.removeItem(key),
|
|
48
|
-
clear: localStorage.clear,
|
|
49
|
-
};
|
package/src/math.test.ts
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from 'vitest';
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
average,
|
|
5
|
-
gcd,
|
|
6
|
-
getPrimeFactors,
|
|
7
|
-
lowestPrimeFactor,
|
|
8
|
-
roundToNearestN,
|
|
9
|
-
} from './math';
|
|
10
|
-
|
|
11
|
-
describe('roundToNearestN', () => {
|
|
12
|
-
test('rounds a number to the nearest multiple of n', () => {
|
|
13
|
-
const roundToNearest5 = roundToNearestN(5);
|
|
14
|
-
expect(roundToNearest5(13)).toBe(15);
|
|
15
|
-
expect(roundToNearest5(12)).toBe(10);
|
|
16
|
-
});
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
describe('getPrimeFactors', () => {
|
|
20
|
-
test('returns the prime factors of a number', () => {
|
|
21
|
-
expect(getPrimeFactors(12)).toEqual([2, 2, 3]);
|
|
22
|
-
expect(getPrimeFactors(15)).toEqual([3, 5]);
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
test('edge case: 1', () => {
|
|
26
|
-
expect(getPrimeFactors(1)).toEqual([]);
|
|
27
|
-
});
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
describe('lowestPrimeFactor', () => {
|
|
31
|
-
test('returns the lowest prime factor of a number', () => {
|
|
32
|
-
expect(lowestPrimeFactor(12)).toBe(2);
|
|
33
|
-
expect(lowestPrimeFactor(15)).toBe(3);
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
test('edge case: 1', () => {
|
|
37
|
-
expect(lowestPrimeFactor(1)).toBe(1);
|
|
38
|
-
});
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
describe('gcd', () => {
|
|
42
|
-
test('returns the greatest common divisor of two numbers', () => {
|
|
43
|
-
expect(gcd(12, 15)).toBe(3);
|
|
44
|
-
expect(gcd(12, 18)).toBe(6);
|
|
45
|
-
});
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
describe('average', () => {
|
|
49
|
-
test('returns the average of a list of numbers', () => {
|
|
50
|
-
expect(average([1, 2, 3, 4, 5])).toBe(3);
|
|
51
|
-
expect(average([1, 2, 3, 4, 5, 6])).toBe(3.5);
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
test('edge case: empty list', () => {
|
|
55
|
-
expect(average([])).toBe(0);
|
|
56
|
-
});
|
|
57
|
-
});
|
package/src/random.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* given two numbers, this function returns a random number between them (inclusive)
|
|
3
|
-
*
|
|
4
|
-
* @param min - the lowest number
|
|
5
|
-
* @param max - the highest number
|
|
6
|
-
* @returns a random number between min and max
|
|
7
|
-
*/
|
|
8
|
-
export const getRandomInRange = (min: number, max: number) =>
|
|
9
|
-
Math.round(Math.random() * (max - min) + min);
|
|
10
|
-
|
|
11
|
-
export const getRandomPointOnCanvas = (
|
|
12
|
-
canvas: HTMLCanvasElement,
|
|
13
|
-
buffer = 50,
|
|
14
|
-
) => ({
|
|
15
|
-
x: getRandomInRange(buffer, canvas.width - buffer),
|
|
16
|
-
y: getRandomInRange(buffer, canvas.height - buffer),
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* get a random element from an array
|
|
21
|
-
*
|
|
22
|
-
* @param array
|
|
23
|
-
* @returns random element from given array
|
|
24
|
-
*/
|
|
25
|
-
export const getRandomElement = <T>(array: ArrayLike<T>) => {
|
|
26
|
-
return array[Math.floor(Math.random() * array.length)];
|
|
27
|
-
};
|
package/src/string.test.ts
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from 'vitest';
|
|
2
|
-
|
|
3
|
-
import { camelCaseToTitleCase, capitalize } from './string';
|
|
4
|
-
|
|
5
|
-
describe('capitalize', () => {
|
|
6
|
-
test('capitalizes the first letter of a string', () => {
|
|
7
|
-
expect(capitalize('hello')).toBe('Hello');
|
|
8
|
-
expect(capitalize('this is one string')).toBe('This is one string');
|
|
9
|
-
});
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
describe('camelCaseToTitleCase', () => {
|
|
13
|
-
test('converts camelCase to title case', () => {
|
|
14
|
-
expect(camelCaseToTitleCase('camelCase')).toBe('Camel Case');
|
|
15
|
-
expect(camelCaseToTitleCase('thisIsOneString')).toBe('This Is One String');
|
|
16
|
-
});
|
|
17
|
-
});
|
package/tsconfig.json
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"outDir": "./dist",
|
|
4
|
-
"rootDir": "./src",
|
|
5
|
-
|
|
6
|
-
"target": "ES2022",
|
|
7
|
-
"module": "ESNext",
|
|
8
|
-
"lib": ["ES2023", "DOM"],
|
|
9
|
-
"types": ["node"],
|
|
10
|
-
"moduleResolution": "node",
|
|
11
|
-
"allowJs": false,
|
|
12
|
-
"strict": true,
|
|
13
|
-
"esModuleInterop": true,
|
|
14
|
-
"forceConsistentCasingInFileNames": true,
|
|
15
|
-
"resolveJsonModule": true,
|
|
16
|
-
"skipLibCheck": true,
|
|
17
|
-
"declaration": true,
|
|
18
|
-
},
|
|
19
|
-
"include": ["src/**/*"],
|
|
20
|
-
}
|
|
File without changes
|