effect-query 0.1.0 → 0.1.1

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,24 @@
1
+ <p align="center">
2
+ <img src="https://github.com/voidhashcom/effect-query/raw/main/banner.png" alt="Effect Query Logo">
3
+ </p>
4
+
5
+ # Effect Query
6
+
7
+ Integration of Effect-ts with Tanstack Query. Run your Effects from Tanstack Query. Fully type-safe and compatible with Effect RPC and Effect HttpApi.
8
+
9
+ ## Quick Start
10
+
11
+ ```bash
12
+ # Install the package
13
+ npm install effect-query
14
+
15
+ # Install peer dependencies (if not already installed)
16
+ npm install @tanstack/react-query effect
17
+
18
+ ```
19
+
20
+ ### Read the full documentation [here](https://github.com/voidhashcom/effect-query)
21
+
22
+ <p align="center">
23
+ Made with ❤️ by <a href="https://voidhash.com">Voidhash</a>
24
+ </p>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "effect-query",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Effect adapter for Tanstack Query",
5
5
  "repository": {
6
6
  "type": "git",
@@ -20,7 +20,6 @@
20
20
  },
21
21
  "files": [
22
22
  "dist",
23
- "src",
24
23
  "README.md",
25
24
  "package.json",
26
25
  "!**/*.test.*",
package/src/errors.ts DELETED
@@ -1,74 +0,0 @@
1
- import { Cause } from "effect";
2
-
3
- const EffectQueryFailureTag = "EffectQueryFailure" as const;
4
- const EffectQueryDefectTag = "EffectQueryDefect" as const;
5
-
6
- type EffectQueryErrorMatcher<
7
- TFailure extends { _tag: string } | never = never,
8
- TReturn = unknown,
9
- > = {
10
- OrElse: (cause: Cause.Cause<unknown>) => TReturn;
11
- } & ([TFailure] extends [never]
12
- ? Record<never, never>
13
- : TFailure extends { _tag: string }
14
- ? {
15
- [K in TFailure["_tag"]]?: (
16
- failure: Extract<TFailure, { _tag: K }>
17
- ) => TReturn;
18
- }
19
- : Record<never, never>);
20
-
21
- export class EffectQueryFailure<
22
- TFailure extends { _tag: string } | never = never,
23
- > extends Error {
24
- readonly _tag: typeof EffectQueryFailureTag;
25
- readonly failure: TFailure;
26
- readonly failureCause: Cause.Cause<TFailure>;
27
- constructor(
28
- message: string,
29
- failure: TFailure,
30
- cause: Cause.Cause<TFailure>
31
- ) {
32
- super(message);
33
- this._tag = EffectQueryFailureTag;
34
- this.failure = failure;
35
- this.failureCause = cause;
36
- }
37
-
38
- match<TReturn>(
39
- matcher: EffectQueryErrorMatcher<
40
- TFailure extends { _tag: string } ? TFailure : never,
41
- TReturn
42
- >
43
- ): TReturn {
44
- if (
45
- this.failure &&
46
- typeof this.failure === "object" &&
47
- "_tag" in this.failure
48
- ) {
49
- const tag = this.failure._tag;
50
- // biome-ignore lint/suspicious/noExplicitAny: Dynamic tag matching requires any
51
- const handler = (matcher as any)[tag];
52
- if (typeof handler === "function") {
53
- return handler(this.failure);
54
- }
55
- }
56
- return matcher.OrElse(this.failureCause);
57
- }
58
- }
59
-
60
- export class EffectQueryDefect<TDefect> extends Error {
61
- readonly _tag: typeof EffectQueryDefectTag;
62
- readonly defectCause: Cause.Cause<TDefect>;
63
- constructor(message: string, defect: TDefect) {
64
- super(message);
65
- this._tag = EffectQueryDefectTag;
66
- this.defectCause = Cause.die(defect);
67
- }
68
-
69
- match<TReturn>(
70
- matcher: EffectQueryErrorMatcher<never, TReturn> & Record<string, unknown>
71
- ): TReturn {
72
- return matcher.OrElse(this.defectCause);
73
- }
74
- }
package/src/index.ts DELETED
@@ -1,86 +0,0 @@
1
- import { type Layer, ManagedRuntime } from "effect";
2
- import {
3
- type EffectInfiniteQueryOptionsInput,
4
- effectInfiniteQueryOptions,
5
- } from "./infiniteQueryOptions";
6
- import {
7
- type EffectQueryMutationOptionsInput,
8
- effectQueryMutationOptions,
9
- } from "./mutationOptions";
10
- import {
11
- type EffectQueryOptionsInput,
12
- effectQueryQueryOptions,
13
- } from "./queryOptions";
14
- import { EffectQueryRunner } from "./runner";
15
-
16
- export function createEffectQuery<Input>(
17
- layer: Layer.Layer<Input, never, never>
18
- ) {
19
- const runtime = ManagedRuntime.make(layer);
20
- const runner = new EffectQueryRunner(runtime);
21
-
22
- return {
23
- queryOptions: <
24
- TFnResult,
25
- TFnErrorResult extends { _tag: string },
26
- TFnRequirements,
27
- >(
28
- options: EffectQueryOptionsInput<
29
- TFnResult,
30
- TFnErrorResult,
31
- TFnRequirements
32
- >
33
- ) =>
34
- effectQueryQueryOptions<
35
- typeof runtime,
36
- TFnResult,
37
- TFnErrorResult,
38
- TFnRequirements
39
- >(
40
- {
41
- queryKey: options.queryKey,
42
- queryFn: options.queryFn,
43
- },
44
- {
45
- runner,
46
- }
47
- ),
48
- infiniteQueryOptions: <
49
- TFnResult,
50
- TFnErrorResult extends { _tag: string },
51
- TFnRequirements,
52
- >(
53
- options: EffectInfiniteQueryOptionsInput<
54
- TFnResult,
55
- TFnErrorResult,
56
- TFnRequirements
57
- >
58
- ) =>
59
- effectInfiniteQueryOptions<
60
- typeof runtime,
61
- TFnResult,
62
- TFnErrorResult,
63
- TFnRequirements
64
- >(options, { runner }),
65
- mutationOptions: <
66
- TFnResult,
67
- TFnErrorResult extends { _tag: string },
68
- TFnRequirements,
69
- TVariables,
70
- >(
71
- options: EffectQueryMutationOptionsInput<
72
- TFnResult,
73
- TFnErrorResult,
74
- TFnRequirements,
75
- TVariables
76
- >
77
- ) =>
78
- effectQueryMutationOptions<
79
- typeof runtime,
80
- TFnResult,
81
- TFnErrorResult,
82
- TFnRequirements,
83
- TVariables
84
- >(options, { runner }),
85
- };
86
- }
@@ -1,144 +0,0 @@
1
- import {
2
- type DefinedInitialDataInfiniteOptions,
3
- type InfiniteData,
4
- infiniteQueryOptions,
5
- type QueryFunctionContext,
6
- type SkipToken,
7
- type UndefinedInitialDataInfiniteOptions,
8
- type UnusedSkipTokenInfiniteOptions,
9
- type UseInfiniteQueryOptions,
10
- } from "@tanstack/react-query";
11
- import { Cause, type Effect, Exit } from "effect";
12
- import type { ManagedRuntime } from "effect/ManagedRuntime";
13
- import { EffectQueryDefect, EffectQueryFailure } from "./errors";
14
- import type { EffectQueryRunner } from "./runner";
15
- import type { EffectQueryQueryKey } from "./types";
16
-
17
- type EffectInfiniteQueryQueryFn<
18
- TFnResult,
19
- TFnErrorResult,
20
- TFnRequirements,
21
- TQueryKey extends EffectQueryQueryKey = EffectQueryQueryKey,
22
- TPageParam = never,
23
- > = (
24
- context: QueryFunctionContext<TQueryKey, TPageParam>
25
- ) => Effect.Effect<TFnResult, TFnErrorResult, TFnRequirements>;
26
-
27
- type EffectInfiniteQueryUndefinedInitialDataOptions<
28
- TQueryFnData,
29
- TError,
30
- TData = InfiniteData<TQueryFnData, unknown>,
31
- TQueryKey extends EffectQueryQueryKey = EffectQueryQueryKey,
32
- > = UndefinedInitialDataInfiniteOptions<TQueryFnData, TError, TData, TQueryKey>;
33
-
34
- type EffectInfiniteQueryUnusedSkipTokenOptions<
35
- TQueryFnData,
36
- TError,
37
- TData = InfiniteData<TQueryFnData, unknown>,
38
- TQueryKey extends EffectQueryQueryKey = EffectQueryQueryKey,
39
- > = UnusedSkipTokenInfiniteOptions<TQueryFnData, TError, TData, TQueryKey>;
40
-
41
- type EffectInfiniteQueryDefinedInitialDataOptions<
42
- TQueryFnData,
43
- TError,
44
- TData = InfiniteData<TQueryFnData, unknown>,
45
- TQueryKey extends EffectQueryQueryKey = EffectQueryQueryKey,
46
- > = DefinedInitialDataInfiniteOptions<TQueryFnData, TError, TData, TQueryKey>;
47
-
48
- export type EffectInfiniteQueryOptionsInput<
49
- TFnResult,
50
- TFnErrorResult,
51
- TFnRequirements,
52
- TQueryKey extends EffectQueryQueryKey = EffectQueryQueryKey,
53
- > = Omit<
54
- | EffectInfiniteQueryUndefinedInitialDataOptions<
55
- TFnResult,
56
- TFnErrorResult,
57
- TFnResult,
58
- TQueryKey
59
- >
60
- | EffectInfiniteQueryDefinedInitialDataOptions<
61
- TFnResult,
62
- TFnErrorResult,
63
- TFnResult,
64
- TQueryKey
65
- >
66
- | EffectInfiniteQueryUnusedSkipTokenOptions<
67
- TFnResult,
68
- TFnErrorResult,
69
- TFnResult,
70
- TQueryKey
71
- >,
72
- "queryFn" | "queryKey"
73
- > & {
74
- queryKey: EffectQueryQueryKey;
75
- queryFn: EffectInfiniteQueryQueryFn<
76
- TFnResult,
77
- TFnErrorResult,
78
- TFnRequirements,
79
- EffectQueryQueryKey
80
- >;
81
- };
82
-
83
- /**
84
- * @internal
85
- */
86
- export function effectInfiniteQueryOptions<
87
- // biome-ignore lint/suspicious/noExplicitAny: generic
88
- TManagedRuntime extends ManagedRuntime<any, never>,
89
- TFnResult,
90
- TFnErrorResult extends { _tag: string },
91
- TFnRequirements,
92
- >(
93
- inputOptions: EffectInfiniteQueryOptionsInput<
94
- TFnResult,
95
- TFnErrorResult,
96
- TFnRequirements
97
- >,
98
- context: {
99
- runner: EffectQueryRunner<TManagedRuntime>;
100
- signal?: AbortSignal;
101
- }
102
- ) {
103
- const [spanName] = inputOptions.queryKey;
104
-
105
- const queryFn: Exclude<
106
- UseInfiniteQueryOptions<
107
- TFnResult,
108
- TFnErrorResult,
109
- InfiniteData<TFnResult, unknown>,
110
- EffectQueryQueryKey,
111
- unknown
112
- >["queryFn"],
113
- SkipToken | undefined
114
- > = async (queryFnContext) => {
115
- const effect = inputOptions.queryFn(queryFnContext);
116
- const result = await context.runner.run(effect, spanName, {
117
- signal: context.signal,
118
- });
119
- return Exit.match(result, {
120
- onSuccess: (value) => value,
121
- onFailure: (cause) => {
122
- if (cause._tag === "Fail") {
123
- const failure = cause.error;
124
- throw new EffectQueryFailure(Cause.pretty(cause), failure, cause);
125
- }
126
- throw new EffectQueryDefect(Cause.pretty(cause), cause);
127
- },
128
- });
129
- };
130
-
131
- // The as UseQueryOptions is a workaround to set the correct error type. React Query has no way to infer the error type from the Effect.
132
- return infiniteQueryOptions({
133
- ...inputOptions,
134
- queryKey: inputOptions.queryKey as EffectQueryQueryKey,
135
- queryFn,
136
- }) as UseInfiniteQueryOptions<
137
- TFnResult,
138
- [TFnErrorResult] extends [never]
139
- ? EffectQueryDefect<unknown>
140
- : EffectQueryFailure<TFnErrorResult> | EffectQueryDefect<unknown>,
141
- TFnResult,
142
- EffectQueryQueryKey
143
- >;
144
- }
@@ -1,97 +0,0 @@
1
- import {
2
- type MutationFunction,
3
- mutationOptions,
4
- type skipToken,
5
- type UseMutationOptions,
6
- } from "@tanstack/react-query";
7
- import { Cause, type Effect, Exit } from "effect";
8
- import type { ManagedRuntime } from "effect/ManagedRuntime";
9
- import { EffectQueryDefect, EffectQueryFailure } from "./errors";
10
- import type { EffectQueryRunner } from "./runner";
11
-
12
- type EffectfulMutationFunction<
13
- TFnResult,
14
- TFnErrorResult,
15
- TFnRequirements,
16
- TVariables,
17
- > = (
18
- variables: TVariables
19
- ) => Effect.Effect<TFnResult, TFnErrorResult, TFnRequirements>;
20
-
21
- export type EffectQueryMutationOptionsInput<
22
- TFnResult,
23
- TFnErrorResult,
24
- TFnRequirements,
25
- TVariables,
26
- > = Omit<
27
- UseMutationOptions<TFnResult, TFnErrorResult, TVariables>,
28
- "mutationKey" | "mutationFn"
29
- > & {
30
- mutationKey: string;
31
- mutationFn:
32
- | EffectfulMutationFunction<
33
- TFnResult,
34
- TFnErrorResult,
35
- TFnRequirements,
36
- TVariables
37
- >
38
- | typeof skipToken;
39
- };
40
-
41
- /**
42
- * @internal
43
- */
44
- export function effectQueryMutationOptions<
45
- // biome-ignore lint/suspicious/noExplicitAny: generic
46
- TManagedRuntime extends ManagedRuntime<any, never>,
47
- TFnResult,
48
- TFnErrorResult extends { _tag: string },
49
- TFnRequirements,
50
- TVariables,
51
- >(
52
- inputOptions: EffectQueryMutationOptionsInput<
53
- TFnResult,
54
- TFnErrorResult,
55
- TFnRequirements,
56
- TVariables
57
- >,
58
- context: {
59
- runner: EffectQueryRunner<TManagedRuntime>;
60
- }
61
- ) {
62
- const spanName = inputOptions.mutationKey;
63
- const mutationFn: MutationFunction<TFnResult, TVariables> = async (
64
- variables: TVariables
65
- ) => {
66
- const effect = (
67
- inputOptions.mutationFn as EffectfulMutationFunction<
68
- TFnResult,
69
- TFnErrorResult,
70
- TFnRequirements,
71
- TVariables
72
- >
73
- )(variables);
74
- const result = await context.runner.run(effect, spanName);
75
- return Exit.match(result, {
76
- onSuccess: (value) => value,
77
- onFailure: (cause) => {
78
- if (cause._tag === "Fail") {
79
- const failure = cause.error;
80
- throw new EffectQueryFailure(Cause.pretty(cause), failure, cause);
81
- }
82
- throw new EffectQueryDefect(Cause.pretty(cause), cause);
83
- },
84
- });
85
- };
86
-
87
- return mutationOptions({
88
- ...inputOptions,
89
- mutationFn,
90
- }) as UseMutationOptions<
91
- TFnResult,
92
- [TFnErrorResult] extends [never]
93
- ? never
94
- : EffectQueryFailure<TFnErrorResult> | EffectQueryDefect<unknown>,
95
- TVariables
96
- >;
97
- }
@@ -1,136 +0,0 @@
1
- import {
2
- type DefinedInitialDataOptions,
3
- type QueryFunction,
4
- type QueryFunctionContext,
5
- queryOptions,
6
- type UndefinedInitialDataOptions,
7
- type UnusedSkipTokenOptions,
8
- type UseQueryOptions,
9
- } from "@tanstack/react-query";
10
- import { Cause, type Effect, Exit } from "effect";
11
- import type { ManagedRuntime } from "effect/ManagedRuntime";
12
- import { EffectQueryDefect, EffectQueryFailure } from "./errors";
13
- import type { EffectQueryRunner } from "./runner";
14
- import type { EffectQueryQueryKey } from "./types";
15
-
16
- type EffectQueryQueryFn<
17
- TFnResult,
18
- TFnErrorResult,
19
- TFnRequirements,
20
- TQueryKey extends EffectQueryQueryKey = EffectQueryQueryKey,
21
- TPageParam = never,
22
- > = (
23
- context: QueryFunctionContext<TQueryKey, TPageParam>
24
- ) => Effect.Effect<TFnResult, TFnErrorResult, TFnRequirements>;
25
-
26
- type EffectQueryUndefinedInitialDataOptions<
27
- TQueryFnData,
28
- TError,
29
- TData = TQueryFnData,
30
- TQueryKey extends EffectQueryQueryKey = EffectQueryQueryKey,
31
- > = UndefinedInitialDataOptions<TQueryFnData, TError, TData, TQueryKey>;
32
-
33
- type EffectQueryUnusedSkipTokenOptions<
34
- TQueryFnData,
35
- TError,
36
- TData = TQueryFnData,
37
- TQueryKey extends EffectQueryQueryKey = EffectQueryQueryKey,
38
- > = UnusedSkipTokenOptions<TQueryFnData, TError, TData, TQueryKey>;
39
-
40
- type EffectQueryDefinedInitialDataOptions<
41
- TQueryFnData,
42
- TError,
43
- TData = TQueryFnData,
44
- TQueryKey extends EffectQueryQueryKey = EffectQueryQueryKey,
45
- > = DefinedInitialDataOptions<TQueryFnData, TError, TData, TQueryKey>;
46
-
47
- export type EffectQueryOptionsInput<
48
- TFnResult,
49
- TFnErrorResult,
50
- TFnRequirements,
51
- TQueryKey extends EffectQueryQueryKey = EffectQueryQueryKey,
52
- > = Omit<
53
- | EffectQueryUndefinedInitialDataOptions<
54
- TFnResult,
55
- TFnErrorResult,
56
- TFnResult,
57
- TQueryKey
58
- >
59
- | EffectQueryDefinedInitialDataOptions<
60
- TFnResult,
61
- TFnErrorResult,
62
- TFnResult,
63
- TQueryKey
64
- >
65
- | EffectQueryUnusedSkipTokenOptions<
66
- TFnResult,
67
- TFnErrorResult,
68
- TFnResult,
69
- TQueryKey
70
- >,
71
- "queryFn" | "queryKey"
72
- > & {
73
- queryKey: EffectQueryQueryKey;
74
- queryFn: EffectQueryQueryFn<
75
- TFnResult,
76
- TFnErrorResult,
77
- TFnRequirements,
78
- EffectQueryQueryKey
79
- >;
80
- };
81
-
82
- /**
83
- * @internal
84
- */
85
- export function effectQueryQueryOptions<
86
- // biome-ignore lint/suspicious/noExplicitAny: generic
87
- TManagedRuntime extends ManagedRuntime<any, never>,
88
- TFnResult,
89
- TFnErrorResult extends { _tag: string },
90
- TFnRequirements,
91
- >(
92
- inputOptions: EffectQueryOptionsInput<
93
- TFnResult,
94
- TFnErrorResult,
95
- TFnRequirements
96
- >,
97
- context: {
98
- runner: EffectQueryRunner<TManagedRuntime>;
99
- signal?: AbortSignal;
100
- }
101
- ) {
102
- const [spanName] = inputOptions.queryKey;
103
-
104
- const queryFn: QueryFunction<TFnResult, EffectQueryQueryKey> = async (
105
- queryFnContext
106
- ) => {
107
- const effect = inputOptions.queryFn(queryFnContext);
108
- const result = await context.runner.run(effect, spanName, {
109
- signal: context.signal,
110
- });
111
- return Exit.match(result, {
112
- onSuccess: (value) => value,
113
- onFailure: (cause) => {
114
- if (cause._tag === "Fail") {
115
- const failure = cause.error;
116
- throw new EffectQueryFailure(Cause.pretty(cause), failure, cause);
117
- }
118
- throw new EffectQueryDefect(Cause.pretty(cause), cause);
119
- },
120
- });
121
- };
122
-
123
- // The as UseQueryOptions is a workaround to set the correct error type. React Query has no way to infer the error type from the Effect.
124
- return queryOptions({
125
- ...inputOptions,
126
- queryKey: inputOptions.queryKey,
127
- queryFn,
128
- }) as UseQueryOptions<
129
- TFnResult,
130
- [TFnErrorResult] extends [never]
131
- ? EffectQueryDefect<unknown>
132
- : EffectQueryFailure<TFnErrorResult> | EffectQueryDefect<unknown>,
133
- TFnResult,
134
- EffectQueryQueryKey
135
- >;
136
- }
package/src/runner.ts DELETED
@@ -1,25 +0,0 @@
1
- import { Effect, type Exit } from "effect";
2
- import type { ManagedRuntime } from "effect/ManagedRuntime";
3
-
4
- export class EffectQueryRunner<
5
- // biome-ignore lint/suspicious/noExplicitAny: generic
6
- TManagedRuntime extends ManagedRuntime<any, never>,
7
- > {
8
- readonly runtime: TManagedRuntime;
9
- constructor(runtime: TManagedRuntime) {
10
- this.runtime = runtime;
11
- }
12
-
13
- async run<TResult, TError, TRequirements>(
14
- effect: Effect.Effect<TResult, TError, TRequirements>,
15
- span: string,
16
- options: { signal?: AbortSignal } = {}
17
- ): Promise<Exit.Exit<TResult, TError>> {
18
- const runnable = Effect.scoped(
19
- effect.pipe(Effect.withSpan(span), Effect.tapErrorCause(Effect.logError))
20
- );
21
- return await this.runtime.runPromiseExit(runnable, {
22
- signal: options.signal,
23
- });
24
- }
25
- }
package/src/types.ts DELETED
@@ -1,5 +0,0 @@
1
- export type EffectQueryQueryKey = readonly [
2
- string,
3
- string,
4
- Record<string, unknown>?,
5
- ];