unwrapped 0.1.1 → 0.1.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/CHANGELOG.md +12 -0
- package/README.md +553 -2
- package/dist/core/index.d.mts +483 -44
- package/dist/core/index.d.ts +483 -44
- package/dist/core/index.js +529 -117
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.mjs +529 -117
- package/dist/core/index.mjs.map +1 -1
- package/dist/vue/index.d.mts +103 -22
- package/dist/vue/index.d.ts +103 -22
- package/dist/vue/index.js +16 -44
- package/dist/vue/index.js.map +1 -1
- package/dist/vue/index.mjs +18 -46
- package/dist/vue/index.mjs.map +1 -1
- package/package.json +2 -2
- package/src/core/asyncResult.ts +0 -312
- package/src/core/cache.ts +0 -81
- package/src/core/error.ts +0 -23
- package/src/core/index.ts +0 -4
- package/src/core/result.ts +0 -101
- package/src/vue/components/asyncResultLoader.ts +0 -99
- package/src/vue/composables.ts +0 -106
- package/src/vue/index.ts +0 -2
package/src/core/result.ts
DELETED
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
import { ErrorBase } from "./error";
|
|
2
|
-
|
|
3
|
-
export type ResultState<T, E = ErrorBase> =
|
|
4
|
-
| { status: 'success'; value: T }
|
|
5
|
-
| { status: 'error'; error: E };
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
export class Result<T, E = ErrorBase> {
|
|
9
|
-
private _state: ResultState<T, E>;
|
|
10
|
-
|
|
11
|
-
constructor(state: ResultState<T, E>) {
|
|
12
|
-
this._state = state;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
get state() {
|
|
16
|
-
return this._state;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
static ok<T, E = ErrorBase>(value: T): Result<T, E> {
|
|
20
|
-
return new Result({ status: 'success', value });
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
static err<E>(error: E): Result<never, E> {
|
|
24
|
-
return new Result({ status: 'error', error });
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
static errTag(code: string, message?: string): Result<never, ErrorBase> {
|
|
28
|
-
return Result.err(new ErrorBase(code, message));
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
unwrapOrNull(): T | null {
|
|
32
|
-
if (this._state.status === 'success') {
|
|
33
|
-
return this._state.value;
|
|
34
|
-
}
|
|
35
|
-
return null;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
unwrapOrThrow(): T {
|
|
39
|
-
if (this._state.status === 'success') {
|
|
40
|
-
return this._state.value;
|
|
41
|
-
}
|
|
42
|
-
throw new Error('Tried to unwrap a Result that is not successful');
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
unwrapOr<O>(defaultValue: O): T | O {
|
|
46
|
-
if (this._state.status === 'success') {
|
|
47
|
-
return this._state.value;
|
|
48
|
-
}
|
|
49
|
-
return defaultValue;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
isSuccess(): boolean {
|
|
53
|
-
return this._state.status === 'success';
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
isError(): boolean {
|
|
57
|
-
return this._state.status === 'error';
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
static tryPromise<T, E>(promise: Promise<T>, errorMapper: (error: unknown) => E): Promise<Result<T, E>> {
|
|
61
|
-
return promise
|
|
62
|
-
.then((value) => Result.ok<T, E>(value))
|
|
63
|
-
.catch((error) => Result.err(errorMapper(error)));
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
static tryFunction<T, E extends ErrorBase = ErrorBase>(fn: () => Promise<T>, errorMapper: (error: unknown) => E): Promise<Result<T, E>> {
|
|
67
|
-
return Result.tryPromise(fn(), errorMapper);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
chain<O, E2>(fn: (input: T) => ResultState<O, E | E2>): Result<O, E | E2> {
|
|
71
|
-
if (this._state.status === 'success') {
|
|
72
|
-
return new Result<O, E | E2>(fn(this._state.value));
|
|
73
|
-
}
|
|
74
|
-
return Result.err<E>(this._state.error);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
*[Symbol.iterator](): Generator<Result<T, E>, T, any> {
|
|
78
|
-
yield this;
|
|
79
|
-
|
|
80
|
-
if (this._state.status === 'success') {
|
|
81
|
-
return this._state.value;
|
|
82
|
-
}
|
|
83
|
-
return undefined as T;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
87
|
-
static run<T, E>(generator: () => Generator<Result<any, E>, T, any>): Result<T, E> {
|
|
88
|
-
const iterator = generator();
|
|
89
|
-
let result = iterator.next();
|
|
90
|
-
|
|
91
|
-
while (!result.done) {
|
|
92
|
-
const yielded = result.value;
|
|
93
|
-
if (yielded._state.status === 'error') {
|
|
94
|
-
return Result.err(yielded._state.error);
|
|
95
|
-
}
|
|
96
|
-
result = iterator.next(yielded._state.value);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
return Result.ok(result.value);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
import { defineComponent, ref, watch, onUnmounted, h, type VNode } from "vue"
|
|
2
|
-
import type { AsyncResult, AsyncResultState } from "unwrapped/core"
|
|
3
|
-
|
|
4
|
-
export const AsyncResultLoader = defineComponent({
|
|
5
|
-
name: "AsyncResultLoader",
|
|
6
|
-
|
|
7
|
-
props: {
|
|
8
|
-
result: {
|
|
9
|
-
type: Object as () => AsyncResult<unknown, unknown>,
|
|
10
|
-
required: true
|
|
11
|
-
}
|
|
12
|
-
},
|
|
13
|
-
|
|
14
|
-
setup(props, { slots }) {
|
|
15
|
-
const state = ref<AsyncResultState<unknown, unknown>>(props.result.state)
|
|
16
|
-
let unlisten: (() => void) | null = null
|
|
17
|
-
|
|
18
|
-
// Unsubscribe on destroy
|
|
19
|
-
onUnmounted(() => {
|
|
20
|
-
if (unlisten) unlisten()
|
|
21
|
-
})
|
|
22
|
-
|
|
23
|
-
// Watch for prop changes & update listener
|
|
24
|
-
watch(
|
|
25
|
-
() => props.result,
|
|
26
|
-
(newResult) => {
|
|
27
|
-
if (unlisten) unlisten()
|
|
28
|
-
state.value = newResult.state
|
|
29
|
-
unlisten = newResult.listen((res) => {
|
|
30
|
-
state.value = res.state
|
|
31
|
-
})
|
|
32
|
-
},
|
|
33
|
-
{ immediate: true }
|
|
34
|
-
)
|
|
35
|
-
|
|
36
|
-
return () => {
|
|
37
|
-
const s = state.value
|
|
38
|
-
|
|
39
|
-
// Choose what to render based on status
|
|
40
|
-
switch (s.status) {
|
|
41
|
-
case "loading":
|
|
42
|
-
return slots.loading
|
|
43
|
-
? slots.loading()
|
|
44
|
-
: h("div", { class: "loading" }, "Loading…")
|
|
45
|
-
|
|
46
|
-
case "error":
|
|
47
|
-
return slots.error
|
|
48
|
-
? slots.error({ error: s.error })
|
|
49
|
-
: h("div", { class: "error" }, `Error: ${s.error}`)
|
|
50
|
-
|
|
51
|
-
case "success":
|
|
52
|
-
return slots.default
|
|
53
|
-
? slots.default({ value: s.value })
|
|
54
|
-
: null
|
|
55
|
-
|
|
56
|
-
default:
|
|
57
|
-
// "idle"
|
|
58
|
-
return slots.idle
|
|
59
|
-
? slots.idle()
|
|
60
|
-
: h("div", { class: "idle" }, "Idle")
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
})
|
|
65
|
-
|
|
66
|
-
interface CustomSlots<E> {
|
|
67
|
-
loading?: () => VNode;
|
|
68
|
-
error?: (props: { error: E }) => VNode;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
export function buildCustomAsyncResultLoader<T, E>(slots: CustomSlots<E>) {
|
|
72
|
-
const comp = defineComponent({
|
|
73
|
-
name: "CustomAsyncResultLoader",
|
|
74
|
-
props: {
|
|
75
|
-
result: {
|
|
76
|
-
type: Object as () => AsyncResult<T, E>,
|
|
77
|
-
required: true
|
|
78
|
-
}
|
|
79
|
-
},
|
|
80
|
-
setup(props, context) {
|
|
81
|
-
return () => {
|
|
82
|
-
const renderLoading = context.slots.loading ?? slots.loading ?? (() => undefined);
|
|
83
|
-
const renderError = context.slots.error ?? slots.error ?? (() => undefined);
|
|
84
|
-
return h(
|
|
85
|
-
AsyncResultLoader,
|
|
86
|
-
{ result: props.result },
|
|
87
|
-
{
|
|
88
|
-
default: context.slots.default ? (propsDefault: { value: T }) => context.slots.default!(propsDefault) : undefined,
|
|
89
|
-
|
|
90
|
-
loading: () => renderLoading(),
|
|
91
|
-
error: ((propsError: { error: E }) => renderError(propsError))
|
|
92
|
-
}
|
|
93
|
-
)
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
return comp;
|
|
99
|
-
}
|
package/src/vue/composables.ts
DELETED
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
import { computed, onUnmounted, ref, toRef, triggerRef, watch, type Ref, type WatchSource } from "vue";
|
|
2
|
-
import { AsyncResult, type FlatChainFunction, type Result } from "unwrapped/core";
|
|
3
|
-
|
|
4
|
-
export function useAsyncResultRef<T, E>(asyncResult: AsyncResult<T, E>) {
|
|
5
|
-
const state = ref<AsyncResult<T, E>>(asyncResult) as Ref<AsyncResult<T, E>>;
|
|
6
|
-
|
|
7
|
-
const unsub = asyncResult.listen(() => {
|
|
8
|
-
triggerRef(state);
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
onUnmounted(() => {
|
|
12
|
-
unsub();
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
return state;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export function useReactiveResult<T, E, Inputs>(source: WatchSource<Inputs>, pipe: FlatChainFunction<Inputs, T, E>, options:{ immediate: boolean } = { immediate: true }): Ref<AsyncResult<T, E>> {
|
|
19
|
-
const result = new AsyncResult<T, E>();
|
|
20
|
-
const resultRef = useAsyncResultRef(result);
|
|
21
|
-
|
|
22
|
-
let unsub: (() => void) | null = null;
|
|
23
|
-
|
|
24
|
-
watch(source, (newInputs) => {
|
|
25
|
-
unsub?.();
|
|
26
|
-
unsub = result.mirror(pipe(newInputs));
|
|
27
|
-
}, { immediate: options.immediate });
|
|
28
|
-
|
|
29
|
-
onUnmounted(() => {
|
|
30
|
-
unsub?.();
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
return resultRef;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export function useAsyncResultRefFromPromise<T, E>(promise: Promise<Result<T, E>>) {
|
|
37
|
-
return useAsyncResultRef(AsyncResult.fromResultPromise(promise));
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export type Action<T,E> = () => Promise<Result<T, E>>;
|
|
41
|
-
export function useImmediateAction<T, E>(action: Action<T, E>): Ref<AsyncResult<T, E>> {
|
|
42
|
-
return useAsyncResultRefFromPromise(action());
|
|
43
|
-
}
|
|
44
|
-
export interface LazyActionReturn<T, E> {
|
|
45
|
-
resultRef: Ref<AsyncResult<T, E>>;
|
|
46
|
-
trigger: () => void;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export function useLazyAction<T, E>(action: Action<T, E>): LazyActionReturn<T, E> {
|
|
50
|
-
const result = new AsyncResult<T, E>();
|
|
51
|
-
const resultRef = useAsyncResultRef(result);
|
|
52
|
-
|
|
53
|
-
const trigger = () => {
|
|
54
|
-
result.updateFromResultPromise(action());
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
return { resultRef, trigger };
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export function useReactiveAction<I, O, E>(input: I | Ref<I> | (() => I), pipe: FlatChainFunction<I, O, E>, options:{ immediate: boolean } = { immediate: true }): Ref<AsyncResult<O, E>> {
|
|
61
|
-
const source = typeof input === 'function' ? computed(input as () => I) : toRef(input)
|
|
62
|
-
|
|
63
|
-
const outputRef = ref<AsyncResult<O, E>>(new AsyncResult()) as Ref<AsyncResult<O, E>>;
|
|
64
|
-
let unsub: (() => void) | null = null;
|
|
65
|
-
|
|
66
|
-
watch(source, () => {
|
|
67
|
-
unsub?.();
|
|
68
|
-
const newOutput = pipe(source.value);
|
|
69
|
-
unsub = newOutput.listen((newState) => {
|
|
70
|
-
outputRef.value.setState(newState.state);
|
|
71
|
-
triggerRef(outputRef);
|
|
72
|
-
});
|
|
73
|
-
}, { immediate: options.immediate });
|
|
74
|
-
|
|
75
|
-
onUnmounted(() => {
|
|
76
|
-
unsub?.();
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
return outputRef;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
export function useGenerator<T>(generatorFunc: () => Generator<AsyncResult<any, any>, T, any>): Ref<AsyncResult<T, any>> {
|
|
83
|
-
const resultRef = useAsyncResultRef(AsyncResult.run(generatorFunc));
|
|
84
|
-
return resultRef;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
export function useLazyGenerator<T>(generatorFunc: () => Generator<AsyncResult<any, any>, T, any>): { resultRef: Ref<AsyncResult<T, any>>, trigger: () => void } {
|
|
88
|
-
const result = new AsyncResult<T, any>();
|
|
89
|
-
const resultRef = useAsyncResultRef(result);
|
|
90
|
-
|
|
91
|
-
const trigger = () => {
|
|
92
|
-
result.runInPlace(generatorFunc);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
return { resultRef, trigger };
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
export function useReactiveGenerator<T, E, Inputs>(source: WatchSource<Inputs>, generatorFunc: (args: Inputs) => Generator<AsyncResult<any, any>, T, any>, options:{ immediate: boolean } = { immediate: true }): Ref<AsyncResult<T, E>> {
|
|
99
|
-
const resultRef = useAsyncResultRef(new AsyncResult<T, E>());
|
|
100
|
-
|
|
101
|
-
watch(source, (newInputs) => {
|
|
102
|
-
resultRef.value.runInPlace(() => generatorFunc(newInputs));
|
|
103
|
-
}, { immediate: options.immediate });
|
|
104
|
-
|
|
105
|
-
return resultRef;
|
|
106
|
-
}
|
package/src/vue/index.ts
DELETED