unwrapped 0.1.2 → 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 +6 -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/dist/vue/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/vue/index.ts","../../src/vue/composables.ts","../../src/vue/components/asyncResultLoader.ts"],"sourcesContent":["export * from \"./composables\"\nexport * from \"./components/asyncResultLoader\"","import { computed, onUnmounted, ref, toRef, triggerRef, watch, type Ref, type WatchSource } from \"vue\";\nimport { AsyncResult, type FlatChainFunction, type Result } from \"unwrapped/core\";\n\nexport function useAsyncResultRef<T, E>(asyncResult: AsyncResult<T, E>) {\n const state = ref<AsyncResult<T, E>>(asyncResult) as Ref<AsyncResult<T, E>>;\n\n const unsub = asyncResult.listen(() => {\n triggerRef(state);\n });\n\n onUnmounted(() => {\n unsub();\n });\n\n return state;\n}\n\nexport function useReactiveResult<T, E, Inputs>(source: WatchSource<Inputs>, pipe: FlatChainFunction<Inputs, T, E>, options:{ immediate: boolean } = { immediate: true }): Ref<AsyncResult<T, E>> {\n const result = new AsyncResult<T, E>();\n const resultRef = useAsyncResultRef(result);\n\n let unsub: (() => void) | null = null;\n\n watch(source, (newInputs) => {\n unsub?.();\n unsub = result.mirror(pipe(newInputs));\n }, { immediate: options.immediate });\n \n onUnmounted(() => {\n unsub?.();\n });\n\n return resultRef;\n}\n\nexport function useAsyncResultRefFromPromise<T, E>(promise: Promise<Result<T, E>>) {\n return useAsyncResultRef(AsyncResult.fromResultPromise(promise));\n}\n\nexport type Action<T,E> = () => Promise<Result<T, E>>;\nexport function useImmediateAction<T, E>(action: Action<T, E>): Ref<AsyncResult<T, E>> {\n return useAsyncResultRefFromPromise(action());\n}\nexport interface LazyActionReturn<T, E> {\n resultRef: Ref<AsyncResult<T, E>>;\n trigger: () => void;\n}\n\nexport function useLazyAction<T, E>(action: Action<T, E>): LazyActionReturn<T, E> {\n const result = new AsyncResult<T, E>();\n const resultRef = useAsyncResultRef(result);\n\n const trigger = () => {\n result.updateFromResultPromise(action());\n }\n\n return { resultRef, trigger };\n}\n\nexport function useReactiveAction<I, O, E>(input: I | Ref<I> | (() => I), pipe: FlatChainFunction<I, O, E>, options:{ immediate: boolean } = { immediate: true }): Ref<AsyncResult<O, E>> {\n const source = typeof input === 'function' ? computed(input as () => I) : toRef(input)\n\n const outputRef = ref<AsyncResult<O, E>>(new AsyncResult()) as Ref<AsyncResult<O, E>>;\n let unsub: (() => void) | null = null;\n\n watch(source, () => {\n unsub?.();\n const newOutput = pipe(source.value);\n unsub = newOutput.listen((newState) => {\n outputRef.value.setState(newState.state);\n triggerRef(outputRef);\n });\n }, { immediate: options.immediate });\n\n onUnmounted(() => {\n unsub?.();\n });\n\n return outputRef;\n}\n\nexport function useGenerator<T>(generatorFunc: () => Generator<AsyncResult<any, any>, T, any>): Ref<AsyncResult<T, any>> {\n const resultRef = useAsyncResultRef(AsyncResult.run(generatorFunc));\n return resultRef;\n}\n\nexport function useLazyGenerator<T>(generatorFunc: () => Generator<AsyncResult<any, any>, T, any>): { resultRef: Ref<AsyncResult<T, any>>, trigger: () => void } {\n const result = new AsyncResult<T, any>();\n const resultRef = useAsyncResultRef(result);\n\n const trigger = () => {\n result.runInPlace(generatorFunc);\n }\n\n return { resultRef, trigger };\n}\n\nexport 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>> {\n const resultRef = useAsyncResultRef(new AsyncResult<T, E>());\n\n watch(source, (newInputs) => {\n resultRef.value.runInPlace(() => generatorFunc(newInputs));\n }, { immediate: options.immediate });\n\n return resultRef;\n}","import { defineComponent, ref, watch, onUnmounted, h, type VNode } from \"vue\"\nimport type { AsyncResult, AsyncResultState } from \"unwrapped/core\"\n\nexport const AsyncResultLoader = defineComponent({\n name: \"AsyncResultLoader\",\n\n props: {\n result: {\n type: Object as () => AsyncResult<unknown, unknown>,\n required: true\n }\n },\n\n setup(props, { slots }) {\n const state = ref<AsyncResultState<unknown, unknown>>(props.result.state)\n let unlisten: (() => void) | null = null\n\n // Unsubscribe on destroy\n onUnmounted(() => {\n if (unlisten) unlisten()\n })\n\n // Watch for prop changes & update listener\n watch(\n () => props.result,\n (newResult) => {\n if (unlisten) unlisten()\n state.value = newResult.state\n unlisten = newResult.listen((res) => {\n state.value = res.state\n })\n },\n { immediate: true }\n )\n\n return () => {\n const s = state.value\n\n // Choose what to render based on status\n switch (s.status) {\n case \"loading\":\n return slots.loading\n ? slots.loading()\n : h(\"div\", { class: \"loading\" }, \"Loading…\")\n\n case \"error\":\n return slots.error\n ? slots.error({ error: s.error })\n : h(\"div\", { class: \"error\" }, `Error: ${s.error}`)\n\n case \"success\":\n return slots.default\n ? slots.default({ value: s.value })\n : null\n\n default:\n // \"idle\"\n return slots.idle\n ? slots.idle()\n : h(\"div\", { class: \"idle\" }, \"Idle\")\n }\n }\n }\n})\n\ninterface CustomSlots<E> {\n loading?: () => VNode;\n error?: (props: { error: E }) => VNode;\n}\n\nexport function buildCustomAsyncResultLoader<T, E>(slots: CustomSlots<E>) {\n const comp = defineComponent({\n name: \"CustomAsyncResultLoader\",\n props: {\n result: {\n type: Object as () => AsyncResult<T, E>,\n required: true\n }\n },\n setup(props, context) {\n return () => {\n const renderLoading = context.slots.loading ?? slots.loading ?? (() => undefined);\n const renderError = context.slots.error ?? slots.error ?? (() => undefined);\n return h(\n AsyncResultLoader,\n { result: props.result },\n {\n default: context.slots.default ? (propsDefault: { value: T }) => context.slots.default!(propsDefault) : undefined,\n\n loading: () => renderLoading(),\n error: ((propsError: { error: E }) => renderError(propsError))\n }\n )\n }\n }\n });\n\n return comp;\n}\n"],"mappings":"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","names":["import_vue"]}
|
|
1
|
+
{"version":3,"sources":["../../src/vue/index.ts","../../src/vue/composables.ts","../../src/vue/components/asyncResultLoader.ts"],"sourcesContent":["export * from \"./composables\"\nexport * from \"./components/asyncResultLoader\"","import { onUnmounted, ref, triggerRef, watch, type Ref, type WatchSource } from \"vue\";\nimport { AsyncResult, ErrorBase, type Action, type AsyncResultGenerator, type FlatChainStep, type Result } from \"unwrapped/core\";\n\n\n// === Vue specific types ===\n\ninterface ReactiveProcessOptions {\n immediate: boolean;\n}\n\n\n\n// === Vue Composables for AsyncResult ===\n\n/**\n * Makes a ref to the given AsyncResult. Whenever the state of the AsyncResult changes,\n * the ref gets retriggered, making those changes visible to Vue's reactivity system.\n * \n * @param asyncResult the result to make reactive\n * @returns the ref to the result\n */\nexport function useAsyncResultRef<T, E extends ErrorBase = ErrorBase>(asyncResult: AsyncResult<T, E>) {\n const state = ref<AsyncResult<T, E>>(asyncResult) as Ref<AsyncResult<T, E>>;\n\n const unsub = asyncResult.listen(() => {\n triggerRef(state);\n });\n\n onUnmounted(() => {\n unsub();\n });\n\n return state;\n}\n\n/**\n * Creates an AsyncResult ref from a promise returning a Result.\n * @param promise the promise returning a Result\n * @returns the ref to the AsyncResult\n */\nexport function useAsyncResultRefFromPromise<T, E extends ErrorBase = ErrorBase>(promise: Promise<Result<T, E>>) {\n return useAsyncResultRef(AsyncResult.fromResultPromise(promise));\n}\n\n\n\n// === Vue Composables for Chains ===\n\n/**\n * Watches a source, gives it as inputs to the function provided, and updates the result contained in the ref accordingly.\n * \n * @param source the inputs to react to\n * @param pipe the function to run when the inputs change\n * @param options optional settings\n * @returns ref to the result\n */\nexport function useReactiveChain<Inputs, T, E extends ErrorBase = ErrorBase>(source: WatchSource<Inputs>, pipe: FlatChainStep<Inputs, T, E>, options: ReactiveProcessOptions = { immediate: true }): Ref<AsyncResult<T, E>> {\n const result = new AsyncResult<T, E>();\n const resultRef = useAsyncResultRef(result);\n\n let unsub: (() => void) | null = null;\n\n watch(source, (newInputs) => {\n unsub?.();\n unsub = result.mirror(pipe(newInputs));\n }, { immediate: options.immediate });\n\n onUnmounted(() => {\n unsub?.();\n });\n\n return resultRef;\n}\n\n\n// === Vue Composables for Actions ===\n\n/**\n * The return type of useLazyAction.\n */\nexport interface LazyActionRef<T, E extends ErrorBase = ErrorBase> {\n resultRef: Ref<AsyncResult<T, E>>;\n trigger: () => void;\n}\n\n/**\n * Executes an action immediately and returns a ref to the AsyncResult representing the action's state.\n * \n * Same as useAsyncResultRefFromPromise(action()).\n * \n * @param action the action to execute immediately\n * @returns a ref to the AsyncResult representing the action's state\n */\nexport function useAction<T, E extends ErrorBase = ErrorBase>(action: Action<T, E>): Ref<AsyncResult<T, E>> {\n return useAsyncResultRefFromPromise(action());\n}\n\n/**\n * Creates a lazy action that can be triggered manually.\n * \n * Same as AsyncResult.makeLazyAction(action), but the AsyncResult is wrapped in a ref for Vue reactivity.\n * \n * @param action the action to execute when triggered\n * @returns an object containing a ref to the AsyncResult and a trigger function\n */\nexport function useLazyAction<T, E extends ErrorBase = ErrorBase>(action: Action<T, E>): LazyActionRef<T, E> {\n const lazyAction = AsyncResult.makeLazyAction<T, E>(action);\n const resultRef = useAsyncResultRef(lazyAction.result);\n\n return { resultRef, trigger: lazyAction.trigger };\n}\n\n\n// === Vue Composables for Generators ===\n\n/**\n * Runs a generator function immediately and returns a ref to the AsyncResult representing the generator's state.\n * @param generatorFunc the generator function to run immediately\n * @returns a ref to the AsyncResult representing the generator's state\n */\nexport function useGenerator<T>(generatorFunc: () => AsyncResultGenerator<T>): Ref<AsyncResult<T, any>> {\n const resultRef = useAsyncResultRef(AsyncResult.run(generatorFunc));\n return resultRef;\n}\n\n/**\n * Creates a lazy generator that can be triggered manually.\n * \n * @param generatorFunc the generator function to run when triggered\n * @returns an object containing a ref to the AsyncResult and a trigger function\n */\nexport function useLazyGenerator<T>(generatorFunc: () => AsyncResultGenerator<T>): { resultRef: Ref<AsyncResult<T, any>>, trigger: () => void } {\n const result = new AsyncResult<T, any>();\n const resultRef = useAsyncResultRef(result);\n\n const trigger = () => {\n result.runInPlace(generatorFunc);\n }\n\n return { resultRef, trigger };\n}\n\n/**\n * Watches a source, gives it as inputs to the generator function provided, and updates the result contained in the ref accordingly.\n * \n * @param source the inputs to react to\n * @param generatorFunc the generator function to run when the inputs change\n * @param options optional settings\n * @returns ref to the result\n */\nexport function useReactiveGenerator<Inputs, T, E extends ErrorBase = ErrorBase>(source: WatchSource<Inputs>, generatorFunc: (args: Inputs) => AsyncResultGenerator<T>, options: ReactiveProcessOptions = { immediate: true }): Ref<AsyncResult<T, E>> {\n const resultRef = useAsyncResultRef(new AsyncResult<T, E>());\n\n watch(source, (newInputs) => {\n resultRef.value.runInPlace(() => generatorFunc(newInputs));\n }, { immediate: options.immediate });\n\n return resultRef;\n}","import { defineComponent, watch, h, type VNode } from \"vue\"\nimport type { AsyncResult, ErrorBase } from \"unwrapped/core\"\nimport { useAsyncResultRef } from \"../composables\"\n\n/**\n * A Vue component that displays different content based on the state of an AsyncResult.\n * It supports slots for 'loading', 'error', 'success' (default), and 'idle' states.\n * \n * @example\n * <AsyncResultLoader :result=\"myAsyncResult\">\n * <template #loading>\n * <div>Loading data...</div>\n * </template>\n * <template #error=\"{ error }\">\n * <div>Error occurred: {{ error.message }}</div>\n * </template>\n * <template #default=\"{ value }\">\n * <div>Data loaded: {{ value }}</div>\n * </template>\n * <template #idle>\n * <div>Waiting to start...</div>\n * </template>\n * </AsyncResultLoader>\n */\nexport const AsyncResultLoader = defineComponent({\n name: \"AsyncResultLoader\",\n\n props: {\n result: {\n type: Object as () => AsyncResult<unknown>,\n required: true\n }\n },\n\n setup(props, { slots }) {\n let resultRef = useAsyncResultRef(props.result);\n\n // Watch for prop changes & update listener\n watch(\n () => props.result,\n (newResult, oldResult) => {\n if (newResult === oldResult) return;\n resultRef = useAsyncResultRef(newResult);\n },\n { immediate: true }\n )\n\n return () => {\n const s = resultRef.value.state;\n\n // Choose what to render based on status\n switch (s.status) {\n case \"loading\":\n return slots.loading\n ? slots.loading()\n : h(\"div\", { class: \"loading\" }, \"Loading…\");\n\n case \"error\":\n return slots.error\n ? slots.error({ error: s.error })\n : h(\"div\", { class: \"error\" }, `Error: ${s.error}`);\n\n case \"success\":\n return slots.default\n ? slots.default({ value: s.value })\n : null;\n\n default:\n // \"idle\"\n return slots.idle\n ? slots.idle()\n : h(\"div\", { class: \"idle\" }, \"Idle\");\n }\n }\n }\n})\n\n\n\ninterface CustomSlots<E extends ErrorBase = ErrorBase> {\n loading?: () => VNode;\n error?: (props: { error: E }) => VNode;\n}\n\n/**\n * Builds a custom AsyncResultLoader component with predefined slots for loading and error states.\n * \n * Useful for creating reusable components with consistent loading and error handling UI (eg. framework-specific spinners, etc...).\n * \n * @param slots the custom slots for loading and error states\n * @returns a Vue component that uses the provided slots\n */\nexport function buildCustomAsyncResultLoader<T, E extends ErrorBase = ErrorBase>(slots: CustomSlots<E>) {\n const comp = defineComponent({\n name: \"CustomAsyncResultLoader\",\n props: {\n result: {\n type: Object as () => AsyncResult<T, E>,\n required: true\n }\n },\n setup(props, context) {\n return () => {\n const renderLoading = context.slots.loading ?? slots.loading ?? (() => undefined);\n const renderError = context.slots.error ?? slots.error ?? (() => undefined);\n return h(\n AsyncResultLoader,\n { result: props.result },\n {\n default: context.slots.default ? (propsDefault: { value: T }) => context.slots.default!(propsDefault) : undefined,\n\n loading: () => renderLoading(),\n error: ((propsError: { error: E }) => renderError(propsError))\n }\n )\n }\n }\n });\n\n return comp;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,iBAAgF;AAChF,kBAAgH;AAoBzG,SAAS,kBAAsD,aAAgC;AAClG,QAAM,YAAQ,gBAAuB,WAAW;AAEhD,QAAM,QAAQ,YAAY,OAAO,MAAM;AACnC,+BAAW,KAAK;AAAA,EACpB,CAAC;AAED,8BAAY,MAAM;AACd,UAAM;AAAA,EACV,CAAC;AAED,SAAO;AACX;AAOO,SAAS,6BAAiE,SAAgC;AAC7G,SAAO,kBAAkB,wBAAY,kBAAkB,OAAO,CAAC;AACnE;AAcO,SAAS,iBAA6D,QAA6B,MAAmC,UAAkC,EAAE,WAAW,KAAK,GAA2B;AACxN,QAAM,SAAS,IAAI,wBAAkB;AACrC,QAAM,YAAY,kBAAkB,MAAM;AAE1C,MAAI,QAA6B;AAEjC,wBAAM,QAAQ,CAAC,cAAc;AACzB,YAAQ;AACR,YAAQ,OAAO,OAAO,KAAK,SAAS,CAAC;AAAA,EACzC,GAAG,EAAE,WAAW,QAAQ,UAAU,CAAC;AAEnC,8BAAY,MAAM;AACd,YAAQ;AAAA,EACZ,CAAC;AAED,SAAO;AACX;AAqBO,SAAS,UAA8C,QAA8C;AACxG,SAAO,6BAA6B,OAAO,CAAC;AAChD;AAUO,SAAS,cAAkD,QAA2C;AACzG,QAAM,aAAa,wBAAY,eAAqB,MAAM;AAC1D,QAAM,YAAY,kBAAkB,WAAW,MAAM;AAErD,SAAO,EAAE,WAAW,SAAS,WAAW,QAAQ;AACpD;AAUO,SAAS,aAAgB,eAAwE;AACpG,QAAM,YAAY,kBAAkB,wBAAY,IAAI,aAAa,CAAC;AAClE,SAAO;AACX;AAQO,SAAS,iBAAoB,eAA4G;AAC5I,QAAM,SAAS,IAAI,wBAAoB;AACvC,QAAM,YAAY,kBAAkB,MAAM;AAE1C,QAAM,UAAU,MAAM;AAClB,WAAO,WAAW,aAAa;AAAA,EACnC;AAEA,SAAO,EAAE,WAAW,QAAQ;AAChC;AAUO,SAAS,qBAAiE,QAA6B,eAA0D,UAAkC,EAAE,WAAW,KAAK,GAA2B;AACnP,QAAM,YAAY,kBAAkB,IAAI,wBAAkB,CAAC;AAE3D,wBAAM,QAAQ,CAAC,cAAc;AACzB,cAAU,MAAM,WAAW,MAAM,cAAc,SAAS,CAAC;AAAA,EAC7D,GAAG,EAAE,WAAW,QAAQ,UAAU,CAAC;AAEnC,SAAO;AACX;;;AC9JA,IAAAA,cAAsD;AAwB/C,IAAM,wBAAoB,6BAAgB;AAAA,EAC7C,MAAM;AAAA,EAEN,OAAO;AAAA,IACH,QAAQ;AAAA,MACJ,MAAM;AAAA,MACN,UAAU;AAAA,IACd;AAAA,EACJ;AAAA,EAEA,MAAM,OAAO,EAAE,MAAM,GAAG;AACpB,QAAI,YAAY,kBAAkB,MAAM,MAAM;AAG9C;AAAA,MACI,MAAM,MAAM;AAAA,MACZ,CAAC,WAAW,cAAc;AACtB,YAAI,cAAc,UAAW;AAC7B,oBAAY,kBAAkB,SAAS;AAAA,MAC3C;AAAA,MACA,EAAE,WAAW,KAAK;AAAA,IACtB;AAEA,WAAO,MAAM;AACT,YAAM,IAAI,UAAU,MAAM;AAG1B,cAAQ,EAAE,QAAQ;AAAA,QACd,KAAK;AACD,iBAAO,MAAM,UACP,MAAM,QAAQ,QACd,eAAE,OAAO,EAAE,OAAO,UAAU,GAAG,eAAU;AAAA,QAEnD,KAAK;AACD,iBAAO,MAAM,QACP,MAAM,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,QAC9B,eAAE,OAAO,EAAE,OAAO,QAAQ,GAAG,UAAU,EAAE,KAAK,EAAE;AAAA,QAE1D,KAAK;AACD,iBAAO,MAAM,UACP,MAAM,QAAQ,EAAE,OAAO,EAAE,MAAM,CAAC,IAChC;AAAA,QAEV;AAEI,iBAAO,MAAM,OACP,MAAM,KAAK,QACX,eAAE,OAAO,EAAE,OAAO,OAAO,GAAG,MAAM;AAAA,MAChD;AAAA,IACJ;AAAA,EACJ;AACJ,CAAC;AAiBM,SAAS,6BAAiE,OAAuB;AACpG,QAAM,WAAO,6BAAgB;AAAA,IACzB,MAAM;AAAA,IACN,OAAO;AAAA,MACH,QAAQ;AAAA,QACJ,MAAM;AAAA,QACN,UAAU;AAAA,MACd;AAAA,IACJ;AAAA,IACA,MAAM,OAAO,SAAS;AAClB,aAAO,MAAM;AACT,cAAM,gBAAgB,QAAQ,MAAM,WAAW,MAAM,YAAY,MAAM;AACvE,cAAM,cAAc,QAAQ,MAAM,SAAS,MAAM,UAAU,MAAM;AACjE,mBAAO;AAAA,UACH;AAAA,UACA,EAAE,QAAQ,MAAM,OAAO;AAAA,UACvB;AAAA,YACI,SAAS,QAAQ,MAAM,UAAU,CAAC,iBAA+B,QAAQ,MAAM,QAAS,YAAY,IAAI;AAAA,YAExG,SAAS,MAAM,cAAc;AAAA,YAC7B,QAAQ,CAAC,eAA6B,YAAY,UAAU;AAAA,UAChE;AAAA,QACJ;AAAA,MACJ;AAAA,IACJ;AAAA,EACJ,CAAC;AAED,SAAO;AACX;","names":["import_vue"]}
|
package/dist/vue/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/vue/composables.ts
|
|
2
|
-
import {
|
|
2
|
+
import { onUnmounted, ref, triggerRef, watch } from "vue";
|
|
3
3
|
import { AsyncResult } from "unwrapped/core";
|
|
4
4
|
function useAsyncResultRef(asyncResult) {
|
|
5
5
|
const state = ref(asyncResult);
|
|
@@ -11,7 +11,10 @@ function useAsyncResultRef(asyncResult) {
|
|
|
11
11
|
});
|
|
12
12
|
return state;
|
|
13
13
|
}
|
|
14
|
-
function
|
|
14
|
+
function useAsyncResultRefFromPromise(promise) {
|
|
15
|
+
return useAsyncResultRef(AsyncResult.fromResultPromise(promise));
|
|
16
|
+
}
|
|
17
|
+
function useReactiveChain(source, pipe, options = { immediate: true }) {
|
|
15
18
|
const result = new AsyncResult();
|
|
16
19
|
const resultRef = useAsyncResultRef(result);
|
|
17
20
|
let unsub = null;
|
|
@@ -24,36 +27,13 @@ function useReactiveResult(source, pipe, options = { immediate: true }) {
|
|
|
24
27
|
});
|
|
25
28
|
return resultRef;
|
|
26
29
|
}
|
|
27
|
-
function
|
|
28
|
-
return useAsyncResultRef(AsyncResult.fromResultPromise(promise));
|
|
29
|
-
}
|
|
30
|
-
function useImmediateAction(action) {
|
|
30
|
+
function useAction(action) {
|
|
31
31
|
return useAsyncResultRefFromPromise(action());
|
|
32
32
|
}
|
|
33
33
|
function useLazyAction(action) {
|
|
34
|
-
const
|
|
35
|
-
const resultRef = useAsyncResultRef(result);
|
|
36
|
-
|
|
37
|
-
result.updateFromResultPromise(action());
|
|
38
|
-
};
|
|
39
|
-
return { resultRef, trigger };
|
|
40
|
-
}
|
|
41
|
-
function useReactiveAction(input, pipe, options = { immediate: true }) {
|
|
42
|
-
const source = typeof input === "function" ? computed(input) : toRef(input);
|
|
43
|
-
const outputRef = ref(new AsyncResult());
|
|
44
|
-
let unsub = null;
|
|
45
|
-
watch(source, () => {
|
|
46
|
-
unsub?.();
|
|
47
|
-
const newOutput = pipe(source.value);
|
|
48
|
-
unsub = newOutput.listen((newState) => {
|
|
49
|
-
outputRef.value.setState(newState.state);
|
|
50
|
-
triggerRef(outputRef);
|
|
51
|
-
});
|
|
52
|
-
}, { immediate: options.immediate });
|
|
53
|
-
onUnmounted(() => {
|
|
54
|
-
unsub?.();
|
|
55
|
-
});
|
|
56
|
-
return outputRef;
|
|
34
|
+
const lazyAction = AsyncResult.makeLazyAction(action);
|
|
35
|
+
const resultRef = useAsyncResultRef(lazyAction.result);
|
|
36
|
+
return { resultRef, trigger: lazyAction.trigger };
|
|
57
37
|
}
|
|
58
38
|
function useGenerator(generatorFunc) {
|
|
59
39
|
const resultRef = useAsyncResultRef(AsyncResult.run(generatorFunc));
|
|
@@ -76,7 +56,7 @@ function useReactiveGenerator(source, generatorFunc, options = { immediate: true
|
|
|
76
56
|
}
|
|
77
57
|
|
|
78
58
|
// src/vue/components/asyncResultLoader.ts
|
|
79
|
-
import { defineComponent,
|
|
59
|
+
import { defineComponent, watch as watch2, h } from "vue";
|
|
80
60
|
var AsyncResultLoader = defineComponent({
|
|
81
61
|
name: "AsyncResultLoader",
|
|
82
62
|
props: {
|
|
@@ -86,24 +66,17 @@ var AsyncResultLoader = defineComponent({
|
|
|
86
66
|
}
|
|
87
67
|
},
|
|
88
68
|
setup(props, { slots }) {
|
|
89
|
-
|
|
90
|
-
let unlisten = null;
|
|
91
|
-
onUnmounted2(() => {
|
|
92
|
-
if (unlisten) unlisten();
|
|
93
|
-
});
|
|
69
|
+
let resultRef = useAsyncResultRef(props.result);
|
|
94
70
|
watch2(
|
|
95
71
|
() => props.result,
|
|
96
|
-
(newResult) => {
|
|
97
|
-
if (
|
|
98
|
-
|
|
99
|
-
unlisten = newResult.listen((res) => {
|
|
100
|
-
state.value = res.state;
|
|
101
|
-
});
|
|
72
|
+
(newResult, oldResult) => {
|
|
73
|
+
if (newResult === oldResult) return;
|
|
74
|
+
resultRef = useAsyncResultRef(newResult);
|
|
102
75
|
},
|
|
103
76
|
{ immediate: true }
|
|
104
77
|
);
|
|
105
78
|
return () => {
|
|
106
|
-
const s =
|
|
79
|
+
const s = resultRef.value.state;
|
|
107
80
|
switch (s.status) {
|
|
108
81
|
case "loading":
|
|
109
82
|
return slots.loading ? slots.loading() : h("div", { class: "loading" }, "Loading\u2026");
|
|
@@ -147,14 +120,13 @@ function buildCustomAsyncResultLoader(slots) {
|
|
|
147
120
|
export {
|
|
148
121
|
AsyncResultLoader,
|
|
149
122
|
buildCustomAsyncResultLoader,
|
|
123
|
+
useAction,
|
|
150
124
|
useAsyncResultRef,
|
|
151
125
|
useAsyncResultRefFromPromise,
|
|
152
126
|
useGenerator,
|
|
153
|
-
useImmediateAction,
|
|
154
127
|
useLazyAction,
|
|
155
128
|
useLazyGenerator,
|
|
156
|
-
|
|
157
|
-
useReactiveGenerator
|
|
158
|
-
useReactiveResult
|
|
129
|
+
useReactiveChain,
|
|
130
|
+
useReactiveGenerator
|
|
159
131
|
};
|
|
160
132
|
//# sourceMappingURL=index.mjs.map
|
package/dist/vue/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/vue/composables.ts","../../src/vue/components/asyncResultLoader.ts"],"sourcesContent":["import { computed, onUnmounted, ref, toRef, triggerRef, watch, type Ref, type WatchSource } from \"vue\";\nimport { AsyncResult, type FlatChainFunction, type Result } from \"unwrapped/core\";\n\nexport function useAsyncResultRef<T, E>(asyncResult: AsyncResult<T, E>) {\n const state = ref<AsyncResult<T, E>>(asyncResult) as Ref<AsyncResult<T, E>>;\n\n const unsub = asyncResult.listen(() => {\n triggerRef(state);\n });\n\n onUnmounted(() => {\n unsub();\n });\n\n return state;\n}\n\nexport function useReactiveResult<T, E, Inputs>(source: WatchSource<Inputs>, pipe: FlatChainFunction<Inputs, T, E>, options:{ immediate: boolean } = { immediate: true }): Ref<AsyncResult<T, E>> {\n const result = new AsyncResult<T, E>();\n const resultRef = useAsyncResultRef(result);\n\n let unsub: (() => void) | null = null;\n\n watch(source, (newInputs) => {\n unsub?.();\n unsub = result.mirror(pipe(newInputs));\n }, { immediate: options.immediate });\n \n onUnmounted(() => {\n unsub?.();\n });\n\n return resultRef;\n}\n\nexport function useAsyncResultRefFromPromise<T, E>(promise: Promise<Result<T, E>>) {\n return useAsyncResultRef(AsyncResult.fromResultPromise(promise));\n}\n\nexport type Action<T,E> = () => Promise<Result<T, E>>;\nexport function useImmediateAction<T, E>(action: Action<T, E>): Ref<AsyncResult<T, E>> {\n return useAsyncResultRefFromPromise(action());\n}\nexport interface LazyActionReturn<T, E> {\n resultRef: Ref<AsyncResult<T, E>>;\n trigger: () => void;\n}\n\nexport function useLazyAction<T, E>(action: Action<T, E>): LazyActionReturn<T, E> {\n const result = new AsyncResult<T, E>();\n const resultRef = useAsyncResultRef(result);\n\n const trigger = () => {\n result.updateFromResultPromise(action());\n }\n\n return { resultRef, trigger };\n}\n\nexport function useReactiveAction<I, O, E>(input: I | Ref<I> | (() => I), pipe: FlatChainFunction<I, O, E>, options:{ immediate: boolean } = { immediate: true }): Ref<AsyncResult<O, E>> {\n const source = typeof input === 'function' ? computed(input as () => I) : toRef(input)\n\n const outputRef = ref<AsyncResult<O, E>>(new AsyncResult()) as Ref<AsyncResult<O, E>>;\n let unsub: (() => void) | null = null;\n\n watch(source, () => {\n unsub?.();\n const newOutput = pipe(source.value);\n unsub = newOutput.listen((newState) => {\n outputRef.value.setState(newState.state);\n triggerRef(outputRef);\n });\n }, { immediate: options.immediate });\n\n onUnmounted(() => {\n unsub?.();\n });\n\n return outputRef;\n}\n\nexport function useGenerator<T>(generatorFunc: () => Generator<AsyncResult<any, any>, T, any>): Ref<AsyncResult<T, any>> {\n const resultRef = useAsyncResultRef(AsyncResult.run(generatorFunc));\n return resultRef;\n}\n\nexport function useLazyGenerator<T>(generatorFunc: () => Generator<AsyncResult<any, any>, T, any>): { resultRef: Ref<AsyncResult<T, any>>, trigger: () => void } {\n const result = new AsyncResult<T, any>();\n const resultRef = useAsyncResultRef(result);\n\n const trigger = () => {\n result.runInPlace(generatorFunc);\n }\n\n return { resultRef, trigger };\n}\n\nexport 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>> {\n const resultRef = useAsyncResultRef(new AsyncResult<T, E>());\n\n watch(source, (newInputs) => {\n resultRef.value.runInPlace(() => generatorFunc(newInputs));\n }, { immediate: options.immediate });\n\n return resultRef;\n}","import { defineComponent, ref, watch, onUnmounted, h, type VNode } from \"vue\"\nimport type { AsyncResult, AsyncResultState } from \"unwrapped/core\"\n\nexport const AsyncResultLoader = defineComponent({\n name: \"AsyncResultLoader\",\n\n props: {\n result: {\n type: Object as () => AsyncResult<unknown, unknown>,\n required: true\n }\n },\n\n setup(props, { slots }) {\n const state = ref<AsyncResultState<unknown, unknown>>(props.result.state)\n let unlisten: (() => void) | null = null\n\n // Unsubscribe on destroy\n onUnmounted(() => {\n if (unlisten) unlisten()\n })\n\n // Watch for prop changes & update listener\n watch(\n () => props.result,\n (newResult) => {\n if (unlisten) unlisten()\n state.value = newResult.state\n unlisten = newResult.listen((res) => {\n state.value = res.state\n })\n },\n { immediate: true }\n )\n\n return () => {\n const s = state.value\n\n // Choose what to render based on status\n switch (s.status) {\n case \"loading\":\n return slots.loading\n ? slots.loading()\n : h(\"div\", { class: \"loading\" }, \"Loading…\")\n\n case \"error\":\n return slots.error\n ? slots.error({ error: s.error })\n : h(\"div\", { class: \"error\" }, `Error: ${s.error}`)\n\n case \"success\":\n return slots.default\n ? slots.default({ value: s.value })\n : null\n\n default:\n // \"idle\"\n return slots.idle\n ? slots.idle()\n : h(\"div\", { class: \"idle\" }, \"Idle\")\n }\n }\n }\n})\n\ninterface CustomSlots<E> {\n loading?: () => VNode;\n error?: (props: { error: E }) => VNode;\n}\n\nexport function buildCustomAsyncResultLoader<T, E>(slots: CustomSlots<E>) {\n const comp = defineComponent({\n name: \"CustomAsyncResultLoader\",\n props: {\n result: {\n type: Object as () => AsyncResult<T, E>,\n required: true\n }\n },\n setup(props, context) {\n return () => {\n const renderLoading = context.slots.loading ?? slots.loading ?? (() => undefined);\n const renderError = context.slots.error ?? slots.error ?? (() => undefined);\n return h(\n AsyncResultLoader,\n { result: props.result },\n {\n default: context.slots.default ? (propsDefault: { value: T }) => context.slots.default!(propsDefault) : undefined,\n\n loading: () => renderLoading(),\n error: ((propsError: { error: E }) => renderError(propsError))\n }\n )\n }\n }\n });\n\n return comp;\n}\n"],"mappings":"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","names":["ref","watch","onUnmounted"]}
|
|
1
|
+
{"version":3,"sources":["../../src/vue/composables.ts","../../src/vue/components/asyncResultLoader.ts"],"sourcesContent":["import { onUnmounted, ref, triggerRef, watch, type Ref, type WatchSource } from \"vue\";\nimport { AsyncResult, ErrorBase, type Action, type AsyncResultGenerator, type FlatChainStep, type Result } from \"unwrapped/core\";\n\n\n// === Vue specific types ===\n\ninterface ReactiveProcessOptions {\n immediate: boolean;\n}\n\n\n\n// === Vue Composables for AsyncResult ===\n\n/**\n * Makes a ref to the given AsyncResult. Whenever the state of the AsyncResult changes,\n * the ref gets retriggered, making those changes visible to Vue's reactivity system.\n * \n * @param asyncResult the result to make reactive\n * @returns the ref to the result\n */\nexport function useAsyncResultRef<T, E extends ErrorBase = ErrorBase>(asyncResult: AsyncResult<T, E>) {\n const state = ref<AsyncResult<T, E>>(asyncResult) as Ref<AsyncResult<T, E>>;\n\n const unsub = asyncResult.listen(() => {\n triggerRef(state);\n });\n\n onUnmounted(() => {\n unsub();\n });\n\n return state;\n}\n\n/**\n * Creates an AsyncResult ref from a promise returning a Result.\n * @param promise the promise returning a Result\n * @returns the ref to the AsyncResult\n */\nexport function useAsyncResultRefFromPromise<T, E extends ErrorBase = ErrorBase>(promise: Promise<Result<T, E>>) {\n return useAsyncResultRef(AsyncResult.fromResultPromise(promise));\n}\n\n\n\n// === Vue Composables for Chains ===\n\n/**\n * Watches a source, gives it as inputs to the function provided, and updates the result contained in the ref accordingly.\n * \n * @param source the inputs to react to\n * @param pipe the function to run when the inputs change\n * @param options optional settings\n * @returns ref to the result\n */\nexport function useReactiveChain<Inputs, T, E extends ErrorBase = ErrorBase>(source: WatchSource<Inputs>, pipe: FlatChainStep<Inputs, T, E>, options: ReactiveProcessOptions = { immediate: true }): Ref<AsyncResult<T, E>> {\n const result = new AsyncResult<T, E>();\n const resultRef = useAsyncResultRef(result);\n\n let unsub: (() => void) | null = null;\n\n watch(source, (newInputs) => {\n unsub?.();\n unsub = result.mirror(pipe(newInputs));\n }, { immediate: options.immediate });\n\n onUnmounted(() => {\n unsub?.();\n });\n\n return resultRef;\n}\n\n\n// === Vue Composables for Actions ===\n\n/**\n * The return type of useLazyAction.\n */\nexport interface LazyActionRef<T, E extends ErrorBase = ErrorBase> {\n resultRef: Ref<AsyncResult<T, E>>;\n trigger: () => void;\n}\n\n/**\n * Executes an action immediately and returns a ref to the AsyncResult representing the action's state.\n * \n * Same as useAsyncResultRefFromPromise(action()).\n * \n * @param action the action to execute immediately\n * @returns a ref to the AsyncResult representing the action's state\n */\nexport function useAction<T, E extends ErrorBase = ErrorBase>(action: Action<T, E>): Ref<AsyncResult<T, E>> {\n return useAsyncResultRefFromPromise(action());\n}\n\n/**\n * Creates a lazy action that can be triggered manually.\n * \n * Same as AsyncResult.makeLazyAction(action), but the AsyncResult is wrapped in a ref for Vue reactivity.\n * \n * @param action the action to execute when triggered\n * @returns an object containing a ref to the AsyncResult and a trigger function\n */\nexport function useLazyAction<T, E extends ErrorBase = ErrorBase>(action: Action<T, E>): LazyActionRef<T, E> {\n const lazyAction = AsyncResult.makeLazyAction<T, E>(action);\n const resultRef = useAsyncResultRef(lazyAction.result);\n\n return { resultRef, trigger: lazyAction.trigger };\n}\n\n\n// === Vue Composables for Generators ===\n\n/**\n * Runs a generator function immediately and returns a ref to the AsyncResult representing the generator's state.\n * @param generatorFunc the generator function to run immediately\n * @returns a ref to the AsyncResult representing the generator's state\n */\nexport function useGenerator<T>(generatorFunc: () => AsyncResultGenerator<T>): Ref<AsyncResult<T, any>> {\n const resultRef = useAsyncResultRef(AsyncResult.run(generatorFunc));\n return resultRef;\n}\n\n/**\n * Creates a lazy generator that can be triggered manually.\n * \n * @param generatorFunc the generator function to run when triggered\n * @returns an object containing a ref to the AsyncResult and a trigger function\n */\nexport function useLazyGenerator<T>(generatorFunc: () => AsyncResultGenerator<T>): { resultRef: Ref<AsyncResult<T, any>>, trigger: () => void } {\n const result = new AsyncResult<T, any>();\n const resultRef = useAsyncResultRef(result);\n\n const trigger = () => {\n result.runInPlace(generatorFunc);\n }\n\n return { resultRef, trigger };\n}\n\n/**\n * Watches a source, gives it as inputs to the generator function provided, and updates the result contained in the ref accordingly.\n * \n * @param source the inputs to react to\n * @param generatorFunc the generator function to run when the inputs change\n * @param options optional settings\n * @returns ref to the result\n */\nexport function useReactiveGenerator<Inputs, T, E extends ErrorBase = ErrorBase>(source: WatchSource<Inputs>, generatorFunc: (args: Inputs) => AsyncResultGenerator<T>, options: ReactiveProcessOptions = { immediate: true }): Ref<AsyncResult<T, E>> {\n const resultRef = useAsyncResultRef(new AsyncResult<T, E>());\n\n watch(source, (newInputs) => {\n resultRef.value.runInPlace(() => generatorFunc(newInputs));\n }, { immediate: options.immediate });\n\n return resultRef;\n}","import { defineComponent, watch, h, type VNode } from \"vue\"\nimport type { AsyncResult, ErrorBase } from \"unwrapped/core\"\nimport { useAsyncResultRef } from \"../composables\"\n\n/**\n * A Vue component that displays different content based on the state of an AsyncResult.\n * It supports slots for 'loading', 'error', 'success' (default), and 'idle' states.\n * \n * @example\n * <AsyncResultLoader :result=\"myAsyncResult\">\n * <template #loading>\n * <div>Loading data...</div>\n * </template>\n * <template #error=\"{ error }\">\n * <div>Error occurred: {{ error.message }}</div>\n * </template>\n * <template #default=\"{ value }\">\n * <div>Data loaded: {{ value }}</div>\n * </template>\n * <template #idle>\n * <div>Waiting to start...</div>\n * </template>\n * </AsyncResultLoader>\n */\nexport const AsyncResultLoader = defineComponent({\n name: \"AsyncResultLoader\",\n\n props: {\n result: {\n type: Object as () => AsyncResult<unknown>,\n required: true\n }\n },\n\n setup(props, { slots }) {\n let resultRef = useAsyncResultRef(props.result);\n\n // Watch for prop changes & update listener\n watch(\n () => props.result,\n (newResult, oldResult) => {\n if (newResult === oldResult) return;\n resultRef = useAsyncResultRef(newResult);\n },\n { immediate: true }\n )\n\n return () => {\n const s = resultRef.value.state;\n\n // Choose what to render based on status\n switch (s.status) {\n case \"loading\":\n return slots.loading\n ? slots.loading()\n : h(\"div\", { class: \"loading\" }, \"Loading…\");\n\n case \"error\":\n return slots.error\n ? slots.error({ error: s.error })\n : h(\"div\", { class: \"error\" }, `Error: ${s.error}`);\n\n case \"success\":\n return slots.default\n ? slots.default({ value: s.value })\n : null;\n\n default:\n // \"idle\"\n return slots.idle\n ? slots.idle()\n : h(\"div\", { class: \"idle\" }, \"Idle\");\n }\n }\n }\n})\n\n\n\ninterface CustomSlots<E extends ErrorBase = ErrorBase> {\n loading?: () => VNode;\n error?: (props: { error: E }) => VNode;\n}\n\n/**\n * Builds a custom AsyncResultLoader component with predefined slots for loading and error states.\n * \n * Useful for creating reusable components with consistent loading and error handling UI (eg. framework-specific spinners, etc...).\n * \n * @param slots the custom slots for loading and error states\n * @returns a Vue component that uses the provided slots\n */\nexport function buildCustomAsyncResultLoader<T, E extends ErrorBase = ErrorBase>(slots: CustomSlots<E>) {\n const comp = defineComponent({\n name: \"CustomAsyncResultLoader\",\n props: {\n result: {\n type: Object as () => AsyncResult<T, E>,\n required: true\n }\n },\n setup(props, context) {\n return () => {\n const renderLoading = context.slots.loading ?? slots.loading ?? (() => undefined);\n const renderError = context.slots.error ?? slots.error ?? (() => undefined);\n return h(\n AsyncResultLoader,\n { result: props.result },\n {\n default: context.slots.default ? (propsDefault: { value: T }) => context.slots.default!(propsDefault) : undefined,\n\n loading: () => renderLoading(),\n error: ((propsError: { error: E }) => renderError(propsError))\n }\n )\n }\n }\n });\n\n return comp;\n}\n"],"mappings":"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","names":["watch","watch"]}
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "unwrapped",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
7
|
-
"license": "
|
|
7
|
+
"license": "LGPL-3.0-or-later",
|
|
8
8
|
"main": "./dist/core/index.js",
|
|
9
9
|
"module": "./dist/core/index.mjs",
|
|
10
10
|
"types": "./dist/core/index.d.ts",
|