@confect/js 10.0.0-next.2 → 10.0.0-next.21

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.
@@ -1,153 +1,38 @@
1
- import * as Ref from "@confect/core/Ref";
2
1
  import { ConvexClient } from "convex/browser";
3
- import * as Cause from "effect/Cause";
4
2
  import * as Context from "effect/Context";
5
3
  import * as Effect from "effect/Effect";
6
4
  import * as Layer from "effect/Layer";
7
- import * as Queue from "effect/Queue";
8
- import * as Schema from "effect/Schema";
9
- import * as Stream from "effect/Stream";
5
+ import * as InternalWebSocketClient from "./internal/WebSocketClient";
10
6
 
11
- export class WebSocketClientError extends Schema.TaggedErrorClass<WebSocketClientError>()(
12
- "WebSocketClientError",
13
- {
14
- cause: Schema.Unknown,
15
- },
16
- ) {}
7
+ export { WebSocketClientError } from "./internal/WebSocketClient";
17
8
 
18
9
  const make = (
19
10
  address: string,
20
11
  options?: ConstructorParameters<typeof ConvexClient>[1],
21
12
  ) =>
22
- Effect.acquireRelease(
23
- Effect.sync(() => new ConvexClient(address, options)),
24
- (convexClient) => Effect.promise(() => convexClient.close()),
25
- ).pipe(
26
- Effect.map((convexClient) => {
27
- const url = address;
28
-
29
- const setAuth = (
30
- fetchToken: (args: {
31
- forceRefreshToken: boolean;
32
- }) => Effect.Effect<string | null | undefined>,
33
- onChange?: (isAuthenticated: boolean) => Effect.Effect<void>,
34
- ) =>
35
- Effect.sync(() => {
36
- convexClient.setAuth(
37
- (args) => Effect.runPromise(fetchToken(args)),
38
- ...(onChange
39
- ? [
40
- (isAuthenticated: boolean) =>
41
- Effect.runFork(onChange(isAuthenticated)),
42
- ]
43
- : []),
44
- );
45
- });
46
-
47
- const mapUnknownError = (cause: unknown) =>
48
- new WebSocketClientError({ cause });
49
-
50
- const query = <Query extends Ref.AnyPublicQuery>(
51
- ref: Query,
52
- ...rest: Ref.OptionalArgs<Query>
53
- ): Effect.Effect<
54
- Ref.Returns<Query>,
55
- Ref.Error<Query> | WebSocketClientError | Schema.SchemaError
56
- > => {
57
- const args = (rest[0] ?? {}) as Ref.Args<Query>;
58
- return Ref.runWithCodec(
59
- ref,
60
- args,
61
- (functionReference, encodedArgs) =>
62
- convexClient.query(functionReference, encodedArgs),
63
- mapUnknownError,
64
- );
65
- };
66
-
67
- const mutation = <Mutation extends Ref.AnyPublicMutation>(
68
- ref: Mutation,
69
- ...rest: Ref.OptionalArgs<Mutation>
70
- ): Effect.Effect<
71
- Ref.Returns<Mutation>,
72
- Ref.Error<Mutation> | WebSocketClientError | Schema.SchemaError
73
- > => {
74
- const args = (rest[0] ?? {}) as Ref.Args<Mutation>;
75
- return Ref.runWithCodec(
76
- ref,
77
- args,
78
- (functionReference, encodedArgs) =>
79
- convexClient.mutation(functionReference, encodedArgs),
80
- mapUnknownError,
81
- );
82
- };
83
-
84
- const action = <Action extends Ref.AnyPublicAction>(
85
- ref: Action,
86
- ...rest: Ref.OptionalArgs<Action>
87
- ): Effect.Effect<
88
- Ref.Returns<Action>,
89
- Ref.Error<Action> | WebSocketClientError | Schema.SchemaError
90
- > => {
91
- const args = (rest[0] ?? {}) as Ref.Args<Action>;
92
- return Ref.runWithCodec(
93
- ref,
94
- args,
95
- (functionReference, encodedArgs) =>
96
- convexClient.action(functionReference, encodedArgs),
97
- mapUnknownError,
98
- );
99
- };
100
-
101
- const reactiveQuery = <Query extends Ref.AnyPublicQuery>(
102
- ref: Query,
103
- ...rest: Ref.OptionalArgs<Query>
104
- ): Stream.Stream<
105
- Ref.Returns<Query>,
106
- Ref.Error<Query> | WebSocketClientError | Schema.SchemaError
107
- > => {
108
- const args = (rest[0] ?? {}) as Ref.Args<Query>;
109
- const functionReference = Ref.getFunctionReference(ref);
110
- const onError = Ref.decodeErrorOrElse(ref, mapUnknownError);
111
-
112
- return Stream.unwrap(
113
- Effect.gen(function* () {
114
- const encodedArgs = yield* Ref.encodeArgs(ref, args);
115
-
116
- return Stream.callback<
117
- unknown,
118
- Ref.Error<Query> | WebSocketClientError
119
- >((queue) =>
120
- Effect.gen(function* () {
121
- const unsubscribe = convexClient.onUpdate(
122
- functionReference,
123
- encodedArgs,
124
- (result) => {
125
- Queue.offerUnsafe(queue, result);
126
- },
127
- (error) => {
128
- Queue.failCauseUnsafe(queue, Cause.fail(onError(error)));
129
- },
130
- );
131
- yield* Effect.addFinalizer(() =>
132
- Effect.sync(() => unsubscribe()),
133
- );
134
- }),
135
- );
136
- }),
137
- ).pipe(
138
- Stream.mapEffect((encodedReturns) =>
139
- Ref.decodeReturns(ref, encodedReturns),
140
- ),
141
- );
142
- };
143
-
13
+ InternalWebSocketClient.makeScoped(
14
+ address,
15
+ Effect.sync(() => {
16
+ const convexClient = new ConvexClient(address, options);
144
17
  return {
145
- url,
146
- setAuth,
147
- query,
148
- mutation,
149
- action,
150
- reactiveQuery,
18
+ setAuth: (fetchToken, onChange) =>
19
+ onChange === undefined
20
+ ? convexClient.setAuth(fetchToken)
21
+ : convexClient.setAuth(fetchToken, onChange),
22
+ close: () => convexClient.close(),
23
+ query: (functionReference, encodedArgs) =>
24
+ convexClient.query(functionReference, encodedArgs),
25
+ mutation: (functionReference, encodedArgs) =>
26
+ convexClient.mutation(functionReference, encodedArgs),
27
+ action: (functionReference, encodedArgs) =>
28
+ convexClient.action(functionReference, encodedArgs),
29
+ onUpdate: (functionReference, encodedArgs, onUpdate, onError) =>
30
+ convexClient.onUpdate(
31
+ functionReference,
32
+ encodedArgs,
33
+ onUpdate,
34
+ onError,
35
+ ),
151
36
  };
152
37
  }),
153
38
  );
@@ -0,0 +1,101 @@
1
+ import * as Ref from "@confect/core/Ref";
2
+ import * as Effect from "effect/Effect";
3
+ import * as Schema from "effect/Schema";
4
+
5
+ export class HttpClientError extends Schema.TaggedError<HttpClientError>()(
6
+ "HttpClientError",
7
+ {
8
+ cause: Schema.Unknown,
9
+ },
10
+ ) {}
11
+
12
+ export interface Transport {
13
+ readonly url: string;
14
+ readonly setAuth: (token: string) => void;
15
+ readonly clearAuth: () => void;
16
+ readonly query: (
17
+ functionReference: Ref.FunctionReference<Ref.AnyPublicQuery>,
18
+ encodedArgs: unknown,
19
+ ) => PromiseLike<unknown>;
20
+ readonly mutation: (
21
+ functionReference: Ref.FunctionReference<Ref.AnyPublicMutation>,
22
+ encodedArgs: unknown,
23
+ ) => PromiseLike<unknown>;
24
+ readonly action: (
25
+ functionReference: Ref.FunctionReference<Ref.AnyPublicAction>,
26
+ encodedArgs: unknown,
27
+ ) => PromiseLike<unknown>;
28
+ }
29
+
30
+ export const make = (client: Transport) => {
31
+ const setAuth = (token: string) =>
32
+ Effect.sync(() => {
33
+ client.setAuth(token);
34
+ });
35
+
36
+ const clearAuth = Effect.sync(() => {
37
+ client.clearAuth();
38
+ });
39
+
40
+ const mapUnknownError = (cause: unknown) => new HttpClientError({ cause });
41
+
42
+ const query = <Query extends Ref.AnyPublicQuery>(
43
+ ref: Query,
44
+ ...rest: Ref.OptionalArgs<Query>
45
+ ): Effect.Effect<
46
+ Ref.Returns<Query>,
47
+ Ref.Error<Query> | HttpClientError | Schema.SchemaError
48
+ > => {
49
+ const args = (rest[0] ?? {}) as Ref.Args<Query>;
50
+ return Ref.runWithCodec(
51
+ ref,
52
+ args,
53
+ (functionReference, encodedArgs) =>
54
+ client.query(functionReference, encodedArgs),
55
+ mapUnknownError,
56
+ );
57
+ };
58
+
59
+ const mutation = <Mutation extends Ref.AnyPublicMutation>(
60
+ ref: Mutation,
61
+ ...rest: Ref.OptionalArgs<Mutation>
62
+ ): Effect.Effect<
63
+ Ref.Returns<Mutation>,
64
+ Ref.Error<Mutation> | HttpClientError | Schema.SchemaError
65
+ > => {
66
+ const args = (rest[0] ?? {}) as Ref.Args<Mutation>;
67
+ return Ref.runWithCodec(
68
+ ref,
69
+ args,
70
+ (functionReference, encodedArgs) =>
71
+ client.mutation(functionReference, encodedArgs),
72
+ mapUnknownError,
73
+ );
74
+ };
75
+
76
+ const action = <Action extends Ref.AnyPublicAction>(
77
+ ref: Action,
78
+ ...rest: Ref.OptionalArgs<Action>
79
+ ): Effect.Effect<
80
+ Ref.Returns<Action>,
81
+ Ref.Error<Action> | HttpClientError | Schema.SchemaError
82
+ > => {
83
+ const args = (rest[0] ?? {}) as Ref.Args<Action>;
84
+ return Ref.runWithCodec(
85
+ ref,
86
+ args,
87
+ (functionReference, encodedArgs) =>
88
+ client.action(functionReference, encodedArgs),
89
+ mapUnknownError,
90
+ );
91
+ };
92
+
93
+ return {
94
+ url: client.url,
95
+ setAuth,
96
+ clearAuth,
97
+ query,
98
+ mutation,
99
+ action,
100
+ };
101
+ };
@@ -0,0 +1,202 @@
1
+ import * as Ref from "@confect/core/Ref";
2
+ import * as Effect from "effect/Effect";
3
+ import * as Queue from "effect/Queue";
4
+ import * as Result from "effect/Result";
5
+ import * as Schema from "effect/Schema";
6
+ import * as Stream from "effect/Stream";
7
+
8
+ export class WebSocketClientError extends Schema.TaggedError<WebSocketClientError>()(
9
+ "WebSocketClientError",
10
+ {
11
+ cause: Schema.Defect(),
12
+ },
13
+ ) {}
14
+
15
+ export interface Transport {
16
+ readonly setAuth: (
17
+ fetchToken: (args: {
18
+ readonly forceRefreshToken: boolean;
19
+ }) => Promise<string | null | undefined>,
20
+ onChange?: (isAuthenticated: boolean) => void,
21
+ ) => void;
22
+ readonly close: () => Promise<void>;
23
+ readonly query: (
24
+ functionReference: Ref.FunctionReference<Ref.AnyPublicQuery>,
25
+ encodedArgs: unknown,
26
+ ) => PromiseLike<unknown>;
27
+ readonly mutation: (
28
+ functionReference: Ref.FunctionReference<Ref.AnyPublicMutation>,
29
+ encodedArgs: unknown,
30
+ ) => PromiseLike<unknown>;
31
+ readonly action: (
32
+ functionReference: Ref.FunctionReference<Ref.AnyPublicAction>,
33
+ encodedArgs: unknown,
34
+ ) => PromiseLike<unknown>;
35
+ readonly onUpdate: (
36
+ functionReference: Ref.FunctionReference<Ref.AnyPublicQuery>,
37
+ encodedArgs: unknown,
38
+ onUpdate: (result: unknown) => void,
39
+ onError: (error: Error) => void,
40
+ ) => () => void;
41
+ }
42
+
43
+ export const make = (address: string, convexClient: Transport) => {
44
+ const setAuth = (
45
+ fetchToken: (args: {
46
+ forceRefreshToken: boolean;
47
+ }) => Effect.Effect<string | null | undefined>,
48
+ onChange?: (isAuthenticated: boolean) => Effect.Effect<void>,
49
+ ) =>
50
+ Effect.sync(() => {
51
+ convexClient.setAuth(
52
+ (args) => Effect.runPromise(fetchToken(args)),
53
+ ...(onChange
54
+ ? [
55
+ (isAuthenticated: boolean) =>
56
+ Effect.runFork(onChange(isAuthenticated)),
57
+ ]
58
+ : []),
59
+ );
60
+ });
61
+
62
+ const mapUnknownError = (cause: unknown) =>
63
+ new WebSocketClientError({ cause });
64
+
65
+ const query = <Query extends Ref.AnyPublicQuery>(
66
+ ref: Query,
67
+ ...rest: Ref.OptionalArgs<Query>
68
+ ): Effect.Effect<
69
+ Ref.Returns<Query>,
70
+ Ref.Error<Query> | WebSocketClientError | Schema.SchemaError
71
+ > => {
72
+ const args = (rest[0] ?? {}) as Ref.Args<Query>;
73
+ return Ref.runWithCodec(
74
+ ref,
75
+ args,
76
+ (functionReference, encodedArgs) =>
77
+ convexClient.query(functionReference, encodedArgs),
78
+ mapUnknownError,
79
+ );
80
+ };
81
+
82
+ const mutation = <Mutation extends Ref.AnyPublicMutation>(
83
+ ref: Mutation,
84
+ ...rest: Ref.OptionalArgs<Mutation>
85
+ ): Effect.Effect<
86
+ Ref.Returns<Mutation>,
87
+ Ref.Error<Mutation> | WebSocketClientError | Schema.SchemaError
88
+ > => {
89
+ const args = (rest[0] ?? {}) as Ref.Args<Mutation>;
90
+ return Ref.runWithCodec(
91
+ ref,
92
+ args,
93
+ (functionReference, encodedArgs) =>
94
+ convexClient.mutation(functionReference, encodedArgs),
95
+ mapUnknownError,
96
+ );
97
+ };
98
+
99
+ const action = <Action extends Ref.AnyPublicAction>(
100
+ ref: Action,
101
+ ...rest: Ref.OptionalArgs<Action>
102
+ ): Effect.Effect<
103
+ Ref.Returns<Action>,
104
+ Ref.Error<Action> | WebSocketClientError | Schema.SchemaError
105
+ > => {
106
+ const args = (rest[0] ?? {}) as Ref.Args<Action>;
107
+ return Ref.runWithCodec(
108
+ ref,
109
+ args,
110
+ (functionReference, encodedArgs) =>
111
+ convexClient.action(functionReference, encodedArgs),
112
+ mapUnknownError,
113
+ );
114
+ };
115
+
116
+ type ReactiveQueryError<Query extends Ref.AnyPublicQuery> =
117
+ | Ref.Error<Query>
118
+ | WebSocketClientError
119
+ | Schema.SchemaError;
120
+
121
+ const reactiveQueryResult = <Query extends Ref.AnyPublicQuery>(
122
+ ref: Query,
123
+ ...rest: Ref.OptionalArgs<Query>
124
+ ): Stream.Stream<
125
+ Result.Result<Ref.Returns<Query>, ReactiveQueryError<Query>>
126
+ > => {
127
+ type QueryResult = Result.Result<
128
+ Ref.Returns<Query>,
129
+ ReactiveQueryError<Query>
130
+ >;
131
+ const args = (rest[0] ?? {}) as Ref.Args<Query>;
132
+ const functionReference = Ref.getFunctionReference(ref);
133
+ const onError = Ref.decodeErrorOrElse(ref, mapUnknownError);
134
+
135
+ return Stream.unwrap(
136
+ Effect.match(Ref.encodeArgs(ref, args), {
137
+ onFailure: (error) => Stream.succeed<QueryResult>(Result.fail(error)),
138
+ onSuccess: (encodedArgs) =>
139
+ Stream.callback<Result.Result<unknown, ReactiveQueryError<Query>>>(
140
+ (queue) =>
141
+ Effect.gen(function* () {
142
+ const unsubscribe = convexClient.onUpdate(
143
+ functionReference,
144
+ encodedArgs,
145
+ (result) => {
146
+ Queue.offerUnsafe(queue, Result.succeed(result));
147
+ },
148
+ (error) => {
149
+ Queue.offerUnsafe(queue, Result.fail(onError(error)));
150
+ },
151
+ );
152
+ yield* Effect.addFinalizer(() =>
153
+ Effect.sync(() => unsubscribe()),
154
+ );
155
+ }),
156
+ ),
157
+ }),
158
+ ).pipe(
159
+ Stream.mapEffect((result): Effect.Effect<QueryResult> =>
160
+ Result.match(result, {
161
+ onFailure: (error) => Effect.succeed(Result.fail(error)),
162
+ onSuccess: (encodedReturns) =>
163
+ Effect.match(Ref.decodeReturns(ref, encodedReturns), {
164
+ onFailure: Result.fail,
165
+ onSuccess: Result.succeed,
166
+ }),
167
+ }),
168
+ ),
169
+ );
170
+ };
171
+
172
+ const reactiveQuery = <Query extends Ref.AnyPublicQuery>(
173
+ ref: Query,
174
+ ...rest: Ref.OptionalArgs<Query>
175
+ ): Stream.Stream<Ref.Returns<Query>, ReactiveQueryError<Query>> =>
176
+ reactiveQueryResult(ref, ...rest).pipe(
177
+ Stream.mapEffect(
178
+ Result.match({
179
+ onFailure: Effect.fail,
180
+ onSuccess: Effect.succeed,
181
+ }),
182
+ ),
183
+ );
184
+
185
+ return {
186
+ url: address,
187
+ setAuth,
188
+ query,
189
+ mutation,
190
+ action,
191
+ reactiveQuery,
192
+ reactiveQueryResult,
193
+ };
194
+ };
195
+
196
+ export const makeScoped = <E, R>(
197
+ address: string,
198
+ acquire: Effect.Effect<Transport, E, R>,
199
+ ) =>
200
+ Effect.acquireRelease(acquire, (convexClient) =>
201
+ Effect.promise(() => convexClient.close()),
202
+ ).pipe(Effect.map((convexClient) => make(address, convexClient)));