utilium 0.0.1

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.
@@ -0,0 +1,5 @@
1
+ export * from './misc.js';
2
+ export * from './numbers.js';
3
+ export * from './objects.js';
4
+ export * from './random.js';
5
+ export * from './types.js';
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ export * from './misc.js';
2
+ export * from './numbers.js';
3
+ export * from './objects.js';
4
+ export * from './random.js';
5
+ export * from './types.js';
package/dist/misc.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ export declare function wait(time: number): Promise<unknown>;
2
+ export declare const greekLetterNames: string[];
3
+ export declare function isHex(str: string): boolean;
package/dist/misc.js ADDED
@@ -0,0 +1,33 @@
1
+ export function wait(time) {
2
+ return new Promise(resolve => setTimeout(resolve, time));
3
+ }
4
+ export const greekLetterNames = [
5
+ 'Alpha',
6
+ 'Beta',
7
+ 'Gamma',
8
+ 'Delta',
9
+ 'Epsilon',
10
+ 'Zeta',
11
+ 'Eta',
12
+ 'Theta',
13
+ 'Iota',
14
+ 'Kappa',
15
+ 'Lambda',
16
+ 'Mu',
17
+ 'Nu',
18
+ 'Xi',
19
+ 'Omicron',
20
+ 'Pi',
21
+ 'Rho',
22
+ 'Sigma',
23
+ 'Tau',
24
+ 'Upsilon',
25
+ 'Phi',
26
+ 'Chi',
27
+ 'Psi',
28
+ 'Omega',
29
+ ];
30
+ const hexRegex = /^[0-9a-f-.]+$/;
31
+ export function isHex(str) {
32
+ return hexRegex.test(str);
33
+ }
@@ -0,0 +1,3 @@
1
+ export declare function range(min: number, max: number): number[];
2
+ export declare function toDegrees(radians: number): number;
3
+ export declare function toRadians(degrees: number): number;
@@ -0,0 +1,13 @@
1
+ export function range(min, max) {
2
+ const a = [];
3
+ for (let i = min; i < max; i++) {
4
+ a.push(i);
5
+ }
6
+ return a;
7
+ }
8
+ export function toDegrees(radians) {
9
+ return (radians * 180) / Math.PI;
10
+ }
11
+ export function toRadians(degrees) {
12
+ return (degrees / 180) * Math.PI;
13
+ }
@@ -0,0 +1,81 @@
1
+ /// <reference types="node" resolution-mode="require"/>
2
+ export declare function filterObject<T extends object, K extends keyof T>(object: T, ...keys: K[]): Omit<T, K>;
3
+ import type * as FS from 'fs';
4
+ export declare function isJSON(str: string): boolean;
5
+ export declare abstract class FileMap<V> implements Map<string, V> {
6
+ protected readonly path: string;
7
+ protected fs: typeof FS;
8
+ get [Symbol.toStringTag](): string;
9
+ constructor(path: string, fs?: typeof FS);
10
+ protected abstract readonly _map: Map<string, V>;
11
+ abstract clear(): void;
12
+ abstract delete(key: string): boolean;
13
+ abstract get(key: string): V;
14
+ abstract has(key: string): boolean;
15
+ abstract set(key: string, value: V): this;
16
+ get size(): number;
17
+ get [Symbol.iterator](): any;
18
+ get keys(): typeof this._map.keys;
19
+ get values(): typeof this._map.values;
20
+ get entries(): typeof this._map.entries;
21
+ get forEach(): typeof this._map.forEach;
22
+ }
23
+ export type JSONObject<Key extends string | number | symbol = string> = {
24
+ [K in Key]: JSONValue;
25
+ };
26
+ export type JSONValue<Key extends string | number | symbol = string> = string | number | boolean | JSONObject<Key> | Array<JSONValue>;
27
+ export interface JSONFileMapOptions {
28
+ /**
29
+ * Should an invalid JSON file be overwritten
30
+ */
31
+ overwrite_invalid_json: boolean;
32
+ /**
33
+ *
34
+ */
35
+ fs: typeof FS;
36
+ }
37
+ /**
38
+ * A Map overlaying a JSON file
39
+ */
40
+ export declare class JSONFileMap<T extends JSONValue = JSONValue> extends FileMap<T> {
41
+ readonly options: JSONFileMapOptions;
42
+ get [Symbol.toStringTag](): 'JSONFileMap';
43
+ constructor(path: string, options: JSONFileMapOptions);
44
+ get _map(): Map<string, T>;
45
+ _write(map: Map<string, T>): void;
46
+ clear(): void;
47
+ delete(key: string): boolean;
48
+ get<U extends T>(key: string): U;
49
+ has(key: string): boolean;
50
+ set(key: string, value: T): this;
51
+ }
52
+ export interface FolderMapOptions {
53
+ /**
54
+ * Suffix to append to keys to resolve file names
55
+ */
56
+ suffix: string;
57
+ fs?: typeof FS;
58
+ }
59
+ /**
60
+ * A Map overlaying a folder
61
+ */
62
+ export declare class FolderMap extends FileMap<string> {
63
+ readonly options: Partial<FolderMapOptions>;
64
+ get [Symbol.toStringTag](): 'FolderMap';
65
+ constructor(path: string, options: Partial<FolderMapOptions>);
66
+ protected get _names(): string[];
67
+ protected _join(path: string): string;
68
+ protected get _map(): Map<string, string>;
69
+ clear(): void;
70
+ delete(key: string): boolean;
71
+ get(key: string): string;
72
+ has(key: string): boolean;
73
+ set(key: string, value: string): this;
74
+ }
75
+ export declare function resolveConstructors(object: object): string[];
76
+ /**
77
+ * Gets a random int, r, with the probability P(r) = (base)**r
78
+ * For example, with a probability of 1/2: P(1) = 1/2, P(2) = 1/4, etc.
79
+ * @param probability the probability
80
+ */
81
+ export declare function getRandomIntWithRecursiveProbability(probability?: number): number;
@@ -0,0 +1,178 @@
1
+ export function filterObject(object, ...keys) {
2
+ const entries = Object.entries(object);
3
+ return Object.fromEntries(entries.filter(([key]) => keys.includes(key)));
4
+ }
5
+ let _fs;
6
+ try {
7
+ _fs = await import('fs');
8
+ }
9
+ catch (e) {
10
+ _fs = null;
11
+ }
12
+ export function isJSON(str) {
13
+ try {
14
+ JSON.parse(str);
15
+ return true;
16
+ }
17
+ catch (e) {
18
+ return false;
19
+ }
20
+ }
21
+ export class FileMap {
22
+ get [Symbol.toStringTag]() {
23
+ return 'FileMap';
24
+ }
25
+ constructor(path, fs = _fs) {
26
+ this.path = path;
27
+ this.fs = fs;
28
+ if (!path) {
29
+ throw new ReferenceError('No path specified');
30
+ }
31
+ if (!fs) {
32
+ throw new ReferenceError('No filesystem API');
33
+ }
34
+ }
35
+ get size() {
36
+ return this._map.size;
37
+ }
38
+ get [Symbol.iterator]() {
39
+ return this._map[Symbol.iterator].bind(this._map);
40
+ }
41
+ get keys() {
42
+ return this._map.keys.bind(this._map);
43
+ }
44
+ get values() {
45
+ return this._map.values.bind(this._map);
46
+ }
47
+ get entries() {
48
+ return this._map.entries.bind(this._map);
49
+ }
50
+ get forEach() {
51
+ return this._map.forEach.bind(this._map);
52
+ }
53
+ }
54
+ /**
55
+ * A Map overlaying a JSON file
56
+ */
57
+ export class JSONFileMap extends FileMap {
58
+ get [Symbol.toStringTag]() {
59
+ return 'JSONFileMap';
60
+ }
61
+ constructor(path, options) {
62
+ super(path, options.fs);
63
+ this.options = options;
64
+ if (!this.fs.existsSync(path)) {
65
+ this.fs.writeFileSync(path, '{}');
66
+ }
67
+ }
68
+ get _map() {
69
+ const content = this.fs.readFileSync(this.path, 'utf8');
70
+ if (!isJSON(content)) {
71
+ if (!this.options.overwrite_invalid_json) {
72
+ throw new SyntaxError('Invalid JSON file: ' + this.path);
73
+ }
74
+ console.warn('Invalid JSON file (overwriting): ' + this.path);
75
+ this.clear();
76
+ return new Map();
77
+ }
78
+ return new Map(Object.entries(JSON.parse(content)));
79
+ }
80
+ _write(map) {
81
+ if (!this.fs.existsSync(this.path)) {
82
+ this.fs.writeFileSync(this.path, '{}');
83
+ }
84
+ const content = JSON.stringify(Object.fromEntries(map));
85
+ this.fs.writeFileSync(this.path, content);
86
+ }
87
+ clear() {
88
+ this.fs.writeFileSync(this.path, '{}');
89
+ }
90
+ delete(key) {
91
+ const map = this._map;
92
+ const rt = map.delete(key);
93
+ this._write(map);
94
+ return rt;
95
+ }
96
+ get(key) {
97
+ return this._map.get(key);
98
+ }
99
+ has(key) {
100
+ return this._map.has(key);
101
+ }
102
+ set(key, value) {
103
+ const map = this._map;
104
+ map.set(key, value);
105
+ this._write(map);
106
+ return this;
107
+ }
108
+ }
109
+ /**
110
+ * A Map overlaying a folder
111
+ */
112
+ export class FolderMap extends FileMap {
113
+ get [Symbol.toStringTag]() {
114
+ return 'FolderMap';
115
+ }
116
+ constructor(path, options) {
117
+ super(path, options.fs);
118
+ this.options = options;
119
+ }
120
+ get _names() {
121
+ return this.fs
122
+ .readdirSync(this.path)
123
+ .filter(p => p.endsWith(this.options.suffix))
124
+ .map(p => p.slice(0, -this.options.suffix.length));
125
+ }
126
+ _join(path) {
127
+ return `${this.path}/${path}${this.options.suffix}`;
128
+ }
129
+ get _map() {
130
+ const entries = [];
131
+ for (const name of this._names) {
132
+ const content = this.fs.readFileSync(this._join(name), 'utf8');
133
+ entries.push([name, content]);
134
+ }
135
+ return new Map(entries);
136
+ }
137
+ clear() {
138
+ for (const name of this._names) {
139
+ this.fs.unlinkSync(this._join(name));
140
+ }
141
+ }
142
+ delete(key) {
143
+ if (!this.has(key)) {
144
+ return false;
145
+ }
146
+ this.fs.unlinkSync(this._join(key));
147
+ return true;
148
+ }
149
+ get(key) {
150
+ if (this.has(key)) {
151
+ return this.fs.readFileSync(this._join(key), 'utf8');
152
+ }
153
+ }
154
+ has(key) {
155
+ return this._names.includes(key);
156
+ }
157
+ set(key, value) {
158
+ this.fs.writeFileSync(this._join(key), value);
159
+ return this;
160
+ }
161
+ }
162
+ export function resolveConstructors(object) {
163
+ const constructors = [];
164
+ let prototype = object;
165
+ while (prototype && !['Function', 'Object'].includes(prototype.constructor.name)) {
166
+ prototype = Object.getPrototypeOf(prototype);
167
+ constructors.push(prototype.constructor.name);
168
+ }
169
+ return constructors;
170
+ }
171
+ /**
172
+ * Gets a random int, r, with the probability P(r) = (base)**r
173
+ * For example, with a probability of 1/2: P(1) = 1/2, P(2) = 1/4, etc.
174
+ * @param probability the probability
175
+ */
176
+ export function getRandomIntWithRecursiveProbability(probability = 0.5) {
177
+ return -Math.floor(Math.log(Math.random()) / Math.log(1 / probability));
178
+ }
@@ -0,0 +1,5 @@
1
+ export declare function randomFloat(min?: number, max?: number): number;
2
+ export declare function randomHex(length?: number): string;
3
+ export declare function randomBoolean(): boolean;
4
+ export declare function randomBinaryString(length?: number): string;
5
+ export declare function randomInt(min?: number, max?: number): number;
package/dist/random.js ADDED
@@ -0,0 +1,23 @@
1
+ export function randomFloat(min = 0, max = 1) {
2
+ return Math.random() * (max - min) + min;
3
+ }
4
+ export function randomHex(length = 1) {
5
+ let s = '';
6
+ for (let i = 0; i < length; i++) {
7
+ s += Math.floor(Math.random() * 16).toString(16);
8
+ }
9
+ return s;
10
+ }
11
+ export function randomBoolean() {
12
+ return !!Math.round(Math.random());
13
+ }
14
+ export function randomBinaryString(length = 1) {
15
+ let b = '';
16
+ for (let i = 0; i < length; i++) {
17
+ b += Math.round(Math.random());
18
+ }
19
+ return b;
20
+ }
21
+ export function randomInt(min = 0, max = 1) {
22
+ return Math.round(Math.random() * (max - min) + min);
23
+ }
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Expands the type T (for intellisense and debugging)
3
+ * @see https://stackoverflow.com/a/69288824/17637456
4
+ */
5
+ export type Expand<T> = T extends (...args: infer A) => infer R ? (...args: Expand<A>) => Expand<R> : T extends infer O ? {
6
+ [K in keyof O]: O[K];
7
+ } : never;
8
+ /**
9
+ * Recursivly expands the type T (for intellisense and debugging)
10
+ * @see https://stackoverflow.com/a/69288824/17637456
11
+ */
12
+ export type ExpandRecursively<T> = T extends (...args: infer A) => infer R ? (...args: ExpandRecursively<A>) => ExpandRecursively<R> : T extends object ? T extends infer O ? {
13
+ [K in keyof O]: ExpandRecursively<O[K]>;
14
+ } : never : T;
15
+ /**
16
+ * Extracts an object with properties assignable to P from an object T
17
+ * @see https://stackoverflow.com/a/71532723/17637456
18
+ */
19
+ export type ExtractProperties<T, P> = {
20
+ [K in keyof T as T[K] extends infer Prop ? (Prop extends P ? K : never) : never]: T[K];
21
+ };
22
+ /**
23
+ * Extract the keys of T which are required
24
+ * @see https://stackoverflow.com/a/55247867/17637456
25
+ */
26
+ export type RequiredKeys<T> = {
27
+ [K in keyof T]-?: {} extends {
28
+ [P in K]: T[K];
29
+ } ? never : K;
30
+ }[keyof T];
31
+ /**
32
+ * @see https://dev.to/tmhao2005/ts-useful-advanced-types-3k5e
33
+ */
34
+ export type RequiredProperties<T extends object, K extends keyof T = keyof T> = Omit<T, K> & Required<Pick<T, K>>;
35
+ /**
36
+ * @see https://dev.to/tmhao2005/ts-useful-advanced-types-3k5e
37
+ */
38
+ export type DeepRequired<T> = {
39
+ [K in keyof T]: Required<DeepRequired<T[K]>>;
40
+ };
41
+ /**
42
+ * @see https://dev.to/tmhao2005/ts-useful-advanced-types-3k5e
43
+ */
44
+ export type Optional<T, K extends keyof T> = Omit<T, K> & Partial<T>;
45
+ /**
46
+ * @see https://dev.to/tmhao2005/ts-useful-advanced-types-3k5e
47
+ */
48
+ export type NestedKeys<T extends object> = {
49
+ [P in keyof T & (string | number)]: T[P] extends Date ? `${P}` : T[P] extends Record<string, unknown> ? `${P}` | `${P}.${NestedKeys<T[P]>}` : `${P}`;
50
+ }[keyof T & (string | number)];
51
+ /**
52
+ * @see https://dev.to/tmhao2005/ts-useful-advanced-types-3k5e
53
+ */
54
+ export type PartialRecursive<T> = {
55
+ [P in keyof T]?: T[P] extends (infer U)[] ? PartialRecursive<U>[] : T[P] extends object | undefined ? PartialRecursive<T[P]> : T[P];
56
+ };
57
+ /**
58
+ * Get the keys of a union of objects
59
+ * @see https://stackoverflow.com/a/65805753/17637456
60
+ */
61
+ export type UnionKeys<T> = T extends T ? keyof T : never;
62
+ type StrictUnionHelper<T, TAll> = T extends unknown ? T & Partial<Record<Exclude<UnionKeys<TAll>, keyof T>, never>> : never;
63
+ /**
64
+ * @see https://stackoverflow.com/a/65805753/17637456
65
+ */
66
+ export type StrictUnion<T> = Expand<StrictUnionHelper<T, T>>;
67
+ export {};
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "utilium",
3
+ "version": "0.0.1",
4
+ "description": "Utilies",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "type": "module",
8
+ "scripts": {
9
+ "format:check": "prettier --check .",
10
+ "format": "prettier --write .",
11
+ "lint": "eslint src && tsc --noEmit",
12
+ "build": "tsc -p tsconfig.json",
13
+ "build:docs": "typedoc src/index.ts --out docs",
14
+ "prepublishOnly": "npm run build"
15
+ },
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/dr-vortex/utilium.git"
19
+ },
20
+ "author": "James P. <jp@drvortex.dev> (https://drvortex.dev)",
21
+ "license": "MIT",
22
+ "bugs": {
23
+ "url": "https://github.com/dr-vortex/utilium/issues"
24
+ },
25
+ "homepage": "https://github.com/dr-vortex/utilium#readme",
26
+ "devDependencies": {
27
+ "@typescript-eslint/eslint-plugin": "^6.2.0",
28
+ "@typescript-eslint/parser": "^6.2.0",
29
+ "eslint": "^8.45.0",
30
+ "prettier": "^3.2.5",
31
+ "typedoc": "^0.24.8",
32
+ "typescript": "^5.1.6"
33
+ },
34
+ "dependencies": {
35
+ "@types/node": "^20.12.7"
36
+ }
37
+ }
package/readme.md ADDED
@@ -0,0 +1,3 @@
1
+ # Utilium
2
+
3
+ A bunch of utilies for Typescript that I have found to be very useful
package/src/index.ts ADDED
@@ -0,0 +1,5 @@
1
+ export * from './misc.js';
2
+ export * from './numbers.js';
3
+ export * from './objects.js';
4
+ export * from './random.js';
5
+ export * from './types.js';
package/src/misc.ts ADDED
@@ -0,0 +1,36 @@
1
+ export function wait(time: number) {
2
+ return new Promise(resolve => setTimeout(resolve, time));
3
+ }
4
+
5
+ export const greekLetterNames = [
6
+ 'Alpha',
7
+ 'Beta',
8
+ 'Gamma',
9
+ 'Delta',
10
+ 'Epsilon',
11
+ 'Zeta',
12
+ 'Eta',
13
+ 'Theta',
14
+ 'Iota',
15
+ 'Kappa',
16
+ 'Lambda',
17
+ 'Mu',
18
+ 'Nu',
19
+ 'Xi',
20
+ 'Omicron',
21
+ 'Pi',
22
+ 'Rho',
23
+ 'Sigma',
24
+ 'Tau',
25
+ 'Upsilon',
26
+ 'Phi',
27
+ 'Chi',
28
+ 'Psi',
29
+ 'Omega',
30
+ ];
31
+
32
+ const hexRegex = /^[0-9a-f-.]+$/;
33
+
34
+ export function isHex(str: string) {
35
+ return hexRegex.test(str);
36
+ }
package/src/numbers.ts ADDED
@@ -0,0 +1,15 @@
1
+ export function range(min: number, max: number): number[] {
2
+ const a = [];
3
+ for (let i = min; i < max; i++) {
4
+ a.push(i);
5
+ }
6
+ return a;
7
+ }
8
+
9
+ export function toDegrees(radians: number): number {
10
+ return (radians * 180) / Math.PI;
11
+ }
12
+
13
+ export function toRadians(degrees: number): number {
14
+ return (degrees / 180) * Math.PI;
15
+ }
package/src/objects.ts ADDED
@@ -0,0 +1,253 @@
1
+ export function filterObject<T extends object, K extends keyof T>(object: T, ...keys: K[]): Omit<T, K> {
2
+ const entries = <[K, T[K]][]>Object.entries(object);
3
+ return <Omit<T, K>>(<unknown>Object.fromEntries(entries.filter(([key]) => keys.includes(key))));
4
+ }
5
+
6
+ import type * as FS from 'fs';
7
+
8
+ let _fs: typeof FS;
9
+ try {
10
+ _fs = await import('fs');
11
+ } catch (e) {
12
+ _fs = null;
13
+ }
14
+
15
+ export function isJSON(str: string) {
16
+ try {
17
+ JSON.parse(str);
18
+ return true;
19
+ } catch (e) {
20
+ return false;
21
+ }
22
+ }
23
+
24
+ export abstract class FileMap<V> implements Map<string, V> {
25
+ public get [Symbol.toStringTag](): string {
26
+ return 'FileMap';
27
+ }
28
+
29
+ public constructor(
30
+ protected readonly path: string,
31
+ protected fs: typeof FS = _fs
32
+ ) {
33
+ if (!path) {
34
+ throw new ReferenceError('No path specified');
35
+ }
36
+
37
+ if (!fs) {
38
+ throw new ReferenceError('No filesystem API');
39
+ }
40
+ }
41
+
42
+ protected abstract readonly _map: Map<string, V>;
43
+
44
+ public abstract clear(): void;
45
+
46
+ public abstract delete(key: string): boolean;
47
+
48
+ public abstract get(key: string): V;
49
+
50
+ public abstract has(key: string): boolean;
51
+
52
+ public abstract set(key: string, value: V): this;
53
+
54
+ public get size() {
55
+ return this._map.size;
56
+ }
57
+
58
+ public get [Symbol.iterator]() {
59
+ return this._map[Symbol.iterator].bind(this._map);
60
+ }
61
+
62
+ public get keys(): typeof this._map.keys {
63
+ return this._map.keys.bind(this._map);
64
+ }
65
+
66
+ public get values(): typeof this._map.values {
67
+ return this._map.values.bind(this._map);
68
+ }
69
+
70
+ public get entries(): typeof this._map.entries {
71
+ return this._map.entries.bind(this._map);
72
+ }
73
+
74
+ public get forEach(): typeof this._map.forEach {
75
+ return this._map.forEach.bind(this._map);
76
+ }
77
+ }
78
+
79
+ export type JSONObject<Key extends string | number | symbol = string> = { [K in Key]: JSONValue };
80
+
81
+ export type JSONValue<Key extends string | number | symbol = string> = string | number | boolean | JSONObject<Key> | Array<JSONValue>;
82
+
83
+ export interface JSONFileMapOptions {
84
+ /**
85
+ * Should an invalid JSON file be overwritten
86
+ */
87
+ overwrite_invalid_json: boolean;
88
+
89
+ /**
90
+ *
91
+ */
92
+ fs: typeof FS;
93
+ }
94
+
95
+ /**
96
+ * A Map overlaying a JSON file
97
+ */
98
+ export class JSONFileMap<T extends JSONValue = JSONValue> extends FileMap<T> {
99
+ public get [Symbol.toStringTag](): 'JSONFileMap' {
100
+ return 'JSONFileMap';
101
+ }
102
+
103
+ public constructor(
104
+ path: string,
105
+ public readonly options: JSONFileMapOptions
106
+ ) {
107
+ super(path, options.fs);
108
+ if (!this.fs.existsSync(path)) {
109
+ this.fs.writeFileSync(path, '{}');
110
+ }
111
+ }
112
+
113
+ public get _map(): Map<string, T> {
114
+ const content = this.fs.readFileSync(this.path, 'utf8');
115
+ if (!isJSON(content)) {
116
+ if (!this.options.overwrite_invalid_json) {
117
+ throw new SyntaxError('Invalid JSON file: ' + this.path);
118
+ }
119
+ console.warn('Invalid JSON file (overwriting): ' + this.path);
120
+ this.clear();
121
+ return new Map();
122
+ }
123
+ return new Map(Object.entries(JSON.parse(content)));
124
+ }
125
+
126
+ public _write(map: Map<string, T>) {
127
+ if (!this.fs.existsSync(this.path)) {
128
+ this.fs.writeFileSync(this.path, '{}');
129
+ }
130
+ const content = JSON.stringify(Object.fromEntries(map));
131
+ this.fs.writeFileSync(this.path, content);
132
+ }
133
+
134
+ public clear() {
135
+ this.fs.writeFileSync(this.path, '{}');
136
+ }
137
+
138
+ public delete(key: string): boolean {
139
+ const map = this._map;
140
+ const rt = map.delete(key);
141
+ this._write(map);
142
+ return rt;
143
+ }
144
+
145
+ public get<U extends T>(key: string): U {
146
+ return this._map.get(key) as U;
147
+ }
148
+
149
+ public has(key: string): boolean {
150
+ return this._map.has(key);
151
+ }
152
+
153
+ public set(key: string, value: T): this {
154
+ const map = this._map;
155
+ map.set(key, value);
156
+ this._write(map);
157
+ return this;
158
+ }
159
+ }
160
+
161
+ export interface FolderMapOptions {
162
+ /**
163
+ * Suffix to append to keys to resolve file names
164
+ */
165
+ suffix: string;
166
+
167
+ fs?: typeof FS;
168
+ }
169
+
170
+ /**
171
+ * A Map overlaying a folder
172
+ */
173
+ export class FolderMap extends FileMap<string> {
174
+ public get [Symbol.toStringTag](): 'FolderMap' {
175
+ return 'FolderMap';
176
+ }
177
+
178
+ public constructor(
179
+ path: string,
180
+ public readonly options: Partial<FolderMapOptions>
181
+ ) {
182
+ super(path, options.fs);
183
+ }
184
+
185
+ protected get _names(): string[] {
186
+ return this.fs
187
+ .readdirSync(this.path)
188
+ .filter(p => p.endsWith(this.options.suffix))
189
+ .map(p => p.slice(0, -this.options.suffix.length));
190
+ }
191
+
192
+ protected _join(path: string): string {
193
+ return `${this.path}/${path}${this.options.suffix}`;
194
+ }
195
+
196
+ protected get _map(): Map<string, string> {
197
+ const entries = [];
198
+ for (const name of this._names) {
199
+ const content = this.fs.readFileSync(this._join(name), 'utf8');
200
+ entries.push([name, content]);
201
+ }
202
+ return new Map(entries);
203
+ }
204
+
205
+ public clear(): void {
206
+ for (const name of this._names) {
207
+ this.fs.unlinkSync(this._join(name));
208
+ }
209
+ }
210
+
211
+ public delete(key: string): boolean {
212
+ if (!this.has(key)) {
213
+ return false;
214
+ }
215
+
216
+ this.fs.unlinkSync(this._join(key));
217
+ return true;
218
+ }
219
+
220
+ public get(key: string): string {
221
+ if (this.has(key)) {
222
+ return this.fs.readFileSync(this._join(key), 'utf8');
223
+ }
224
+ }
225
+
226
+ public has(key: string): boolean {
227
+ return this._names.includes(key);
228
+ }
229
+
230
+ public set(key: string, value: string): this {
231
+ this.fs.writeFileSync(this._join(key), value);
232
+ return this;
233
+ }
234
+ }
235
+
236
+ export function resolveConstructors(object: object): string[] {
237
+ const constructors = [];
238
+ let prototype = object;
239
+ while (prototype && !['Function', 'Object'].includes(prototype.constructor.name)) {
240
+ prototype = Object.getPrototypeOf(prototype);
241
+ constructors.push(prototype.constructor.name);
242
+ }
243
+ return constructors;
244
+ }
245
+
246
+ /**
247
+ * Gets a random int, r, with the probability P(r) = (base)**r
248
+ * For example, with a probability of 1/2: P(1) = 1/2, P(2) = 1/4, etc.
249
+ * @param probability the probability
250
+ */
251
+ export function getRandomIntWithRecursiveProbability(probability = 0.5): number {
252
+ return -Math.floor(Math.log(Math.random()) / Math.log(1 / probability));
253
+ }
package/src/random.ts ADDED
@@ -0,0 +1,27 @@
1
+ export function randomFloat(min = 0, max = 1): number {
2
+ return Math.random() * (max - min) + min;
3
+ }
4
+
5
+ export function randomHex(length = 1): string {
6
+ let s = '';
7
+ for (let i = 0; i < length; i++) {
8
+ s += Math.floor(Math.random() * 16).toString(16);
9
+ }
10
+ return s;
11
+ }
12
+
13
+ export function randomBoolean(): boolean {
14
+ return !!Math.round(Math.random());
15
+ }
16
+
17
+ export function randomBinaryString(length = 1): string {
18
+ let b = '';
19
+ for (let i = 0; i < length; i++) {
20
+ b += Math.round(Math.random());
21
+ }
22
+ return b;
23
+ }
24
+
25
+ export function randomInt(min = 0, max = 1): number {
26
+ return Math.round(Math.random() * (max - min) + min);
27
+ }
package/src/types.ts ADDED
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Expands the type T (for intellisense and debugging)
3
+ * @see https://stackoverflow.com/a/69288824/17637456
4
+ */
5
+ export type Expand<T> = T extends (...args: infer A) => infer R ? (...args: Expand<A>) => Expand<R> : T extends infer O ? { [K in keyof O]: O[K] } : never;
6
+
7
+ /**
8
+ * Recursivly expands the type T (for intellisense and debugging)
9
+ * @see https://stackoverflow.com/a/69288824/17637456
10
+ */
11
+ export type ExpandRecursively<T> = T extends (...args: infer A) => infer R
12
+ ? (...args: ExpandRecursively<A>) => ExpandRecursively<R>
13
+ : T extends object
14
+ ? T extends infer O
15
+ ? { [K in keyof O]: ExpandRecursively<O[K]> }
16
+ : never
17
+ : T;
18
+
19
+ /**
20
+ * Extracts an object with properties assignable to P from an object T
21
+ * @see https://stackoverflow.com/a/71532723/17637456
22
+ */
23
+ export type ExtractProperties<T, P> = {
24
+ [K in keyof T as T[K] extends infer Prop ? (Prop extends P ? K : never) : never]: T[K];
25
+ };
26
+
27
+ /**
28
+ * Extract the keys of T which are required
29
+ * @see https://stackoverflow.com/a/55247867/17637456
30
+ */
31
+ // eslint-disable-next-line @typescript-eslint/ban-types
32
+ export type RequiredKeys<T> = { [K in keyof T]-?: {} extends { [P in K]: T[K] } ? never : K }[keyof T];
33
+
34
+ /**
35
+ * @see https://dev.to/tmhao2005/ts-useful-advanced-types-3k5e
36
+ */
37
+ export type RequiredProperties<T extends object, K extends keyof T = keyof T> = Omit<T, K> & Required<Pick<T, K>>;
38
+
39
+ /**
40
+ * @see https://dev.to/tmhao2005/ts-useful-advanced-types-3k5e
41
+ */
42
+ export type DeepRequired<T> = {
43
+ [K in keyof T]: Required<DeepRequired<T[K]>>;
44
+ };
45
+
46
+ /**
47
+ * @see https://dev.to/tmhao2005/ts-useful-advanced-types-3k5e
48
+ */
49
+ export type Optional<T, K extends keyof T> = Omit<T, K> & Partial<T>;
50
+
51
+ /**
52
+ * @see https://dev.to/tmhao2005/ts-useful-advanced-types-3k5e
53
+ */
54
+ export type NestedKeys<T extends object> = {
55
+ [P in keyof T & (string | number)]: T[P] extends Date ? `${P}` : T[P] extends Record<string, unknown> ? `${P}` | `${P}.${NestedKeys<T[P]>}` : `${P}`;
56
+ }[keyof T & (string | number)];
57
+
58
+ /**
59
+ * @see https://dev.to/tmhao2005/ts-useful-advanced-types-3k5e
60
+ */
61
+ export type PartialRecursive<T> = {
62
+ [P in keyof T]?: T[P] extends (infer U)[] ? PartialRecursive<U>[] : T[P] extends object | undefined ? PartialRecursive<T[P]> : T[P];
63
+ };
64
+
65
+ /**
66
+ * Get the keys of a union of objects
67
+ * @see https://stackoverflow.com/a/65805753/17637456
68
+ */
69
+ export type UnionKeys<T> = T extends T ? keyof T : never;
70
+
71
+ type StrictUnionHelper<T, TAll> = T extends unknown ? T & Partial<Record<Exclude<UnionKeys<TAll>, keyof T>, never>> : never;
72
+
73
+ /**
74
+ * @see https://stackoverflow.com/a/65805753/17637456
75
+ */
76
+ export type StrictUnion<T> = Expand<StrictUnionHelper<T, T>>;