@terrygonguet/utils 0.0.1 → 0.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/package.json +20 -5
- package/src/async.ts +3 -0
- package/src/functional/index.ts +7 -0
- package/src/functional/maybe.ts +81 -0
- package/src/functional/result.ts +102 -0
- package/src/main.ts +23 -1
- package/tsconfig.json +3 -3
- package/types/async.d.ts +1 -0
- package/types/functional/index.d.ts +4 -0
- package/types/functional/maybe.d.ts +22 -0
- package/types/functional/result.d.ts +30 -0
- package/types/main.d.ts +7 -0
- package/vite.config.js +15 -13
package/package.json
CHANGED
|
@@ -1,20 +1,35 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@terrygonguet/utils",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"dev": "vite",
|
|
7
7
|
"build": "tsc && vite build",
|
|
8
|
-
"
|
|
8
|
+
"test": "vitest test --ui --watch --update",
|
|
9
|
+
"coverage": "vitest run --coverage"
|
|
9
10
|
},
|
|
10
11
|
"devDependencies": {
|
|
12
|
+
"@vitest/ui": "^0.32.0",
|
|
13
|
+
"prettier": "^2.8.8",
|
|
11
14
|
"typescript": "^5.0.2",
|
|
12
|
-
"vite": "^4.3.9"
|
|
15
|
+
"vite": "^4.3.9",
|
|
16
|
+
"vitest": "^0.32.0"
|
|
13
17
|
},
|
|
14
18
|
"exports": {
|
|
15
19
|
".": {
|
|
16
|
-
"types": "./
|
|
17
|
-
"import": "./
|
|
20
|
+
"types": "./types/main.js",
|
|
21
|
+
"import": "./dist/main.d.ts"
|
|
22
|
+
},
|
|
23
|
+
"./async": {
|
|
24
|
+
"types": "./types/async.js",
|
|
25
|
+
"import": "./dist/async.d.ts"
|
|
26
|
+
},
|
|
27
|
+
"./functional": {
|
|
28
|
+
"types": "./types/functional/index.d.ts",
|
|
29
|
+
"import": "./dist/functional.js"
|
|
18
30
|
}
|
|
31
|
+
},
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"just-compose": "^2.3.0"
|
|
19
34
|
}
|
|
20
35
|
}
|
package/src/async.ts
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { identity } from "./index.ts"
|
|
2
|
+
|
|
3
|
+
interface API<T> {
|
|
4
|
+
isSome(this: Maybe<T>): this is Some<T>
|
|
5
|
+
isNone(this: Maybe<T>): this is None<T>
|
|
6
|
+
orDefault(this: Maybe<T>, defaultValue: T): T
|
|
7
|
+
map<U>(this: Maybe<T>, f: (value: T) => U): Maybe<U>
|
|
8
|
+
flatMap<U>(this: Maybe<T>, f: (value: T) => Maybe<U>): Maybe<U>
|
|
9
|
+
toJSON(this: Maybe<T>): Object
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export type Some<T> = { value: T } & API<T>
|
|
13
|
+
export type None<T> = API<T>
|
|
14
|
+
export type Maybe<T> = Some<T> | None<T>
|
|
15
|
+
|
|
16
|
+
const $_kind = "@terrygonguet/utils/functional/maybe"
|
|
17
|
+
const $_variant_Some = "@terrygonguet/utils/functional/maybe/Some"
|
|
18
|
+
const $_variant_None = "@terrygonguet/utils/functional/maybe/None"
|
|
19
|
+
|
|
20
|
+
export function Some<T>(value: NonNullable<T>): Some<T> {
|
|
21
|
+
return Object.create(
|
|
22
|
+
{
|
|
23
|
+
isSome: () => true,
|
|
24
|
+
isNone: () => false,
|
|
25
|
+
orDefault(this: Some<T>) {
|
|
26
|
+
return this.value
|
|
27
|
+
},
|
|
28
|
+
map(this: Some<T>, f) {
|
|
29
|
+
return Some(f(this.value)!)
|
|
30
|
+
},
|
|
31
|
+
flatMap(this: Some<T>, f) {
|
|
32
|
+
return f(this.value)
|
|
33
|
+
},
|
|
34
|
+
toJSON(this: Some<T>) {
|
|
35
|
+
return { $_kind, $_variant: $_variant_Some, value: this.value }
|
|
36
|
+
},
|
|
37
|
+
} as API<T>,
|
|
38
|
+
{
|
|
39
|
+
$_kind: { value: $_kind, enumerable: false, writable: false },
|
|
40
|
+
$_variant: { value: $_variant_Some, enumerable: false, writable: false },
|
|
41
|
+
value: { value, writable: false },
|
|
42
|
+
},
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export const None: None<any> = Object.create(
|
|
47
|
+
{
|
|
48
|
+
isSome: () => false,
|
|
49
|
+
isNone: () => true,
|
|
50
|
+
orDefault: identity,
|
|
51
|
+
map: () => None,
|
|
52
|
+
flatMap: () => None,
|
|
53
|
+
toJSON: () => ({ $_kind, $_variant: $_variant_None }),
|
|
54
|
+
} as API<any>,
|
|
55
|
+
{
|
|
56
|
+
$_kind: { value: $_kind, enumerable: false, writable: false },
|
|
57
|
+
$_variant: { value: $_variant_None, enumerable: false, writable: false },
|
|
58
|
+
},
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
export const Maybe = {
|
|
62
|
+
Some,
|
|
63
|
+
None,
|
|
64
|
+
from<T>(value: T | undefined | null): Maybe<T> {
|
|
65
|
+
switch (value) {
|
|
66
|
+
case null:
|
|
67
|
+
case undefined:
|
|
68
|
+
return None
|
|
69
|
+
default:
|
|
70
|
+
return Some(value!)
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
JSONReviver(_key: string, value: any) {
|
|
74
|
+
if (value?.$_kind == $_kind) {
|
|
75
|
+
const $_variant = value?.$_variant
|
|
76
|
+
if ($_variant == $_variant_Some) return Some<unknown>(value?.value)
|
|
77
|
+
else if ($_variant == $_variant_None) return None
|
|
78
|
+
else return value
|
|
79
|
+
} else return value
|
|
80
|
+
},
|
|
81
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { identity } from "./index.ts"
|
|
2
|
+
|
|
3
|
+
interface API<S, F> {
|
|
4
|
+
isSuccess(this: Result<S, F>): this is Success<S, F>
|
|
5
|
+
isFailure(this: Result<S, F>): this is Failure<S, F>
|
|
6
|
+
map<S2>(this: Result<S, F>, f: (value: S) => S2): Result<S2, F>
|
|
7
|
+
flatMap<S2, F2>(this: Result<S, F>, f: (value: S) => Result<S2, F | F2>): Result<S2, F | F2>
|
|
8
|
+
toJSON(this: Result<S, F>): Object
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export type Success<S, F> = { value: S } & API<S, F>
|
|
12
|
+
export type Failure<S, F> = { reason: F } & API<S, F>
|
|
13
|
+
export type Result<S, F> = Success<S, F> | Failure<S, F>
|
|
14
|
+
|
|
15
|
+
const $_kind = "@terrygonguet/utils/functional/result"
|
|
16
|
+
const $_variant_Success = "@terrygonguet/utils/functional/result/Success"
|
|
17
|
+
const $_variant_Failure = "@terrygonguet/utils/functional/result/Failure"
|
|
18
|
+
|
|
19
|
+
export function Success<S, F>(value: S): Success<S, F> {
|
|
20
|
+
return Object.create(
|
|
21
|
+
{
|
|
22
|
+
isSuccess: () => true,
|
|
23
|
+
isFailure: () => false,
|
|
24
|
+
map<S2>(this: Success<S, F>, f: (value: S) => S2) {
|
|
25
|
+
return Success(f(this.value))
|
|
26
|
+
},
|
|
27
|
+
flatMap<S2, F2>(this: Success<S, F>, f: (value: S) => Result<S2, F2>) {
|
|
28
|
+
return f(this.value)
|
|
29
|
+
},
|
|
30
|
+
toJSON(this: Success<S, F>) {
|
|
31
|
+
return { $_kind, $_variant: $_variant_Success, value: this.value }
|
|
32
|
+
},
|
|
33
|
+
} as API<S, F>,
|
|
34
|
+
{
|
|
35
|
+
$_kind: { value: $_kind, enumerable: false, writable: false },
|
|
36
|
+
$_variant: { value: $_variant_Success, enumerable: false, writable: false },
|
|
37
|
+
value: { value, writable: false },
|
|
38
|
+
},
|
|
39
|
+
)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function Failure<S, F>(reason: F): Failure<S, F> {
|
|
43
|
+
return Object.create(
|
|
44
|
+
{
|
|
45
|
+
isSuccess: () => false,
|
|
46
|
+
isFailure: () => true,
|
|
47
|
+
map<S2>(this: Failure<S, F>) {
|
|
48
|
+
return this as unknown as Result<S2, F>
|
|
49
|
+
},
|
|
50
|
+
flatMap<S2, F2>(this: Failure<S, F>) {
|
|
51
|
+
return this as unknown as Result<S2, F | F2>
|
|
52
|
+
},
|
|
53
|
+
toJSON(this: Failure<S, F>) {
|
|
54
|
+
return { $_kind, $_variant: $_variant_Failure, reason: this.reason }
|
|
55
|
+
},
|
|
56
|
+
} as API<S, F>,
|
|
57
|
+
{
|
|
58
|
+
$_kind: { value: $_kind, enumerable: false, writable: false },
|
|
59
|
+
$_variant: { value: $_variant_Success, enumerable: false, writable: false },
|
|
60
|
+
reason: { value: reason, writable: false },
|
|
61
|
+
},
|
|
62
|
+
)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export const Result = {
|
|
66
|
+
Success,
|
|
67
|
+
Failure,
|
|
68
|
+
try<S, F>(tryFn: () => S) {
|
|
69
|
+
return new TryCatch<S, F>(tryFn)
|
|
70
|
+
},
|
|
71
|
+
JSONReviver(_key: string, value: any) {
|
|
72
|
+
if (value?.$_kind == $_kind) {
|
|
73
|
+
const $_variant = value?.$_variant
|
|
74
|
+
if ($_variant == $_variant_Success) return Success<unknown, unknown>(value?.value)
|
|
75
|
+
else if ($_variant == $_variant_Failure) return Failure<unknown, unknown>(value?.reason)
|
|
76
|
+
else return value
|
|
77
|
+
} else return value
|
|
78
|
+
},
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export class TryCatch<S, F> {
|
|
82
|
+
tryFn: () => S
|
|
83
|
+
catchFn: (err: unknown) => F
|
|
84
|
+
|
|
85
|
+
constructor(tryFn: () => S) {
|
|
86
|
+
this.tryFn = tryFn
|
|
87
|
+
this.catchFn = identity as any
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
catch(catchFn: (err: unknown) => F) {
|
|
91
|
+
this.catchFn = catchFn
|
|
92
|
+
return this
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
exec(): Result<S, F> {
|
|
96
|
+
try {
|
|
97
|
+
return Success<S, F>(this.tryFn())
|
|
98
|
+
} catch (error) {
|
|
99
|
+
return Failure<S, F>(this.catchFn(error))
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
package/src/main.ts
CHANGED
|
@@ -1,3 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Behaviour is undefined when max < min
|
|
3
|
+
*/
|
|
1
4
|
export function clamp(value: number, min: number, max: number) {
|
|
2
5
|
return Math.min(Math.max(value, min), max)
|
|
3
|
-
}
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
type JSONReviver = (key: string, value: any) => any
|
|
9
|
+
|
|
10
|
+
export function safeParse<T>(str: string, defaultValue: T, reviver?: JSONReviver) {
|
|
11
|
+
try {
|
|
12
|
+
return JSON.parse(str, reviver)
|
|
13
|
+
} catch (_) {
|
|
14
|
+
return defaultValue
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function composeJSONRevivers(...revivers: JSONReviver[]): JSONReviver {
|
|
19
|
+
return function (key, value) {
|
|
20
|
+
for (const reviver of revivers) {
|
|
21
|
+
value = reviver(key, value)
|
|
22
|
+
}
|
|
23
|
+
return value
|
|
24
|
+
}
|
|
25
|
+
}
|
package/tsconfig.json
CHANGED
|
@@ -3,12 +3,13 @@
|
|
|
3
3
|
"target": "ES2020",
|
|
4
4
|
"useDefineForClassFields": true,
|
|
5
5
|
"module": "ESNext",
|
|
6
|
-
"lib": ["ES2020"],
|
|
6
|
+
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
|
7
7
|
"skipLibCheck": true,
|
|
8
8
|
|
|
9
9
|
/* Bundler mode */
|
|
10
10
|
"moduleResolution": "node16",
|
|
11
11
|
"allowImportingTsExtensions": true,
|
|
12
|
+
"allowSyntheticDefaultImports": true,
|
|
12
13
|
"resolveJsonModule": true,
|
|
13
14
|
"isolatedModules": true,
|
|
14
15
|
"declaration": true,
|
|
@@ -18,8 +19,7 @@
|
|
|
18
19
|
/* Linting */
|
|
19
20
|
"strict": true,
|
|
20
21
|
"noUnusedLocals": true,
|
|
21
|
-
"noUnusedParameters": true
|
|
22
|
-
"noFallthroughCasesInSwitch": true
|
|
22
|
+
"noUnusedParameters": true
|
|
23
23
|
},
|
|
24
24
|
"include": ["src"]
|
|
25
25
|
}
|
package/types/async.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function wait(ms: number): Promise<void>;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
interface API<T> {
|
|
2
|
+
isSome(this: Maybe<T>): this is Some<T>;
|
|
3
|
+
isNone(this: Maybe<T>): this is None<T>;
|
|
4
|
+
orDefault(this: Maybe<T>, defaultValue: T): T;
|
|
5
|
+
map<U>(this: Maybe<T>, f: (value: T) => U): Maybe<U>;
|
|
6
|
+
flatMap<U>(this: Maybe<T>, f: (value: T) => Maybe<U>): Maybe<U>;
|
|
7
|
+
toJSON(this: Maybe<T>): Object;
|
|
8
|
+
}
|
|
9
|
+
export type Some<T> = {
|
|
10
|
+
value: T;
|
|
11
|
+
} & API<T>;
|
|
12
|
+
export type None<T> = API<T>;
|
|
13
|
+
export type Maybe<T> = Some<T> | None<T>;
|
|
14
|
+
export declare function Some<T>(value: NonNullable<T>): Some<T>;
|
|
15
|
+
export declare const None: None<any>;
|
|
16
|
+
export declare const Maybe: {
|
|
17
|
+
Some: typeof Some;
|
|
18
|
+
None: None<any>;
|
|
19
|
+
from<T>(value: T | null | undefined): Maybe<T>;
|
|
20
|
+
JSONReviver(_key: string, value: any): any;
|
|
21
|
+
};
|
|
22
|
+
export {};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
interface API<S, F> {
|
|
2
|
+
isSuccess(this: Result<S, F>): this is Success<S, F>;
|
|
3
|
+
isFailure(this: Result<S, F>): this is Failure<S, F>;
|
|
4
|
+
map<S2>(this: Result<S, F>, f: (value: S) => S2): Result<S2, F>;
|
|
5
|
+
flatMap<S2, F2>(this: Result<S, F>, f: (value: S) => Result<S2, F | F2>): Result<S2, F | F2>;
|
|
6
|
+
toJSON(this: Result<S, F>): Object;
|
|
7
|
+
}
|
|
8
|
+
export type Success<S, F> = {
|
|
9
|
+
value: S;
|
|
10
|
+
} & API<S, F>;
|
|
11
|
+
export type Failure<S, F> = {
|
|
12
|
+
reason: F;
|
|
13
|
+
} & API<S, F>;
|
|
14
|
+
export type Result<S, F> = Success<S, F> | Failure<S, F>;
|
|
15
|
+
export declare function Success<S, F>(value: S): Success<S, F>;
|
|
16
|
+
export declare function Failure<S, F>(reason: F): Failure<S, F>;
|
|
17
|
+
export declare const Result: {
|
|
18
|
+
Success: typeof Success;
|
|
19
|
+
Failure: typeof Failure;
|
|
20
|
+
try<S, F>(tryFn: () => S): TryCatch<S, F>;
|
|
21
|
+
JSONReviver(_key: string, value: any): any;
|
|
22
|
+
};
|
|
23
|
+
export declare class TryCatch<S, F> {
|
|
24
|
+
tryFn: () => S;
|
|
25
|
+
catchFn: (err: unknown) => F;
|
|
26
|
+
constructor(tryFn: () => S);
|
|
27
|
+
catch(catchFn: (err: unknown) => F): this;
|
|
28
|
+
exec(): Result<S, F>;
|
|
29
|
+
}
|
|
30
|
+
export {};
|
package/types/main.d.ts
CHANGED
|
@@ -1 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Behaviour is undefined when max < min
|
|
3
|
+
*/
|
|
1
4
|
export declare function clamp(value: number, min: number, max: number): number;
|
|
5
|
+
type JSONReviver = (key: string, value: any) => any;
|
|
6
|
+
export declare function safeParse<T>(str: string, defaultValue: T, reviver?: JSONReviver): any;
|
|
7
|
+
export declare function composeJSONRevivers(...revivers: JSONReviver[]): JSONReviver;
|
|
8
|
+
export {};
|
package/vite.config.js
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
|
-
import { resolve } from "path"
|
|
2
|
-
import { defineConfig } from "vite"
|
|
3
|
-
|
|
4
|
-
export default defineConfig({
|
|
5
|
-
build: {
|
|
6
|
-
lib: {
|
|
7
|
-
formats: ["es"],
|
|
8
|
-
entry: {
|
|
9
|
-
main: resolve(__dirname, "src/main.ts"),
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
}
|
|
1
|
+
import { resolve } from "path"
|
|
2
|
+
import { defineConfig } from "vite"
|
|
3
|
+
|
|
4
|
+
export default defineConfig({
|
|
5
|
+
build: {
|
|
6
|
+
lib: {
|
|
7
|
+
formats: ["es"],
|
|
8
|
+
entry: {
|
|
9
|
+
main: resolve(__dirname, "src/main.ts"),
|
|
10
|
+
async: resolve(__dirname, "src/async.ts"),
|
|
11
|
+
functional: resolve(__dirname, "src/functional/index.ts"),
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
})
|