@effectify/solid-query 0.0.1 → 0.1.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/README.md ADDED
@@ -0,0 +1,111 @@
1
+ # @effectify/solid-query
2
+
3
+ Integration of [Effect](https://effect.website/) with [TanStack Query](https://tanstack.com/query/latest) for [Solid.js](https://www.solidjs.com/).
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ # npm
9
+ npm install @effectify/solid-query
10
+
11
+ # yarn
12
+ yarn add @effectify/solid-query
13
+
14
+ # pnpm
15
+ pnpm add @effectify/solid-query
16
+
17
+ # bun
18
+ bun add @effectify/solid-query
19
+ ```
20
+
21
+ ## Basic Usage
22
+
23
+ ```tsx
24
+ import * as Layer from 'effect/Layer'
25
+ import * as Effect from 'effect/Effect'
26
+ import { tanstackQueryEffect } from '@effectify/solid-query'
27
+
28
+ // Create an Effect layer
29
+ const AppLayer = Layer.succeed('AppConfig', { apiUrl: 'https://api.example.com' })
30
+
31
+ // Initialize the TanStack Query integration
32
+ const {
33
+ RuntimeProvider,
34
+ useRuntime,
35
+ useEffectQuery,
36
+ useEffectMutation,
37
+ useRxSubscribe,
38
+ useRxSubscriptionRef,
39
+ } = tanstackQueryEffect(AppLayer)
40
+
41
+ // Wrap your application with the provider
42
+ function App() {
43
+ return (
44
+ <RuntimeProvider>
45
+ <YourApp />
46
+ </RuntimeProvider>
47
+ )
48
+ }
49
+
50
+ // Use in components
51
+ function YourComponent() {
52
+ const query = useEffectQuery({
53
+ queryKey: ['data'],
54
+ queryFn: () => Effect.succeed(['item1', 'item2']),
55
+ })
56
+
57
+ return (
58
+ <div>
59
+ {query.isPending ? (
60
+ <p>Loading...</p>
61
+ ) : query.isError ? (
62
+ <p>Error: {query.error.message}</p>
63
+ ) : (
64
+ <ul>
65
+ {query.data.map((item) => (
66
+ <li>{item}</li>
67
+ ))}
68
+ </ul>
69
+ )}
70
+ </div>
71
+ )
72
+ }
73
+ ```
74
+
75
+ ## API
76
+
77
+ ### `tanstackQueryEffect(layer)`
78
+
79
+ Creates an instance of the TanStack Query integration.
80
+
81
+ #### Parameters
82
+
83
+ - `layer`: An Effect layer that provides the necessary dependencies.
84
+
85
+ #### Returns
86
+
87
+ An object with the following properties:
88
+
89
+ - `RuntimeProvider`: Provider component that should wrap your application.
90
+ - `useRuntime`: Hook to access the Effect runtime.
91
+ - `useEffectQuery`: Hook to perform queries with Effect.
92
+ - `useEffectMutation`: Hook to perform mutations with Effect.
93
+ - `useRxSubscribe`: Hook to subscribe to Effect streams.
94
+ - `useRxSubscriptionRef`: Hook to maintain a reference to a subscription.
95
+
96
+ ### Helpers
97
+
98
+ - `createQueryDataHelpers`: Utility to create query data manipulation helpers.
99
+ - `createQueryKey`: Utility to create typed query keys.
100
+
101
+ ## Complete Example
102
+
103
+ Check out the example application in [apps/tanstack-solid-app](../../apps/tanstack-solid-app) to see a complete use case.
104
+
105
+ ## Credits & Inspiration
106
+
107
+ This package was inspired by the excellent educational content from [Lucas Barake](https://www.youtube.com/@lucas-barake), particularly his [video on Effect and TanStack Query](https://www.youtube.com/watch?v=zl4w3BQAoJM&t=1011s) which provides great insights into these technologies.
108
+
109
+ ## License
110
+
111
+ MIT
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@effectify/solid-query",
3
+ "version": "0.0.3",
4
+ "description": "Integration of Effect with TanStack Query for Solid.js",
5
+ "type": "module",
6
+ "main": "./dist/src/index.js",
7
+ "module": "./dist/src/index.js",
8
+ "types": "./dist/src/index.d.ts",
9
+ "publishConfig": {
10
+ "access": "public"
11
+ },
12
+ "exports": {
13
+ "./package.json": "./package.json",
14
+ ".": {
15
+ "types": "./dist/src/index.d.ts",
16
+ "import": "./dist/src/index.js",
17
+ "default": "./dist/src/index.js"
18
+ }
19
+ },
20
+ "files": [
21
+ "dist",
22
+ "!**/*.tsbuildinfo"
23
+ ],
24
+ "dependencies": {
25
+ "tslib": "catalog:",
26
+ "@tanstack/solid-query": "catalog:",
27
+ "@tanstack/query-core": "catalog:",
28
+ "effect": "catalog:",
29
+ "solid-js": "catalog:"
30
+ },
31
+ "devDependencies": {
32
+ "typescript": "catalog:"
33
+ },
34
+ "peerDependencies": {
35
+ "@tanstack/solid-query": "catalog:",
36
+ "solid-js": "catalog:"
37
+ },
38
+ "optionalDependencies": {}
39
+ }
@@ -0,0 +1,3 @@
1
+ export * from './lib/internal/query-data-helpers.js';
2
+ export * from './lib/tanstack-query-effect.jsx';
3
+ export * from './lib/types.js';
@@ -0,0 +1,7 @@
1
+ export * from './lib/internal/query-data-helpers.js';
2
+ export * from './lib/tanstack-query-effect.jsx';
3
+ export * from './lib/types.js';
4
+ // Release test: Real code change to verify release workflow
5
+ // Should release to patch version
6
+ // Should not release to minor version
7
+ // Should not release to major version
@@ -0,0 +1,3 @@
1
+ import { type UseMutationResult } from '@tanstack/solid-query';
2
+ import type { EffectfulError, EffectfulMutationOptions, Runner } from '../types.js';
3
+ export declare const makeUseEffectMutation: <R>(createRunner: Runner<R>) => <TData, TError extends EffectfulError, TVariables>(options: EffectfulMutationOptions<TData, TError, TVariables, R>) => UseMutationResult<TData, Error, TVariables>;
@@ -0,0 +1,13 @@
1
+ import { useMutation } from '@tanstack/solid-query';
2
+ export const makeUseEffectMutation = (createRunner) => (options) => {
3
+ const effectRunner = createRunner();
4
+ const [spanName] = options.mutationKey;
5
+ const mutationFn = () => (variables) => {
6
+ const effect = options.mutationFn(variables);
7
+ return effectRunner()(spanName)(effect);
8
+ };
9
+ return useMutation(() => ({
10
+ ...options,
11
+ mutationFn: mutationFn(),
12
+ }));
13
+ };
@@ -0,0 +1,3 @@
1
+ import { type UseQueryResult } from '@tanstack/solid-query';
2
+ import type { EffectfulError, EffectfulQueryOptions, QueryKey, Runner } from '../types.js';
3
+ export declare const makeUseEffectQuery: <R>(createRunner: Runner<R>) => <TData, TError extends EffectfulError, TQueryKey extends QueryKey = QueryKey>({ gcTime, staleTime, ...options }: EffectfulQueryOptions<TData, TError, R, TQueryKey>) => UseQueryResult<TData, Error>;
@@ -0,0 +1,21 @@
1
+ import { skipToken } from '@tanstack/query-core';
2
+ import { useQuery } from '@tanstack/solid-query';
3
+ import * as Duration from 'effect/Duration';
4
+ import { createMemo } from 'solid-js';
5
+ export const makeUseEffectQuery = (createRunner) => ({ gcTime, staleTime, ...options }) => {
6
+ const effectRunner = createRunner();
7
+ const [spanName] = options.queryKey;
8
+ const queryFn = createMemo(() => (context) => {
9
+ const effect = options.queryFn(context);
10
+ return effect.pipe(effectRunner()(spanName));
11
+ })();
12
+ return useQuery(() => ({
13
+ ...options,
14
+ queryKey: options.queryKey, // Aseguramos que queryKey es no-undefined
15
+ queryFn: options.queryFn === skipToken ? skipToken : queryFn,
16
+ ...(staleTime !== undefined && {
17
+ staleTime: Duration.toMillis(staleTime),
18
+ }),
19
+ ...(gcTime !== undefined && { gcTime: Duration.toMillis(gcTime) }),
20
+ }));
21
+ };
@@ -0,0 +1,6 @@
1
+ import * as Effect from 'effect/Effect';
2
+ import type * as ManagedRuntime from 'effect/ManagedRuntime';
3
+ import * as SubscriptionRef from 'effect/SubscriptionRef';
4
+ import { type Context } from 'solid-js';
5
+ import type { Subscribable, SubscriptionOptions } from '../types.js';
6
+ export declare const makeUseRxSubscriptionRef: <R, E>(RuntimeContext: Context<ManagedRuntime.ManagedRuntime<R, E> | null>) => <A, E2>(subscribable: Subscribable<A, E2> | Effect.Effect<Subscribable<A, E2>, never, R> | Effect.Effect<SubscriptionRef.SubscriptionRef<A>, never, R>, onNext: (value: A) => void, opts?: SubscriptionOptions) => A;
@@ -0,0 +1,53 @@
1
+ import * as Effect from 'effect/Effect';
2
+ import * as Fiber from 'effect/Fiber';
3
+ import * as Stream from 'effect/Stream';
4
+ import * as SubscriptionRef from 'effect/SubscriptionRef';
5
+ import { createEffect, createSignal, onCleanup, useContext } from 'solid-js';
6
+ export const makeUseRxSubscriptionRef = (RuntimeContext) => (subscribable, onNext, opts) => {
7
+ const options = {
8
+ skipInitial: opts?.skipInitial ?? true,
9
+ };
10
+ const runtime = useContext(RuntimeContext);
11
+ if (!runtime) {
12
+ throw new Error('Runtime context not found. Make sure to wrap your app with RuntimeProvider');
13
+ }
14
+ const setInitialValue = () => {
15
+ const initialValue = Effect.gen(function* () {
16
+ const resolved = Effect.isEffect(subscribable) ? yield* subscribable : subscribable;
17
+ const resolvedValue = SubscriptionRef.SubscriptionRefTypeId in resolved ? yield* SubscriptionRef.get(resolved) : resolved.get();
18
+ if (!options?.skipInitial) {
19
+ onNext(resolvedValue);
20
+ }
21
+ return resolvedValue;
22
+ });
23
+ const newVal = runtime.runSync(initialValue);
24
+ return newVal;
25
+ };
26
+ const [value, setValue] = createSignal(setInitialValue());
27
+ createEffect(() => {
28
+ const fiber = Effect.gen(function* () {
29
+ const resolved = Effect.isEffect(subscribable) ? yield* subscribable : subscribable;
30
+ const adaptedSubscribable = SubscriptionRef.SubscriptionRefTypeId in resolved
31
+ ? {
32
+ changes: resolved.changes,
33
+ get: () => runtime.runSync(SubscriptionRef.get(resolved)),
34
+ }
35
+ : resolved;
36
+ const currentValue = adaptedSubscribable.get();
37
+ setValue(() => currentValue);
38
+ let hasEmittedInitial = false;
39
+ return yield* adaptedSubscribable.changes.pipe(Stream.tap((val) => Effect.sync(() => {
40
+ setValue(() => val);
41
+ if (options?.skipInitial && !hasEmittedInitial) {
42
+ hasEmittedInitial = true;
43
+ return;
44
+ }
45
+ onNext(val);
46
+ })), Stream.runDrain, Effect.forever, Effect.forkDaemon);
47
+ }).pipe(runtime.runSync);
48
+ onCleanup(() => {
49
+ runtime.runCallback(Fiber.interrupt(fiber));
50
+ });
51
+ });
52
+ return value();
53
+ };
@@ -0,0 +1,5 @@
1
+ import * as Effect from 'effect/Effect';
2
+ import type * as ManagedRuntime from 'effect/ManagedRuntime';
3
+ import * as Stream from 'effect/Stream';
4
+ import { type Context } from 'solid-js';
5
+ export declare const makeUseRxSubscribe: <R, E>(RuntimeContext: Context<ManagedRuntime.ManagedRuntime<R, E> | null>) => <E2, A>(stream: Stream.Stream<A, E2, R> | Effect.Effect<Stream.Stream<A, E2, R>, E2, R>, initialValue: A, onNext: (value: A) => void, onError?: (error: E2) => void) => import("solid-js").Accessor<A | undefined>;
@@ -0,0 +1,37 @@
1
+ import * as Effect from 'effect/Effect';
2
+ import * as Exit from 'effect/Exit';
3
+ import * as Fiber from 'effect/Fiber';
4
+ import * as Stream from 'effect/Stream';
5
+ import { createSignal, onCleanup, useContext } from 'solid-js';
6
+ export const makeUseRxSubscribe = (RuntimeContext) => {
7
+ return (stream, initialValue, onNext, onError) => {
8
+ const runtime = useContext(RuntimeContext);
9
+ if (!runtime) {
10
+ throw new Error('Runtime context not found. Make sure to wrap your app with RuntimeProvider');
11
+ }
12
+ const [value, setValue] = createSignal(initialValue);
13
+ const [fiberRef, setFiberRef] = createSignal(null);
14
+ const finalStream = Effect.isEffect(stream) ? Stream.unwrap(stream) : stream;
15
+ const subscription = finalStream.pipe(Stream.tap((a) => Effect.sync(() => {
16
+ setValue(() => a);
17
+ onNext(a);
18
+ })), Stream.catchAll((e) => Stream.fromEffect(Effect.sync(() => {
19
+ onError?.(e);
20
+ return;
21
+ }))), Stream.runDrain, Effect.forever, Effect.forkDaemon);
22
+ runtime.runCallback(subscription, {
23
+ onExit: (exit) => {
24
+ if (Exit.isSuccess(exit)) {
25
+ setFiberRef(exit.value);
26
+ }
27
+ },
28
+ });
29
+ onCleanup(() => {
30
+ if (fiberRef() !== null) {
31
+ // biome-ignore lint/style/noNonNullAssertion: <testing>
32
+ runtime.runCallback(Fiber.interrupt(fiberRef()));
33
+ }
34
+ });
35
+ return value;
36
+ };
37
+ };
@@ -0,0 +1,75 @@
1
+ import type { QueryClient } from '@tanstack/query-core';
2
+ type DeepMutable<T> = {
3
+ -readonly [P in keyof T]: T[P] extends object ? DeepMutable<T[P]> : T[P];
4
+ };
5
+ export type QueryDataUpdater<TData> = (draft: DeepMutable<TData>) => void;
6
+ type QueryKey<TKey extends string, TVariables = void> = TVariables extends void ? readonly [TKey] : readonly [TKey, TVariables];
7
+ /**
8
+ * Creates a type-safe query key factory that can be used with or without variables
9
+ * @template TKey The string literal type for the query key
10
+ * @template TVariables Optional variables type. If not provided, the factory will not accept variables
11
+ * @param key The query key string
12
+ * @returns A function that creates a query key tuple
13
+ *
14
+ * @example Without variables:
15
+ * ```typescript
16
+ * const userKey = createQueryKey("user");
17
+ * const key = userKey(); // returns ["user"]
18
+ * ```
19
+ *
20
+ * @example With variables:
21
+ * ```typescript
22
+ * type UserVars = { id: string };
23
+ * const userKey = createQueryKey<"user", UserVars>("user");
24
+ * const key = userKey({ id: "123" }); // returns ["user", { id: "123" }]
25
+ * ```
26
+ */
27
+ export declare function createQueryKey<TKey extends string, TVariables = void>(key: TKey): TVariables extends void ? () => QueryKey<TKey> : (variables: TVariables) => QueryKey<TKey, TVariables>;
28
+ type QueryDataHelpers<TData, TVariables> = {
29
+ removeQuery: (variables: TVariables) => void;
30
+ removeAllQueries: () => void;
31
+ setData: (variables: TVariables, updater: QueryDataUpdater<TData>) => TData | undefined;
32
+ invalidateQuery: (variables: TVariables) => Promise<void>;
33
+ invalidateAllQueries: () => Promise<void>;
34
+ refetchQuery: (variables: TVariables) => Promise<void>;
35
+ refetchAllQueries: () => Promise<void>;
36
+ };
37
+ /**
38
+ * Creates a set of helpers to manage query data in the cache
39
+ * @template TData The type of data stored in the query
40
+ * @template TVariables Automatically inferred from the queryKey function parameter
41
+ * @param queryKey A function that creates a query key tuple from variables
42
+ * @returns An object with methods to remove, update, invalidate, and invalidate all query data
43
+ *
44
+ * @example Without variables:
45
+ * ```typescript
46
+ * const userKey = createQueryKey("user");
47
+ * type User = { name: string };
48
+ *
49
+ * // Types are inferred from userKey
50
+ * const helpers = createQueryDataHelpers<User>(userKey);
51
+ * helpers.setData(undefined, (draft) => {
52
+ * draft.name = "New Name";
53
+ * });
54
+ * ```
55
+ *
56
+ * @example With variables and explicit types:
57
+ * ```typescript
58
+ * type UserVars = { id: string };
59
+ * type User = { id: string; name: string };
60
+ *
61
+ * const userKey = createQueryKey<"user", UserVars>("user");
62
+ * const helpers = createQueryDataHelpers<User, UserVars>(userKey);
63
+ *
64
+ * helpers.setData({ id: "123" }, (draft) => {
65
+ * draft.name = "New Name";
66
+ * });
67
+ *
68
+ * // Other helper methods
69
+ * await helpers.invalidateQuery({ id: "123" });
70
+ * await helpers.refetchQuery({ id: "123" });
71
+ * helpers.removeQuery({ id: "123" });
72
+ * ```
73
+ */
74
+ export declare const makeCreateQueryDataHelpers: (queryClient: QueryClient) => <TData, TVariables = void>(queryKey: (variables: TVariables) => readonly [string, ...unknown[]]) => QueryDataHelpers<TData, TVariables>;
75
+ export {};
@@ -0,0 +1,107 @@
1
+ // Simple deep clone function to replace mutative
2
+ function deepClone(obj) {
3
+ if (obj === null || typeof obj !== 'object') {
4
+ return obj;
5
+ }
6
+ if (obj instanceof Date) {
7
+ return new Date(obj.getTime());
8
+ }
9
+ if (Array.isArray(obj)) {
10
+ return obj.map((item) => deepClone(item));
11
+ }
12
+ if (typeof obj === 'object') {
13
+ const cloned = {};
14
+ for (const key in obj) {
15
+ if (Object.hasOwn(obj, key)) {
16
+ cloned[key] = deepClone(obj[key]);
17
+ }
18
+ }
19
+ return cloned;
20
+ }
21
+ return obj;
22
+ }
23
+ /**
24
+ * Creates a type-safe query key factory that can be used with or without variables
25
+ * @template TKey The string literal type for the query key
26
+ * @template TVariables Optional variables type. If not provided, the factory will not accept variables
27
+ * @param key The query key string
28
+ * @returns A function that creates a query key tuple
29
+ *
30
+ * @example Without variables:
31
+ * ```typescript
32
+ * const userKey = createQueryKey("user");
33
+ * const key = userKey(); // returns ["user"]
34
+ * ```
35
+ *
36
+ * @example With variables:
37
+ * ```typescript
38
+ * type UserVars = { id: string };
39
+ * const userKey = createQueryKey<"user", UserVars>("user");
40
+ * const key = userKey({ id: "123" }); // returns ["user", { id: "123" }]
41
+ * ```
42
+ */
43
+ export function createQueryKey(key) {
44
+ return ((variables) => variables === undefined ? [key] : [key, variables]);
45
+ }
46
+ /**
47
+ * Creates a set of helpers to manage query data in the cache
48
+ * @template TData The type of data stored in the query
49
+ * @template TVariables Automatically inferred from the queryKey function parameter
50
+ * @param queryKey A function that creates a query key tuple from variables
51
+ * @returns An object with methods to remove, update, invalidate, and invalidate all query data
52
+ *
53
+ * @example Without variables:
54
+ * ```typescript
55
+ * const userKey = createQueryKey("user");
56
+ * type User = { name: string };
57
+ *
58
+ * // Types are inferred from userKey
59
+ * const helpers = createQueryDataHelpers<User>(userKey);
60
+ * helpers.setData(undefined, (draft) => {
61
+ * draft.name = "New Name";
62
+ * });
63
+ * ```
64
+ *
65
+ * @example With variables and explicit types:
66
+ * ```typescript
67
+ * type UserVars = { id: string };
68
+ * type User = { id: string; name: string };
69
+ *
70
+ * const userKey = createQueryKey<"user", UserVars>("user");
71
+ * const helpers = createQueryDataHelpers<User, UserVars>(userKey);
72
+ *
73
+ * helpers.setData({ id: "123" }, (draft) => {
74
+ * draft.name = "New Name";
75
+ * });
76
+ *
77
+ * // Other helper methods
78
+ * await helpers.invalidateQuery({ id: "123" });
79
+ * await helpers.refetchQuery({ id: "123" });
80
+ * helpers.removeQuery({ id: "123" });
81
+ * ```
82
+ */
83
+ export const makeCreateQueryDataHelpers = (queryClient) => (queryKey) => {
84
+ const [namespaceKey] = queryKey(undefined);
85
+ return {
86
+ removeQuery: (variables) => {
87
+ queryClient.removeQueries({ queryKey: queryKey(variables) });
88
+ },
89
+ removeAllQueries: () => {
90
+ queryClient.removeQueries({ queryKey: [namespaceKey], exact: false });
91
+ },
92
+ setData: (variables, updater) => {
93
+ return queryClient.setQueryData(queryKey(variables), (oldData) => {
94
+ if (oldData === undefined) {
95
+ return oldData;
96
+ }
97
+ const clonedData = deepClone(oldData);
98
+ updater(clonedData);
99
+ return clonedData;
100
+ });
101
+ },
102
+ invalidateQuery: (variables) => queryClient.invalidateQueries({ queryKey: queryKey(variables) }),
103
+ invalidateAllQueries: () => queryClient.invalidateQueries({ queryKey: [namespaceKey], exact: false }),
104
+ refetchQuery: (variables) => queryClient.refetchQueries({ queryKey: queryKey(variables) }),
105
+ refetchAllQueries: () => queryClient.refetchQueries({ queryKey: [namespaceKey], exact: false }),
106
+ };
107
+ };
@@ -0,0 +1,28 @@
1
+ import { type QueryClient } from '@tanstack/solid-query';
2
+ import * as Effect from 'effect/Effect';
3
+ import type * as Layer from 'effect/Layer';
4
+ import * as ManagedRuntime from 'effect/ManagedRuntime';
5
+ import { type Component, type JSX } from 'solid-js';
6
+ export declare const tanstackQueryEffect: <R, E>({ layer, queryClient, }: {
7
+ layer: Layer.Layer<R, E, never>;
8
+ queryClient: QueryClient;
9
+ }) => {
10
+ useRunner: () => import("solid-js").Accessor<(<A, E2>(span: string) => (effect: Effect.Effect<A, E2, R>) => Promise<A>)>;
11
+ RuntimeProvider: Component<{
12
+ children: JSX.Element;
13
+ }>;
14
+ useRuntime: () => ManagedRuntime.ManagedRuntime<R, E>;
15
+ useEffectQuery: <TData, TError extends import("./types.js").EffectfulError, TQueryKey extends import("./types.js").QueryKey = import("./types.js").QueryKey>({ gcTime, staleTime, ...options }: import("./types.js").EffectfulQueryOptions<TData, TError, R, TQueryKey>) => import("@tanstack/solid-query").UseQueryResult<TData, Error>;
16
+ useEffectMutation: <TData, TError_1 extends import("./types.js").EffectfulError, TVariables>(options: import("./types.js").EffectfulMutationOptions<TData, TError_1, TVariables, R>) => import("@tanstack/solid-query").UseMutationResult<TData, Error, TVariables>;
17
+ useRxSubscribe: <E2_1, A>(stream: import("effect/Stream").Stream<A, E2_1, R> | Effect.Effect<import("effect/Stream").Stream<A, E2_1, R>, E2_1, R>, initialValue: A, onNext: (value: A) => void, onError?: ((error: E2_1) => void) | undefined) => import("solid-js").Accessor<A | undefined>;
18
+ useRxSubscriptionRef: <A_1, E2_1>(subscribable: import("./types.js").Subscribable<A_1, E2_1> | Effect.Effect<import("./types.js").Subscribable<A_1, E2_1>, never, R> | Effect.Effect<import("effect/SubscriptionRef").SubscriptionRef<A_1>, never, R>, onNext: (value: A_1) => void, opts?: import("./types.js").SubscriptionOptions) => A_1;
19
+ createQueryDataHelpers: <TData, TVariables_1 = void>(queryKey: (variables: TVariables_1) => readonly [string, ...unknown[]]) => {
20
+ removeQuery: (variables: TVariables_1) => void;
21
+ removeAllQueries: () => void;
22
+ setData: (variables: TVariables_1, updater: import("./internal/query-data-helpers.js").QueryDataUpdater<TData>) => TData | undefined;
23
+ invalidateQuery: (variables: TVariables_1) => Promise<void>;
24
+ invalidateAllQueries: () => Promise<void>;
25
+ refetchQuery: (variables: TVariables_1) => Promise<void>;
26
+ refetchAllQueries: () => Promise<void>;
27
+ };
28
+ };
@@ -0,0 +1,45 @@
1
+ import { QueryClientProvider } from '@tanstack/solid-query';
2
+ import * as Effect from 'effect/Effect';
3
+ import * as ManagedRuntime from 'effect/ManagedRuntime';
4
+ import { createContext, createMemo, onCleanup, useContext } from 'solid-js';
5
+ import { makeUseEffectMutation } from './internal/make-use-effect-mutation.js';
6
+ import { makeUseEffectQuery } from './internal/make-use-effect-query.js';
7
+ import { makeUseRxSubscriptionRef } from './internal/make-use-rx-subsciption-ref.js';
8
+ import { makeUseRxSubscribe } from './internal/make-use-rx-subscribe.js';
9
+ import { makeCreateQueryDataHelpers } from './internal/query-data-helpers.js';
10
+ export const tanstackQueryEffect = ({ layer, queryClient, }) => {
11
+ const RuntimeContext = createContext(null);
12
+ const useRunner = () => {
13
+ const runtime = useContext(RuntimeContext);
14
+ if (!runtime) {
15
+ throw new Error('Runtime context not found. Make sure to wrap your app with RuntimeProvider');
16
+ }
17
+ return createMemo(() => (span) => (effect) => effect.pipe(Effect.withSpan(span), Effect.tapErrorCause(Effect.logError), runtime.runPromise));
18
+ };
19
+ const RuntimeProvider = (props) => {
20
+ const runtime = ManagedRuntime.make(layer);
21
+ onCleanup(() => {
22
+ runtime.dispose();
23
+ });
24
+ return (<RuntimeContext.Provider value={runtime}>
25
+ <QueryClientProvider client={queryClient}>{props.children}</QueryClientProvider>
26
+ </RuntimeContext.Provider>);
27
+ };
28
+ const useRuntime = () => {
29
+ const runtime = useContext(RuntimeContext);
30
+ if (!runtime) {
31
+ throw new Error('Runtime context not found. Make sure to wrap your app with RuntimeProvider');
32
+ }
33
+ return runtime;
34
+ };
35
+ return {
36
+ useRunner,
37
+ RuntimeProvider,
38
+ useRuntime,
39
+ useEffectQuery: makeUseEffectQuery(useRunner),
40
+ useEffectMutation: makeUseEffectMutation(useRunner),
41
+ useRxSubscribe: makeUseRxSubscribe(RuntimeContext),
42
+ useRxSubscriptionRef: makeUseRxSubscriptionRef(RuntimeContext),
43
+ createQueryDataHelpers: makeCreateQueryDataHelpers(queryClient),
44
+ };
45
+ };
@@ -0,0 +1,31 @@
1
+ import type { QueryFunctionContext, skipToken } from '@tanstack/query-core';
2
+ import type { UseMutationOptions, UseQueryOptions } from '@tanstack/solid-query';
3
+ import type { DurationInput } from 'effect/Duration';
4
+ import type * as Effect from 'effect/Effect';
5
+ import type * as Stream from 'effect/Stream';
6
+ import type { Accessor } from 'solid-js';
7
+ export type QueryKey = readonly [string, Record<string, unknown>?];
8
+ export type EffectfulError = {
9
+ _tag: string;
10
+ };
11
+ export type Runner<R> = () => Accessor<(<A, E>(span: string) => (effect: Effect.Effect<A, E, R>) => Promise<A>)>;
12
+ export type EffectfulMutationOptions<TData, TError extends EffectfulError, TVariables, R> = Omit<UseMutationOptions<TData, Error, TVariables>, // Actualizado a UseMutationOptions
13
+ // Actualizado a UseMutationOptions
14
+ 'mutationFn' | 'onSuccess' | 'onError' | 'onSettled' | 'onMutate' | 'retry' | 'retryDelay'> & {
15
+ mutationKey: QueryKey;
16
+ mutationFn: (variables: TVariables) => Effect.Effect<TData, TError, R>;
17
+ };
18
+ export type EffectfulQueryFunction<TData, TError, TQueryKey extends QueryKey = QueryKey, R = never, TPageParam = never> = (context: QueryFunctionContext<TQueryKey, TPageParam>) => Effect.Effect<TData, TError, R>;
19
+ export type EffectfulQueryOptions<TData, TError, R, TQueryKey extends QueryKey = QueryKey, TPageParam = never> = Omit<UseQueryOptions<TData, Error, TData, TQueryKey>, 'queryKey' | 'queryFn' | 'retry' | 'retryDelay' | 'staleTime' | 'gcTime'> & {
20
+ queryKey: TQueryKey;
21
+ queryFn: EffectfulQueryFunction<TData, TError, TQueryKey, R, TPageParam> | typeof skipToken;
22
+ staleTime?: DurationInput;
23
+ gcTime?: DurationInput;
24
+ };
25
+ export interface Subscribable<A, E = never> {
26
+ readonly changes: Stream.Stream<A, E>;
27
+ readonly get: () => A;
28
+ }
29
+ export interface SubscriptionOptions {
30
+ readonly skipInitial?: boolean;
31
+ }
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,20 +1,20 @@
1
1
  {
2
2
  "name": "@effectify/solid-query",
3
- "version": "0.0.1",
3
+ "version": "0.1.0",
4
+ "description": "Integration of Effect with TanStack Query for Solid.js",
4
5
  "type": "module",
5
- "main": "./dist/index.js",
6
- "module": "./dist/index.js",
7
- "types": "./dist/index.d.ts",
6
+ "main": "./dist/src/index.js",
7
+ "module": "./dist/src/index.js",
8
+ "types": "./dist/src/index.d.ts",
8
9
  "publishConfig": {
9
10
  "access": "public"
10
11
  },
11
12
  "exports": {
12
13
  "./package.json": "./package.json",
13
14
  ".": {
14
- "development": "./src/index.ts",
15
- "types": "./dist/index.d.ts",
16
- "import": "./dist/index.js",
17
- "default": "./dist/index.js"
15
+ "types": "./dist/src/index.d.ts",
16
+ "import": "./dist/src/index.js",
17
+ "default": "./dist/src/index.js"
18
18
  }
19
19
  },
20
20
  "files": [
@@ -22,9 +22,18 @@
22
22
  "!**/*.tsbuildinfo"
23
23
  ],
24
24
  "dependencies": {
25
- "tslib": "^2.3.0"
25
+ "tslib": "2.8.1",
26
+ "@tanstack/solid-query": "5.90.3",
27
+ "@tanstack/query-core": "5.90.2",
28
+ "effect": "3.17.14",
29
+ "solid-js": "1.9.9"
26
30
  },
27
31
  "devDependencies": {
28
- "typescript": "~5.7.2"
29
- }
30
- }
32
+ "typescript": "5.9.2"
33
+ },
34
+ "peerDependencies": {
35
+ "@tanstack/solid-query": "5.90.3",
36
+ "solid-js": "1.9.9"
37
+ },
38
+ "optionalDependencies": {}
39
+ }
package/dist/index.d.ts DELETED
@@ -1,2 +0,0 @@
1
- export * from './lib/solid-query.js';
2
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,sBAAsB,CAAC"}
package/dist/index.js DELETED
@@ -1 +0,0 @@
1
- function r(){return"solid-query-hola"}export{r as solidQuery};
@@ -1,2 +0,0 @@
1
- export declare function solidQuery(): string;
2
- //# sourceMappingURL=solid-query.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"solid-query.d.ts","sourceRoot":"","sources":["../../src/lib/solid-query.ts"],"names":[],"mappings":"AAAA,wBAAgB,UAAU,IAAI,MAAM,CAEnC"}