@confect/js 4.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 ADDED
@@ -0,0 +1,11 @@
1
+ # @confect/js
2
+
3
+ ## 4.0.0
4
+
5
+ ### Minor Changes
6
+
7
+ - dbefea8: Add `HttpClient`, an Effect service wrapping Convex's `ConvexHttpClient` with automatic schema encoding and decoding. Works in any JavaScript runtime that supports `fetch`.
8
+
9
+ ### Patch Changes
10
+
11
+ - @confect/core@4.0.0
package/LICENSE ADDED
@@ -0,0 +1,7 @@
1
+ ISC License
2
+
3
+ Copyright 2024 RJ Dellecese
4
+
5
+ Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,11 @@
1
+ # Confect 🧁
2
+
3
+ Confect is a framework that deeply integrates Effect with Convex. It's more than just Effect bindings! Confect allows you to:
4
+
5
+ - Define your Convex database schema using Effect schemas.
6
+ - Write Convex function args and returns validators using Effect's schema library.
7
+ - Use Confect functions to automatically decode and encode your data according to your Effect schema definitions for end-to-end rich types, from client to function to database (and back).
8
+ - Use Effect's HTTP API modules to define your HTTP API(s). Includes interactive OpenAPI documentation powered by [Scalar](https://github.com/scalar/scalar).
9
+ - Access Convex platform capabilities via Effect services.
10
+
11
+ Want to learn more? Read the [docs](https://confect.dev)!
@@ -0,0 +1,45 @@
1
+ import * as Ref from "@confect/core/Ref";
2
+ import { ConvexHttpClient } from "convex/browser";
3
+ import { Context, Effect, Layer, ParseResult, Schema } from "effect";
4
+
5
+ //#region src/HttpClient.d.ts
6
+ declare namespace HttpClient_d_exports {
7
+ export { HttpClient, HttpClientError, layer };
8
+ }
9
+ declare const HttpClientError_base: Schema.TaggedErrorClass<HttpClientError, "HttpClientError", {
10
+ readonly _tag: Schema.tag<"HttpClientError">;
11
+ } & {
12
+ cause: typeof Schema.Unknown;
13
+ }>;
14
+ declare class HttpClientError extends HttpClientError_base {}
15
+ type OptionalArgs<R extends Ref.AnyQuery | Ref.AnyMutation | Ref.AnyAction> = keyof Ref.Args<R> extends never ? [args?: Ref.Args<R>] : [args: Ref.Args<R>];
16
+ /**
17
+ * A Confect client which uses HTTP to communicate with your Convex backend. Works in any JS runtime that supports `fetch`. Wraps [ConvexHttpClient](https://docs.convex.dev/api/classes/browser.ConvexHttpClient).
18
+ */
19
+ declare const HttpClient: Context.Tag<{
20
+ url: string;
21
+ setAuth: (token: string) => Effect.Effect<void, never, never>;
22
+ clearAuth: Effect.Effect<void, never, never>;
23
+ query: <Query extends Ref.AnyQuery>(ref: Query, ...rest: OptionalArgs<Query>) => Effect.Effect<Ref.Returns<Query>, HttpClientError | ParseResult.ParseError>;
24
+ mutation: <Mutation extends Ref.AnyMutation>(ref: Mutation, ...rest: OptionalArgs<Mutation>) => Effect.Effect<Ref.Returns<Mutation>, HttpClientError | ParseResult.ParseError>;
25
+ action: <Action extends Ref.AnyAction>(ref: Action, ...rest: OptionalArgs<Action>) => Effect.Effect<Ref.Returns<Action>, HttpClientError | ParseResult.ParseError>;
26
+ }, {
27
+ url: string;
28
+ setAuth: (token: string) => Effect.Effect<void, never, never>;
29
+ clearAuth: Effect.Effect<void, never, never>;
30
+ query: <Query extends Ref.AnyQuery>(ref: Query, ...rest: OptionalArgs<Query>) => Effect.Effect<Ref.Returns<Query>, HttpClientError | ParseResult.ParseError>;
31
+ mutation: <Mutation extends Ref.AnyMutation>(ref: Mutation, ...rest: OptionalArgs<Mutation>) => Effect.Effect<Ref.Returns<Mutation>, HttpClientError | ParseResult.ParseError>;
32
+ action: <Action extends Ref.AnyAction>(ref: Action, ...rest: OptionalArgs<Action>) => Effect.Effect<Ref.Returns<Action>, HttpClientError | ParseResult.ParseError>;
33
+ }>;
34
+ type HttpClient = typeof HttpClient.Identifier;
35
+ declare const layer: (address: string, options?: ConstructorParameters<typeof ConvexHttpClient>[1]) => Layer.Layer<{
36
+ url: string;
37
+ setAuth: (token: string) => Effect.Effect<void, never, never>;
38
+ clearAuth: Effect.Effect<void, never, never>;
39
+ query: <Query extends Ref.AnyQuery>(ref: Query, ...rest: OptionalArgs<Query>) => Effect.Effect<Ref.Returns<Query>, HttpClientError | ParseResult.ParseError>;
40
+ mutation: <Mutation extends Ref.AnyMutation>(ref: Mutation, ...rest: OptionalArgs<Mutation>) => Effect.Effect<Ref.Returns<Mutation>, HttpClientError | ParseResult.ParseError>;
41
+ action: <Action extends Ref.AnyAction>(ref: Action, ...rest: OptionalArgs<Action>) => Effect.Effect<Ref.Returns<Action>, HttpClientError | ParseResult.ParseError>;
42
+ }, never, never>;
43
+ //#endregion
44
+ export { HttpClient_d_exports };
45
+ //# sourceMappingURL=HttpClient.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HttpClient.d.ts","names":[],"sources":["../src/HttpClient.ts"],"mappings":";;;;;;;;cAI+D,oBAAA;;;;;cAElD,eAAA,SAAwB,oBAAA;AAAA,KAOhC,YAAA,WAAuB,GAAA,CAAI,QAAA,GAAW,GAAA,CAAI,WAAA,GAAc,GAAA,CAAI,SAAA,UACzD,GAAA,CAAI,IAAA,CAAK,CAAA,mBAAoB,IAAA,GAAO,GAAA,CAAI,IAAA,CAAK,CAAA,MAAO,IAAA,EAAM,GAAA,CAAI,IAAA,CAAK,CAAA;;;;cA2J9D,UAAA,EAAU,OAAA,CAAA,GAAA;;8BAjJS,MAAA,CAAA,MAAA;;wBASD,GAAA,CAAI,QAAA,EAAQ,GAAA,EAClC,KAAA,KAAK,IAAA,EACD,YAAA,CAAa,KAAA,MACrB,MAAA,CAAO,MAAA,CACR,GAAA,CAAI,OAAA,CAAQ,KAAA,GACZ,eAAA,GAAkB,WAAA,CAAY,UAAA;8BAoCG,GAAA,CAAI,WAAA,EAAW,GAAA,EAC3C,QAAA,KAAQ,IAAA,EACJ,YAAA,CAAa,QAAA,MACrB,MAAA,CAAO,MAAA,CACR,GAAA,CAAI,OAAA,CAAQ,QAAA,GACZ,eAAA,GAAkB,WAAA,CAAY,UAAA;0BAoCD,GAAA,CAAI,SAAA,EAAS,GAAA,EACrC,MAAA,KAAM,IAAA,EACF,YAAA,CAAa,MAAA,MACrB,MAAA,CAAO,MAAA,CACR,GAAA,CAAI,OAAA,CAAQ,MAAA,GACZ,eAAA,GAAkB,WAAA,CAAY,UAAA;AAAA;;8BAhGF,MAAA,CAAA,MAAA;;wBASD,GAAA,CAAI,QAAA,EAAQ,GAAA,EAClC,KAAA,KAAK,IAAA,EACD,YAAA,CAAa,KAAA,MACrB,MAAA,CAAO,MAAA,CACR,GAAA,CAAI,OAAA,CAAQ,KAAA,GACZ,eAAA,GAAkB,WAAA,CAAY,UAAA;8BAoCG,GAAA,CAAI,WAAA,EAAW,GAAA,EAC3C,QAAA,KAAQ,IAAA,EACJ,YAAA,CAAa,QAAA,MACrB,MAAA,CAAO,MAAA,CACR,GAAA,CAAI,OAAA,CAAQ,QAAA,GACZ,eAAA,GAAkB,WAAA,CAAY,UAAA;0BAoCD,GAAA,CAAI,SAAA,EAAS,GAAA,EACrC,MAAA,KAAM,IAAA,EACF,YAAA,CAAa,MAAA,MACrB,MAAA,CAAO,MAAA,CACR,GAAA,CAAI,OAAA,CAAQ,MAAA,GACZ,eAAA,GAAkB,WAAA,CAAY,UAAA;AAAA;AAAA,KAqDtB,UAAA,UAAoB,UAAA,CAAW,UAAA;AAAA,cAE9B,KAAA,GACX,OAAA,UACA,OAAA,GAAU,qBAAA,QAA6B,gBAAA,SAAoB,KAAA,CAAA,KAAA;;8BAzJ7B,MAAA,CAAA,MAAA;;wBASD,GAAA,CAAI,QAAA,EAAQ,GAAA,EAClC,KAAA,KAAK,IAAA,EACD,YAAA,CAAa,KAAA,MACrB,MAAA,CAAO,MAAA,CACR,GAAA,CAAI,OAAA,CAAQ,KAAA,GACZ,eAAA,GAAkB,WAAA,CAAY,UAAA;8BAoCG,GAAA,CAAI,WAAA,EAAW,GAAA,EAC3C,QAAA,KAAQ,IAAA,EACJ,YAAA,CAAa,QAAA,MACrB,MAAA,CAAO,MAAA,CACR,GAAA,CAAI,OAAA,CAAQ,QAAA,GACZ,eAAA,GAAkB,WAAA,CAAY,UAAA;0BAoCD,GAAA,CAAI,SAAA,EAAS,GAAA,EACrC,MAAA,KAAM,IAAA,EACF,YAAA,CAAa,MAAA,MACrB,MAAA,CAAO,MAAA,CACR,GAAA,CAAI,OAAA,CAAQ,MAAA,GACZ,eAAA,GAAkB,WAAA,CAAY,UAAA;AAAA"}
@@ -0,0 +1,87 @@
1
+ import { __exportAll } from "./_virtual/_rolldown/runtime.js";
2
+ import * as Ref from "@confect/core/Ref";
3
+ import { ConvexHttpClient } from "convex/browser";
4
+ import { Context, Effect, Layer, Match, Schema } from "effect";
5
+
6
+ //#region src/HttpClient.ts
7
+ var HttpClient_exports = /* @__PURE__ */ __exportAll({
8
+ HttpClient: () => HttpClient,
9
+ HttpClientError: () => HttpClientError,
10
+ layer: () => layer
11
+ });
12
+ var HttpClientError = class extends Schema.TaggedError()("HttpClientError", { cause: Schema.Unknown }) {};
13
+ const make = (address, options) => {
14
+ const client = new ConvexHttpClient(address, options);
15
+ const url = client.url;
16
+ const setAuth = (token) => Effect.sync(() => {
17
+ client.setAuth(token);
18
+ });
19
+ const clearAuth = Effect.sync(() => {
20
+ client.clearAuth();
21
+ });
22
+ const query = (ref, ...rest) => Effect.gen(function* () {
23
+ const args = rest[0] ?? {};
24
+ const functionSpec = Ref.getFunctionSpec(ref);
25
+ const functionName = Ref.getConvexFunctionName(ref);
26
+ return yield* Match.value(functionSpec.functionProvenance).pipe(Match.tag("Confect", (confectFunctionSpec) => Effect.gen(function* () {
27
+ const encodedArgs = yield* Schema.encode(confectFunctionSpec.args)(args);
28
+ const encodedResult = yield* Effect.tryPromise({
29
+ try: () => client.query(functionName, encodedArgs),
30
+ catch: (cause) => new HttpClientError({ cause })
31
+ });
32
+ return yield* Schema.decode(confectFunctionSpec.returns)(encodedResult);
33
+ })), Match.tag("Convex", () => Effect.tryPromise({
34
+ try: () => client.query(functionName, args),
35
+ catch: (cause) => new HttpClientError({ cause })
36
+ })), Match.exhaustive);
37
+ });
38
+ const mutation = (ref, ...rest) => Effect.gen(function* () {
39
+ const args = rest[0] ?? {};
40
+ const functionSpec = Ref.getFunctionSpec(ref);
41
+ const functionName = Ref.getConvexFunctionName(ref);
42
+ return yield* Match.value(functionSpec.functionProvenance).pipe(Match.tag("Confect", (confectFunctionSpec) => Effect.gen(function* () {
43
+ const encodedArgs = yield* Schema.encode(confectFunctionSpec.args)(args);
44
+ const encodedResult = yield* Effect.tryPromise({
45
+ try: () => client.mutation(functionName, encodedArgs),
46
+ catch: (cause) => new HttpClientError({ cause })
47
+ });
48
+ return yield* Schema.decode(confectFunctionSpec.returns)(encodedResult);
49
+ })), Match.tag("Convex", () => Effect.tryPromise({
50
+ try: () => client.mutation(functionName, args),
51
+ catch: (cause) => new HttpClientError({ cause })
52
+ })), Match.exhaustive);
53
+ });
54
+ const action = (ref, ...rest) => Effect.gen(function* () {
55
+ const args = rest[0] ?? {};
56
+ const functionSpec = Ref.getFunctionSpec(ref);
57
+ const functionName = Ref.getConvexFunctionName(ref);
58
+ return yield* Match.value(functionSpec.functionProvenance).pipe(Match.tag("Confect", (confectFunctionSpec) => Effect.gen(function* () {
59
+ const encodedArgs = yield* Schema.encode(confectFunctionSpec.args)(args);
60
+ const encodedResult = yield* Effect.tryPromise({
61
+ try: () => client.action(functionName, encodedArgs),
62
+ catch: (cause) => new HttpClientError({ cause })
63
+ });
64
+ return yield* Schema.decode(confectFunctionSpec.returns)(encodedResult);
65
+ })), Match.tag("Convex", () => Effect.tryPromise({
66
+ try: () => client.action(functionName, args),
67
+ catch: (cause) => new HttpClientError({ cause })
68
+ })), Match.exhaustive);
69
+ });
70
+ return {
71
+ url,
72
+ setAuth,
73
+ clearAuth,
74
+ query,
75
+ mutation,
76
+ action
77
+ };
78
+ };
79
+ /**
80
+ * A Confect client which uses HTTP to communicate with your Convex backend. Works in any JS runtime that supports `fetch`. Wraps [ConvexHttpClient](https://docs.convex.dev/api/classes/browser.ConvexHttpClient).
81
+ */
82
+ const HttpClient = Context.GenericTag("@confect/js/HttpClient");
83
+ const layer = (address, options) => Layer.sync(HttpClient, () => make(address, options));
84
+
85
+ //#endregion
86
+ export { HttpClient_exports };
87
+ //# sourceMappingURL=HttpClient.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HttpClient.js","names":[],"sources":["../src/HttpClient.ts"],"sourcesContent":["import * as Ref from \"@confect/core/Ref\";\nimport { ConvexHttpClient } from \"convex/browser\";\nimport type { FunctionReference } from \"convex/server\";\nimport type { ParseResult } from \"effect\";\nimport { Context, Effect, Layer, Match, Schema } from \"effect\";\n\nexport class HttpClientError extends Schema.TaggedError<HttpClientError>()(\n \"HttpClientError\",\n {\n cause: Schema.Unknown,\n },\n) {}\n\ntype OptionalArgs<R extends Ref.AnyQuery | Ref.AnyMutation | Ref.AnyAction> =\n keyof Ref.Args<R> extends never ? [args?: Ref.Args<R>] : [args: Ref.Args<R>];\n\nconst make = (\n address: string,\n options?: ConstructorParameters<typeof ConvexHttpClient>[1],\n) => {\n const client = new ConvexHttpClient(address, options);\n\n const url = client.url;\n\n const setAuth = (token: string) =>\n Effect.sync(() => {\n client.setAuth(token);\n });\n\n const clearAuth = Effect.sync(() => {\n client.clearAuth();\n });\n\n const query = <Query extends Ref.AnyQuery>(\n ref: Query,\n ...rest: OptionalArgs<Query>\n ): Effect.Effect<\n Ref.Returns<Query>,\n HttpClientError | ParseResult.ParseError\n > =>\n Effect.gen(function* () {\n const args = (rest[0] ?? {}) as Ref.Args<Query>;\n const functionSpec = Ref.getFunctionSpec(ref);\n const functionName = Ref.getConvexFunctionName(\n ref,\n ) as unknown as FunctionReference<\"query\">;\n\n return yield* Match.value(functionSpec.functionProvenance).pipe(\n Match.tag(\"Confect\", (confectFunctionSpec) =>\n Effect.gen(function* () {\n const encodedArgs = yield* Schema.encode(confectFunctionSpec.args)(\n args,\n );\n\n const encodedResult = yield* Effect.tryPromise({\n try: () => client.query(functionName, encodedArgs),\n catch: (cause) => new HttpClientError({ cause }),\n });\n\n return yield* Schema.decode(confectFunctionSpec.returns)(\n encodedResult,\n );\n }),\n ),\n Match.tag(\"Convex\", () =>\n Effect.tryPromise({\n try: () => client.query(functionName, args),\n catch: (cause) => new HttpClientError({ cause }),\n }),\n ),\n Match.exhaustive,\n );\n });\n\n const mutation = <Mutation extends Ref.AnyMutation>(\n ref: Mutation,\n ...rest: OptionalArgs<Mutation>\n ): Effect.Effect<\n Ref.Returns<Mutation>,\n HttpClientError | ParseResult.ParseError\n > =>\n Effect.gen(function* () {\n const args = (rest[0] ?? {}) as Ref.Args<Mutation>;\n const functionSpec = Ref.getFunctionSpec(ref);\n const functionName = Ref.getConvexFunctionName(\n ref,\n ) as unknown as FunctionReference<\"mutation\">;\n\n return yield* Match.value(functionSpec.functionProvenance).pipe(\n Match.tag(\"Confect\", (confectFunctionSpec) =>\n Effect.gen(function* () {\n const encodedArgs = yield* Schema.encode(confectFunctionSpec.args)(\n args,\n );\n\n const encodedResult = yield* Effect.tryPromise({\n try: () => client.mutation(functionName, encodedArgs),\n catch: (cause) => new HttpClientError({ cause }),\n });\n\n return yield* Schema.decode(confectFunctionSpec.returns)(\n encodedResult,\n );\n }),\n ),\n Match.tag(\"Convex\", () =>\n Effect.tryPromise({\n try: () => client.mutation(functionName, args),\n catch: (cause) => new HttpClientError({ cause }),\n }),\n ),\n Match.exhaustive,\n );\n });\n\n const action = <Action extends Ref.AnyAction>(\n ref: Action,\n ...rest: OptionalArgs<Action>\n ): Effect.Effect<\n Ref.Returns<Action>,\n HttpClientError | ParseResult.ParseError\n > =>\n Effect.gen(function* () {\n const args = (rest[0] ?? {}) as Ref.Args<Action>;\n const functionSpec = Ref.getFunctionSpec(ref);\n const functionName = Ref.getConvexFunctionName(\n ref,\n ) as unknown as FunctionReference<\"action\">;\n\n return yield* Match.value(functionSpec.functionProvenance).pipe(\n Match.tag(\"Confect\", (confectFunctionSpec) =>\n Effect.gen(function* () {\n const encodedArgs = yield* Schema.encode(confectFunctionSpec.args)(\n args,\n );\n\n const encodedResult = yield* Effect.tryPromise({\n try: () => client.action(functionName, encodedArgs),\n catch: (cause) => new HttpClientError({ cause }),\n });\n\n return yield* Schema.decode(confectFunctionSpec.returns)(\n encodedResult,\n );\n }),\n ),\n Match.tag(\"Convex\", () =>\n Effect.tryPromise({\n try: () => client.action(functionName, args),\n catch: (cause) => new HttpClientError({ cause }),\n }),\n ),\n Match.exhaustive,\n );\n });\n\n return {\n url,\n setAuth,\n clearAuth,\n query,\n mutation,\n action,\n };\n};\n\n/**\n * A Confect client which uses HTTP to communicate with your Convex backend. Works in any JS runtime that supports `fetch`. Wraps [ConvexHttpClient](https://docs.convex.dev/api/classes/browser.ConvexHttpClient).\n */\nexport const HttpClient = Context.GenericTag<ReturnType<typeof make>>(\n \"@confect/js/HttpClient\",\n);\n\nexport type HttpClient = typeof HttpClient.Identifier;\n\nexport const layer = (\n address: string,\n options?: ConstructorParameters<typeof ConvexHttpClient>[1],\n) => Layer.sync(HttpClient, () => make(address, options));\n"],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ //#region \0rolldown/runtime.js
2
+ var __defProp = Object.defineProperty;
3
+ var __exportAll = (all, no_symbols) => {
4
+ let target = {};
5
+ for (var name in all) {
6
+ __defProp(target, name, {
7
+ get: all[name],
8
+ enumerable: true
9
+ });
10
+ }
11
+ if (!no_symbols) {
12
+ __defProp(target, Symbol.toStringTag, { value: "Module" });
13
+ }
14
+ return target;
15
+ };
16
+
17
+ //#endregion
18
+ export { __exportAll };
@@ -0,0 +1,2 @@
1
+ import { HttpClient_d_exports } from "./HttpClient.js";
2
+ export { HttpClient_d_exports as HttpClient };
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ import { HttpClient_exports } from "./HttpClient.js";
2
+
3
+ export { HttpClient_exports as HttpClient };
package/package.json ADDED
@@ -0,0 +1,70 @@
1
+ {
2
+ "name": "@confect/js",
3
+ "version": "4.0.0",
4
+ "description": "JavaScript client bindings for any JS runtime",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/rjdellecese/confect.git"
8
+ },
9
+ "bugs": {
10
+ "url": "https://github.com/rjdellecese/confect/issues"
11
+ },
12
+ "homepage": "https://confect.dev",
13
+ "sideEffects": false,
14
+ "type": "module",
15
+ "files": [
16
+ "CHANGELOG.md",
17
+ "LICENSE",
18
+ "README.md",
19
+ "dist",
20
+ "package.json",
21
+ "src"
22
+ ],
23
+ "exports": {
24
+ ".": {
25
+ "types": "./dist/index.d.ts",
26
+ "default": "./dist/index.js"
27
+ },
28
+ "./package.json": "./package.json"
29
+ },
30
+ "keywords": [
31
+ "effect",
32
+ "convex"
33
+ ],
34
+ "author": "RJ Dellecese",
35
+ "license": "ISC",
36
+ "devDependencies": {
37
+ "@effect/vitest": "0.27.0",
38
+ "@eslint/js": "10.0.1",
39
+ "@types/node": "25.3.3",
40
+ "effect": "3.19.19",
41
+ "eslint": "10.0.2",
42
+ "prettier": "3.8.1",
43
+ "tsdown": "0.20.3",
44
+ "typescript": "5.9.3",
45
+ "typescript-eslint": "8.56.1",
46
+ "vitest": "3.2.4"
47
+ },
48
+ "peerDependencies": {
49
+ "convex": "^1.30.0",
50
+ "effect": "^3.19.16",
51
+ "@confect/core": "4.0.0"
52
+ },
53
+ "engines": {
54
+ "node": ">=22",
55
+ "pnpm": ">=10"
56
+ },
57
+ "main": "./dist/index.js",
58
+ "module": "./dist/index.js",
59
+ "types": "./dist/index.d.ts",
60
+ "scripts": {
61
+ "build": "tsdown --config-loader unrun",
62
+ "dev": "tsdown --watch",
63
+ "test": "vitest run",
64
+ "typecheck": "tsc --noEmit --project tsconfig.json",
65
+ "fix": "prettier --write . && eslint --fix . --max-warnings=0",
66
+ "format": "prettier --check .",
67
+ "lint": "eslint . --max-warnings=0",
68
+ "clean": "rm -rf dist coverage node_modules"
69
+ }
70
+ }
@@ -0,0 +1,179 @@
1
+ import * as Ref from "@confect/core/Ref";
2
+ import { ConvexHttpClient } from "convex/browser";
3
+ import type { FunctionReference } from "convex/server";
4
+ import type { ParseResult } from "effect";
5
+ import { Context, Effect, Layer, Match, Schema } from "effect";
6
+
7
+ export class HttpClientError extends Schema.TaggedError<HttpClientError>()(
8
+ "HttpClientError",
9
+ {
10
+ cause: Schema.Unknown,
11
+ },
12
+ ) {}
13
+
14
+ type OptionalArgs<R extends Ref.AnyQuery | Ref.AnyMutation | Ref.AnyAction> =
15
+ keyof Ref.Args<R> extends never ? [args?: Ref.Args<R>] : [args: Ref.Args<R>];
16
+
17
+ const make = (
18
+ address: string,
19
+ options?: ConstructorParameters<typeof ConvexHttpClient>[1],
20
+ ) => {
21
+ const client = new ConvexHttpClient(address, options);
22
+
23
+ const url = client.url;
24
+
25
+ const setAuth = (token: string) =>
26
+ Effect.sync(() => {
27
+ client.setAuth(token);
28
+ });
29
+
30
+ const clearAuth = Effect.sync(() => {
31
+ client.clearAuth();
32
+ });
33
+
34
+ const query = <Query extends Ref.AnyQuery>(
35
+ ref: Query,
36
+ ...rest: OptionalArgs<Query>
37
+ ): Effect.Effect<
38
+ Ref.Returns<Query>,
39
+ HttpClientError | ParseResult.ParseError
40
+ > =>
41
+ Effect.gen(function* () {
42
+ const args = (rest[0] ?? {}) as Ref.Args<Query>;
43
+ const functionSpec = Ref.getFunctionSpec(ref);
44
+ const functionName = Ref.getConvexFunctionName(
45
+ ref,
46
+ ) as unknown as FunctionReference<"query">;
47
+
48
+ return yield* Match.value(functionSpec.functionProvenance).pipe(
49
+ Match.tag("Confect", (confectFunctionSpec) =>
50
+ Effect.gen(function* () {
51
+ const encodedArgs = yield* Schema.encode(confectFunctionSpec.args)(
52
+ args,
53
+ );
54
+
55
+ const encodedResult = yield* Effect.tryPromise({
56
+ try: () => client.query(functionName, encodedArgs),
57
+ catch: (cause) => new HttpClientError({ cause }),
58
+ });
59
+
60
+ return yield* Schema.decode(confectFunctionSpec.returns)(
61
+ encodedResult,
62
+ );
63
+ }),
64
+ ),
65
+ Match.tag("Convex", () =>
66
+ Effect.tryPromise({
67
+ try: () => client.query(functionName, args),
68
+ catch: (cause) => new HttpClientError({ cause }),
69
+ }),
70
+ ),
71
+ Match.exhaustive,
72
+ );
73
+ });
74
+
75
+ const mutation = <Mutation extends Ref.AnyMutation>(
76
+ ref: Mutation,
77
+ ...rest: OptionalArgs<Mutation>
78
+ ): Effect.Effect<
79
+ Ref.Returns<Mutation>,
80
+ HttpClientError | ParseResult.ParseError
81
+ > =>
82
+ Effect.gen(function* () {
83
+ const args = (rest[0] ?? {}) as Ref.Args<Mutation>;
84
+ const functionSpec = Ref.getFunctionSpec(ref);
85
+ const functionName = Ref.getConvexFunctionName(
86
+ ref,
87
+ ) as unknown as FunctionReference<"mutation">;
88
+
89
+ return yield* Match.value(functionSpec.functionProvenance).pipe(
90
+ Match.tag("Confect", (confectFunctionSpec) =>
91
+ Effect.gen(function* () {
92
+ const encodedArgs = yield* Schema.encode(confectFunctionSpec.args)(
93
+ args,
94
+ );
95
+
96
+ const encodedResult = yield* Effect.tryPromise({
97
+ try: () => client.mutation(functionName, encodedArgs),
98
+ catch: (cause) => new HttpClientError({ cause }),
99
+ });
100
+
101
+ return yield* Schema.decode(confectFunctionSpec.returns)(
102
+ encodedResult,
103
+ );
104
+ }),
105
+ ),
106
+ Match.tag("Convex", () =>
107
+ Effect.tryPromise({
108
+ try: () => client.mutation(functionName, args),
109
+ catch: (cause) => new HttpClientError({ cause }),
110
+ }),
111
+ ),
112
+ Match.exhaustive,
113
+ );
114
+ });
115
+
116
+ const action = <Action extends Ref.AnyAction>(
117
+ ref: Action,
118
+ ...rest: OptionalArgs<Action>
119
+ ): Effect.Effect<
120
+ Ref.Returns<Action>,
121
+ HttpClientError | ParseResult.ParseError
122
+ > =>
123
+ Effect.gen(function* () {
124
+ const args = (rest[0] ?? {}) as Ref.Args<Action>;
125
+ const functionSpec = Ref.getFunctionSpec(ref);
126
+ const functionName = Ref.getConvexFunctionName(
127
+ ref,
128
+ ) as unknown as FunctionReference<"action">;
129
+
130
+ return yield* Match.value(functionSpec.functionProvenance).pipe(
131
+ Match.tag("Confect", (confectFunctionSpec) =>
132
+ Effect.gen(function* () {
133
+ const encodedArgs = yield* Schema.encode(confectFunctionSpec.args)(
134
+ args,
135
+ );
136
+
137
+ const encodedResult = yield* Effect.tryPromise({
138
+ try: () => client.action(functionName, encodedArgs),
139
+ catch: (cause) => new HttpClientError({ cause }),
140
+ });
141
+
142
+ return yield* Schema.decode(confectFunctionSpec.returns)(
143
+ encodedResult,
144
+ );
145
+ }),
146
+ ),
147
+ Match.tag("Convex", () =>
148
+ Effect.tryPromise({
149
+ try: () => client.action(functionName, args),
150
+ catch: (cause) => new HttpClientError({ cause }),
151
+ }),
152
+ ),
153
+ Match.exhaustive,
154
+ );
155
+ });
156
+
157
+ return {
158
+ url,
159
+ setAuth,
160
+ clearAuth,
161
+ query,
162
+ mutation,
163
+ action,
164
+ };
165
+ };
166
+
167
+ /**
168
+ * A Confect client which uses HTTP to communicate with your Convex backend. Works in any JS runtime that supports `fetch`. Wraps [ConvexHttpClient](https://docs.convex.dev/api/classes/browser.ConvexHttpClient).
169
+ */
170
+ export const HttpClient = Context.GenericTag<ReturnType<typeof make>>(
171
+ "@confect/js/HttpClient",
172
+ );
173
+
174
+ export type HttpClient = typeof HttpClient.Identifier;
175
+
176
+ export const layer = (
177
+ address: string,
178
+ options?: ConstructorParameters<typeof ConvexHttpClient>[1],
179
+ ) => Layer.sync(HttpClient, () => make(address, options));
package/src/index.ts ADDED
@@ -0,0 +1 @@
1
+ export * as HttpClient from "./HttpClient";