@confect/js 10.0.0-next.8 → 10.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/CHANGELOG.md +768 -14
- package/README.md +3 -1
- package/dist/HttpClient.d.ts +20 -25
- package/dist/HttpClient.d.ts.map +1 -1
- package/dist/HttpClient.js +11 -32
- package/dist/HttpClient.js.map +1 -1
- package/dist/WebSocketClient.d.ts +28 -29
- package/dist/WebSocketClient.d.ts.map +1 -1
- package/dist/WebSocketClient.js +14 -47
- package/dist/WebSocketClient.js.map +1 -1
- package/dist/internal/HttpClient.d.ts +26 -0
- package/dist/internal/HttpClient.d.ts.map +1 -0
- package/dist/internal/HttpClient.js +41 -0
- package/dist/internal/HttpClient.js.map +1 -0
- package/dist/internal/WebSocketClient.d.ts +44 -0
- package/dist/internal/WebSocketClient.d.ts.map +1 -0
- package/dist/internal/WebSocketClient.js +74 -0
- package/dist/internal/WebSocketClient.js.map +1 -0
- package/package.json +20 -14
- package/src/HttpClient.ts +15 -82
- package/src/WebSocketClient.ts +29 -140
- package/src/internal/HttpClient.ts +119 -0
- package/src/internal/WebSocketClient.ts +224 -0
- package/dist/tsconfig.src.tsbuildinfo +0 -1
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
import * as Ref from "@confect/core/Ref";
|
|
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
|
+
//#region src/internal/WebSocketClient.ts
|
|
8
|
+
var WebSocketClientError = class extends Schema.TaggedError()("WebSocketClientError", { cause: Schema.Defect() }) {};
|
|
9
|
+
const runQuery = Effect.fn("WebSocketClient.query")((effect) => effect);
|
|
10
|
+
const runMutation = Effect.fn("WebSocketClient.mutation")((effect) => effect);
|
|
11
|
+
const runAction = Effect.fn("WebSocketClient.action")((effect) => effect);
|
|
12
|
+
const make = (address, convexClient) => {
|
|
13
|
+
const setAuth = (fetchToken, onChange) => Effect.gen(function* () {
|
|
14
|
+
const context = yield* Effect.context();
|
|
15
|
+
const runPromise = Effect.runPromiseWith(context);
|
|
16
|
+
const runFork = Effect.runForkWith(context);
|
|
17
|
+
convexClient.setAuth((args) => runPromise(Effect.suspend(() => fetchToken(args))), ...onChange ? [(isAuthenticated) => {
|
|
18
|
+
runFork(Effect.suspend(() => onChange(isAuthenticated)));
|
|
19
|
+
}] : []);
|
|
20
|
+
});
|
|
21
|
+
const mapUnknownError = (cause) => new WebSocketClientError({ cause });
|
|
22
|
+
const query = (ref, ...rest) => {
|
|
23
|
+
const args = rest[0] ?? {};
|
|
24
|
+
return runQuery(Ref.runWithCodec(ref, args, (functionReference, encodedArgs) => convexClient.query(functionReference, encodedArgs), mapUnknownError));
|
|
25
|
+
};
|
|
26
|
+
const mutation = (ref, ...rest) => {
|
|
27
|
+
const args = rest[0] ?? {};
|
|
28
|
+
return runMutation(Ref.runWithCodec(ref, args, (functionReference, encodedArgs) => convexClient.mutation(functionReference, encodedArgs), mapUnknownError));
|
|
29
|
+
};
|
|
30
|
+
const action = (ref, ...rest) => {
|
|
31
|
+
const args = rest[0] ?? {};
|
|
32
|
+
return runAction(Ref.runWithCodec(ref, args, (functionReference, encodedArgs) => convexClient.action(functionReference, encodedArgs), mapUnknownError));
|
|
33
|
+
};
|
|
34
|
+
const reactiveQueryResult = (ref, ...rest) => {
|
|
35
|
+
const args = rest[0] ?? {};
|
|
36
|
+
const functionReference = Ref.getFunctionReference(ref);
|
|
37
|
+
const onError = Ref.decodeErrorOrElse(ref, mapUnknownError);
|
|
38
|
+
return Stream.unwrap(Effect.match(Ref.encodeArgs(ref, args), {
|
|
39
|
+
onFailure: (error) => Stream.succeed(Result.fail(error)),
|
|
40
|
+
onSuccess: (encodedArgs) => Stream.callback((queue) => Effect.gen(function* () {
|
|
41
|
+
const unsubscribe = convexClient.onUpdate(functionReference, encodedArgs, (result) => {
|
|
42
|
+
Queue.offerUnsafe(queue, Result.succeed(result));
|
|
43
|
+
}, (error) => {
|
|
44
|
+
Queue.offerUnsafe(queue, Result.fail(onError(error)));
|
|
45
|
+
});
|
|
46
|
+
yield* Effect.addFinalizer(() => Effect.sync(() => unsubscribe()));
|
|
47
|
+
}))
|
|
48
|
+
})).pipe(Stream.mapEffect((result) => Result.match(result, {
|
|
49
|
+
onFailure: (error) => Effect.succeed(Result.fail(error)),
|
|
50
|
+
onSuccess: (encodedReturns) => Effect.match(Ref.decodeReturns(ref, encodedReturns), {
|
|
51
|
+
onFailure: Result.fail,
|
|
52
|
+
onSuccess: Result.succeed
|
|
53
|
+
})
|
|
54
|
+
})));
|
|
55
|
+
};
|
|
56
|
+
const reactiveQuery = (ref, ...rest) => reactiveQueryResult(ref, ...rest).pipe(Stream.mapEffect(Result.match({
|
|
57
|
+
onFailure: Effect.fail,
|
|
58
|
+
onSuccess: Effect.succeed
|
|
59
|
+
})));
|
|
60
|
+
return {
|
|
61
|
+
url: address,
|
|
62
|
+
setAuth,
|
|
63
|
+
query,
|
|
64
|
+
mutation,
|
|
65
|
+
action,
|
|
66
|
+
reactiveQuery,
|
|
67
|
+
reactiveQueryResult
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
const makeScoped = (address, acquire) => Effect.acquireRelease(acquire, (convexClient) => Effect.promise(() => convexClient.close())).pipe(Effect.map((convexClient) => make(address, convexClient)));
|
|
71
|
+
//#endregion
|
|
72
|
+
export { WebSocketClientError, make, makeScoped };
|
|
73
|
+
|
|
74
|
+
//# sourceMappingURL=WebSocketClient.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WebSocketClient.js","names":[],"sources":["../../src/internal/WebSocketClient.ts"],"sourcesContent":["import * as Ref from \"@confect/core/Ref\";\nimport * as Effect from \"effect/Effect\";\nimport * as Queue from \"effect/Queue\";\nimport * as Result from \"effect/Result\";\nimport * as Schema from \"effect/Schema\";\nimport * as Stream from \"effect/Stream\";\n\nexport class WebSocketClientError extends Schema.TaggedError<WebSocketClientError>()(\n \"WebSocketClientError\",\n {\n cause: Schema.Defect(),\n },\n) {}\n\nexport interface Transport {\n readonly setAuth: (\n fetchToken: (args: {\n readonly forceRefreshToken: boolean;\n }) => Promise<string | null | undefined>,\n onChange?: (isAuthenticated: boolean) => void,\n ) => void;\n readonly close: () => Promise<void>;\n readonly query: (\n functionReference: Ref.FunctionReference<Ref.AnyPublicQuery>,\n encodedArgs: unknown,\n ) => PromiseLike<unknown>;\n readonly mutation: (\n functionReference: Ref.FunctionReference<Ref.AnyPublicMutation>,\n encodedArgs: unknown,\n ) => PromiseLike<unknown>;\n readonly action: (\n functionReference: Ref.FunctionReference<Ref.AnyPublicAction>,\n encodedArgs: unknown,\n ) => PromiseLike<unknown>;\n readonly onUpdate: (\n functionReference: Ref.FunctionReference<Ref.AnyPublicQuery>,\n encodedArgs: unknown,\n onUpdate: (result: unknown) => void,\n onError: (error: Error) => void,\n ) => () => void;\n}\n\nconst runQuery = Effect.fn(\"WebSocketClient.query\")(\n <A, E>(effect: Effect.Effect<A, E>): Effect.Effect<A, E> => effect,\n);\n\nconst runMutation = Effect.fn(\"WebSocketClient.mutation\")(\n <A, E>(effect: Effect.Effect<A, E>): Effect.Effect<A, E> => effect,\n);\n\nconst runAction = Effect.fn(\"WebSocketClient.action\")(\n <A, E>(effect: Effect.Effect<A, E>): Effect.Effect<A, E> => effect,\n);\n\nexport const make = (address: string, convexClient: Transport) => {\n const setAuth = <R1 = never, R2 = never>(\n fetchToken: (args: {\n forceRefreshToken: boolean;\n }) => Effect.Effect<string | null | undefined, never, R1>,\n onChange?: (isAuthenticated: boolean) => Effect.Effect<void, never, R2>,\n ): Effect.Effect<void, never, R1 | R2> =>\n Effect.gen(function* () {\n const context = yield* Effect.context<R1 | R2>();\n const runPromise = Effect.runPromiseWith(context);\n const runFork = Effect.runForkWith(context);\n convexClient.setAuth(\n (args) => runPromise(Effect.suspend(() => fetchToken(args))),\n ...(onChange\n ? [\n (isAuthenticated: boolean) => {\n runFork(Effect.suspend(() => onChange(isAuthenticated)));\n },\n ]\n : []),\n );\n });\n\n const mapUnknownError = (cause: unknown) =>\n new WebSocketClientError({ cause });\n\n const query = <Query extends Ref.AnyPublicQuery>(\n ref: Query,\n ...rest: Ref.OptionalArgs<Query>\n ): Effect.Effect<\n Ref.Returns<Query>,\n Ref.Error<Query> | WebSocketClientError | Schema.SchemaError\n > => {\n const args = (rest[0] ?? {}) as Ref.Args<Query>;\n return runQuery(\n Ref.runWithCodec(\n ref,\n args,\n (functionReference, encodedArgs) =>\n convexClient.query(functionReference, encodedArgs),\n mapUnknownError,\n ),\n );\n };\n\n const mutation = <Mutation extends Ref.AnyPublicMutation>(\n ref: Mutation,\n ...rest: Ref.OptionalArgs<Mutation>\n ): Effect.Effect<\n Ref.Returns<Mutation>,\n Ref.Error<Mutation> | WebSocketClientError | Schema.SchemaError\n > => {\n const args = (rest[0] ?? {}) as Ref.Args<Mutation>;\n return runMutation(\n Ref.runWithCodec(\n ref,\n args,\n (functionReference, encodedArgs) =>\n convexClient.mutation(functionReference, encodedArgs),\n mapUnknownError,\n ),\n );\n };\n\n const action = <Action extends Ref.AnyPublicAction>(\n ref: Action,\n ...rest: Ref.OptionalArgs<Action>\n ): Effect.Effect<\n Ref.Returns<Action>,\n Ref.Error<Action> | WebSocketClientError | Schema.SchemaError\n > => {\n const args = (rest[0] ?? {}) as Ref.Args<Action>;\n return runAction(\n Ref.runWithCodec(\n ref,\n args,\n (functionReference, encodedArgs) =>\n convexClient.action(functionReference, encodedArgs),\n mapUnknownError,\n ),\n );\n };\n\n type ReactiveQueryError<Query extends Ref.AnyPublicQuery> =\n | Ref.Error<Query>\n | WebSocketClientError\n | Schema.SchemaError;\n\n const reactiveQueryResult = <Query extends Ref.AnyPublicQuery>(\n ref: Query,\n ...rest: Ref.OptionalArgs<Query>\n ): Stream.Stream<\n Result.Result<Ref.Returns<Query>, ReactiveQueryError<Query>>\n > => {\n type QueryResult = Result.Result<\n Ref.Returns<Query>,\n ReactiveQueryError<Query>\n >;\n const args = (rest[0] ?? {}) as Ref.Args<Query>;\n const functionReference = Ref.getFunctionReference(ref);\n const onError = Ref.decodeErrorOrElse(ref, mapUnknownError);\n\n return Stream.unwrap(\n Effect.match(Ref.encodeArgs(ref, args), {\n onFailure: (error) => Stream.succeed<QueryResult>(Result.fail(error)),\n onSuccess: (encodedArgs) =>\n Stream.callback<Result.Result<unknown, ReactiveQueryError<Query>>>(\n (queue) =>\n Effect.gen(function* () {\n const unsubscribe = convexClient.onUpdate(\n functionReference,\n encodedArgs,\n (result) => {\n Queue.offerUnsafe(queue, Result.succeed(result));\n },\n (error) => {\n Queue.offerUnsafe(queue, Result.fail(onError(error)));\n },\n );\n yield* Effect.addFinalizer(() =>\n Effect.sync(() => unsubscribe()),\n );\n }),\n ),\n }),\n ).pipe(\n Stream.mapEffect((result): Effect.Effect<QueryResult> =>\n Result.match(result, {\n onFailure: (error) => Effect.succeed(Result.fail(error)),\n onSuccess: (encodedReturns) =>\n Effect.match(Ref.decodeReturns(ref, encodedReturns), {\n onFailure: Result.fail,\n onSuccess: Result.succeed,\n }),\n }),\n ),\n );\n };\n\n const reactiveQuery = <Query extends Ref.AnyPublicQuery>(\n ref: Query,\n ...rest: Ref.OptionalArgs<Query>\n ): Stream.Stream<Ref.Returns<Query>, ReactiveQueryError<Query>> =>\n reactiveQueryResult(ref, ...rest).pipe(\n Stream.mapEffect(\n Result.match({\n onFailure: Effect.fail,\n onSuccess: Effect.succeed,\n }),\n ),\n );\n\n return {\n url: address,\n setAuth,\n query,\n mutation,\n action,\n reactiveQuery,\n reactiveQueryResult,\n };\n};\n\nexport const makeScoped = <E, R>(\n address: string,\n acquire: Effect.Effect<Transport, E, R>,\n) =>\n Effect.acquireRelease(acquire, (convexClient) =>\n Effect.promise(() => convexClient.close()),\n ).pipe(Effect.map((convexClient) => make(address, convexClient)));\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,28 +1,32 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@confect/js",
|
|
3
3
|
"description": "JavaScript client bindings for any JS runtime",
|
|
4
|
-
"version": "10.0.0
|
|
4
|
+
"version": "10.0.0",
|
|
5
5
|
"author": "RJ Dellecese",
|
|
6
6
|
"bugs": {
|
|
7
7
|
"url": "https://github.com/rjdellecese/confect/issues"
|
|
8
8
|
},
|
|
9
9
|
"devDependencies": {
|
|
10
|
-
"@effect/vitest": "4.0.0
|
|
11
|
-
"@types/node": "
|
|
12
|
-
"effect": "4.0.0
|
|
13
|
-
"tsdown": "0.
|
|
14
|
-
"typescript": "
|
|
10
|
+
"@effect/vitest": "4.0.0",
|
|
11
|
+
"@types/node": "24.13.3",
|
|
12
|
+
"effect": "4.0.0",
|
|
13
|
+
"tsdown": "0.23.0",
|
|
14
|
+
"typescript": "7.0.2",
|
|
15
15
|
"unrun": "0.3.1",
|
|
16
|
-
"vitest": "
|
|
16
|
+
"vitest": "5.0.2"
|
|
17
17
|
},
|
|
18
18
|
"engines": {
|
|
19
|
-
"node": ">=
|
|
19
|
+
"node": ">=24"
|
|
20
20
|
},
|
|
21
21
|
"exports": {
|
|
22
22
|
".": {
|
|
23
23
|
"types": "./dist/index.d.ts",
|
|
24
24
|
"default": "./dist/index.js"
|
|
25
25
|
},
|
|
26
|
+
"./*": {
|
|
27
|
+
"types": "./dist/*.d.ts",
|
|
28
|
+
"default": "./dist/*.js"
|
|
29
|
+
},
|
|
26
30
|
"./package.json": "./package.json"
|
|
27
31
|
},
|
|
28
32
|
"files": [
|
|
@@ -30,6 +34,7 @@
|
|
|
30
34
|
"LICENSE",
|
|
31
35
|
"README.md",
|
|
32
36
|
"dist",
|
|
37
|
+
"!dist/*.tsbuildinfo",
|
|
33
38
|
"package.json",
|
|
34
39
|
"src"
|
|
35
40
|
],
|
|
@@ -42,21 +47,22 @@
|
|
|
42
47
|
"main": "./dist/index.js",
|
|
43
48
|
"module": "./dist/index.js",
|
|
44
49
|
"peerDependencies": {
|
|
50
|
+
"@confect/core": "^10.0.0",
|
|
45
51
|
"convex": "^1.32.0",
|
|
46
|
-
"effect": "^4.0.0
|
|
47
|
-
"@confect/core": "^10.0.0-next.8"
|
|
52
|
+
"effect": "^4.0.0"
|
|
48
53
|
},
|
|
49
54
|
"repository": {
|
|
50
55
|
"type": "git",
|
|
51
|
-
"url": "https://github.com/rjdellecese/confect.git"
|
|
56
|
+
"url": "git+https://github.com/rjdellecese/confect.git"
|
|
52
57
|
},
|
|
53
58
|
"sideEffects": false,
|
|
54
59
|
"type": "module",
|
|
55
60
|
"types": "./dist/index.d.ts",
|
|
56
61
|
"scripts": {
|
|
57
|
-
"clean": "rm -rf dist coverage node_modules",
|
|
58
|
-
"dev": "tsdown --watch --config-loader unrun",
|
|
62
|
+
"clean": "shx rm -rf dist coverage node_modules",
|
|
63
|
+
"dev": "tsdown --watch --no-clean --config-loader unrun",
|
|
59
64
|
"test": "vitest run",
|
|
60
|
-
"typecheck": "tsc -b tsconfig.src.json"
|
|
65
|
+
"typecheck": "tsc -b tsconfig.src.json",
|
|
66
|
+
"typecheck:inexact": "tsc --noEmit --project tsconfig.json --exactOptionalPropertyTypes false"
|
|
61
67
|
}
|
|
62
68
|
}
|
package/src/HttpClient.ts
CHANGED
|
@@ -1,99 +1,32 @@
|
|
|
1
|
-
import * as Ref from "@confect/core/Ref";
|
|
2
1
|
import { ConvexHttpClient } from "convex/browser";
|
|
3
2
|
import * as Context from "effect/Context";
|
|
4
|
-
import * as Effect from "effect/Effect";
|
|
5
3
|
import * as Layer from "effect/Layer";
|
|
6
|
-
import * as
|
|
4
|
+
import * as InternalHttpClient from "./internal/HttpClient";
|
|
7
5
|
|
|
8
|
-
export
|
|
9
|
-
"HttpClientError",
|
|
10
|
-
{
|
|
11
|
-
cause: Schema.Unknown,
|
|
12
|
-
},
|
|
13
|
-
) {}
|
|
6
|
+
export { HttpClientError } from "./internal/HttpClient";
|
|
14
7
|
|
|
15
8
|
const make = (
|
|
16
9
|
address: string,
|
|
17
10
|
options?: ConstructorParameters<typeof ConvexHttpClient>[1],
|
|
18
11
|
) => {
|
|
19
12
|
const client = new ConvexHttpClient(address, options);
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
client.
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
13
|
+
return InternalHttpClient.make({
|
|
14
|
+
url: client.url,
|
|
15
|
+
setAuth: (token) => client.setAuth(token),
|
|
16
|
+
clearAuth: () => client.clearAuth(),
|
|
17
|
+
query: (functionReference, encodedArgs) =>
|
|
18
|
+
client.query(functionReference, encodedArgs),
|
|
19
|
+
mutation: (functionReference, encodedArgs) =>
|
|
20
|
+
client.mutation(functionReference, encodedArgs),
|
|
21
|
+
action: (functionReference, encodedArgs) =>
|
|
22
|
+
client.action(functionReference, encodedArgs),
|
|
30
23
|
});
|
|
31
|
-
|
|
32
|
-
const mapUnknownError = (cause: unknown) => new HttpClientError({ cause });
|
|
33
|
-
|
|
34
|
-
const query = <Query extends Ref.AnyPublicQuery>(
|
|
35
|
-
ref: Query,
|
|
36
|
-
...rest: Ref.OptionalArgs<Query>
|
|
37
|
-
): Effect.Effect<
|
|
38
|
-
Ref.Returns<Query>,
|
|
39
|
-
Ref.Error<Query> | HttpClientError | Schema.SchemaError
|
|
40
|
-
> => {
|
|
41
|
-
const args = (rest[0] ?? {}) as Ref.Args<Query>;
|
|
42
|
-
return Ref.runWithCodec(
|
|
43
|
-
ref,
|
|
44
|
-
args,
|
|
45
|
-
(functionReference, encodedArgs) =>
|
|
46
|
-
client.query(functionReference, encodedArgs),
|
|
47
|
-
mapUnknownError,
|
|
48
|
-
);
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
const mutation = <Mutation extends Ref.AnyPublicMutation>(
|
|
52
|
-
ref: Mutation,
|
|
53
|
-
...rest: Ref.OptionalArgs<Mutation>
|
|
54
|
-
): Effect.Effect<
|
|
55
|
-
Ref.Returns<Mutation>,
|
|
56
|
-
Ref.Error<Mutation> | HttpClientError | Schema.SchemaError
|
|
57
|
-
> => {
|
|
58
|
-
const args = (rest[0] ?? {}) as Ref.Args<Mutation>;
|
|
59
|
-
return Ref.runWithCodec(
|
|
60
|
-
ref,
|
|
61
|
-
args,
|
|
62
|
-
(functionReference, encodedArgs) =>
|
|
63
|
-
client.mutation(functionReference, encodedArgs),
|
|
64
|
-
mapUnknownError,
|
|
65
|
-
);
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
const action = <Action extends Ref.AnyPublicAction>(
|
|
69
|
-
ref: Action,
|
|
70
|
-
...rest: Ref.OptionalArgs<Action>
|
|
71
|
-
): Effect.Effect<
|
|
72
|
-
Ref.Returns<Action>,
|
|
73
|
-
Ref.Error<Action> | HttpClientError | Schema.SchemaError
|
|
74
|
-
> => {
|
|
75
|
-
const args = (rest[0] ?? {}) as Ref.Args<Action>;
|
|
76
|
-
return Ref.runWithCodec(
|
|
77
|
-
ref,
|
|
78
|
-
args,
|
|
79
|
-
(functionReference, encodedArgs) =>
|
|
80
|
-
client.action(functionReference, encodedArgs),
|
|
81
|
-
mapUnknownError,
|
|
82
|
-
);
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
return {
|
|
86
|
-
url,
|
|
87
|
-
setAuth,
|
|
88
|
-
clearAuth,
|
|
89
|
-
query,
|
|
90
|
-
mutation,
|
|
91
|
-
action,
|
|
92
|
-
};
|
|
93
24
|
};
|
|
94
25
|
|
|
95
26
|
/**
|
|
96
|
-
* A Confect client which uses HTTP to communicate with your Convex backend.
|
|
27
|
+
* A Confect client which uses HTTP to communicate with your Convex backend.
|
|
28
|
+
* Works in any JS runtime that supports `fetch`. Wraps
|
|
29
|
+
* [ConvexHttpClient](https://docs.convex.dev/api/classes/browser.ConvexHttpClient).
|
|
97
30
|
*/
|
|
98
31
|
export const HttpClient = Context.Service<ReturnType<typeof make>>(
|
|
99
32
|
"@confect/js/HttpClient",
|
package/src/WebSocketClient.ts
CHANGED
|
@@ -1,159 +1,48 @@
|
|
|
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
|
|
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
|
|
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
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
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
|
);
|
|
154
39
|
|
|
155
40
|
/**
|
|
156
|
-
* A Confect client which uses a WebSocket to communicate with your Convex
|
|
41
|
+
* A Confect client which uses a WebSocket to communicate with your Convex
|
|
42
|
+
* backend and supports reactive query subscriptions. The WebSocket connection
|
|
43
|
+
* is managed by the layer's scope and closed automatically when the scope ends.
|
|
44
|
+
* Wraps
|
|
45
|
+
* [ConvexClient](https://docs.convex.dev/api/classes/browser.ConvexClient).
|
|
157
46
|
*/
|
|
158
47
|
export const WebSocketClient = Context.Service<
|
|
159
48
|
Effect.Success<ReturnType<typeof make>>
|
|
@@ -0,0 +1,119 @@
|
|
|
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
|
+
const runQuery = Effect.fn("HttpClient.query")(
|
|
31
|
+
<A, E>(effect: Effect.Effect<A, E>): Effect.Effect<A, E> => effect,
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
const runMutation = Effect.fn("HttpClient.mutation")(
|
|
35
|
+
<A, E>(effect: Effect.Effect<A, E>): Effect.Effect<A, E> => effect,
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
const runAction = Effect.fn("HttpClient.action")(
|
|
39
|
+
<A, E>(effect: Effect.Effect<A, E>): Effect.Effect<A, E> => effect,
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
export const make = (client: Transport) => {
|
|
43
|
+
const setAuth = (token: string) =>
|
|
44
|
+
Effect.sync(() => {
|
|
45
|
+
client.setAuth(token);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const clearAuth = Effect.sync(() => {
|
|
49
|
+
client.clearAuth();
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const mapUnknownError = (cause: unknown) => new HttpClientError({ cause });
|
|
53
|
+
|
|
54
|
+
const query = <Query extends Ref.AnyPublicQuery>(
|
|
55
|
+
ref: Query,
|
|
56
|
+
...rest: Ref.OptionalArgs<Query>
|
|
57
|
+
): Effect.Effect<
|
|
58
|
+
Ref.Returns<Query>,
|
|
59
|
+
Ref.Error<Query> | HttpClientError | Schema.SchemaError
|
|
60
|
+
> => {
|
|
61
|
+
const args = (rest[0] ?? {}) as Ref.Args<Query>;
|
|
62
|
+
return runQuery(
|
|
63
|
+
Ref.runWithCodec(
|
|
64
|
+
ref,
|
|
65
|
+
args,
|
|
66
|
+
(functionReference, encodedArgs) =>
|
|
67
|
+
client.query(functionReference, encodedArgs),
|
|
68
|
+
mapUnknownError,
|
|
69
|
+
),
|
|
70
|
+
);
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const mutation = <Mutation extends Ref.AnyPublicMutation>(
|
|
74
|
+
ref: Mutation,
|
|
75
|
+
...rest: Ref.OptionalArgs<Mutation>
|
|
76
|
+
): Effect.Effect<
|
|
77
|
+
Ref.Returns<Mutation>,
|
|
78
|
+
Ref.Error<Mutation> | HttpClientError | Schema.SchemaError
|
|
79
|
+
> => {
|
|
80
|
+
const args = (rest[0] ?? {}) as Ref.Args<Mutation>;
|
|
81
|
+
return runMutation(
|
|
82
|
+
Ref.runWithCodec(
|
|
83
|
+
ref,
|
|
84
|
+
args,
|
|
85
|
+
(functionReference, encodedArgs) =>
|
|
86
|
+
client.mutation(functionReference, encodedArgs),
|
|
87
|
+
mapUnknownError,
|
|
88
|
+
),
|
|
89
|
+
);
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const action = <Action extends Ref.AnyPublicAction>(
|
|
93
|
+
ref: Action,
|
|
94
|
+
...rest: Ref.OptionalArgs<Action>
|
|
95
|
+
): Effect.Effect<
|
|
96
|
+
Ref.Returns<Action>,
|
|
97
|
+
Ref.Error<Action> | HttpClientError | Schema.SchemaError
|
|
98
|
+
> => {
|
|
99
|
+
const args = (rest[0] ?? {}) as Ref.Args<Action>;
|
|
100
|
+
return runAction(
|
|
101
|
+
Ref.runWithCodec(
|
|
102
|
+
ref,
|
|
103
|
+
args,
|
|
104
|
+
(functionReference, encodedArgs) =>
|
|
105
|
+
client.action(functionReference, encodedArgs),
|
|
106
|
+
mapUnknownError,
|
|
107
|
+
),
|
|
108
|
+
);
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
return {
|
|
112
|
+
url: client.url,
|
|
113
|
+
setAuth,
|
|
114
|
+
clearAuth,
|
|
115
|
+
query,
|
|
116
|
+
mutation,
|
|
117
|
+
action,
|
|
118
|
+
};
|
|
119
|
+
};
|