@thomasfosterau/effect-svelte 0.0.0
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/LICENSE +21 -0
- package/README.md +58 -0
- package/dist/Store.d.ts +154 -0
- package/dist/Store.js +183 -0
- package/dist/Store.js.map +1 -0
- package/dist/_virtual/_rolldown/runtime.js +13 -0
- package/dist/context.svelte.d.ts +37 -0
- package/dist/context.svelte.js +53 -0
- package/dist/context.svelte.js.map +1 -0
- package/dist/derived.svelte.d.ts +43 -0
- package/dist/derived.svelte.js +65 -0
- package/dist/derived.svelte.js.map +1 -0
- package/dist/effect.svelte.d.ts +51 -0
- package/dist/effect.svelte.js +68 -0
- package/dist/effect.svelte.js.map +1 -0
- package/dist/emitter.d.ts +45 -0
- package/dist/emitter.js +60 -0
- package/dist/emitter.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +13 -0
- package/dist/internal/result.svelte.js +36 -0
- package/dist/internal/result.svelte.js.map +1 -0
- package/dist/internal/run.d.ts +15 -0
- package/dist/internal/run.js +25 -0
- package/dist/internal/run.js.map +1 -0
- package/dist/internal/subscribe.js +79 -0
- package/dist/internal/subscribe.js.map +1 -0
- package/dist/query.svelte.d.ts +45 -0
- package/dist/query.svelte.js +41 -0
- package/dist/query.svelte.js.map +1 -0
- package/dist/reactivity.svelte.d.ts +123 -0
- package/dist/reactivity.svelte.js +186 -0
- package/dist/reactivity.svelte.js.map +1 -0
- package/dist/runtime.d.ts +81 -0
- package/dist/runtime.js +85 -0
- package/dist/runtime.js.map +1 -0
- package/dist/scope.svelte.d.ts +49 -0
- package/dist/scope.svelte.js +68 -0
- package/dist/scope.svelte.js.map +1 -0
- package/dist/stream.svelte.d.ts +48 -0
- package/dist/stream.svelte.js +34 -0
- package/dist/stream.svelte.js.map +1 -0
- package/dist/subscription.svelte.d.ts +69 -0
- package/dist/subscription.svelte.js +69 -0
- package/dist/subscription.svelte.js.map +1 -0
- package/package.json +53 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import * as AsyncResult from "effect/unstable/reactivity/AsyncResult";
|
|
3
|
+
|
|
4
|
+
//#region src/effect.svelte.d.ts
|
|
5
|
+
interface UseEffectOptions {
|
|
6
|
+
/**
|
|
7
|
+
* If true, the effect will run immediately on mount.
|
|
8
|
+
* If false, the effect will only run when manually triggered via run().
|
|
9
|
+
* @default false
|
|
10
|
+
*/
|
|
11
|
+
immediate?: boolean;
|
|
12
|
+
}
|
|
13
|
+
interface UseEffectReturn<A, E> {
|
|
14
|
+
/**
|
|
15
|
+
* The current result state of the effect. Re-running preserves the previous
|
|
16
|
+
* value as a waiting `Success` (stale-while-revalidate).
|
|
17
|
+
*/
|
|
18
|
+
readonly current: AsyncResult.AsyncResult<A, E>;
|
|
19
|
+
/**
|
|
20
|
+
* Manually trigger execution of the effect
|
|
21
|
+
*/
|
|
22
|
+
run: () => void;
|
|
23
|
+
/**
|
|
24
|
+
* Interrupt the currently running effect
|
|
25
|
+
*/
|
|
26
|
+
interrupt: () => void;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Run a single Effect and track its result state.
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```svelte
|
|
33
|
+
* <script lang="ts">
|
|
34
|
+
* import { useEffect, AsyncResult } from '@thomasfosterau/effect-svelte';
|
|
35
|
+
* import { Effect } from 'effect';
|
|
36
|
+
*
|
|
37
|
+
* const fetchData = useEffect(
|
|
38
|
+
* Effect.delay(Effect.succeed(42), '1 second'),
|
|
39
|
+
* { immediate: true }
|
|
40
|
+
* );
|
|
41
|
+
* </script>
|
|
42
|
+
*
|
|
43
|
+
* {#if AsyncResult.isSuccess(fetchData.current)}
|
|
44
|
+
* <p>Result: {fetchData.current.value}</p>
|
|
45
|
+
* {/if}
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
declare function useEffect<A, E, R>(effect: Effect.Effect<A, E, R>, options?: UseEffectOptions): UseEffectReturn<A, E>;
|
|
49
|
+
//#endregion
|
|
50
|
+
export { UseEffectOptions, UseEffectReturn, useEffect };
|
|
51
|
+
//# sourceMappingURL=effect.svelte.d.ts.map
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { getRuntimeContext } from "./context.svelte.js";
|
|
2
|
+
import { interruptFiber, runFork } from "./internal/run.js";
|
|
3
|
+
import { makeResult } from "./internal/result.svelte.js";
|
|
4
|
+
import { Effect, Fiber } from "effect";
|
|
5
|
+
//#region src/effect.svelte.ts
|
|
6
|
+
/**
|
|
7
|
+
* Run a single Effect and track its result state.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```svelte
|
|
11
|
+
* <script lang="ts">
|
|
12
|
+
* import { useEffect, AsyncResult } from '@thomasfosterau/effect-svelte';
|
|
13
|
+
* import { Effect } from 'effect';
|
|
14
|
+
*
|
|
15
|
+
* const fetchData = useEffect(
|
|
16
|
+
* Effect.delay(Effect.succeed(42), '1 second'),
|
|
17
|
+
* { immediate: true }
|
|
18
|
+
* );
|
|
19
|
+
* <\/script>
|
|
20
|
+
*
|
|
21
|
+
* {#if AsyncResult.isSuccess(fetchData.current)}
|
|
22
|
+
* <p>Result: {fetchData.current.value}</p>
|
|
23
|
+
* {/if}
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
function useEffect(effect, options = {}) {
|
|
27
|
+
const { immediate = false } = options;
|
|
28
|
+
const runtime = getRuntimeContext();
|
|
29
|
+
const state = makeResult();
|
|
30
|
+
let currentFiber = null;
|
|
31
|
+
const run = () => {
|
|
32
|
+
if (currentFiber !== null) interruptFiber(currentFiber);
|
|
33
|
+
state.startWaiting();
|
|
34
|
+
const runningFiber = runFork(runtime)(effect);
|
|
35
|
+
currentFiber = runningFiber;
|
|
36
|
+
runFork(runtime)(Effect.gen(function* () {
|
|
37
|
+
const exit = yield* Fiber.await(runningFiber);
|
|
38
|
+
if (currentFiber === runningFiber) {
|
|
39
|
+
state.settle(exit);
|
|
40
|
+
currentFiber = null;
|
|
41
|
+
}
|
|
42
|
+
}));
|
|
43
|
+
};
|
|
44
|
+
const interrupt = () => {
|
|
45
|
+
if (currentFiber !== null) {
|
|
46
|
+
interruptFiber(currentFiber);
|
|
47
|
+
currentFiber = null;
|
|
48
|
+
state.reset();
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
$effect(() => {
|
|
52
|
+
if (immediate) run();
|
|
53
|
+
return () => {
|
|
54
|
+
interrupt();
|
|
55
|
+
};
|
|
56
|
+
});
|
|
57
|
+
return {
|
|
58
|
+
get current() {
|
|
59
|
+
return state.current;
|
|
60
|
+
},
|
|
61
|
+
run,
|
|
62
|
+
interrupt
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
//#endregion
|
|
66
|
+
export { useEffect };
|
|
67
|
+
|
|
68
|
+
//# sourceMappingURL=effect.svelte.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"effect.svelte.js","names":["getRuntime"],"sources":["../src/effect.svelte.ts"],"sourcesContent":["import { Effect, Fiber } from \"effect\";\nimport type * as AsyncResult from \"effect/unstable/reactivity/AsyncResult\";\nimport { getRuntime } from \"./context.svelte.js\";\nimport { interruptFiber, runFork, type RuntimeLike } from \"./internal/run.js\";\nimport { makeResult } from \"./internal/result.svelte.js\";\n\nexport interface UseEffectOptions {\n /**\n * If true, the effect will run immediately on mount.\n * If false, the effect will only run when manually triggered via run().\n * @default false\n */\n immediate?: boolean;\n}\n\nexport interface UseEffectReturn<A, E> {\n /**\n * The current result state of the effect. Re-running preserves the previous\n * value as a waiting `Success` (stale-while-revalidate).\n */\n readonly current: AsyncResult.AsyncResult<A, E>;\n\n /**\n * Manually trigger execution of the effect\n */\n run: () => void;\n\n /**\n * Interrupt the currently running effect\n */\n interrupt: () => void;\n}\n\n/**\n * Run a single Effect and track its result state.\n *\n * @example\n * ```svelte\n * <script lang=\"ts\">\n * import { useEffect, AsyncResult } from '@thomasfosterau/effect-svelte';\n * import { Effect } from 'effect';\n *\n * const fetchData = useEffect(\n * Effect.delay(Effect.succeed(42), '1 second'),\n * { immediate: true }\n * );\n * </script>\n *\n * {#if AsyncResult.isSuccess(fetchData.current)}\n * <p>Result: {fetchData.current.value}</p>\n * {/if}\n * ```\n */\nexport function useEffect<A, E, R>(\n effect: Effect.Effect<A, E, R>,\n options: UseEffectOptions = {},\n): UseEffectReturn<A, E> {\n const { immediate = false } = options;\n const runtime = getRuntime() as RuntimeLike<R>;\n\n const state = makeResult<A, E>();\n let currentFiber: Fiber.Fiber<A, E> | null = null;\n\n const run = () => {\n // Interrupt any existing fiber\n if (currentFiber !== null) {\n interruptFiber(currentFiber);\n }\n\n // Mark as waiting, preserving the previous value if there is one.\n state.startWaiting();\n\n // Run the effect and capture the fiber reference\n const runningFiber = runFork(runtime)(effect);\n currentFiber = runningFiber;\n\n // Settle the result when the fiber completes, unless a newer run replaced it.\n runFork(runtime)(\n Effect.gen(function* () {\n const exit = yield* Fiber.await(runningFiber);\n if (currentFiber === runningFiber) {\n state.settle(exit);\n currentFiber = null;\n }\n }),\n );\n };\n\n const interrupt = () => {\n if (currentFiber !== null) {\n interruptFiber(currentFiber);\n currentFiber = null;\n // Return to the initial state so the effect can be run again cleanly.\n state.reset();\n }\n };\n\n // Set up effect lifecycle\n $effect(() => {\n if (immediate) {\n run();\n }\n\n // Cleanup: interrupt any running fiber on unmount\n return () => {\n interrupt();\n };\n });\n\n return {\n get current() {\n return state.current;\n },\n run,\n interrupt,\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAqDA,SAAgB,UACd,QACA,UAA4B,CAAC,GACN;CACvB,MAAM,EAAE,YAAY,UAAU;CAC9B,MAAM,UAAUA,kBAAW;CAE3B,MAAM,QAAQ,WAAiB;CAC/B,IAAI,eAAyC;CAE7C,MAAM,YAAY;EAEhB,IAAI,iBAAiB,MACnB,eAAe,YAAY;EAI7B,MAAM,aAAa;EAGnB,MAAM,eAAe,QAAQ,OAAO,EAAE,MAAM;EAC5C,eAAe;EAGf,QAAQ,OAAO,EACb,OAAO,IAAI,aAAa;GACtB,MAAM,OAAO,OAAO,MAAM,MAAM,YAAY;GAC5C,IAAI,iBAAiB,cAAc;IACjC,MAAM,OAAO,IAAI;IACjB,eAAe;GACjB;EACF,CAAC,CACH;CACF;CAEA,MAAM,kBAAkB;EACtB,IAAI,iBAAiB,MAAM;GACzB,eAAe,YAAY;GAC3B,eAAe;GAEf,MAAM,MAAM;EACd;CACF;CAGA,cAAc;EACZ,IAAI,WACF,IAAI;EAIN,aAAa;GACX,UAAU;EACZ;CACF,CAAC;CAED,OAAO;EACL,IAAI,UAAU;GACZ,OAAO,MAAM;EACf;EACA;EACA;CACF;AACF"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Stream } from "effect";
|
|
2
|
+
|
|
3
|
+
//#region src/emitter.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* A bridge that converts Svelte reactive state into Effect streams.
|
|
6
|
+
* Use emit() to push values from Svelte's $effect, and consume via the stream property.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```svelte
|
|
10
|
+
* <script lang="ts">
|
|
11
|
+
* import { SignalEmitter } from 'effect-svelte';
|
|
12
|
+
*
|
|
13
|
+
* let count = $state(0);
|
|
14
|
+
* const emitter = new SignalEmitter<number>();
|
|
15
|
+
*
|
|
16
|
+
* $effect(() => {
|
|
17
|
+
* emitter.emit(count);
|
|
18
|
+
* });
|
|
19
|
+
*
|
|
20
|
+
* // Now emitter.stream can be used in Effect pipelines
|
|
21
|
+
* // Example: Process the stream with Effect
|
|
22
|
+
* const processed = emitter.stream.pipe(
|
|
23
|
+
* Stream.map(n => n * 2)
|
|
24
|
+
* );
|
|
25
|
+
* </script>
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
declare class SignalEmitter<A> {
|
|
29
|
+
private _stream;
|
|
30
|
+
private _emit;
|
|
31
|
+
constructor();
|
|
32
|
+
/**
|
|
33
|
+
* Emit a value into the stream.
|
|
34
|
+
* This should typically be called from within a $effect to track reactive dependencies.
|
|
35
|
+
*/
|
|
36
|
+
emit(value: A): void;
|
|
37
|
+
/**
|
|
38
|
+
* The Effect stream that emits values pushed via emit().
|
|
39
|
+
* Can be used in Effect pipelines, composed with other streams, etc.
|
|
40
|
+
*/
|
|
41
|
+
get stream(): Stream.Stream<A, never, never>;
|
|
42
|
+
}
|
|
43
|
+
//#endregion
|
|
44
|
+
export { SignalEmitter };
|
|
45
|
+
//# sourceMappingURL=emitter.d.ts.map
|
package/dist/emitter.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { Effect, Queue, Stream } from "effect";
|
|
2
|
+
//#region src/emitter.ts
|
|
3
|
+
/**
|
|
4
|
+
* A bridge that converts Svelte reactive state into Effect streams.
|
|
5
|
+
* Use emit() to push values from Svelte's $effect, and consume via the stream property.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```svelte
|
|
9
|
+
* <script lang="ts">
|
|
10
|
+
* import { SignalEmitter } from 'effect-svelte';
|
|
11
|
+
*
|
|
12
|
+
* let count = $state(0);
|
|
13
|
+
* const emitter = new SignalEmitter<number>();
|
|
14
|
+
*
|
|
15
|
+
* $effect(() => {
|
|
16
|
+
* emitter.emit(count);
|
|
17
|
+
* });
|
|
18
|
+
*
|
|
19
|
+
* // Now emitter.stream can be used in Effect pipelines
|
|
20
|
+
* // Example: Process the stream with Effect
|
|
21
|
+
* const processed = emitter.stream.pipe(
|
|
22
|
+
* Stream.map(n => n * 2)
|
|
23
|
+
* );
|
|
24
|
+
* <\/script>
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
var SignalEmitter = class {
|
|
28
|
+
_stream;
|
|
29
|
+
_emit = () => {};
|
|
30
|
+
constructor() {
|
|
31
|
+
this._stream = Stream.callback((queue) => {
|
|
32
|
+
let isDone = false;
|
|
33
|
+
this._emit = (value) => {
|
|
34
|
+
if (!isDone) Queue.offerUnsafe(queue, value);
|
|
35
|
+
};
|
|
36
|
+
return Effect.sync(() => {
|
|
37
|
+
isDone = true;
|
|
38
|
+
this._emit = () => {};
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Emit a value into the stream.
|
|
44
|
+
* This should typically be called from within a $effect to track reactive dependencies.
|
|
45
|
+
*/
|
|
46
|
+
emit(value) {
|
|
47
|
+
this._emit(value);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* The Effect stream that emits values pushed via emit().
|
|
51
|
+
* Can be used in Effect pipelines, composed with other streams, etc.
|
|
52
|
+
*/
|
|
53
|
+
get stream() {
|
|
54
|
+
return this._stream;
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
//#endregion
|
|
58
|
+
export { SignalEmitter };
|
|
59
|
+
|
|
60
|
+
//# sourceMappingURL=emitter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"emitter.js","names":[],"sources":["../src/emitter.ts"],"sourcesContent":["import { Stream, Effect, Queue } from \"effect\";\n\n/**\n * A bridge that converts Svelte reactive state into Effect streams.\n * Use emit() to push values from Svelte's $effect, and consume via the stream property.\n *\n * @example\n * ```svelte\n * <script lang=\"ts\">\n * import { SignalEmitter } from 'effect-svelte';\n *\n * let count = $state(0);\n * const emitter = new SignalEmitter<number>();\n *\n * $effect(() => {\n * emitter.emit(count);\n * });\n *\n * // Now emitter.stream can be used in Effect pipelines\n * // Example: Process the stream with Effect\n * const processed = emitter.stream.pipe(\n * Stream.map(n => n * 2)\n * );\n * </script>\n * ```\n */\nexport class SignalEmitter<A> {\n private _stream: Stream.Stream<A, never, never>;\n private _emit: (value: A) => void = () => {};\n\n constructor() {\n // Use Stream.callback to create a stream with a push-based API\n this._stream = Stream.callback<A>((queue) => {\n let isDone = false;\n\n // Set up the emit function to immediately emit values\n this._emit = (value: A) => {\n if (!isDone) {\n Queue.offerUnsafe(queue, value);\n }\n };\n\n // Return cleanup function\n return Effect.sync(() => {\n isDone = true;\n this._emit = () => {};\n });\n });\n }\n\n /**\n * Emit a value into the stream.\n * This should typically be called from within a $effect to track reactive dependencies.\n */\n emit(value: A): void {\n this._emit(value);\n }\n\n /**\n * The Effect stream that emits values pushed via emit().\n * Can be used in Effect pipelines, composed with other streams, etc.\n */\n get stream(): Stream.Stream<A, never, never> {\n return this._stream;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AA0BA,IAAa,gBAAb,MAA8B;CAC5B;CACA,cAA0C,CAAC;CAE3C,cAAc;EAEZ,KAAK,UAAU,OAAO,UAAa,UAAU;GAC3C,IAAI,SAAS;GAGb,KAAK,SAAS,UAAa;IACzB,IAAI,CAAC,QACH,MAAM,YAAY,OAAO,KAAK;GAElC;GAGA,OAAO,OAAO,WAAW;IACvB,SAAS;IACT,KAAK,cAAc,CAAC;GACtB,CAAC;EACH,CAAC;CACH;;;;;CAMA,KAAK,OAAgB;EACnB,KAAK,MAAM,KAAK;CAClB;;;;;CAMA,IAAI,SAAyC;EAC3C,OAAO,KAAK;CACd;AACF"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { RuntimeLike } from "./internal/run.js";
|
|
2
|
+
import { getRuntimeContext, provideRuntime } from "./context.svelte.js";
|
|
3
|
+
import { SvelteRuntime, defaultSvelteRuntime } from "./runtime.js";
|
|
4
|
+
import { useScope, useScopeCallback } from "./scope.svelte.js";
|
|
5
|
+
import { UseEffectOptions, UseEffectReturn, useEffect } from "./effect.svelte.js";
|
|
6
|
+
import { UseStreamReturn, useStream } from "./stream.svelte.js";
|
|
7
|
+
import { DerivedReturn, useDerived } from "./derived.svelte.js";
|
|
8
|
+
import { UseQueryReturn, useQuery } from "./query.svelte.js";
|
|
9
|
+
import { UsePubSubReturn, UseSubscriptionRefReturn, usePubSub, useSubscriptionRef } from "./subscription.svelte.js";
|
|
10
|
+
import { ReactiveMutationReturn, ReactiveQueryReturn, ReactiveStreamReturn, reactiveMutation, reactiveQuery, reactiveStream, useInvalidateKeys } from "./reactivity.svelte.js";
|
|
11
|
+
import { SignalEmitter } from "./emitter.js";
|
|
12
|
+
import { Store_d_exports } from "./Store.js";
|
|
13
|
+
import * as AsyncResult from "effect/unstable/reactivity/AsyncResult";
|
|
14
|
+
export { AsyncResult, type DerivedReturn, type ReactiveMutationReturn, type ReactiveQueryReturn, type ReactiveStreamReturn, type RuntimeLike, SignalEmitter, Store_d_exports as Store, SvelteRuntime, type UseEffectOptions, type UseEffectReturn, type UsePubSubReturn, type UseQueryReturn, type UseStreamReturn, type UseSubscriptionRefReturn, defaultSvelteRuntime, getRuntimeContext as getRuntime, provideRuntime, reactiveMutation, reactiveQuery, reactiveStream, useDerived, useEffect, useInvalidateKeys, usePubSub, useQuery, useScope, useScopeCallback, useStream, useSubscriptionRef };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { SvelteRuntime, defaultSvelteRuntime } from "./runtime.js";
|
|
2
|
+
import { getRuntimeContext, provideRuntime } from "./context.svelte.js";
|
|
3
|
+
import { useScope, useScopeCallback } from "./scope.svelte.js";
|
|
4
|
+
import { useEffect } from "./effect.svelte.js";
|
|
5
|
+
import { useStream } from "./stream.svelte.js";
|
|
6
|
+
import { useDerived } from "./derived.svelte.js";
|
|
7
|
+
import { useQuery } from "./query.svelte.js";
|
|
8
|
+
import { usePubSub, useSubscriptionRef } from "./subscription.svelte.js";
|
|
9
|
+
import { reactiveMutation, reactiveQuery, reactiveStream, useInvalidateKeys } from "./reactivity.svelte.js";
|
|
10
|
+
import { SignalEmitter } from "./emitter.js";
|
|
11
|
+
import { Store_exports } from "./Store.js";
|
|
12
|
+
import * as AsyncResult from "effect/unstable/reactivity/AsyncResult";
|
|
13
|
+
export { AsyncResult, SignalEmitter, Store_exports as Store, SvelteRuntime, defaultSvelteRuntime, getRuntimeContext as getRuntime, provideRuntime, reactiveMutation, reactiveQuery, reactiveStream, useDerived, useEffect, useInvalidateKeys, usePubSub, useQuery, useScope, useScopeCallback, useStream, useSubscriptionRef };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Cause, Exit, Option } from "effect";
|
|
2
|
+
import * as AsyncResult from "effect/unstable/reactivity/AsyncResult";
|
|
3
|
+
//#region src/internal/result.svelte.ts
|
|
4
|
+
/**
|
|
5
|
+
* Creates a {@link ResultState}. Must be called during component
|
|
6
|
+
* initialisation (it allocates `$state`).
|
|
7
|
+
*/
|
|
8
|
+
function makeResult() {
|
|
9
|
+
let current = $state(AsyncResult.initial());
|
|
10
|
+
return {
|
|
11
|
+
get current() {
|
|
12
|
+
return current;
|
|
13
|
+
},
|
|
14
|
+
startWaiting() {
|
|
15
|
+
current = AsyncResult.waiting(current);
|
|
16
|
+
},
|
|
17
|
+
reset() {
|
|
18
|
+
current = AsyncResult.initial();
|
|
19
|
+
},
|
|
20
|
+
settle(exit) {
|
|
21
|
+
if (Exit.isFailure(exit) && Cause.hasInterrupts(exit.cause)) return;
|
|
22
|
+
current = AsyncResult.fromExitWithPrevious(exit, Option.some(current));
|
|
23
|
+
},
|
|
24
|
+
succeed(value) {
|
|
25
|
+
current = AsyncResult.success(value);
|
|
26
|
+
},
|
|
27
|
+
failCause(cause) {
|
|
28
|
+
if (Cause.hasInterrupts(cause)) return;
|
|
29
|
+
current = AsyncResult.failureWithPrevious(cause, { previous: Option.some(current) });
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
//#endregion
|
|
34
|
+
export { makeResult };
|
|
35
|
+
|
|
36
|
+
//# sourceMappingURL=result.svelte.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"result.svelte.js","names":[],"sources":["../../src/internal/result.svelte.ts"],"sourcesContent":["import { Cause, Exit, Option } from \"effect\";\nimport * as AsyncResult from \"effect/unstable/reactivity/AsyncResult\";\n\n/**\n * A reactive holder for an {@link AsyncResult.AsyncResult}, used by the hooks\n * that drive a result from a fiber's lifecycle (`useEffect`, `useDerived`,\n * `reactiveQuery`, ...).\n *\n * The value lives in a `$state` field, so reassigning it through the methods\n * below re-runs any effect or template that read `current`. The transitions\n * follow Effect's native stale-while-revalidate semantics: marking a result as\n * waiting preserves the previous value, and settling a failure carries the last\n * success forward so the UI never has to flash back to a spinner.\n */\nexport interface ResultState<A, E> {\n /** The current result. Read this inside an effect or template to track it. */\n readonly current: AsyncResult.AsyncResult<A, E>;\n\n /**\n * Mark the current result as waiting, preserving any value it already holds\n * (an `Initial` becomes a waiting `Initial`, a `Success` keeps its value).\n */\n startWaiting(): void;\n\n /** Reset back to the non-waiting `Initial` state. */\n reset(): void;\n\n /**\n * Settle from a fiber `Exit`. Interruptions are ignored (a new run is\n * presumably starting); successes and failures carry the previous value\n * forward.\n */\n settle(exit: Exit.Exit<A, E>): void;\n\n /** Replace the result with a non-waiting `Success`. */\n succeed(value: A): void;\n\n /**\n * Replace the result with a `Failure` (carrying the previous success\n * forward). Interrupt-only causes are ignored.\n */\n failCause(cause: Cause.Cause<E>): void;\n}\n\n/**\n * Creates a {@link ResultState}. Must be called during component\n * initialisation (it allocates `$state`).\n */\nexport function makeResult<A, E>(): ResultState<A, E> {\n let current = $state<AsyncResult.AsyncResult<A, E>>(AsyncResult.initial<A, E>());\n\n return {\n get current() {\n return current;\n },\n startWaiting() {\n current = AsyncResult.waiting(current);\n },\n reset() {\n current = AsyncResult.initial<A, E>();\n },\n settle(exit) {\n if (Exit.isFailure(exit) && Cause.hasInterrupts(exit.cause)) return;\n current = AsyncResult.fromExitWithPrevious(exit, Option.some(current));\n },\n succeed(value) {\n current = AsyncResult.success<A, E>(value);\n },\n failCause(cause) {\n if (Cause.hasInterrupts(cause)) return;\n current = AsyncResult.failureWithPrevious<A, E>(cause, { previous: Option.some(current) });\n },\n };\n}\n"],"mappings":";;;;;;;AAgDA,SAAgB,aAAsC;CACpD,IAAI,UAAU,OAAsC,YAAY,QAAc,CAAC;CAE/E,OAAO;EACL,IAAI,UAAU;GACZ,OAAO;EACT;EACA,eAAe;GACb,UAAU,YAAY,QAAQ,OAAO;EACvC;EACA,QAAQ;GACN,UAAU,YAAY,QAAc;EACtC;EACA,OAAO,MAAM;GACX,IAAI,KAAK,UAAU,IAAI,KAAK,MAAM,cAAc,KAAK,KAAK,GAAG;GAC7D,UAAU,YAAY,qBAAqB,MAAM,OAAO,KAAK,OAAO,CAAC;EACvE;EACA,QAAQ,OAAO;GACb,UAAU,YAAY,QAAc,KAAK;EAC3C;EACA,UAAU,OAAO;GACf,IAAI,MAAM,cAAc,KAAK,GAAG;GAChC,UAAU,YAAY,oBAA0B,OAAO,EAAE,UAAU,OAAO,KAAK,OAAO,EAAE,CAAC;EAC3F;CACF;AACF"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Context, Effect, Fiber, ManagedRuntime } from "effect";
|
|
2
|
+
|
|
3
|
+
//#region src/internal/run.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* A runtime that the Svelte hooks can run effects against.
|
|
6
|
+
*
|
|
7
|
+
* Both a plain services context (e.g. `Context.empty()` or
|
|
8
|
+
* `defaultSvelteRuntime`) and managed runtimes (e.g.
|
|
9
|
+
* `SvelteRuntime.defaultRuntime` or the result of `SvelteRuntime.extend`)
|
|
10
|
+
* are supported.
|
|
11
|
+
*/
|
|
12
|
+
type RuntimeLike<R> = Context.Context<R> | ManagedRuntime.ManagedRuntime<R, never>;
|
|
13
|
+
//#endregion
|
|
14
|
+
export { RuntimeLike };
|
|
15
|
+
//# sourceMappingURL=run.d.ts.map
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Context, Effect } from "effect";
|
|
2
|
+
//#region src/internal/run.ts
|
|
3
|
+
/**
|
|
4
|
+
* Forks an effect on the given runtime, returning the running fiber.
|
|
5
|
+
* Dispatches to the correct API depending on the kind of runtime provided.
|
|
6
|
+
*/
|
|
7
|
+
const runFork = (runtime) => (effect) => Context.isContext(runtime) ? Effect.runForkWith(runtime)(effect) : runtime.runFork(effect);
|
|
8
|
+
/**
|
|
9
|
+
* Runs an effect synchronously on the given runtime.
|
|
10
|
+
* Dispatches to the correct API depending on the kind of runtime provided.
|
|
11
|
+
*/
|
|
12
|
+
const runSync = (runtime) => (effect) => Context.isContext(runtime) ? Effect.runSyncWith(runtime)(effect) : runtime.runSync(effect);
|
|
13
|
+
/**
|
|
14
|
+
* Interrupts a fiber in the background without waiting for it to finish.
|
|
15
|
+
*
|
|
16
|
+
* Interruption must not be run with `runSync` because interrupting a fiber
|
|
17
|
+
* that is suspended on an async operation cannot complete synchronously.
|
|
18
|
+
*/
|
|
19
|
+
const interruptFiber = (fiber) => {
|
|
20
|
+
fiber.interruptUnsafe();
|
|
21
|
+
};
|
|
22
|
+
//#endregion
|
|
23
|
+
export { interruptFiber, runFork, runSync };
|
|
24
|
+
|
|
25
|
+
//# sourceMappingURL=run.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.js","names":[],"sources":["../../src/internal/run.ts"],"sourcesContent":["import { Context, Effect, Fiber, ManagedRuntime } from \"effect\";\n\n/**\n * A runtime that the Svelte hooks can run effects against.\n *\n * Both a plain services context (e.g. `Context.empty()` or\n * `defaultSvelteRuntime`) and managed runtimes (e.g.\n * `SvelteRuntime.defaultRuntime` or the result of `SvelteRuntime.extend`)\n * are supported.\n */\nexport type RuntimeLike<R> = Context.Context<R> | ManagedRuntime.ManagedRuntime<R, never>;\n\n/**\n * Forks an effect on the given runtime, returning the running fiber.\n * Dispatches to the correct API depending on the kind of runtime provided.\n */\nexport const runFork =\n <R>(runtime: RuntimeLike<R>) =>\n <A, E>(effect: Effect.Effect<A, E, R>): Fiber.Fiber<A, E> =>\n Context.isContext(runtime) ? Effect.runForkWith(runtime)(effect) : runtime.runFork(effect);\n\n/**\n * Runs an effect synchronously on the given runtime.\n * Dispatches to the correct API depending on the kind of runtime provided.\n */\nexport const runSync =\n <R>(runtime: RuntimeLike<R>) =>\n <A, E>(effect: Effect.Effect<A, E, R>): A =>\n Context.isContext(runtime) ? Effect.runSyncWith(runtime)(effect) : runtime.runSync(effect);\n\n/**\n * Interrupts a fiber in the background without waiting for it to finish.\n *\n * Interruption must not be run with `runSync` because interrupting a fiber\n * that is suspended on an async operation cannot complete synchronously.\n */\nexport const interruptFiber = <A, E>(fiber: Fiber.Fiber<A, E>): void => {\n fiber.interruptUnsafe();\n};\n"],"mappings":";;;;;;AAgBA,MAAa,WACP,aACG,WACL,QAAQ,UAAU,OAAO,IAAI,OAAO,YAAY,OAAO,EAAE,MAAM,IAAI,QAAQ,QAAQ,MAAM;;;;;AAM7F,MAAa,WACP,aACG,WACL,QAAQ,UAAU,OAAO,IAAI,OAAO,YAAY,OAAO,EAAE,MAAM,IAAI,QAAQ,QAAQ,MAAM;;;;;;;AAQ7F,MAAa,kBAAwB,UAAmC;CACtE,MAAM,gBAAgB;AACxB"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { interruptFiber, runFork } from "./run.js";
|
|
2
|
+
import { Cause, Effect, Exit, Option, Stream } from "effect";
|
|
3
|
+
import * as AsyncResult from "effect/unstable/reactivity/AsyncResult";
|
|
4
|
+
import { createSubscriber } from "svelte/reactivity";
|
|
5
|
+
//#region src/internal/subscribe.ts
|
|
6
|
+
/**
|
|
7
|
+
* Reactive bridges built on Svelte's [`createSubscriber`](https://svelte.dev/docs/svelte/svelte-reactivity#createSubscriber).
|
|
8
|
+
*
|
|
9
|
+
* `createSubscriber` is the idiomatic Svelte 5 primitive for turning an
|
|
10
|
+
* external, push-based source into something the runes system can track. The
|
|
11
|
+
* `start` callback runs the first time the returned `subscribe` function is
|
|
12
|
+
* read inside an effect (including a template), and its teardown runs once the
|
|
13
|
+
* last reader is destroyed — so a single Effect fiber backs any number of
|
|
14
|
+
* readers, and is interrupted automatically on unmount. No `$effect` or manual
|
|
15
|
+
* `$state` bookkeeping is required.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Subscribes to a `Stream`, accumulating every emitted value. Tracks the
|
|
19
|
+
* stream's lifecycle as an {@link AsyncResult.AsyncResult} (waiting while the
|
|
20
|
+
* stream is live, then settling on completion or failure).
|
|
21
|
+
*/
|
|
22
|
+
function streamState(runtime, makeStream) {
|
|
23
|
+
let values = [];
|
|
24
|
+
let current = AsyncResult.initial();
|
|
25
|
+
const subscribe = createSubscriber((update) => {
|
|
26
|
+
values = [];
|
|
27
|
+
current = AsyncResult.initial(true);
|
|
28
|
+
const program = Stream.runForEach(makeStream(), (value) => Effect.sync(() => {
|
|
29
|
+
values = [...values, value];
|
|
30
|
+
current = AsyncResult.success(values, { waiting: true });
|
|
31
|
+
update();
|
|
32
|
+
})).pipe(Effect.onExit((exit) => Effect.sync(() => {
|
|
33
|
+
if (Exit.isFailure(exit) && Cause.hasInterrupts(exit.cause)) return;
|
|
34
|
+
current = AsyncResult.fromExitWithPrevious(Exit.map(exit, () => values), Option.some(current));
|
|
35
|
+
update();
|
|
36
|
+
})));
|
|
37
|
+
const fiber = runFork(runtime)(program);
|
|
38
|
+
return () => interruptFiber(fiber);
|
|
39
|
+
});
|
|
40
|
+
return {
|
|
41
|
+
get current() {
|
|
42
|
+
subscribe();
|
|
43
|
+
return current;
|
|
44
|
+
},
|
|
45
|
+
get values() {
|
|
46
|
+
subscribe();
|
|
47
|
+
return values;
|
|
48
|
+
},
|
|
49
|
+
get latest() {
|
|
50
|
+
subscribe();
|
|
51
|
+
return values.length > 0 ? values[values.length - 1] : void 0;
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Subscribes to a `Stream` that always emits a current value (e.g. the
|
|
57
|
+
* `changes` of a `SubscriptionRef`), tracking only the latest value.
|
|
58
|
+
*
|
|
59
|
+
* @param initial - the synchronous starting value, used before the subscription
|
|
60
|
+
* activates and during SSR (where effects do not run).
|
|
61
|
+
*/
|
|
62
|
+
function valueState(runtime, initial, makeStream) {
|
|
63
|
+
let value = initial;
|
|
64
|
+
const subscribe = createSubscriber((update) => {
|
|
65
|
+
const fiber = runFork(runtime)(Stream.runForEach(makeStream(), (next) => Effect.sync(() => {
|
|
66
|
+
value = next;
|
|
67
|
+
update();
|
|
68
|
+
})));
|
|
69
|
+
return () => interruptFiber(fiber);
|
|
70
|
+
});
|
|
71
|
+
return { get current() {
|
|
72
|
+
subscribe();
|
|
73
|
+
return value;
|
|
74
|
+
} };
|
|
75
|
+
}
|
|
76
|
+
//#endregion
|
|
77
|
+
export { streamState, valueState };
|
|
78
|
+
|
|
79
|
+
//# sourceMappingURL=subscribe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subscribe.js","names":[],"sources":["../../src/internal/subscribe.ts"],"sourcesContent":["import { Cause, Effect, Exit, Option, Stream } from \"effect\";\nimport * as AsyncResult from \"effect/unstable/reactivity/AsyncResult\";\nimport { createSubscriber } from \"svelte/reactivity\";\nimport { interruptFiber, runFork, type RuntimeLike } from \"./run.js\";\n\n/**\n * Reactive bridges built on Svelte's [`createSubscriber`](https://svelte.dev/docs/svelte/svelte-reactivity#createSubscriber).\n *\n * `createSubscriber` is the idiomatic Svelte 5 primitive for turning an\n * external, push-based source into something the runes system can track. The\n * `start` callback runs the first time the returned `subscribe` function is\n * read inside an effect (including a template), and its teardown runs once the\n * last reader is destroyed — so a single Effect fiber backs any number of\n * readers, and is interrupted automatically on unmount. No `$effect` or manual\n * `$state` bookkeeping is required.\n */\n\n/**\n * Subscribes to a `Stream`, accumulating every emitted value. Tracks the\n * stream's lifecycle as an {@link AsyncResult.AsyncResult} (waiting while the\n * stream is live, then settling on completion or failure).\n */\nexport function streamState<A, E, R>(\n runtime: RuntimeLike<R>,\n makeStream: () => Stream.Stream<A, E, R>,\n): {\n readonly current: AsyncResult.AsyncResult<ReadonlyArray<A>, E>;\n readonly values: ReadonlyArray<A>;\n readonly latest: A | undefined;\n} {\n let values: Array<A> = [];\n let current: AsyncResult.AsyncResult<ReadonlyArray<A>, E> = AsyncResult.initial();\n\n const subscribe = createSubscriber((update) => {\n values = [];\n current = AsyncResult.initial<ReadonlyArray<A>, E>(true);\n\n const program = Stream.runForEach(makeStream(), (value) =>\n Effect.sync(() => {\n values = [...values, value];\n current = AsyncResult.success<ReadonlyArray<A>, E>(values, { waiting: true });\n update();\n }),\n ).pipe(\n Effect.onExit((exit) =>\n Effect.sync(() => {\n // Interruption is normal teardown; keep the last good state.\n if (Exit.isFailure(exit) && Cause.hasInterrupts(exit.cause)) return;\n current = AsyncResult.fromExitWithPrevious(\n Exit.map(exit, () => values as ReadonlyArray<A>),\n Option.some(current),\n );\n update();\n }),\n ),\n );\n\n const fiber = runFork(runtime)(program);\n return () => interruptFiber(fiber);\n });\n\n return {\n get current() {\n subscribe();\n return current;\n },\n get values() {\n subscribe();\n return values;\n },\n get latest() {\n subscribe();\n return values.length > 0 ? values[values.length - 1] : undefined;\n },\n };\n}\n\n/**\n * Subscribes to a `Stream` that always emits a current value (e.g. the\n * `changes` of a `SubscriptionRef`), tracking only the latest value.\n *\n * @param initial - the synchronous starting value, used before the subscription\n * activates and during SSR (where effects do not run).\n */\nexport function valueState<A, E, R>(\n runtime: RuntimeLike<R>,\n initial: A,\n makeStream: () => Stream.Stream<A, E, R>,\n): { readonly current: A } {\n let value = initial;\n\n const subscribe = createSubscriber((update) => {\n const fiber = runFork(runtime)(\n Stream.runForEach(makeStream(), (next) =>\n Effect.sync(() => {\n value = next;\n update();\n }),\n ),\n );\n return () => interruptFiber(fiber);\n });\n\n return {\n get current() {\n subscribe();\n return value;\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAsBA,SAAgB,YACd,SACA,YAKA;CACA,IAAI,SAAmB,CAAC;CACxB,IAAI,UAAwD,YAAY,QAAQ;CAEhF,MAAM,YAAY,kBAAkB,WAAW;EAC7C,SAAS,CAAC;EACV,UAAU,YAAY,QAA6B,IAAI;EAEvD,MAAM,UAAU,OAAO,WAAW,WAAW,IAAI,UAC/C,OAAO,WAAW;GAChB,SAAS,CAAC,GAAG,QAAQ,KAAK;GAC1B,UAAU,YAAY,QAA6B,QAAQ,EAAE,SAAS,KAAK,CAAC;GAC5E,OAAO;EACT,CAAC,CACH,EAAE,KACA,OAAO,QAAQ,SACb,OAAO,WAAW;GAEhB,IAAI,KAAK,UAAU,IAAI,KAAK,MAAM,cAAc,KAAK,KAAK,GAAG;GAC7D,UAAU,YAAY,qBACpB,KAAK,IAAI,YAAY,MAA0B,GAC/C,OAAO,KAAK,OAAO,CACrB;GACA,OAAO;EACT,CAAC,CACH,CACF;EAEA,MAAM,QAAQ,QAAQ,OAAO,EAAE,OAAO;EACtC,aAAa,eAAe,KAAK;CACnC,CAAC;CAED,OAAO;EACL,IAAI,UAAU;GACZ,UAAU;GACV,OAAO;EACT;EACA,IAAI,SAAS;GACX,UAAU;GACV,OAAO;EACT;EACA,IAAI,SAAS;GACX,UAAU;GACV,OAAO,OAAO,SAAS,IAAI,OAAO,OAAO,SAAS,KAAK,KAAA;EACzD;CACF;AACF;;;;;;;;AASA,SAAgB,WACd,SACA,SACA,YACyB;CACzB,IAAI,QAAQ;CAEZ,MAAM,YAAY,kBAAkB,WAAW;EAC7C,MAAM,QAAQ,QAAQ,OAAO,EAC3B,OAAO,WAAW,WAAW,IAAI,SAC/B,OAAO,WAAW;GAChB,QAAQ;GACR,OAAO;EACT,CAAC,CACH,CACF;EACA,aAAa,eAAe,KAAK;CACnC,CAAC;CAED,OAAO,EACL,IAAI,UAAU;EACZ,UAAU;EACV,OAAO;CACT,EACF;AACF"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import * as AsyncResult from "effect/unstable/reactivity/AsyncResult";
|
|
3
|
+
|
|
4
|
+
//#region src/query.svelte.d.ts
|
|
5
|
+
interface UseQueryReturn<A, E> {
|
|
6
|
+
/**
|
|
7
|
+
* The current result state of the query. Refetching preserves the previous
|
|
8
|
+
* value as a waiting `Success` (stale-while-revalidate).
|
|
9
|
+
*/
|
|
10
|
+
readonly current: AsyncResult.AsyncResult<A, E>;
|
|
11
|
+
/**
|
|
12
|
+
* Re-fetch the data by re-running the effect
|
|
13
|
+
*/
|
|
14
|
+
refetch: () => void;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Query pattern with refetch capability.
|
|
18
|
+
* Runs the effect immediately on mount and provides a refetch function.
|
|
19
|
+
* Built on top of useEffect with immediate: true.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```svelte
|
|
23
|
+
* <script lang="ts">
|
|
24
|
+
* import { useQuery, AsyncResult } from '@thomasfosterau/effect-svelte';
|
|
25
|
+
* import { Effect } from 'effect';
|
|
26
|
+
*
|
|
27
|
+
* const query = useQuery(
|
|
28
|
+
* Effect.gen(function* () {
|
|
29
|
+
* const data = yield* fetchData();
|
|
30
|
+
* return data;
|
|
31
|
+
* })
|
|
32
|
+
* );
|
|
33
|
+
* </script>
|
|
34
|
+
*
|
|
35
|
+
* <button onclick={query.refetch}>Refresh</button>
|
|
36
|
+
*
|
|
37
|
+
* {#if AsyncResult.isSuccess(query.current)}
|
|
38
|
+
* <p>Data: {JSON.stringify(query.current.value)}</p>
|
|
39
|
+
* {/if}
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
declare function useQuery<A, E, R>(effect: Effect.Effect<A, E, R>): UseQueryReturn<A, E>;
|
|
43
|
+
//#endregion
|
|
44
|
+
export { UseQueryReturn, useQuery };
|
|
45
|
+
//# sourceMappingURL=query.svelte.d.ts.map
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { useEffect } from "./effect.svelte.js";
|
|
2
|
+
//#region src/query.svelte.ts
|
|
3
|
+
/**
|
|
4
|
+
* Query pattern with refetch capability.
|
|
5
|
+
* Runs the effect immediately on mount and provides a refetch function.
|
|
6
|
+
* Built on top of useEffect with immediate: true.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```svelte
|
|
10
|
+
* <script lang="ts">
|
|
11
|
+
* import { useQuery, AsyncResult } from '@thomasfosterau/effect-svelte';
|
|
12
|
+
* import { Effect } from 'effect';
|
|
13
|
+
*
|
|
14
|
+
* const query = useQuery(
|
|
15
|
+
* Effect.gen(function* () {
|
|
16
|
+
* const data = yield* fetchData();
|
|
17
|
+
* return data;
|
|
18
|
+
* })
|
|
19
|
+
* );
|
|
20
|
+
* <\/script>
|
|
21
|
+
*
|
|
22
|
+
* <button onclick={query.refetch}>Refresh</button>
|
|
23
|
+
*
|
|
24
|
+
* {#if AsyncResult.isSuccess(query.current)}
|
|
25
|
+
* <p>Data: {JSON.stringify(query.current.value)}</p>
|
|
26
|
+
* {/if}
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
function useQuery(effect) {
|
|
30
|
+
const effectHook = useEffect(effect, { immediate: true });
|
|
31
|
+
return {
|
|
32
|
+
get current() {
|
|
33
|
+
return effectHook.current;
|
|
34
|
+
},
|
|
35
|
+
refetch: effectHook.run
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
//#endregion
|
|
39
|
+
export { useQuery };
|
|
40
|
+
|
|
41
|
+
//# sourceMappingURL=query.svelte.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query.svelte.js","names":[],"sources":["../src/query.svelte.ts"],"sourcesContent":["import type { Effect } from \"effect\";\nimport type * as AsyncResult from \"effect/unstable/reactivity/AsyncResult\";\nimport { useEffect } from \"./effect.svelte.js\";\n\nexport interface UseQueryReturn<A, E> {\n /**\n * The current result state of the query. Refetching preserves the previous\n * value as a waiting `Success` (stale-while-revalidate).\n */\n readonly current: AsyncResult.AsyncResult<A, E>;\n\n /**\n * Re-fetch the data by re-running the effect\n */\n refetch: () => void;\n}\n\n/**\n * Query pattern with refetch capability.\n * Runs the effect immediately on mount and provides a refetch function.\n * Built on top of useEffect with immediate: true.\n *\n * @example\n * ```svelte\n * <script lang=\"ts\">\n * import { useQuery, AsyncResult } from '@thomasfosterau/effect-svelte';\n * import { Effect } from 'effect';\n *\n * const query = useQuery(\n * Effect.gen(function* () {\n * const data = yield* fetchData();\n * return data;\n * })\n * );\n * </script>\n *\n * <button onclick={query.refetch}>Refresh</button>\n *\n * {#if AsyncResult.isSuccess(query.current)}\n * <p>Data: {JSON.stringify(query.current.value)}</p>\n * {/if}\n * ```\n */\nexport function useQuery<A, E, R>(effect: Effect.Effect<A, E, R>): UseQueryReturn<A, E> {\n const effectHook = useEffect(effect, { immediate: true });\n\n return {\n get current() {\n return effectHook.current;\n },\n refetch: effectHook.run,\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2CA,SAAgB,SAAkB,QAAsD;CACtF,MAAM,aAAa,UAAU,QAAQ,EAAE,WAAW,KAAK,CAAC;CAExD,OAAO;EACL,IAAI,UAAU;GACZ,OAAO,WAAW;EACpB;EACA,SAAS,WAAW;CACtB;AACF"}
|