From 4f16a08b7f7022807f724c927912a4f852cce252 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Thu, 5 Sep 2024 00:25:18 +0200 Subject: [PATCH 01/37] Express layers --- bun.lockb | Bin 104113 -> 120381 bytes package.json | 2 + src/Layers/express/ExpressApp.ts | 19 ++++++ src/Layers/express/ExpressNodeHTTPServer.ts | 67 ++++++++++++++++++++ src/Layers/express/index.ts | 2 + 5 files changed, 90 insertions(+) create mode 100644 src/Layers/express/ExpressApp.ts create mode 100644 src/Layers/express/ExpressNodeHTTPServer.ts create mode 100644 src/Layers/express/index.ts diff --git a/bun.lockb b/bun.lockb index 22706bc996ac2568b292fd7a9aaeffed48e21e27..a1e2534d1f7a58f3415692294337c72b8242c405 100755 GIT binary patch delta 24861 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 diff --git a/package.json b/package.json index 2545760..8550066 100644 --- a/package.json +++ b/package.json @@ -83,9 +83,11 @@ "@effect/schema": "^0.72.0", "@prisma/studio-server": "^0.502.0", "@tanstack/form-core": "^0.30.0", + "@types/express": "^4.17.21", "@types/jsonwebtoken": "^9.0.6", "bun-types": "^1.1.26", "effect": "^3.7.0", + "express": "^4.19.2", "jsonwebtoken": "^9.0.2", "mobx": "^6.13.1", "npm-check-updates": "^17.1.1", diff --git a/src/Layers/express/ExpressApp.ts b/src/Layers/express/ExpressApp.ts new file mode 100644 index 0000000..bcf72a6 --- /dev/null +++ b/src/Layers/express/ExpressApp.ts @@ -0,0 +1,19 @@ +import { Config, Context, Effect, Layer } from "effect" +import type { Express } from "express" + + +export class ExpressApp extends Context.Tag("ExpressApp")() {} + + +const importExpress = Effect.tryPromise({ + try: () => import("express"), + catch: cause => new Error("Could not import 'express'. Make sure it is installed.", { cause }), +}) + +export const ExpressAppLive = (config: { + readonly trustProxy?: Config.Config +}) => Layer.effect(ExpressApp, Effect.gen(function*() { + const app = (yield* importExpress).default() + app.set("trust proxy", yield* config.trustProxy || Config.succeed(false)) + return app +})) diff --git a/src/Layers/express/ExpressNodeHTTPServer.ts b/src/Layers/express/ExpressNodeHTTPServer.ts new file mode 100644 index 0000000..c634979 --- /dev/null +++ b/src/Layers/express/ExpressNodeHTTPServer.ts @@ -0,0 +1,67 @@ +import { Config, Context, Effect, Layer, Match } from "effect" +import type { Server } from "node:http" +import type { AddressInfo } from "node:net" +import { ExpressApp } from "./ExpressApp" + + +export class ExpressNodeHTTPServer extends Context.Tag("ExpressNodeHTTPServer")() {} + + +const importNodeHTTP = Effect.tryPromise({ + try: () => import("node:http"), + catch: cause => new Error("Could not import 'node:http'. Make sure you are using a runtime that implements Node APIs.", { cause }), +}) + +const serverListeningMessage = Match.type().pipe( + Match.when(Match.null, () => "HTTP server listening"), + Match.when(Match.string, v => `HTTP server listening on ${ v }`), + Match.orElse(v => `HTTP server listening on ${ v.address }:${ v.port }`), +) + +export const ExpressNodeHTTPServerLive = (config: { + readonly backlog?: Config.Config + readonly exclusive?: Config.Config + readonly host?: Config.Config + readonly ipv6Only?: Config.Config + readonly path?: Config.Config + readonly port?: Config.Config + readonly readableAll?: Config.Config + readonly signal?: AbortSignal + readonly writableAll?: Config.Config +}) => Layer.effect(ExpressNodeHTTPServer, Effect.acquireRelease( + Effect.gen(function*() { + const app = yield* ExpressApp + const http = yield* importNodeHTTP + + const options = { + backlog: yield* config.backlog || Config.succeed(undefined), + exclusive: yield* config.exclusive || Config.succeed(undefined), + host: yield* config.host || Config.succeed(undefined), + ipv6Only: yield* config.ipv6Only || Config.succeed(undefined), + path: yield* config.path || Config.succeed(undefined), + port: yield* config.port || Config.succeed(undefined), + readableAll: yield* config.readableAll || Config.succeed(undefined), + signal: config.signal, + writableAll: yield* config.writableAll || Config.succeed(undefined), + } as const + + return yield* Effect.async(resume => { + const server = http.createServer(app).listen(options, + () => resume( + Effect.succeed(server).pipe( + Effect.tap(Effect.logInfo( + serverListeningMessage(server.address()) + )) + ) + ) + ) + }) + }), + + server => Effect.gen(function*() { + yield* Effect.logInfo("HTTP server is stopping. Waiting for existing connections to end...") + yield* Effect.async(resume => { + server.close(() => resume(Effect.logInfo("HTTP server closed"))) + }) + }), +)) diff --git a/src/Layers/express/index.ts b/src/Layers/express/index.ts new file mode 100644 index 0000000..22f20a7 --- /dev/null +++ b/src/Layers/express/index.ts @@ -0,0 +1,2 @@ +export * from "./ExpressApp" +export * from "./ExpressNodeHTTPServer" -- 2.49.1 From 3879efa44e6b4311c36faf7bba7e47e8c53b556f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Thu, 5 Sep 2024 00:35:59 +0200 Subject: [PATCH 02/37] Fix --- src/Layers/express/ExpressApp.ts | 8 ++++--- src/Layers/express/ExpressNodeHTTPServer.ts | 24 +++++++++++---------- src/Layers/express/tests.ts | 17 +++++++++++++++ 3 files changed, 35 insertions(+), 14 deletions(-) create mode 100644 src/Layers/express/tests.ts diff --git a/src/Layers/express/ExpressApp.ts b/src/Layers/express/ExpressApp.ts index bcf72a6..4548211 100644 --- a/src/Layers/express/ExpressApp.ts +++ b/src/Layers/express/ExpressApp.ts @@ -10,9 +10,11 @@ const importExpress = Effect.tryPromise({ catch: cause => new Error("Could not import 'express'. Make sure it is installed.", { cause }), }) -export const ExpressAppLive = (config: { - readonly trustProxy?: Config.Config -}) => Layer.effect(ExpressApp, Effect.gen(function*() { +export const ExpressAppLive = ( + config: { + readonly trustProxy?: Config.Config + } = {} +) => Layer.effect(ExpressApp, Effect.gen(function*() { const app = (yield* importExpress).default() app.set("trust proxy", yield* config.trustProxy || Config.succeed(false)) return app diff --git a/src/Layers/express/ExpressNodeHTTPServer.ts b/src/Layers/express/ExpressNodeHTTPServer.ts index c634979..3ce7577 100644 --- a/src/Layers/express/ExpressNodeHTTPServer.ts +++ b/src/Layers/express/ExpressNodeHTTPServer.ts @@ -18,17 +18,19 @@ const serverListeningMessage = Match.type().pipe( Match.orElse(v => `HTTP server listening on ${ v.address }:${ v.port }`), ) -export const ExpressNodeHTTPServerLive = (config: { - readonly backlog?: Config.Config - readonly exclusive?: Config.Config - readonly host?: Config.Config - readonly ipv6Only?: Config.Config - readonly path?: Config.Config - readonly port?: Config.Config - readonly readableAll?: Config.Config - readonly signal?: AbortSignal - readonly writableAll?: Config.Config -}) => Layer.effect(ExpressNodeHTTPServer, Effect.acquireRelease( +export const ExpressNodeHTTPServerLive = ( + config: { + readonly backlog?: Config.Config + readonly exclusive?: Config.Config + readonly host?: Config.Config + readonly ipv6Only?: Config.Config + readonly path?: Config.Config + readonly port?: Config.Config + readonly readableAll?: Config.Config + readonly signal?: AbortSignal + readonly writableAll?: Config.Config + } = {} +) => Layer.effect(ExpressNodeHTTPServer, Effect.acquireRelease( Effect.gen(function*() { const app = yield* ExpressApp const http = yield* importNodeHTTP diff --git a/src/Layers/express/tests.ts b/src/Layers/express/tests.ts new file mode 100644 index 0000000..01900f7 --- /dev/null +++ b/src/Layers/express/tests.ts @@ -0,0 +1,17 @@ +import { Config, Effect, Layer } from "effect" +import { ExpressAppLive } from "./ExpressApp" +import { ExpressNodeHTTPServerLive } from "./ExpressNodeHTTPServer" + + +const AppLive = ExpressAppLive() +const HTTPServerLive = ExpressNodeHTTPServerLive() + +const ServerLive = Layer.empty.pipe( + Layer.provideMerge(HTTPServerLive), + Layer.provideMerge(AppLive), +) + +Layer.launch(ServerLive).pipe( + Effect.scoped, + Effect.runPromise, +) -- 2.49.1 From c178ee0ea71b7affedf62a0327a9abb35c9ab737 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Thu, 5 Sep 2024 00:36:21 +0200 Subject: [PATCH 03/37] Fix --- src/Layers/express/tests.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Layers/express/tests.ts b/src/Layers/express/tests.ts index 01900f7..11fd4f1 100644 --- a/src/Layers/express/tests.ts +++ b/src/Layers/express/tests.ts @@ -1,4 +1,4 @@ -import { Config, Effect, Layer } from "effect" +import { Effect, Layer } from "effect" import { ExpressAppLive } from "./ExpressApp" import { ExpressNodeHTTPServerLive } from "./ExpressNodeHTTPServer" -- 2.49.1 From df8cc363046cd01a09dc58cfe34e569f0fa03de1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Thu, 5 Sep 2024 00:47:24 +0200 Subject: [PATCH 04/37] Fix --- src/Layers/express/ExpressNodeHTTPServer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Layers/express/ExpressNodeHTTPServer.ts b/src/Layers/express/ExpressNodeHTTPServer.ts index 3ce7577..73bc0e7 100644 --- a/src/Layers/express/ExpressNodeHTTPServer.ts +++ b/src/Layers/express/ExpressNodeHTTPServer.ts @@ -15,7 +15,7 @@ const importNodeHTTP = Effect.tryPromise({ const serverListeningMessage = Match.type().pipe( Match.when(Match.null, () => "HTTP server listening"), Match.when(Match.string, v => `HTTP server listening on ${ v }`), - Match.orElse(v => `HTTP server listening on ${ v.address }:${ v.port }`), + Match.orElse(v => `HTTP server listening on ${ v.address === "::" ? "*" : v.address }:${ v.port }`), ) export const ExpressNodeHTTPServerLive = ( -- 2.49.1 From c96c01b295df2f088f52d34404ddf89ce969e216 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Thu, 5 Sep 2024 00:51:08 +0200 Subject: [PATCH 05/37] Module refactoring --- src/Layers/express/index.ts | 4 ++-- src/Layers/index.ts | 5 +---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/Layers/express/index.ts b/src/Layers/express/index.ts index 22f20a7..c301111 100644 --- a/src/Layers/express/index.ts +++ b/src/Layers/express/index.ts @@ -1,2 +1,2 @@ -export * from "./ExpressApp" -export * from "./ExpressNodeHTTPServer" +export * as ExpressApp from "./ExpressApp" +export * as ExpressNodeHTTPServer from "./ExpressNodeHTTPServer" diff --git a/src/Layers/index.ts b/src/Layers/index.ts index 77d7071..6a72a2d 100644 --- a/src/Layers/index.ts +++ b/src/Layers/index.ts @@ -1,5 +1,2 @@ -/** - * A wrapper around the jsonwebtoken library for Effect - * Requires `effect`, `jsonwebtoken` and `@types/jsonwebtoken` to be installed - */ +export * from "./express" export * as JSONWebToken from "./JSONWebToken" -- 2.49.1 From 8dc1ed7015efcf3c4229339fcb515cf8a9833f66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Thu, 5 Sep 2024 01:48:48 +0200 Subject: [PATCH 06/37] OpenAIClient --- bun.lockb | Bin 120381 -> 126182 bytes package.json | 1 + src/Layers/OpenAIClient.ts | 68 +++++++++++++++++++++++++++++++++++++ src/Layers/index.ts | 1 + 4 files changed, 70 insertions(+) create mode 100644 src/Layers/OpenAIClient.ts diff --git a/bun.lockb b/bun.lockb index a1e2534d1f7a58f3415692294337c72b8242c405..2cafe940b4bf63eb09cd0196cfc1c3b270c21eea 100755 GIT binary patch delta 17630 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 diff --git a/package.json b/package.json index 8550066..792141c 100644 --- a/package.json +++ b/package.json @@ -92,6 +92,7 @@ "mobx": "^6.13.1", "npm-check-updates": "^17.1.1", "npm-sort": "^0.0.4", + "openai": "^4.57.3", "tsup": "^8.2.4", "tsx": "^4.19.0", "typescript": "^5.5.4" diff --git a/src/Layers/OpenAIClient.ts b/src/Layers/OpenAIClient.ts new file mode 100644 index 0000000..cfab980 --- /dev/null +++ b/src/Layers/OpenAIClient.ts @@ -0,0 +1,68 @@ +import { Config, Context, Effect, Layer } from "effect" +import type { OpenAI } from "openai" + + +export class OpenAIClient extends Context.Tag("OpenAIClient")() {} + +export class OpenAIClientService { + constructor( + readonly openai: Effect.Effect.Success, + readonly client: OpenAI, + ) {} + + try( + try_: ( + client: OpenAI, + signal: AbortSignal, + ) => Promise + ) { + return Effect.tryPromise({ + try: signal => try_(this.client, signal), + catch: e => e instanceof this.openai.OpenAIError + ? e + : new Error(`Unknown OpenAIClient error: ${ e }`), + }) + } +} + + +const importOpenAI = Effect.tryPromise({ + try: () => import("openai"), + catch: cause => new Error("Could not import 'openai'. Make sure it is installed.", { cause }), +}) + +export const OpenAIClientLive = ( + config: { + readonly apiKey: Config.Config + readonly organization?: Config.Config + readonly project?: Config.Config + readonly baseURL?: Config.Config + readonly timeout?: Config.Config + readonly maxRetries?: Config.Config + + readonly httpAgent?: any + readonly fetch?: any + readonly defaultHeaders?: { [x: string]: string } + readonly defaultQuery?: { [x: string]: string } + } +) => Layer.effect(OpenAIClient, Effect.gen(function*() { + const openai = yield* importOpenAI + + return new OpenAIClientService( + openai, + + new openai.OpenAI({ + apiKey: yield* config.apiKey, + organization: (yield* config.organization || Config.succeed(undefined)) || null, + project: (yield* config.project || Config.succeed(undefined)) || null, + baseURL: (yield* config.baseURL || Config.succeed(undefined)) || "https://api.openai.com/v1", + timeout: yield* config.timeout || Config.succeed(undefined), + maxRetries: yield* config.maxRetries || Config.succeed(undefined), + + httpAgent: config.httpAgent, + fetch: config.fetch, + defaultHeaders: config.defaultHeaders, + defaultQuery: config.defaultQuery, + }), + ) +})) diff --git a/src/Layers/index.ts b/src/Layers/index.ts index 6a72a2d..2f143bb 100644 --- a/src/Layers/index.ts +++ b/src/Layers/index.ts @@ -1,2 +1,3 @@ export * from "./express" export * as JSONWebToken from "./JSONWebToken" +export * as OpenAIClient from "./OpenAIClient" -- 2.49.1 From f562a1f334d2190f04685f8131652171ca123ab0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Thu, 5 Sep 2024 01:49:07 +0200 Subject: [PATCH 07/37] Dependencies upgrade --- bun.lockb | Bin 126182 -> 126182 bytes package.json | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bun.lockb b/bun.lockb index 2cafe940b4bf63eb09cd0196cfc1c3b270c21eea..2d6c81a4c409dfdad3718eb678e8407786ed2677 100755 GIT binary patch delta 263 zcmV+i0r>vr*$3v?2aqlxmmKKM=59r&#%oiC2-5zTLDvrx|1Clr&4au}(@> zlh{Hqv&dGE50exqD3d@H2(yT09dAITUgg)~(qvoJIk@=F?6{qXfoLx3WasNCUab5_ z_@{ENdL%GpSQ?qEr;2mxL+E;TnUGq+Jz0md=`GPf9s0kcq-vr*$3v?2aqlxnc~}vDan*cL%WSp=K5ziqL7DR+h2Vka<4DmnBuVou}(@> zlb9Ybv&dGE50eBHD3d@H2(yT09dAHQdfVY4(;m2^-`>qb z0qujjIqhv(C!~|dT&_L9S+fH;pWj^T+<&3N`77?TY{+=C<<)2vKqGE&BP|V&AGFhH zW?oM*smDba!K6vadJ+6l6G`NwC!9f8KEGM8>@nm^4AJN(3*ybKAv_G-o-_cmV*<14 zfU}M3i)jHcml;(7ngKAEv{eC>2m$~A000000Jl+A0md=`Ft-?q0kcq-LYV;|x9iyf N)DZzNx4`QGCIMffciI2| diff --git a/package.json b/package.json index 792141c..23e5299 100644 --- a/package.json +++ b/package.json @@ -80,13 +80,13 @@ "type-fest": "^4.26.0" }, "devDependencies": { - "@effect/schema": "^0.72.0", + "@effect/schema": "^0.72.2", "@prisma/studio-server": "^0.502.0", "@tanstack/form-core": "^0.30.0", "@types/express": "^4.17.21", "@types/jsonwebtoken": "^9.0.6", "bun-types": "^1.1.26", - "effect": "^3.7.0", + "effect": "^3.7.2", "express": "^4.19.2", "jsonwebtoken": "^9.0.2", "mobx": "^6.13.1", -- 2.49.1 From ca10286e1f5aa7b827e90e1d771830cb5a6a981a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Thu, 5 Sep 2024 01:58:24 +0200 Subject: [PATCH 08/37] Config fixes --- src/Layers/OpenAIClient.ts | 10 +++++----- src/Layers/express/ExpressApp.ts | 4 ++-- src/Layers/express/ExpressNodeHTTPServer.ts | 16 ++++++++-------- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/Layers/OpenAIClient.ts b/src/Layers/OpenAIClient.ts index cfab980..5e07d87 100644 --- a/src/Layers/OpenAIClient.ts +++ b/src/Layers/OpenAIClient.ts @@ -53,11 +53,11 @@ export const OpenAIClientLive = ( new openai.OpenAI({ apiKey: yield* config.apiKey, - organization: (yield* config.organization || Config.succeed(undefined)) || null, - project: (yield* config.project || Config.succeed(undefined)) || null, - baseURL: (yield* config.baseURL || Config.succeed(undefined)) || "https://api.openai.com/v1", - timeout: yield* config.timeout || Config.succeed(undefined), - maxRetries: yield* config.maxRetries || Config.succeed(undefined), + organization: (yield* config.organization ?? Config.succeed(undefined)) ?? null, + project: (yield* config.project ?? Config.succeed(undefined)) ?? null, + baseURL: (yield* config.baseURL ?? Config.succeed(undefined)) ?? "https://api.openai.com/v1", + timeout: yield* config.timeout ?? Config.succeed(undefined), + maxRetries: yield* config.maxRetries ?? Config.succeed(undefined), httpAgent: config.httpAgent, fetch: config.fetch, diff --git a/src/Layers/express/ExpressApp.ts b/src/Layers/express/ExpressApp.ts index 4548211..8bf7bef 100644 --- a/src/Layers/express/ExpressApp.ts +++ b/src/Layers/express/ExpressApp.ts @@ -12,10 +12,10 @@ const importExpress = Effect.tryPromise({ export const ExpressAppLive = ( config: { - readonly trustProxy?: Config.Config + readonly trustProxy?: Config.Config } = {} ) => Layer.effect(ExpressApp, Effect.gen(function*() { const app = (yield* importExpress).default() - app.set("trust proxy", yield* config.trustProxy || Config.succeed(false)) + app.set("trust proxy", (yield* config.trustProxy ?? Config.succeed(undefined)) ?? false) return app })) diff --git a/src/Layers/express/ExpressNodeHTTPServer.ts b/src/Layers/express/ExpressNodeHTTPServer.ts index 73bc0e7..a28eaf9 100644 --- a/src/Layers/express/ExpressNodeHTTPServer.ts +++ b/src/Layers/express/ExpressNodeHTTPServer.ts @@ -36,15 +36,15 @@ export const ExpressNodeHTTPServerLive = ( const http = yield* importNodeHTTP const options = { - backlog: yield* config.backlog || Config.succeed(undefined), - exclusive: yield* config.exclusive || Config.succeed(undefined), - host: yield* config.host || Config.succeed(undefined), - ipv6Only: yield* config.ipv6Only || Config.succeed(undefined), - path: yield* config.path || Config.succeed(undefined), - port: yield* config.port || Config.succeed(undefined), - readableAll: yield* config.readableAll || Config.succeed(undefined), + backlog: yield* config.backlog ?? Config.succeed(undefined), + exclusive: yield* config.exclusive ?? Config.succeed(undefined), + host: yield* config.host ?? Config.succeed(undefined), + ipv6Only: yield* config.ipv6Only ?? Config.succeed(undefined), + path: yield* config.path ?? Config.succeed(undefined), + port: yield* config.port ?? Config.succeed(undefined), + readableAll: yield* config.readableAll ?? Config.succeed(undefined), signal: config.signal, - writableAll: yield* config.writableAll || Config.succeed(undefined), + writableAll: yield* config.writableAll ?? Config.succeed(undefined), } as const return yield* Effect.async(resume => { -- 2.49.1 From 85b6340b9758da03cfd7dd434714db6fbd2cd88a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Thu, 5 Sep 2024 06:07:29 +0200 Subject: [PATCH 09/37] TRPCContextCreator --- bun.lockb | Bin 126182 -> 107036 bytes package.json | 6 +- src/Layers/index.ts | 1 + src/Layers/trpc/TRPCContext.ts | 38 +++++++++++++ src/Layers/trpc/TRPCContextCreator.ts | 55 +++++++++++++++++++ src/Layers/trpc/createTRCPErrorMapper.ts | 67 +++++++++++++++++++++++ src/Layers/trpc/index.ts | 1 + 7 files changed, 166 insertions(+), 2 deletions(-) create mode 100644 src/Layers/trpc/TRPCContext.ts create mode 100644 src/Layers/trpc/TRPCContextCreator.ts create mode 100644 src/Layers/trpc/createTRCPErrorMapper.ts create mode 100644 src/Layers/trpc/index.ts diff --git a/bun.lockb b/bun.lockb index 2d6c81a4c409dfdad3718eb678e8407786ed2677..5bb451da1267e8181ca1994b61a2a92a0e90758c 100755 GIT binary patch delta 18021 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 diff --git a/package.json b/package.json index 23e5299..255ffcb 100644 --- a/package.json +++ b/package.json @@ -81,10 +81,11 @@ }, "devDependencies": { "@effect/schema": "^0.72.2", - "@prisma/studio-server": "^0.502.0", "@tanstack/form-core": "^0.30.0", + "@trpc/server": "^10.45.2", "@types/express": "^4.17.21", "@types/jsonwebtoken": "^9.0.6", + "@types/ws": "^8.5.12", "bun-types": "^1.1.26", "effect": "^3.7.2", "express": "^4.19.2", @@ -95,6 +96,7 @@ "openai": "^4.57.3", "tsup": "^8.2.4", "tsx": "^4.19.0", - "typescript": "^5.5.4" + "typescript": "^5.5.4", + "ws": "^8.18.0" } } diff --git a/src/Layers/index.ts b/src/Layers/index.ts index 2f143bb..3784801 100644 --- a/src/Layers/index.ts +++ b/src/Layers/index.ts @@ -1,3 +1,4 @@ export * from "./express" export * as JSONWebToken from "./JSONWebToken" export * as OpenAIClient from "./OpenAIClient" +export * from "./trpc" diff --git a/src/Layers/trpc/TRPCContext.ts b/src/Layers/trpc/TRPCContext.ts new file mode 100644 index 0000000..94089d8 --- /dev/null +++ b/src/Layers/trpc/TRPCContext.ts @@ -0,0 +1,38 @@ +import type { TRPCError } from "@trpc/server" +import { Data, type Effect, type Runtime } from "effect" +import type { RuntimeFiber } from "effect/Fiber" +import type express from "express" +import type { IncomingMessage } from "node:http" +import type { WebSocket } from "ws" + + +export interface TRPCContext { + readonly runtime: Runtime.Runtime + + readonly run: ( + effect: Effect.Effect, + options?: { readonly signal?: AbortSignal }, + ) => Promise + + readonly fork: ( + effect: Effect.Effect, + options?: Runtime.RunForkOptions, + ) => RuntimeFiber + + readonly transaction: TRPCContextTransaction +} + + +export type TRPCContextTransaction = Data.TaggedEnum<{ + readonly Express: { + readonly req: express.Request + readonly res: express.Response + } + + readonly WebSocket: { + readonly req: IncomingMessage + readonly res: WebSocket + } +}> + +export const TRPCContextTransactionEnum = Data.taggedEnum() diff --git a/src/Layers/trpc/TRPCContextCreator.ts b/src/Layers/trpc/TRPCContextCreator.ts new file mode 100644 index 0000000..06278f8 --- /dev/null +++ b/src/Layers/trpc/TRPCContextCreator.ts @@ -0,0 +1,55 @@ +import type { CreateExpressContextOptions } from "@trpc/server/adapters/express" +import type { CreateWSSContextFnOptions } from "@trpc/server/adapters/ws" +import { Context, Effect, Layer, Runtime } from "effect" +import { createTRCPErrorMapper } from "./createTRCPErrorMapper" +import { TRPCContextTransactionEnum, type TRPCContext, type TRPCContextTransaction } from "./TRPCContext" + + +export { TRPCErrorCause } from "./createTRCPErrorMapper" +export { TRPCContextTransactionEnum } from "./TRPCContext" +export type { TRPCContext, TRPCContextTransaction } from "./TRPCContext" + + +export const makeService = () => { + class TRPCContextCreator extends Context.Tag("TRPCContextCreator") TRPCContext + readonly createExpressContext: (context: CreateExpressContextOptions) => TRPCContext + readonly createWebSocketContext: (context: CreateWSSContextFnOptions) => TRPCContext + }>() {} + + const TRPCContextCreatorLive = Layer.effect(TRPCContextCreator, Effect.gen(function*() { + const runtime = yield* Effect.runtime() + const mapErrors = yield* createTRCPErrorMapper + + const run = ( + effect: Effect.Effect, + options?: { readonly signal?: AbortSignal }, + ) => Runtime.runPromise(runtime)( + effect.pipe(mapErrors), + options, + ) + + const fork = ( + effect: Effect.Effect, + options?: Runtime.RunForkOptions, + ) => Runtime.runFork(runtime)( + effect.pipe(mapErrors), + options, + ) + + + const createContext = (transaction: TRPCContextTransaction) => ({ + runtime, + run, + fork, + transaction, + }) + + const createExpressContext = (context: CreateExpressContextOptions) => createContext(TRPCContextTransactionEnum.Express(context)) + const createWebSocketContext = (context: CreateWSSContextFnOptions) => createContext(TRPCContextTransactionEnum.WebSocket(context)) + + return { createContext, createExpressContext, createWebSocketContext } + })) + + return { TRPCContextCreator, TRPCContextCreatorLive } +} diff --git a/src/Layers/trpc/createTRCPErrorMapper.ts b/src/Layers/trpc/createTRCPErrorMapper.ts new file mode 100644 index 0000000..405d636 --- /dev/null +++ b/src/Layers/trpc/createTRCPErrorMapper.ts @@ -0,0 +1,67 @@ +import { Effect, type Cause } from "effect" + + +const importTRPCServer = Effect.tryPromise({ + try: () => import("@trpc/server"), + catch: cause => new Error("Could not import '@trpc/server'. Make sure it is installed.", { cause }), +}) + +export const createTRCPErrorMapper = importTRPCServer.pipe(Effect.map(({ TRPCError }) => + (effect: Effect.Effect) => Effect.sandbox(effect).pipe( + Effect.catchTags({ + Empty: cause => Effect.fail( + new TRPCError({ + code: "INTERNAL_SERVER_ERROR", + cause: new TRPCErrorCause(cause), + }) + ), + + Fail: cause => Effect.fail( + cause.error instanceof TRPCError + ? cause.error + : new TRPCError({ + code: "INTERNAL_SERVER_ERROR", + cause: new TRPCErrorCause(cause), + }) + ), + + Die: cause => Effect.fail( + cause.defect instanceof TRPCError + ? cause.defect + : new TRPCError({ + code: "INTERNAL_SERVER_ERROR", + cause: new TRPCErrorCause(cause), + }) + ), + + Interrupt: cause => Effect.fail( + new TRPCError({ + code: "INTERNAL_SERVER_ERROR", + cause: new TRPCErrorCause(cause), + }) + ), + + Sequential: cause => Effect.fail( + new TRPCError({ + code: "INTERNAL_SERVER_ERROR", + cause: new TRPCErrorCause(cause), + }) + ), + + Parallel: cause => Effect.fail( + new TRPCError({ + code: "INTERNAL_SERVER_ERROR", + cause: new TRPCErrorCause(cause), + }) + ), + }), + + Effect.tapError(Effect.logError), + ) +)) + +export class TRPCErrorCause extends Error { + constructor(readonly cause: Cause.Cause) { + super() + } +} diff --git a/src/Layers/trpc/index.ts b/src/Layers/trpc/index.ts new file mode 100644 index 0000000..b34aba4 --- /dev/null +++ b/src/Layers/trpc/index.ts @@ -0,0 +1 @@ +export * as TRPCContextCreator from "./TRPCContextCreator" -- 2.49.1 From f232da463022517e1ca120998f7e767575de8cb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Thu, 5 Sep 2024 06:11:50 +0200 Subject: [PATCH 10/37] Fix --- src/Layers/trpc/TRPCContextCreator.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Layers/trpc/TRPCContextCreator.ts b/src/Layers/trpc/TRPCContextCreator.ts index 06278f8..5115929 100644 --- a/src/Layers/trpc/TRPCContextCreator.ts +++ b/src/Layers/trpc/TRPCContextCreator.ts @@ -10,7 +10,7 @@ export { TRPCContextTransactionEnum } from "./TRPCContext" export type { TRPCContext, TRPCContextTransaction } from "./TRPCContext" -export const makeService = () => { +export const make = () => { class TRPCContextCreator extends Context.Tag("TRPCContextCreator") TRPCContext readonly createExpressContext: (context: CreateExpressContextOptions) => TRPCContext -- 2.49.1 From 9b80664c95dc56d293ffb9be73e64e9081da3882 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Fri, 6 Sep 2024 03:54:18 +0200 Subject: [PATCH 11/37] tRPC work --- src/Layers/trpc/TRPCBuilder.ts | 18 ++++++++++++++++++ src/Layers/trpc/TRPCContextCreator.ts | 22 ++++++++++++---------- src/Layers/trpc/TRPCRouter.ts | 18 ++++++++++++++++++ src/Layers/trpc/createTRCPErrorMapper.ts | 2 +- src/Layers/trpc/index.ts | 3 +++ 5 files changed, 52 insertions(+), 11 deletions(-) create mode 100644 src/Layers/trpc/TRPCBuilder.ts create mode 100644 src/Layers/trpc/TRPCRouter.ts diff --git a/src/Layers/trpc/TRPCBuilder.ts b/src/Layers/trpc/TRPCBuilder.ts new file mode 100644 index 0000000..d8472f5 --- /dev/null +++ b/src/Layers/trpc/TRPCBuilder.ts @@ -0,0 +1,18 @@ +import { initTRPC } from "@trpc/server" +import { Context, Layer } from "effect" +import { type TRPCContext } from "./TRPCContext" + + +const createTRPC = () => initTRPC.context>().create() + +export class TRPCUnknownBuilder extends Context.Tag("@thilalib/TRPC/TRPCBuilder")> +>() {} + + +export const make = () => { + class TRPCBuilder extends Context.Tag("@thilalib/TRPC/TRPCBuilder")>>() {} + const TRPCBuilderLive = Layer.sync(TRPCBuilder, createTRPC) + + return { TRPCBuilder, TRPCBuilderLive } +} diff --git a/src/Layers/trpc/TRPCContextCreator.ts b/src/Layers/trpc/TRPCContextCreator.ts index 5115929..49425c2 100644 --- a/src/Layers/trpc/TRPCContextCreator.ts +++ b/src/Layers/trpc/TRPCContextCreator.ts @@ -5,17 +5,21 @@ import { createTRCPErrorMapper } from "./createTRCPErrorMapper" import { TRPCContextTransactionEnum, type TRPCContext, type TRPCContextTransaction } from "./TRPCContext" -export { TRPCErrorCause } from "./createTRCPErrorMapper" -export { TRPCContextTransactionEnum } from "./TRPCContext" -export type { TRPCContext, TRPCContextTransaction } from "./TRPCContext" +export interface TRPCContextCreatorService { + readonly createContext: (transaction: TRPCContextTransaction) => TRPCContext + readonly createExpressContext: (context: CreateExpressContextOptions) => TRPCContext + readonly createWebSocketContext: (context: CreateWSSContextFnOptions) => TRPCContext +} + +export class TRPCUnknownContextCreator extends Context.Tag("@thilalib/TRPC/TRPCContextCreator") +>() {} export const make = () => { - class TRPCContextCreator extends Context.Tag("TRPCContextCreator") TRPCContext - readonly createExpressContext: (context: CreateExpressContextOptions) => TRPCContext - readonly createWebSocketContext: (context: CreateWSSContextFnOptions) => TRPCContext - }>() {} + class TRPCContextCreator extends Context.Tag("@thilalib/TRPC/TRPCContextCreator") + >() {} const TRPCContextCreatorLive = Layer.effect(TRPCContextCreator, Effect.gen(function*() { const runtime = yield* Effect.runtime() @@ -37,14 +41,12 @@ export const make = () => { options, ) - const createContext = (transaction: TRPCContextTransaction) => ({ runtime, run, fork, transaction, }) - const createExpressContext = (context: CreateExpressContextOptions) => createContext(TRPCContextTransactionEnum.Express(context)) const createWebSocketContext = (context: CreateWSSContextFnOptions) => createContext(TRPCContextTransactionEnum.WebSocket(context)) diff --git a/src/Layers/trpc/TRPCRouter.ts b/src/Layers/trpc/TRPCRouter.ts new file mode 100644 index 0000000..5ab2263 --- /dev/null +++ b/src/Layers/trpc/TRPCRouter.ts @@ -0,0 +1,18 @@ +import type { AnyRouterDef, Router } from "@trpc/server" +import { Context, Effect, Layer } from "effect" + + +export class TRPCAnyRouter extends Context.Tag("@thilalib/TRCP/TRPCRouter") +>() {} + + +export const make = < + A extends Router, + E, R, +>(router: Effect.Effect) => { + class TRPCRouter extends Context.Tag("@thilalib/TRCP/TRPCRouter")() {} + const TRPCRouterLive = Layer.effect(TRPCRouter, router) + + return { TRPCRouter, TRPCRouterLive } +} diff --git a/src/Layers/trpc/createTRCPErrorMapper.ts b/src/Layers/trpc/createTRCPErrorMapper.ts index 405d636..5a5953c 100644 --- a/src/Layers/trpc/createTRCPErrorMapper.ts +++ b/src/Layers/trpc/createTRCPErrorMapper.ts @@ -60,7 +60,7 @@ export const createTRCPErrorMapper = importTRPCServer.pipe(Effect.map(({ TRPCErr ) )) -export class TRPCErrorCause extends Error { +class TRPCErrorCause extends Error { constructor(readonly cause: Cause.Cause) { super() } diff --git a/src/Layers/trpc/index.ts b/src/Layers/trpc/index.ts index b34aba4..b3b8299 100644 --- a/src/Layers/trpc/index.ts +++ b/src/Layers/trpc/index.ts @@ -1 +1,4 @@ +export * as TRPCBuilder from "./TRPCBuilder" +export * as TRPCContext from "./TRPCContext" export * as TRPCContextCreator from "./TRPCContextCreator" +export * as TRPCRouter from "./TRPCRouter" -- 2.49.1 From 4cf88348a07a88b49988d785fdc91da8320952d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Fri, 6 Sep 2024 04:05:52 +0200 Subject: [PATCH 12/37] TRPCExpressRoute --- src/Layers/trpc/TRPCExpressRoute.ts | 21 +++++++++++++++++++++ src/Layers/trpc/index.ts | 1 + 2 files changed, 22 insertions(+) create mode 100644 src/Layers/trpc/TRPCExpressRoute.ts diff --git a/src/Layers/trpc/TRPCExpressRoute.ts b/src/Layers/trpc/TRPCExpressRoute.ts new file mode 100644 index 0000000..6c47103 --- /dev/null +++ b/src/Layers/trpc/TRPCExpressRoute.ts @@ -0,0 +1,21 @@ +import { createExpressMiddleware } from "@trpc/server/adapters/express" +import { Config, Effect, Layer } from "effect" +import { ExpressApp } from "../express" +import { TRPCUnknownContextCreator } from "./TRPCContextCreator" +import { TRPCAnyRouter } from "./TRPCRouter" + + +export const TRPCExpressRouteLive = ( + config: { + readonly path: Config.Config + } +) => Layer.effectDiscard(Effect.gen(function*() { + const app = yield* ExpressApp.ExpressApp + + app.use(yield* config.path, + createExpressMiddleware({ + router: yield* TRPCAnyRouter, + createContext: (yield* TRPCUnknownContextCreator).createExpressContext, + }) + ) +})) diff --git a/src/Layers/trpc/index.ts b/src/Layers/trpc/index.ts index b3b8299..3703bfc 100644 --- a/src/Layers/trpc/index.ts +++ b/src/Layers/trpc/index.ts @@ -1,4 +1,5 @@ export * as TRPCBuilder from "./TRPCBuilder" export * as TRPCContext from "./TRPCContext" export * as TRPCContextCreator from "./TRPCContextCreator" +export * as TRPCExpressRoute from "./TRPCExpressRoute" export * as TRPCRouter from "./TRPCRouter" -- 2.49.1 From 1b80901f043ce9a6a224717cf4b25de71bf93df6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Fri, 6 Sep 2024 05:48:57 +0200 Subject: [PATCH 13/37] TRPCExpressRouteLive --- src/ImportError.ts | 11 ++++++ src/Layers/JSONWebToken.ts | 3 +- src/Layers/OpenAIClient.ts | 3 +- src/Layers/express/ExpressApp.ts | 3 +- src/Layers/express/ExpressNodeHTTPServer.ts | 3 +- src/Layers/trpc/TRPCExpressRoute.ts | 8 +++- src/Layers/trpc/TRPCWebSocketServer.ts | 44 +++++++++++++++++++++ src/Layers/trpc/createTRCPErrorMapper.ts | 3 +- src/index.ts | 1 + 9 files changed, 73 insertions(+), 6 deletions(-) create mode 100644 src/ImportError.ts create mode 100644 src/Layers/trpc/TRPCWebSocketServer.ts diff --git a/src/ImportError.ts b/src/ImportError.ts new file mode 100644 index 0000000..04b654e --- /dev/null +++ b/src/ImportError.ts @@ -0,0 +1,11 @@ +import { Data } from "effect" + + +export class ImportError extends Data.TaggedError("ImportError")<{ + path: string + cause: unknown +}> { + toString(): string { + return `Could not import '${ this.path }'` + } +} diff --git a/src/Layers/JSONWebToken.ts b/src/Layers/JSONWebToken.ts index 24bf772..f9f76c1 100644 --- a/src/Layers/JSONWebToken.ts +++ b/src/Layers/JSONWebToken.ts @@ -1,5 +1,6 @@ import { Context, Effect, Layer } from "effect" import type * as JWT from "jsonwebtoken" +import { ImportError } from "../ImportError" export class JSONWebToken extends Context.Tag("JSONWebToken") import("jsonwebtoken"), - catch: cause => new Error("Could not import 'jsonwebtoken'. Make sure it is installed.", { cause }), + catch: cause => new ImportError({ path: "jsonwebtoken", cause }), }) export const JSONWebTokenLive = Layer.effect(JSONWebToken, importJWT.pipe( diff --git a/src/Layers/OpenAIClient.ts b/src/Layers/OpenAIClient.ts index 5e07d87..3128a4e 100644 --- a/src/Layers/OpenAIClient.ts +++ b/src/Layers/OpenAIClient.ts @@ -1,5 +1,6 @@ import { Config, Context, Effect, Layer } from "effect" import type { OpenAI } from "openai" +import { ImportError } from "../ImportError" export class OpenAIClient extends Context.Tag("OpenAIClient")() {} @@ -28,7 +29,7 @@ export class OpenAIClientService { const importOpenAI = Effect.tryPromise({ try: () => import("openai"), - catch: cause => new Error("Could not import 'openai'. Make sure it is installed.", { cause }), + catch: cause => new ImportError({ path: "openai", cause }), }) export const OpenAIClientLive = ( diff --git a/src/Layers/express/ExpressApp.ts b/src/Layers/express/ExpressApp.ts index 8bf7bef..8b6fdbe 100644 --- a/src/Layers/express/ExpressApp.ts +++ b/src/Layers/express/ExpressApp.ts @@ -1,5 +1,6 @@ import { Config, Context, Effect, Layer } from "effect" import type { Express } from "express" +import { ImportError } from "../../ImportError" export class ExpressApp extends Context.Tag("ExpressApp")() {} @@ -7,7 +8,7 @@ export class ExpressApp extends Context.Tag("ExpressApp")() const importExpress = Effect.tryPromise({ try: () => import("express"), - catch: cause => new Error("Could not import 'express'. Make sure it is installed.", { cause }), + catch: cause => new ImportError({ path: "express", cause }), }) export const ExpressAppLive = ( diff --git a/src/Layers/express/ExpressNodeHTTPServer.ts b/src/Layers/express/ExpressNodeHTTPServer.ts index a28eaf9..d56f1cb 100644 --- a/src/Layers/express/ExpressNodeHTTPServer.ts +++ b/src/Layers/express/ExpressNodeHTTPServer.ts @@ -1,6 +1,7 @@ import { Config, Context, Effect, Layer, Match } from "effect" import type { Server } from "node:http" import type { AddressInfo } from "node:net" +import { ImportError } from "../../ImportError" import { ExpressApp } from "./ExpressApp" @@ -9,7 +10,7 @@ export class ExpressNodeHTTPServer extends Context.Tag("ExpressNodeHTTPServer")< const importNodeHTTP = Effect.tryPromise({ try: () => import("node:http"), - catch: cause => new Error("Could not import 'node:http'. Make sure you are using a runtime that implements Node APIs.", { cause }), + catch: cause => new ImportError({ path: "node:http", cause }), }) const serverListeningMessage = Match.type().pipe( diff --git a/src/Layers/trpc/TRPCExpressRoute.ts b/src/Layers/trpc/TRPCExpressRoute.ts index 6c47103..8d2a911 100644 --- a/src/Layers/trpc/TRPCExpressRoute.ts +++ b/src/Layers/trpc/TRPCExpressRoute.ts @@ -1,15 +1,21 @@ -import { createExpressMiddleware } from "@trpc/server/adapters/express" import { Config, Effect, Layer } from "effect" +import { ImportError } from "../../ImportError" import { ExpressApp } from "../express" import { TRPCUnknownContextCreator } from "./TRPCContextCreator" import { TRPCAnyRouter } from "./TRPCRouter" +const importTRPCServerExpressAdapter = Effect.tryPromise({ + try: () => import("@trpc/server/adapters/express"), + catch: cause => new ImportError({ path: "@trpc/server/adapters/express", cause }), +}) + export const TRPCExpressRouteLive = ( config: { readonly path: Config.Config } ) => Layer.effectDiscard(Effect.gen(function*() { + const { createExpressMiddleware } = yield* importTRPCServerExpressAdapter const app = yield* ExpressApp.ExpressApp app.use(yield* config.path, diff --git a/src/Layers/trpc/TRPCWebSocketServer.ts b/src/Layers/trpc/TRPCWebSocketServer.ts new file mode 100644 index 0000000..d36f03b --- /dev/null +++ b/src/Layers/trpc/TRPCWebSocketServer.ts @@ -0,0 +1,44 @@ +import { applyWSSHandler } from "@trpc/server/adapters/ws" +import { Context, Effect, Layer } from "effect" +import ws from "ws" +import { ExpressHTTPServer } from "../http/ExpressHTTPServer.service" +import { ServerConfig } from "../ServerConfig" +import { TRPCContextCreator } from "../trpc/TRPCContextCreator.service" +import { RPCRouter } from "./RPCRouter.service" + + +export class TRPCWebSocketServer extends Context.Tag("TRPCWebSocketServer") +}>() {} + +export const RPCWebSocketServerLive = Layer.effect(RPCWebSocketServer, ServerConfig.rpcHTTPRoot.pipe( + Effect.flatMap(rpcHTTPRoot => Effect.acquireRelease( + Effect.gen(function*() { + yield* Effect.logInfo(`WebSocket server started on ${ rpcHTTPRoot }`) + + const wss = new ws.WebSocketServer({ + server: yield* ExpressHTTPServer, + host: rpcHTTPRoot, + }) + + return { + wss, + handler: applyWSSHandler({ + wss, + router: yield* RPCRouter, + createContext: (yield* TRPCContextCreator).createWebSocketContext, + }), + } + }), + + ({ wss, handler }) => Effect.gen(function*() { + yield* Effect.logInfo(`WebSocket server on ${ rpcHTTPRoot } is stopping. Waiting for existing connections to end...`) + + handler.broadcastReconnectNotification() + yield* Effect.async(resume => { + wss.close(() => resume(Effect.logInfo("WebSocket server closed"))) + }) + }), + )) +)) diff --git a/src/Layers/trpc/createTRCPErrorMapper.ts b/src/Layers/trpc/createTRCPErrorMapper.ts index 5a5953c..38af078 100644 --- a/src/Layers/trpc/createTRCPErrorMapper.ts +++ b/src/Layers/trpc/createTRCPErrorMapper.ts @@ -1,9 +1,10 @@ import { Effect, type Cause } from "effect" +import { ImportError } from "../../ImportError" const importTRPCServer = Effect.tryPromise({ try: () => import("@trpc/server"), - catch: cause => new Error("Could not import '@trpc/server'. Make sure it is installed.", { cause }), + catch: cause => new ImportError({ path: "@trpc/server", cause }), }) export const createTRCPErrorMapper = importTRPCServer.pipe(Effect.map(({ TRPCError }) => diff --git a/src/index.ts b/src/index.ts index 1129b8d..f18793d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,4 @@ +export * from "./ImportError" export * as Layers from "./Layers" export * as Schema from "./Schema" export * as Types from "./Types" -- 2.49.1 From 442148b64dd55982d6ffb47ff223f7d8f8aa0ea2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Fri, 6 Sep 2024 06:33:24 +0200 Subject: [PATCH 14/37] TRPCWebSocketServer --- src/Layers/trpc/TRPCWebSocketServer.ts | 60 +++++++++++++++++--------- src/Layers/trpc/index.ts | 1 + 2 files changed, 41 insertions(+), 20 deletions(-) diff --git a/src/Layers/trpc/TRPCWebSocketServer.ts b/src/Layers/trpc/TRPCWebSocketServer.ts index d36f03b..bcf214a 100644 --- a/src/Layers/trpc/TRPCWebSocketServer.ts +++ b/src/Layers/trpc/TRPCWebSocketServer.ts @@ -1,44 +1,64 @@ -import { applyWSSHandler } from "@trpc/server/adapters/ws" -import { Context, Effect, Layer } from "effect" -import ws from "ws" -import { ExpressHTTPServer } from "../http/ExpressHTTPServer.service" -import { ServerConfig } from "../ServerConfig" -import { TRPCContextCreator } from "../trpc/TRPCContextCreator.service" -import { RPCRouter } from "./RPCRouter.service" +import type { applyWSSHandler } from "@trpc/server/adapters/ws" +import { Config, Context, Effect, Layer } from "effect" +import type ws from "ws" +import { ImportError } from "../../ImportError" +import { ExpressNodeHTTPServer } from "../express" +import { TRPCUnknownContextCreator } from "./TRPCContextCreator" +import { TRPCAnyRouter } from "./TRPCRouter" -export class TRPCWebSocketServer extends Context.Tag("TRPCWebSocketServer") }>() {} -export const RPCWebSocketServerLive = Layer.effect(RPCWebSocketServer, ServerConfig.rpcHTTPRoot.pipe( - Effect.flatMap(rpcHTTPRoot => Effect.acquireRelease( - Effect.gen(function*() { - yield* Effect.logInfo(`WebSocket server started on ${ rpcHTTPRoot }`) - const wss = new ws.WebSocketServer({ - server: yield* ExpressHTTPServer, - host: rpcHTTPRoot, +const importWS = Effect.tryPromise({ + try: () => import("ws"), + catch: cause => new ImportError({ path: "ws", cause }), +}) + +const importTRPCServerWSAdapter = Effect.tryPromise({ + try: () => import("@trpc/server/adapters/ws"), + catch: cause => new ImportError({ path: "@trpc/server/adapters/ws", cause }), +}) + +export const TRPCWebSocketServerLive = ( + config: { + readonly host: Config.Config + } +) => Layer.effect(TRPCWebSocketServer, Effect.gen(function*() { + const { WebSocketServer } = yield* importWS + const { applyWSSHandler } = yield* importTRPCServerWSAdapter + + const host = yield* config.host + + return yield* Effect.acquireRelease( + Effect.gen(function*() { + yield* Effect.logInfo(`WebSocket server started on ${ host }`) + + const wss = new WebSocketServer({ + server: yield* ExpressNodeHTTPServer.ExpressNodeHTTPServer, + host, }) return { wss, handler: applyWSSHandler({ wss, - router: yield* RPCRouter, - createContext: (yield* TRPCContextCreator).createWebSocketContext, + router: yield* TRPCAnyRouter, + createContext: (yield* TRPCUnknownContextCreator).createWebSocketContext, }), } }), ({ wss, handler }) => Effect.gen(function*() { - yield* Effect.logInfo(`WebSocket server on ${ rpcHTTPRoot } is stopping. Waiting for existing connections to end...`) + yield* Effect.logInfo(`WebSocket server on ${ host } is stopping. Waiting for existing connections to end...`) handler.broadcastReconnectNotification() yield* Effect.async(resume => { wss.close(() => resume(Effect.logInfo("WebSocket server closed"))) }) }), - )) -)) + ) +})) diff --git a/src/Layers/trpc/index.ts b/src/Layers/trpc/index.ts index 3703bfc..9e23e90 100644 --- a/src/Layers/trpc/index.ts +++ b/src/Layers/trpc/index.ts @@ -3,3 +3,4 @@ export * as TRPCContext from "./TRPCContext" export * as TRPCContextCreator from "./TRPCContextCreator" export * as TRPCExpressRoute from "./TRPCExpressRoute" export * as TRPCRouter from "./TRPCRouter" +export * as TRPCWebSocketServer from "./TRPCWebSocketServer" -- 2.49.1 From 7a0226cb237cab322c6a269e4f03e7860f0b4e9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Fri, 6 Sep 2024 07:29:35 +0200 Subject: [PATCH 15/37] Middlewares --- src/Layers/trpc/createTRCPErrorMapper.ts | 7 +------ src/Layers/trpc/importTRPCServer.ts | 8 ++++++++ src/Layers/trpc/index.ts | 1 + src/Layers/trpc/middlewares.ts | 24 ++++++++++++++++++++++++ 4 files changed, 34 insertions(+), 6 deletions(-) create mode 100644 src/Layers/trpc/importTRPCServer.ts create mode 100644 src/Layers/trpc/middlewares.ts diff --git a/src/Layers/trpc/createTRCPErrorMapper.ts b/src/Layers/trpc/createTRCPErrorMapper.ts index 38af078..d59ac86 100644 --- a/src/Layers/trpc/createTRCPErrorMapper.ts +++ b/src/Layers/trpc/createTRCPErrorMapper.ts @@ -1,12 +1,7 @@ import { Effect, type Cause } from "effect" -import { ImportError } from "../../ImportError" +import { importTRPCServer } from "./importTRPCServer" -const importTRPCServer = Effect.tryPromise({ - try: () => import("@trpc/server"), - catch: cause => new ImportError({ path: "@trpc/server", cause }), -}) - export const createTRCPErrorMapper = importTRPCServer.pipe(Effect.map(({ TRPCError }) => (effect: Effect.Effect) => Effect.sandbox(effect).pipe( Effect.catchTags({ diff --git a/src/Layers/trpc/importTRPCServer.ts b/src/Layers/trpc/importTRPCServer.ts new file mode 100644 index 0000000..26a5ea1 --- /dev/null +++ b/src/Layers/trpc/importTRPCServer.ts @@ -0,0 +1,8 @@ +import { Effect } from "effect" +import { ImportError } from "../../ImportError" + + +export const importTRPCServer = Effect.tryPromise({ + try: () => import("@trpc/server"), + catch: cause => new ImportError({ path: "@trpc/server", cause }), +}) diff --git a/src/Layers/trpc/index.ts b/src/Layers/trpc/index.ts index 9e23e90..71403b6 100644 --- a/src/Layers/trpc/index.ts +++ b/src/Layers/trpc/index.ts @@ -1,3 +1,4 @@ +export * from "./middlewares" export * as TRPCBuilder from "./TRPCBuilder" export * as TRPCContext from "./TRPCContext" export * as TRPCContextCreator from "./TRPCContextCreator" diff --git a/src/Layers/trpc/middlewares.ts b/src/Layers/trpc/middlewares.ts new file mode 100644 index 0000000..c63b0d1 --- /dev/null +++ b/src/Layers/trpc/middlewares.ts @@ -0,0 +1,24 @@ +import { Effect, Match } from "effect" +import type { TRPCContextTransaction } from "./TRPCContext" +import { importTRPCServer } from "./importTRPCServer" + + +export const ExpressOnly = importTRPCServer.pipe(Effect.map(({ + experimental_standaloneMiddleware, + TRPCError, +}) => experimental_standaloneMiddleware<{ + ctx: { readonly transaction: TRPCContextTransaction } +}>().create(opts => + Match.value(opts.ctx.transaction).pipe( + Match.tag("Express", transaction => + opts.next({ ctx: { transaction } }) + ), + + Match.orElse(() => { + throw new TRPCError({ + code: "BAD_REQUEST", + message: "Only Express backend is supported by this procedure", + }) + }), + ) +))) -- 2.49.1 From 4e55a699378526aa5812b47e7fff057263d4d50c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Fri, 6 Sep 2024 07:38:02 +0200 Subject: [PATCH 16/37] WebSocketOnly --- src/Layers/trpc/middlewares.ts | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/Layers/trpc/middlewares.ts b/src/Layers/trpc/middlewares.ts index c63b0d1..fcd57e3 100644 --- a/src/Layers/trpc/middlewares.ts +++ b/src/Layers/trpc/middlewares.ts @@ -17,7 +17,27 @@ export const ExpressOnly = importTRPCServer.pipe(Effect.map(({ Match.orElse(() => { throw new TRPCError({ code: "BAD_REQUEST", - message: "Only Express backend is supported by this procedure", + message: "Only Express transport is supported by this procedure", + }) + }), + ) +))) + +export const WebSocketOnly = importTRPCServer.pipe(Effect.map(({ + experimental_standaloneMiddleware, + TRPCError, +}) => experimental_standaloneMiddleware<{ + ctx: { readonly transaction: TRPCContextTransaction } +}>().create(opts => + Match.value(opts.ctx.transaction).pipe( + Match.tag("WebSocket", transaction => + opts.next({ ctx: { transaction } }) + ), + + Match.orElse(() => { + throw new TRPCError({ + code: "BAD_REQUEST", + message: "Only WebSocket transport is supported by this procedure", }) }), ) -- 2.49.1 From e4975bafdf93566aab3ada7671f5a0f2b83dde42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Fri, 6 Sep 2024 07:44:09 +0200 Subject: [PATCH 17/37] Tests --- src/Layers/trpc/tests.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 src/Layers/trpc/tests.ts diff --git a/src/Layers/trpc/tests.ts b/src/Layers/trpc/tests.ts new file mode 100644 index 0000000..2f51664 --- /dev/null +++ b/src/Layers/trpc/tests.ts @@ -0,0 +1,18 @@ +import { Console, Effect, identity } from "effect" + + +const expansiveComputation = Effect.gen(function*() { + yield* Console.log("executing") + return "value" +}).pipe( + Effect.cached, +) + +await Effect.gen(function*() { + const cached = yield* expansiveComputation + yield* Console.log(yield* cached) + yield* Console.log(yield* cached) + yield* Console.log(yield* cached) +}).pipe( + Effect.runPromise +) -- 2.49.1 From 8b55d8d07873dd09949f43af588ed27e2c5c6ec9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Fri, 6 Sep 2024 07:51:51 +0200 Subject: [PATCH 18/37] tsconfig --- tsconfig.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tsconfig.json b/tsconfig.json index 238655f..ef46c48 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -6,7 +6,7 @@ "module": "ESNext", "moduleDetection": "force", "jsx": "react-jsx", - "allowJs": true, + // "allowJs": true, // Bundler mode "moduleResolution": "bundler", @@ -23,5 +23,7 @@ "noUnusedLocals": false, "noUnusedParameters": false, "noPropertyAccessFromIndexSignature": false - } + }, + + "include": ["./src"] } -- 2.49.1 From 329e38afe04412815c5247b780111c6aa058faf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Fri, 6 Sep 2024 08:08:02 +0200 Subject: [PATCH 19/37] tRPC example --- src/Layers/trpc/TRPCExpressRoute.ts | 4 +-- src/Layers/trpc/tests.ts | 47 +++++++++++++++++++++-------- 2 files changed, 36 insertions(+), 15 deletions(-) diff --git a/src/Layers/trpc/TRPCExpressRoute.ts b/src/Layers/trpc/TRPCExpressRoute.ts index 8d2a911..d46e288 100644 --- a/src/Layers/trpc/TRPCExpressRoute.ts +++ b/src/Layers/trpc/TRPCExpressRoute.ts @@ -12,13 +12,13 @@ const importTRPCServerExpressAdapter = Effect.tryPromise({ export const TRPCExpressRouteLive = ( config: { - readonly path: Config.Config + readonly root: Config.Config } ) => Layer.effectDiscard(Effect.gen(function*() { const { createExpressMiddleware } = yield* importTRPCServerExpressAdapter const app = yield* ExpressApp.ExpressApp - app.use(yield* config.path, + app.use(yield* config.root, createExpressMiddleware({ router: yield* TRPCAnyRouter, createContext: (yield* TRPCUnknownContextCreator).createExpressContext, diff --git a/src/Layers/trpc/tests.ts b/src/Layers/trpc/tests.ts index 2f51664..5e3105a 100644 --- a/src/Layers/trpc/tests.ts +++ b/src/Layers/trpc/tests.ts @@ -1,18 +1,39 @@ -import { Console, Effect, identity } from "effect" +import { Config, Effect, Layer } from "effect" +import * as TRPC from "." +import { ExpressApp, ExpressNodeHTTPServer } from "../express" -const expansiveComputation = Effect.gen(function*() { - yield* Console.log("executing") - return "value" -}).pipe( - Effect.cached, +type Services = never + +const { TRPCContextCreator, TRPCContextCreatorLive } = TRPC.TRPCContextCreator.make() +const { TRPCBuilder, TRPCBuilderLive } = TRPC.TRPCBuilder.make() + + +const router = TRPCBuilder.pipe(Effect.map(t => t.router({ + ping: t.procedure.query(({ ctx }) => ctx.run( + Effect.succeed("pong") + )), +}))) + +const { TRPCRouter, TRPCRouterLive } = TRPC.TRPCRouter.make(router) + + +const ServerLive = Layer.empty.pipe( + Layer.provideMerge(TRPC.TRPCExpressRoute.TRPCExpressRouteLive({ + root: Config.succeed("/rpc") + })), + + Layer.provideMerge(TRPCRouterLive), + Layer.provideMerge(TRPCBuilderLive), + Layer.provideMerge(TRPCContextCreatorLive), + + Layer.provideMerge(ExpressNodeHTTPServer.ExpressNodeHTTPServerLive({ + port: Config.succeed(3000) + })), + Layer.provideMerge(ExpressApp.ExpressAppLive()) ) -await Effect.gen(function*() { - const cached = yield* expansiveComputation - yield* Console.log(yield* cached) - yield* Console.log(yield* cached) - yield* Console.log(yield* cached) -}).pipe( - Effect.runPromise +await Layer.launch(ServerLive).pipe( + Effect.scoped, + Effect.runPromise, ) -- 2.49.1 From 58d0d85dda8ce9b823ee76bd6d020a43e8d933d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sat, 7 Sep 2024 01:44:24 +0200 Subject: [PATCH 20/37] Tests --- src/Layers/trpc/TRPCRouter.ts | 4 ++-- src/Layers/trpc/tests.ts | 9 +++++++-- src/Layers/trpc/tests2.ts | 35 +++++++++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 4 deletions(-) create mode 100644 src/Layers/trpc/tests2.ts diff --git a/src/Layers/trpc/TRPCRouter.ts b/src/Layers/trpc/TRPCRouter.ts index 5ab2263..a475740 100644 --- a/src/Layers/trpc/TRPCRouter.ts +++ b/src/Layers/trpc/TRPCRouter.ts @@ -1,9 +1,9 @@ -import type { AnyRouterDef, Router } from "@trpc/server" +import type { AnyRouter, AnyRouterDef, Router } from "@trpc/server" import { Context, Effect, Layer } from "effect" export class TRPCAnyRouter extends Context.Tag("@thilalib/TRCP/TRPCRouter") + AnyRouter >() {} diff --git a/src/Layers/trpc/tests.ts b/src/Layers/trpc/tests.ts index 5e3105a..366521a 100644 --- a/src/Layers/trpc/tests.ts +++ b/src/Layers/trpc/tests.ts @@ -1,4 +1,4 @@ -import { Config, Effect, Layer } from "effect" +import { Config, Context, Effect, Layer } from "effect" import * as TRPC from "." import { ExpressApp, ExpressNodeHTTPServer } from "../express" @@ -16,6 +16,7 @@ const router = TRPCBuilder.pipe(Effect.map(t => t.router({ }))) const { TRPCRouter, TRPCRouterLive } = TRPC.TRPCRouter.make(router) +type TRouter = Context.Tag.Service const ServerLive = Layer.empty.pipe( @@ -33,7 +34,11 @@ const ServerLive = Layer.empty.pipe( Layer.provideMerge(ExpressApp.ExpressAppLive()) ) -await Layer.launch(ServerLive).pipe( +await Effect.gen(function*() { + // yield* TRPCRouter + + return yield* Layer.launch(ServerLive) +}).pipe( Effect.scoped, Effect.runPromise, ) diff --git a/src/Layers/trpc/tests2.ts b/src/Layers/trpc/tests2.ts new file mode 100644 index 0000000..c1f2330 --- /dev/null +++ b/src/Layers/trpc/tests2.ts @@ -0,0 +1,35 @@ +import type { AnyRouter } from "@trpc/server" +import { Context, Effect, Layer } from "effect" +import * as TRPC from "." + + +const { TRPCBuilder, TRPCBuilderLive } = TRPC.TRPCBuilder.make() + +const router = TRPCBuilder.pipe(Effect.map(t => t.router({ + ping: t.procedure.query(({ ctx }) => ctx.run( + Effect.succeed("pong") + )), +}))) + + +export class TRPCAnyRouter extends Context.Tag("@thilalib/TRCP/TRPCRouter")() {} + +export class TRPCRouter extends Context.Tag("@thilalib/TRCP/TRPCRouter") +>() {} + +const RouterLive = Layer.effect(TRPCRouter, router) +const AnyRouterLive = Layer.effect(TRPCAnyRouter, router) + + +const main = Effect.gen(function*() { + yield* TRPCAnyRouter + yield* TRPCRouter +}) + +const runnable = main.pipe( + Effect.provide(RouterLive), + Effect.provide(TRPCBuilderLive), +) -- 2.49.1 From 65ef5f0896064e15fc172d062033bd01dc07038b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sat, 7 Sep 2024 02:14:11 +0200 Subject: [PATCH 21/37] TRCP refactoring --- src/Layers/trpc/TRPCExpressRoute.ts | 12 ++-- src/Layers/trpc/TRPCRouter.ts | 9 +-- src/Layers/trpc/TRPCWebSocketServer.ts | 84 ++++++++++++++------------ src/Layers/trpc/tests.ts | 3 +- src/Layers/trpc/tests2.ts | 35 ----------- 5 files changed, 55 insertions(+), 88 deletions(-) delete mode 100644 src/Layers/trpc/tests2.ts diff --git a/src/Layers/trpc/TRPCExpressRoute.ts b/src/Layers/trpc/TRPCExpressRoute.ts index d46e288..66bface 100644 --- a/src/Layers/trpc/TRPCExpressRoute.ts +++ b/src/Layers/trpc/TRPCExpressRoute.ts @@ -1,8 +1,8 @@ -import { Config, Effect, Layer } from "effect" +import type { AnyRouter } from "@trpc/server" +import { Config, Context, Effect, Layer } from "effect" import { ImportError } from "../../ImportError" import { ExpressApp } from "../express" import { TRPCUnknownContextCreator } from "./TRPCContextCreator" -import { TRPCAnyRouter } from "./TRPCRouter" const importTRPCServerExpressAdapter = Effect.tryPromise({ @@ -10,17 +10,19 @@ const importTRPCServerExpressAdapter = Effect.tryPromise({ catch: cause => new ImportError({ path: "@trpc/server/adapters/express", cause }), }) -export const TRPCExpressRouteLive = ( +export const TRPCExpressRouteLive = ( + routerTag: Context.Tag<"@thilalib/TRCP/TRPCRouter", T>, + config: { readonly root: Config.Config - } + }, ) => Layer.effectDiscard(Effect.gen(function*() { const { createExpressMiddleware } = yield* importTRPCServerExpressAdapter const app = yield* ExpressApp.ExpressApp app.use(yield* config.root, createExpressMiddleware({ - router: yield* TRPCAnyRouter, + router: yield* routerTag, createContext: (yield* TRPCUnknownContextCreator).createExpressContext, }) ) diff --git a/src/Layers/trpc/TRPCRouter.ts b/src/Layers/trpc/TRPCRouter.ts index a475740..827e478 100644 --- a/src/Layers/trpc/TRPCRouter.ts +++ b/src/Layers/trpc/TRPCRouter.ts @@ -1,14 +1,9 @@ -import type { AnyRouter, AnyRouterDef, Router } from "@trpc/server" +import type { AnyRouter } from "@trpc/server" import { Context, Effect, Layer } from "effect" -export class TRPCAnyRouter extends Context.Tag("@thilalib/TRCP/TRPCRouter")() {} - - export const make = < - A extends Router, + A extends AnyRouter, E, R, >(router: Effect.Effect) => { class TRPCRouter extends Context.Tag("@thilalib/TRCP/TRPCRouter")() {} diff --git a/src/Layers/trpc/TRPCWebSocketServer.ts b/src/Layers/trpc/TRPCWebSocketServer.ts index bcf214a..64169ad 100644 --- a/src/Layers/trpc/TRPCWebSocketServer.ts +++ b/src/Layers/trpc/TRPCWebSocketServer.ts @@ -1,16 +1,10 @@ +import type { AnyRouter } from "@trpc/server" import type { applyWSSHandler } from "@trpc/server/adapters/ws" import { Config, Context, Effect, Layer } from "effect" import type ws from "ws" import { ImportError } from "../../ImportError" import { ExpressNodeHTTPServer } from "../express" import { TRPCUnknownContextCreator } from "./TRPCContextCreator" -import { TRPCAnyRouter } from "./TRPCRouter" - - -export class TRPCWebSocketServer extends Context.Tag("@thilalib/TRPC/TRPCWebSocketServer") -}>() {} const importWS = Effect.tryPromise({ @@ -23,42 +17,54 @@ const importTRPCServerWSAdapter = Effect.tryPromise({ catch: cause => new ImportError({ path: "@trpc/server/adapters/ws", cause }), }) -export const TRPCWebSocketServerLive = ( - config: { - readonly host: Config.Config - } -) => Layer.effect(TRPCWebSocketServer, Effect.gen(function*() { - const { WebSocketServer } = yield* importWS - const { applyWSSHandler } = yield* importTRPCServerWSAdapter - const host = yield* config.host +export const make = ( + routerTag: Context.Tag<"@thilalib/TRCP/TRPCRouter", T> +) => { + class TRPCWebSocketServer extends Context.Tag("@thilalib/TRPC/TRPCWebSocketServer")> + }>() {} - return yield* Effect.acquireRelease( - Effect.gen(function*() { - yield* Effect.logInfo(`WebSocket server started on ${ host }`) + const TRPCWebSocketServerLive = ( + config: { + readonly host: Config.Config + } + ) => Layer.effect(TRPCWebSocketServer, Effect.gen(function*() { + const { WebSocketServer } = yield* importWS + const { applyWSSHandler } = yield* importTRPCServerWSAdapter - const wss = new WebSocketServer({ - server: yield* ExpressNodeHTTPServer.ExpressNodeHTTPServer, - host, - }) + const host = yield* config.host - return { - wss, - handler: applyWSSHandler({ + return yield* Effect.acquireRelease( + Effect.gen(function*() { + yield* Effect.logInfo(`WebSocket server started on ${ host }`) + + const wss = new WebSocketServer({ + server: yield* ExpressNodeHTTPServer.ExpressNodeHTTPServer, + host, + }) + + return { wss, - router: yield* TRPCAnyRouter, - createContext: (yield* TRPCUnknownContextCreator).createWebSocketContext, - }), - } - }), + handler: applyWSSHandler({ + wss, + router: yield* routerTag, + createContext: (yield* TRPCUnknownContextCreator).createWebSocketContext, + }), + } + }), - ({ wss, handler }) => Effect.gen(function*() { - yield* Effect.logInfo(`WebSocket server on ${ host } is stopping. Waiting for existing connections to end...`) + ({ wss, handler }) => Effect.gen(function*() { + yield* Effect.logInfo(`WebSocket server on ${ host } is stopping. Waiting for existing connections to end...`) - handler.broadcastReconnectNotification() - yield* Effect.async(resume => { - wss.close(() => resume(Effect.logInfo("WebSocket server closed"))) - }) - }), - ) -})) + handler.broadcastReconnectNotification() + yield* Effect.async(resume => { + wss.close(() => resume(Effect.logInfo("WebSocket server closed"))) + }) + }), + ) + })) + + return { TRPCWebSocketServer, TRPCWebSocketServerLive } +} diff --git a/src/Layers/trpc/tests.ts b/src/Layers/trpc/tests.ts index 366521a..359fb8b 100644 --- a/src/Layers/trpc/tests.ts +++ b/src/Layers/trpc/tests.ts @@ -16,11 +16,10 @@ const router = TRPCBuilder.pipe(Effect.map(t => t.router({ }))) const { TRPCRouter, TRPCRouterLive } = TRPC.TRPCRouter.make(router) -type TRouter = Context.Tag.Service const ServerLive = Layer.empty.pipe( - Layer.provideMerge(TRPC.TRPCExpressRoute.TRPCExpressRouteLive({ + Layer.provideMerge(TRPC.TRPCExpressRoute.TRPCExpressRouteLive(TRPCRouter, { root: Config.succeed("/rpc") })), diff --git a/src/Layers/trpc/tests2.ts b/src/Layers/trpc/tests2.ts deleted file mode 100644 index c1f2330..0000000 --- a/src/Layers/trpc/tests2.ts +++ /dev/null @@ -1,35 +0,0 @@ -import type { AnyRouter } from "@trpc/server" -import { Context, Effect, Layer } from "effect" -import * as TRPC from "." - - -const { TRPCBuilder, TRPCBuilderLive } = TRPC.TRPCBuilder.make() - -const router = TRPCBuilder.pipe(Effect.map(t => t.router({ - ping: t.procedure.query(({ ctx }) => ctx.run( - Effect.succeed("pong") - )), -}))) - - -export class TRPCAnyRouter extends Context.Tag("@thilalib/TRCP/TRPCRouter")() {} - -export class TRPCRouter extends Context.Tag("@thilalib/TRCP/TRPCRouter") ->() {} - -const RouterLive = Layer.effect(TRPCRouter, router) -const AnyRouterLive = Layer.effect(TRPCAnyRouter, router) - - -const main = Effect.gen(function*() { - yield* TRPCAnyRouter - yield* TRPCRouter -}) - -const runnable = main.pipe( - Effect.provide(RouterLive), - Effect.provide(TRPCBuilderLive), -) -- 2.49.1 From 12b21d3773f914b53efa8df8530567f9c582746f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sat, 7 Sep 2024 02:24:27 +0200 Subject: [PATCH 22/37] TRCP route fix --- src/Layers/trpc/TRPCExpressRoute.ts | 7 +++++-- src/Layers/trpc/TRPCWebSocketServer.ts | 9 ++++++--- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/Layers/trpc/TRPCExpressRoute.ts b/src/Layers/trpc/TRPCExpressRoute.ts index 66bface..62cca46 100644 --- a/src/Layers/trpc/TRPCExpressRoute.ts +++ b/src/Layers/trpc/TRPCExpressRoute.ts @@ -10,8 +10,11 @@ const importTRPCServerExpressAdapter = Effect.tryPromise({ catch: cause => new ImportError({ path: "@trpc/server/adapters/express", cause }), }) -export const TRPCExpressRouteLive = ( - routerTag: Context.Tag<"@thilalib/TRCP/TRPCRouter", T>, +export const TRPCExpressRouteLive = < + Tag, + TagShape extends AnyRouter, +>( + routerTag: Context.TagClass, config: { readonly root: Config.Config diff --git a/src/Layers/trpc/TRPCWebSocketServer.ts b/src/Layers/trpc/TRPCWebSocketServer.ts index 64169ad..19eb7be 100644 --- a/src/Layers/trpc/TRPCWebSocketServer.ts +++ b/src/Layers/trpc/TRPCWebSocketServer.ts @@ -18,12 +18,15 @@ const importTRPCServerWSAdapter = Effect.tryPromise({ }) -export const make = ( - routerTag: Context.Tag<"@thilalib/TRCP/TRPCRouter", T> +export const make = < + Tag, + TagShape extends AnyRouter, +>( + routerTag: Context.TagClass ) => { class TRPCWebSocketServer extends Context.Tag("@thilalib/TRPC/TRPCWebSocketServer")> + handler: ReturnType> }>() {} const TRPCWebSocketServerLive = ( -- 2.49.1 From 03620941e4519cb2865ace65d8a97f2b2e9d2a0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sat, 7 Sep 2024 02:27:18 +0200 Subject: [PATCH 23/37] Working TRPC layers --- src/Layers/trpc/tests.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Layers/trpc/tests.ts b/src/Layers/trpc/tests.ts index 359fb8b..e0ac6c0 100644 --- a/src/Layers/trpc/tests.ts +++ b/src/Layers/trpc/tests.ts @@ -1,4 +1,4 @@ -import { Config, Context, Effect, Layer } from "effect" +import { Config, Effect, Layer } from "effect" import * as TRPC from "." import { ExpressApp, ExpressNodeHTTPServer } from "../express" @@ -17,11 +17,16 @@ const router = TRPCBuilder.pipe(Effect.map(t => t.router({ const { TRPCRouter, TRPCRouterLive } = TRPC.TRPCRouter.make(router) +const { TRPCWebSocketServer, TRPCWebSocketServerLive } = TRPC.TRPCWebSocketServer.make(TRPCRouter) + const ServerLive = Layer.empty.pipe( Layer.provideMerge(TRPC.TRPCExpressRoute.TRPCExpressRouteLive(TRPCRouter, { root: Config.succeed("/rpc") })), + Layer.provideMerge(TRPCWebSocketServerLive({ + host: Config.succeed("/rpc") + })), Layer.provideMerge(TRPCRouterLive), Layer.provideMerge(TRPCBuilderLive), -- 2.49.1 From 8dc794635e6929a347982c242d12d6fcc93a259b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sat, 7 Sep 2024 02:55:23 +0200 Subject: [PATCH 24/37] Fix --- src/Layers/trpc/TRPCBuilder.ts | 20 ++++++++++---------- src/Layers/trpc/tests.ts | 13 +++++++++++-- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/src/Layers/trpc/TRPCBuilder.ts b/src/Layers/trpc/TRPCBuilder.ts index d8472f5..71771fe 100644 --- a/src/Layers/trpc/TRPCBuilder.ts +++ b/src/Layers/trpc/TRPCBuilder.ts @@ -1,18 +1,18 @@ -import { initTRPC } from "@trpc/server" -import { Context, Layer } from "effect" +import { Context, Effect, Layer } from "effect" import { type TRPCContext } from "./TRPCContext" +import { importTRPCServer } from "./importTRPCServer" -const createTRPC = () => initTRPC.context>().create() - -export class TRPCUnknownBuilder extends Context.Tag("@thilalib/TRPC/TRPCBuilder")> ->() {} - +const createTRPC = () => importTRPCServer.pipe(Effect.map(({ initTRPC }) => + initTRPC.context>().create() +)) export const make = () => { - class TRPCBuilder extends Context.Tag("@thilalib/TRPC/TRPCBuilder")>>() {} - const TRPCBuilderLive = Layer.sync(TRPCBuilder, createTRPC) + class TRPCBuilder extends Context.Tag("@thilalib/TRPC/TRPCBuilder")>> + >() {} + + const TRPCBuilderLive = Layer.effect(TRPCBuilder, createTRPC()) return { TRPCBuilder, TRPCBuilderLive } } diff --git a/src/Layers/trpc/tests.ts b/src/Layers/trpc/tests.ts index e0ac6c0..3f2dd92 100644 --- a/src/Layers/trpc/tests.ts +++ b/src/Layers/trpc/tests.ts @@ -1,9 +1,17 @@ import { Config, Effect, Layer } from "effect" import * as TRPC from "." +import { JSONWebToken } from ".." import { ExpressApp, ExpressNodeHTTPServer } from "../express" -type Services = never +// Context available to the router procedures +type Services = + | JSONWebToken.JSONWebToken + +const ServicesLive = Layer.empty.pipe( + Layer.provideMerge(JSONWebToken.JSONWebTokenLive) +) + const { TRPCContextCreator, TRPCContextCreatorLive } = TRPC.TRPCContextCreator.make() const { TRPCBuilder, TRPCBuilderLive } = TRPC.TRPCBuilder.make() @@ -14,9 +22,9 @@ const router = TRPCBuilder.pipe(Effect.map(t => t.router({ Effect.succeed("pong") )), }))) - const { TRPCRouter, TRPCRouterLive } = TRPC.TRPCRouter.make(router) + const { TRPCWebSocketServer, TRPCWebSocketServerLive } = TRPC.TRPCWebSocketServer.make(TRPCRouter) @@ -43,6 +51,7 @@ await Effect.gen(function*() { return yield* Layer.launch(ServerLive) }).pipe( + Effect.provide(ServicesLive), Effect.scoped, Effect.runPromise, ) -- 2.49.1 From f939e516b39e27ee0763cb17fefaff6d669dd64e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sat, 7 Sep 2024 03:08:09 +0200 Subject: [PATCH 25/37] File structure refactoring --- src/{Layers/express => Express}/ExpressApp.ts | 2 +- src/{Layers/express => Express}/ExpressNodeHTTPServer.ts | 2 +- src/{Layers/express/tests.ts => Express/example.ts} | 0 src/{Layers/express => Express}/index.ts | 0 src/{Layers => }/JSONWebToken.ts | 2 +- src/Layers/index.ts | 4 ---- src/{Layers => }/OpenAIClient.ts | 2 +- src/{Layers => }/PrismaStudioRoute.ts | 0 src/{Layers/trpc => TRPC}/TRPCBuilder.ts | 0 src/{Layers/trpc => TRPC}/TRPCContext.ts | 0 src/{Layers/trpc => TRPC}/TRPCContextCreator.ts | 0 src/{Layers/trpc => TRPC}/TRPCExpressRoute.ts | 4 ++-- src/{Layers/trpc => TRPC}/TRPCRouter.ts | 0 src/{Layers/trpc => TRPC}/TRPCWebSocketServer.ts | 4 ++-- src/{Layers/trpc => TRPC}/createTRCPErrorMapper.ts | 0 src/{Layers/trpc/tests.ts => TRPC/example.ts} | 7 +++---- src/{Layers/trpc => TRPC}/importTRPCServer.ts | 2 +- src/{Layers/trpc => TRPC}/index.ts | 0 src/{Layers/trpc => TRPC}/middlewares.ts | 0 src/index.ts | 4 +++- tsup.config.ts | 2 +- 21 files changed, 16 insertions(+), 19 deletions(-) rename src/{Layers/express => Express}/ExpressApp.ts (93%) rename src/{Layers/express => Express}/ExpressNodeHTTPServer.ts (98%) rename src/{Layers/express/tests.ts => Express/example.ts} (100%) rename src/{Layers/express => Express}/index.ts (100%) rename src/{Layers => }/JSONWebToken.ts (97%) delete mode 100644 src/Layers/index.ts rename src/{Layers => }/OpenAIClient.ts (98%) rename src/{Layers => }/PrismaStudioRoute.ts (100%) rename src/{Layers/trpc => TRPC}/TRPCBuilder.ts (100%) rename src/{Layers/trpc => TRPC}/TRPCContext.ts (100%) rename src/{Layers/trpc => TRPC}/TRPCContextCreator.ts (100%) rename src/{Layers/trpc => TRPC}/TRPCExpressRoute.ts (91%) rename src/{Layers/trpc => TRPC}/TRPCRouter.ts (100%) rename src/{Layers/trpc => TRPC}/TRPCWebSocketServer.ts (96%) rename src/{Layers/trpc => TRPC}/createTRCPErrorMapper.ts (100%) rename src/{Layers/trpc/tests.ts => TRPC/example.ts} (86%) rename src/{Layers/trpc => TRPC}/importTRPCServer.ts (80%) rename src/{Layers/trpc => TRPC}/index.ts (100%) rename src/{Layers/trpc => TRPC}/middlewares.ts (100%) diff --git a/src/Layers/express/ExpressApp.ts b/src/Express/ExpressApp.ts similarity index 93% rename from src/Layers/express/ExpressApp.ts rename to src/Express/ExpressApp.ts index 8b6fdbe..af91481 100644 --- a/src/Layers/express/ExpressApp.ts +++ b/src/Express/ExpressApp.ts @@ -1,6 +1,6 @@ import { Config, Context, Effect, Layer } from "effect" import type { Express } from "express" -import { ImportError } from "../../ImportError" +import { ImportError } from "../ImportError" export class ExpressApp extends Context.Tag("ExpressApp")() {} diff --git a/src/Layers/express/ExpressNodeHTTPServer.ts b/src/Express/ExpressNodeHTTPServer.ts similarity index 98% rename from src/Layers/express/ExpressNodeHTTPServer.ts rename to src/Express/ExpressNodeHTTPServer.ts index d56f1cb..25ff95f 100644 --- a/src/Layers/express/ExpressNodeHTTPServer.ts +++ b/src/Express/ExpressNodeHTTPServer.ts @@ -1,7 +1,7 @@ import { Config, Context, Effect, Layer, Match } from "effect" import type { Server } from "node:http" import type { AddressInfo } from "node:net" -import { ImportError } from "../../ImportError" +import { ImportError } from "../ImportError" import { ExpressApp } from "./ExpressApp" diff --git a/src/Layers/express/tests.ts b/src/Express/example.ts similarity index 100% rename from src/Layers/express/tests.ts rename to src/Express/example.ts diff --git a/src/Layers/express/index.ts b/src/Express/index.ts similarity index 100% rename from src/Layers/express/index.ts rename to src/Express/index.ts diff --git a/src/Layers/JSONWebToken.ts b/src/JSONWebToken.ts similarity index 97% rename from src/Layers/JSONWebToken.ts rename to src/JSONWebToken.ts index f9f76c1..f088049 100644 --- a/src/Layers/JSONWebToken.ts +++ b/src/JSONWebToken.ts @@ -1,6 +1,6 @@ import { Context, Effect, Layer } from "effect" import type * as JWT from "jsonwebtoken" -import { ImportError } from "../ImportError" +import { ImportError } from "./ImportError" export class JSONWebToken extends Context.Tag("JSONWebToken")() {} diff --git a/src/Layers/PrismaStudioRoute.ts b/src/PrismaStudioRoute.ts similarity index 100% rename from src/Layers/PrismaStudioRoute.ts rename to src/PrismaStudioRoute.ts diff --git a/src/Layers/trpc/TRPCBuilder.ts b/src/TRPC/TRPCBuilder.ts similarity index 100% rename from src/Layers/trpc/TRPCBuilder.ts rename to src/TRPC/TRPCBuilder.ts diff --git a/src/Layers/trpc/TRPCContext.ts b/src/TRPC/TRPCContext.ts similarity index 100% rename from src/Layers/trpc/TRPCContext.ts rename to src/TRPC/TRPCContext.ts diff --git a/src/Layers/trpc/TRPCContextCreator.ts b/src/TRPC/TRPCContextCreator.ts similarity index 100% rename from src/Layers/trpc/TRPCContextCreator.ts rename to src/TRPC/TRPCContextCreator.ts diff --git a/src/Layers/trpc/TRPCExpressRoute.ts b/src/TRPC/TRPCExpressRoute.ts similarity index 91% rename from src/Layers/trpc/TRPCExpressRoute.ts rename to src/TRPC/TRPCExpressRoute.ts index 62cca46..411ecd5 100644 --- a/src/Layers/trpc/TRPCExpressRoute.ts +++ b/src/TRPC/TRPCExpressRoute.ts @@ -1,7 +1,7 @@ import type { AnyRouter } from "@trpc/server" import { Config, Context, Effect, Layer } from "effect" -import { ImportError } from "../../ImportError" -import { ExpressApp } from "../express" +import { ExpressApp } from "../Express" +import { ImportError } from "../ImportError" import { TRPCUnknownContextCreator } from "./TRPCContextCreator" diff --git a/src/Layers/trpc/TRPCRouter.ts b/src/TRPC/TRPCRouter.ts similarity index 100% rename from src/Layers/trpc/TRPCRouter.ts rename to src/TRPC/TRPCRouter.ts diff --git a/src/Layers/trpc/TRPCWebSocketServer.ts b/src/TRPC/TRPCWebSocketServer.ts similarity index 96% rename from src/Layers/trpc/TRPCWebSocketServer.ts rename to src/TRPC/TRPCWebSocketServer.ts index 19eb7be..9a95b0e 100644 --- a/src/Layers/trpc/TRPCWebSocketServer.ts +++ b/src/TRPC/TRPCWebSocketServer.ts @@ -2,8 +2,8 @@ import type { AnyRouter } from "@trpc/server" import type { applyWSSHandler } from "@trpc/server/adapters/ws" import { Config, Context, Effect, Layer } from "effect" import type ws from "ws" -import { ImportError } from "../../ImportError" -import { ExpressNodeHTTPServer } from "../express" +import { ExpressNodeHTTPServer } from "../Express" +import { ImportError } from "../ImportError" import { TRPCUnknownContextCreator } from "./TRPCContextCreator" diff --git a/src/Layers/trpc/createTRCPErrorMapper.ts b/src/TRPC/createTRCPErrorMapper.ts similarity index 100% rename from src/Layers/trpc/createTRCPErrorMapper.ts rename to src/TRPC/createTRCPErrorMapper.ts diff --git a/src/Layers/trpc/tests.ts b/src/TRPC/example.ts similarity index 86% rename from src/Layers/trpc/tests.ts rename to src/TRPC/example.ts index 3f2dd92..6052a5a 100644 --- a/src/Layers/trpc/tests.ts +++ b/src/TRPC/example.ts @@ -1,7 +1,6 @@ import { Config, Effect, Layer } from "effect" import * as TRPC from "." -import { JSONWebToken } from ".." -import { ExpressApp, ExpressNodeHTTPServer } from "../express" +import { Express, JSONWebToken } from ".." // Context available to the router procedures @@ -40,10 +39,10 @@ const ServerLive = Layer.empty.pipe( Layer.provideMerge(TRPCBuilderLive), Layer.provideMerge(TRPCContextCreatorLive), - Layer.provideMerge(ExpressNodeHTTPServer.ExpressNodeHTTPServerLive({ + Layer.provideMerge(Express.ExpressNodeHTTPServer.ExpressNodeHTTPServerLive({ port: Config.succeed(3000) })), - Layer.provideMerge(ExpressApp.ExpressAppLive()) + Layer.provideMerge(Express.ExpressApp.ExpressAppLive()) ) await Effect.gen(function*() { diff --git a/src/Layers/trpc/importTRPCServer.ts b/src/TRPC/importTRPCServer.ts similarity index 80% rename from src/Layers/trpc/importTRPCServer.ts rename to src/TRPC/importTRPCServer.ts index 26a5ea1..6377caa 100644 --- a/src/Layers/trpc/importTRPCServer.ts +++ b/src/TRPC/importTRPCServer.ts @@ -1,5 +1,5 @@ import { Effect } from "effect" -import { ImportError } from "../../ImportError" +import { ImportError } from "../ImportError" export const importTRPCServer = Effect.tryPromise({ diff --git a/src/Layers/trpc/index.ts b/src/TRPC/index.ts similarity index 100% rename from src/Layers/trpc/index.ts rename to src/TRPC/index.ts diff --git a/src/Layers/trpc/middlewares.ts b/src/TRPC/middlewares.ts similarity index 100% rename from src/Layers/trpc/middlewares.ts rename to src/TRPC/middlewares.ts diff --git a/src/index.ts b/src/index.ts index f18793d..f729f79 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,6 @@ +export * as Express from "./Express" export * from "./ImportError" -export * as Layers from "./Layers" +export * as JSONWebToken from "./JSONWebToken" +export * as OpenAIClient from "./OpenAIClient" export * as Schema from "./Schema" export * as Types from "./Types" diff --git a/tsup.config.ts b/tsup.config.ts index 3ea8c21..12938ba 100644 --- a/tsup.config.ts +++ b/tsup.config.ts @@ -10,7 +10,7 @@ export default defineConfig({ "./src/Schema/TanStackForm/index.ts", "./src/Types/index.ts", ], - format: ["esm", "cjs"], + format: ["esm"], skipNodeModulesBundle: true, dts: true, splitting: true, -- 2.49.1 From 149cfc968724b594ba65a333b8c6d4ed9baecf31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sat, 7 Sep 2024 03:12:18 +0200 Subject: [PATCH 26/37] Build config --- tsup.config.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tsup.config.ts b/tsup.config.ts index 12938ba..c96c727 100644 --- a/tsup.config.ts +++ b/tsup.config.ts @@ -4,11 +4,14 @@ import { defineConfig } from "tsup" export default defineConfig({ entry: [ "./src/index.ts", - "./src/Layers/index.ts", + "./src/Express/index.ts", + "./src/TRPC/index.ts", + "./src/Types/index.ts", "./src/Schema/index.ts", "./src/Schema/MobX/index.ts", "./src/Schema/TanStackForm/index.ts", - "./src/Types/index.ts", + "./src/JSONWebToken.ts", + "./src/OpenAIClient.ts", ], format: ["esm"], skipNodeModulesBundle: true, -- 2.49.1 From c838f38eb25ea2fb5458ae4fb2b2ab3fd15dacf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sat, 7 Sep 2024 03:19:02 +0200 Subject: [PATCH 27/37] Exports --- package.json | 84 +++++++++++++++++++--------------------------------- 1 file changed, 30 insertions(+), 54 deletions(-) diff --git a/package.json b/package.json index 255ffcb..9c8f21e 100644 --- a/package.json +++ b/package.json @@ -8,64 +8,40 @@ "types": "./dist/index.d.ts", "exports": { ".": { - "import": { - "types": "./dist/index.d.ts", - "default": "./dist/index.js" - }, - "require": { - "types": "./dist/index.d.cts", - "default": "./dist/index.cjs" - } + "types": "./dist/index.d.ts", + "default": "./dist/index.js" }, - "./Layers": { - "import": { - "types": "./dist/Layers/index.d.ts", - "default": "./dist/Layers/index.js" - }, - "require": { - "types": "./dist/Layers/index.d.cts", - "default": "./dist/Layers/index.cjs" - } + "./Express": { + "types": "./dist/Express/index.d.ts", + "default": "./dist/Express/index.js" }, - "./Schema": { - "import": { - "types": "./dist/Schema/index.d.ts", - "default": "./dist/Schema/index.js" - }, - "require": { - "types": "./dist/Schema/index.d.cts", - "default": "./dist/Schema/index.cjs" - } - }, - "./Schema/MobX": { - "import": { - "types": "./dist/Schema/MobX/index.d.ts", - "default": "./dist/Schema/MobX/index.js" - }, - "require": { - "types": "./dist/Schema/MobX/index.d.cts", - "default": "./dist/Schema/MobX/index.cjs" - } - }, - "./Schema/TanStackForm": { - "import": { - "types": "./dist/Schema/TanStackForm/index.d.ts", - "default": "./dist/Schema/TanStackForm/index.js" - }, - "require": { - "types": "./dist/Schema/TanStackForm/index.d.cts", - "default": "./dist/Schema/TanStackForm/index.cjs" - } + "./TRPC": { + "types": "./dist/TRPC/index.d.ts", + "default": "./dist/TRPC/index.js" }, "./Types": { - "import": { - "types": "./dist/Types/index.d.ts", - "default": "./dist/Types/index.js" - }, - "require": { - "types": "./dist/Types/index.d.cts", - "default": "./dist/Types/index.cjs" - } + "types": "./dist/Types/index.d.ts", + "default": "./dist/Types/index.js" + }, + "./Schema": { + "types": "./dist/Schema/index.d.ts", + "default": "./dist/Schema/index.js" + }, + "./Schema/MobX": { + "types": "./dist/Schema/MobX/index.d.ts", + "default": "./dist/Schema/MobX/index.js" + }, + "./Schema/TanStackForm": { + "types": "./dist/Schema/TanStackForm/index.d.ts", + "default": "./dist/Schema/TanStackForm/index.js" + }, + "./JSONWebToken": { + "types": "./dist/JSONWebToken.d.ts", + "default": "./dist/JSONWebToken.js" + }, + "./OpenAIClient": { + "types": "./dist/OpenAIClient.d.ts", + "default": "./dist/OpenAIClient.js" } }, "scripts": { -- 2.49.1 From 75da7abb3e0836ceaa2ef4ab15503ddda0853d90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sat, 7 Sep 2024 03:51:01 +0200 Subject: [PATCH 28/37] Export fix --- src/TRPC/importTRPCServer.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/TRPC/importTRPCServer.ts b/src/TRPC/importTRPCServer.ts index 6377caa..43db62b 100644 --- a/src/TRPC/importTRPCServer.ts +++ b/src/TRPC/importTRPCServer.ts @@ -2,7 +2,10 @@ import { Effect } from "effect" import { ImportError } from "../ImportError" -export const importTRPCServer = Effect.tryPromise({ +export const importTRPCServer: Effect.Effect< + typeof import("@trpc/server"), + ImportError +> = Effect.tryPromise({ try: () => import("@trpc/server"), catch: cause => new ImportError({ path: "@trpc/server", cause }), }) -- 2.49.1 From 0299796d136bb6bb210e0b33a9e7a89e5e41ad6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sat, 7 Sep 2024 04:12:54 +0200 Subject: [PATCH 29/37] Dreadful fix --- src/TRPC/TRPCBuilder.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/TRPC/TRPCBuilder.ts b/src/TRPC/TRPCBuilder.ts index 71771fe..0e277b9 100644 --- a/src/TRPC/TRPCBuilder.ts +++ b/src/TRPC/TRPCBuilder.ts @@ -1,4 +1,5 @@ import { Context, Effect, Layer } from "effect" +import type { ImportError } from "../ImportError" import { type TRPCContext } from "./TRPCContext" import { importTRPCServer } from "./importTRPCServer" @@ -7,7 +8,14 @@ const createTRPC = () => importTRPCServer.pipe(Effect.map(({ initTRPC }) => initTRPC.context>().create() )) -export const make = () => { +export const make = (): { + readonly TRPCBuilder: Context.TagClass>>> + readonly TRPCBuilderLive: Layer.Layer< + InstanceType>>>>, + ImportError, + never + > +} => { class TRPCBuilder extends Context.Tag("@thilalib/TRPC/TRPCBuilder")>> >() {} -- 2.49.1 From 9a4d62c89a3c4fb7eb20dbc4d2e5e84683e9e0a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sat, 7 Sep 2024 04:50:10 +0200 Subject: [PATCH 30/37] TRPCBuilder refactoring --- src/TRPC/TRPCBuilder.ts | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/src/TRPC/TRPCBuilder.ts b/src/TRPC/TRPCBuilder.ts index 0e277b9..5edd653 100644 --- a/src/TRPC/TRPCBuilder.ts +++ b/src/TRPC/TRPCBuilder.ts @@ -1,5 +1,4 @@ import { Context, Effect, Layer } from "effect" -import type { ImportError } from "../ImportError" import { type TRPCContext } from "./TRPCContext" import { importTRPCServer } from "./importTRPCServer" @@ -8,19 +7,14 @@ const createTRPC = () => importTRPCServer.pipe(Effect.map(({ initTRPC }) => initTRPC.context>().create() )) -export const make = (): { - readonly TRPCBuilder: Context.TagClass>>> - readonly TRPCBuilderLive: Layer.Layer< - InstanceType>>>>, - ImportError, - never - > -} => { - class TRPCBuilder extends Context.Tag("@thilalib/TRPC/TRPCBuilder")>> - >() {} +export const Identifier = "@thilalib/TRPC/TRPCBuilder" +export interface TRPCBuilder extends Context.Tag> {} +export interface TRPCBuilderService extends Effect.Effect.Success>> {} + +export const make = () => { + const TRPCBuilder = Context.GenericTag>(Identifier) const TRPCBuilderLive = Layer.effect(TRPCBuilder, createTRPC()) - return { TRPCBuilder, TRPCBuilderLive } + return { TRPCBuilder, TRPCBuilderLive } as const } -- 2.49.1 From aa12c67a4eacc2f6bcc4e6d1de6143ba823d73eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sat, 7 Sep 2024 04:54:23 +0200 Subject: [PATCH 31/37] Fix --- src/TRPC/example.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/TRPC/example.ts b/src/TRPC/example.ts index 6052a5a..6d6043f 100644 --- a/src/TRPC/example.ts +++ b/src/TRPC/example.ts @@ -46,8 +46,6 @@ const ServerLive = Layer.empty.pipe( ) await Effect.gen(function*() { - // yield* TRPCRouter - return yield* Layer.launch(ServerLive) }).pipe( Effect.provide(ServicesLive), -- 2.49.1 From 253c3ec00dbb37b6cb61a1c2c3d3f4274939cf34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sat, 7 Sep 2024 05:15:19 +0200 Subject: [PATCH 32/37] TRPCContextCreator refactoring --- src/TRPC/TRPCContextCreator.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/TRPC/TRPCContextCreator.ts b/src/TRPC/TRPCContextCreator.ts index 49425c2..9087b1e 100644 --- a/src/TRPC/TRPCContextCreator.ts +++ b/src/TRPC/TRPCContextCreator.ts @@ -5,21 +5,21 @@ import { createTRCPErrorMapper } from "./createTRCPErrorMapper" import { TRPCContextTransactionEnum, type TRPCContext, type TRPCContextTransaction } from "./TRPCContext" +export const Identifier = "@thilalib/TRPC/TRPCContextCreator" + +export interface TRPCContextCreator extends Context.Tag> {} + export interface TRPCContextCreatorService { readonly createContext: (transaction: TRPCContextTransaction) => TRPCContext readonly createExpressContext: (context: CreateExpressContextOptions) => TRPCContext readonly createWebSocketContext: (context: CreateWSSContextFnOptions) => TRPCContext } -export class TRPCUnknownContextCreator extends Context.Tag("@thilalib/TRPC/TRPCContextCreator") ->() {} +export const TRPCUnknownContextCreator = Context.GenericTag>(Identifier) export const make = () => { - class TRPCContextCreator extends Context.Tag("@thilalib/TRPC/TRPCContextCreator") - >() {} + const TRPCContextCreator = Context.GenericTag>(Identifier) const TRPCContextCreatorLive = Layer.effect(TRPCContextCreator, Effect.gen(function*() { const runtime = yield* Effect.runtime() @@ -53,5 +53,5 @@ export const make = () => { return { createContext, createExpressContext, createWebSocketContext } })) - return { TRPCContextCreator, TRPCContextCreatorLive } + return { TRPCContextCreator, TRPCContextCreatorLive } as const } -- 2.49.1 From 639285af82d94f2d76301e557491fa7283beb229 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sat, 7 Sep 2024 18:48:44 +0200 Subject: [PATCH 33/37] TRCPRouter --- src/TRPC/TRPCRouter.ts | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/TRPC/TRPCRouter.ts b/src/TRPC/TRPCRouter.ts index 827e478..55226bb 100644 --- a/src/TRPC/TRPCRouter.ts +++ b/src/TRPC/TRPCRouter.ts @@ -2,12 +2,17 @@ import type { AnyRouter } from "@trpc/server" import { Context, Effect, Layer } from "effect" -export const make = < - A extends AnyRouter, - E, R, ->(router: Effect.Effect) => { - class TRPCRouter extends Context.Tag("@thilalib/TRCP/TRPCRouter")() {} +export const Identifier = "@thilalib/TRPC/TRPCRouter" +export interface TRPCRouter extends Context.Tag {} + +export const TRPCAnyRouter = Context.GenericTag(Identifier) + + +export const make = ( + router: Effect.Effect +) => { + const TRPCRouter = Context.GenericTag(Identifier) const TRPCRouterLive = Layer.effect(TRPCRouter, router) - return { TRPCRouter, TRPCRouterLive } + return { TRPCRouter, TRPCRouterLive } as const } -- 2.49.1 From 527b8a8f22245d05a5122854a4bd7dca21880a21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sat, 7 Sep 2024 19:10:42 +0200 Subject: [PATCH 34/37] Refactoring --- src/TRPC/TRPCExpressRoute.ts | 15 ++---- src/TRPC/TRPCWebSocketServer.ts | 89 +++++++++++++++------------------ src/TRPC/example.ts | 7 +-- 3 files changed, 48 insertions(+), 63 deletions(-) diff --git a/src/TRPC/TRPCExpressRoute.ts b/src/TRPC/TRPCExpressRoute.ts index 411ecd5..22333dd 100644 --- a/src/TRPC/TRPCExpressRoute.ts +++ b/src/TRPC/TRPCExpressRoute.ts @@ -1,8 +1,8 @@ -import type { AnyRouter } from "@trpc/server" -import { Config, Context, Effect, Layer } from "effect" +import { Config, Effect, Layer } from "effect" import { ExpressApp } from "../Express" import { ImportError } from "../ImportError" import { TRPCUnknownContextCreator } from "./TRPCContextCreator" +import { TRPCAnyRouter } from "./TRPCRouter" const importTRPCServerExpressAdapter = Effect.tryPromise({ @@ -10,22 +10,17 @@ const importTRPCServerExpressAdapter = Effect.tryPromise({ catch: cause => new ImportError({ path: "@trpc/server/adapters/express", cause }), }) -export const TRPCExpressRouteLive = < - Tag, - TagShape extends AnyRouter, ->( - routerTag: Context.TagClass, - +export const TRPCExpressRouteLive = ( config: { readonly root: Config.Config - }, + } ) => Layer.effectDiscard(Effect.gen(function*() { const { createExpressMiddleware } = yield* importTRPCServerExpressAdapter const app = yield* ExpressApp.ExpressApp app.use(yield* config.root, createExpressMiddleware({ - router: yield* routerTag, + router: yield* TRPCAnyRouter, createContext: (yield* TRPCUnknownContextCreator).createExpressContext, }) ) diff --git a/src/TRPC/TRPCWebSocketServer.ts b/src/TRPC/TRPCWebSocketServer.ts index 9a95b0e..22238fe 100644 --- a/src/TRPC/TRPCWebSocketServer.ts +++ b/src/TRPC/TRPCWebSocketServer.ts @@ -1,10 +1,18 @@ -import type { AnyRouter } from "@trpc/server" import type { applyWSSHandler } from "@trpc/server/adapters/ws" import { Config, Context, Effect, Layer } from "effect" import type ws from "ws" import { ExpressNodeHTTPServer } from "../Express" import { ImportError } from "../ImportError" import { TRPCUnknownContextCreator } from "./TRPCContextCreator" +import { TRPCAnyRouter } from "./TRPCRouter" + + +export class TRPCWebSocketServer extends Context.Tag("@thilalib/TRPC/TRPCWebSocketServer")() {} + +export interface TRPCWebSocketServerService { + wss: ws.Server + handler: ReturnType +} const importWS = Effect.tryPromise({ @@ -17,57 +25,42 @@ const importTRPCServerWSAdapter = Effect.tryPromise({ catch: cause => new ImportError({ path: "@trpc/server/adapters/ws", cause }), }) +export const TRPCWebSocketServerLive = ( + config: { + readonly host: Config.Config + } +) => Layer.effect(TRPCWebSocketServer, Effect.gen(function*() { + const { WebSocketServer } = yield* importWS + const { applyWSSHandler } = yield* importTRPCServerWSAdapter -export const make = < - Tag, - TagShape extends AnyRouter, ->( - routerTag: Context.TagClass -) => { - class TRPCWebSocketServer extends Context.Tag("@thilalib/TRPC/TRPCWebSocketServer")> - }>() {} + const host = yield* config.host - const TRPCWebSocketServerLive = ( - config: { - readonly host: Config.Config - } - ) => Layer.effect(TRPCWebSocketServer, Effect.gen(function*() { - const { WebSocketServer } = yield* importWS - const { applyWSSHandler } = yield* importTRPCServerWSAdapter + return yield* Effect.acquireRelease( + Effect.gen(function*() { + yield* Effect.logInfo(`WebSocket server started on ${ host }`) - const host = yield* config.host + const wss = new WebSocketServer({ + server: yield* ExpressNodeHTTPServer.ExpressNodeHTTPServer, + host, + }) - return yield* Effect.acquireRelease( - Effect.gen(function*() { - yield* Effect.logInfo(`WebSocket server started on ${ host }`) - - const wss = new WebSocketServer({ - server: yield* ExpressNodeHTTPServer.ExpressNodeHTTPServer, - host, - }) - - return { + return { + wss, + handler: applyWSSHandler({ wss, - handler: applyWSSHandler({ - wss, - router: yield* routerTag, - createContext: (yield* TRPCUnknownContextCreator).createWebSocketContext, - }), - } - }), + router: yield* TRPCAnyRouter, + createContext: (yield* TRPCUnknownContextCreator).createWebSocketContext, + }), + } + }), - ({ wss, handler }) => Effect.gen(function*() { - yield* Effect.logInfo(`WebSocket server on ${ host } is stopping. Waiting for existing connections to end...`) + ({ wss, handler }) => Effect.gen(function*() { + yield* Effect.logInfo(`WebSocket server on ${ host } is stopping. Waiting for existing connections to end...`) - handler.broadcastReconnectNotification() - yield* Effect.async(resume => { - wss.close(() => resume(Effect.logInfo("WebSocket server closed"))) - }) - }), - ) - })) - - return { TRPCWebSocketServer, TRPCWebSocketServerLive } -} + handler.broadcastReconnectNotification() + yield* Effect.async(resume => { + wss.close(() => resume(Effect.logInfo("WebSocket server closed"))) + }) + }), + ) +})) diff --git a/src/TRPC/example.ts b/src/TRPC/example.ts index 6d6043f..1a3bd14 100644 --- a/src/TRPC/example.ts +++ b/src/TRPC/example.ts @@ -24,14 +24,11 @@ const router = TRPCBuilder.pipe(Effect.map(t => t.router({ const { TRPCRouter, TRPCRouterLive } = TRPC.TRPCRouter.make(router) -const { TRPCWebSocketServer, TRPCWebSocketServerLive } = TRPC.TRPCWebSocketServer.make(TRPCRouter) - - const ServerLive = Layer.empty.pipe( - Layer.provideMerge(TRPC.TRPCExpressRoute.TRPCExpressRouteLive(TRPCRouter, { + Layer.provideMerge(TRPC.TRPCExpressRoute.TRPCExpressRouteLive({ root: Config.succeed("/rpc") })), - Layer.provideMerge(TRPCWebSocketServerLive({ + Layer.provideMerge(TRPC.TRPCWebSocketServer.TRPCWebSocketServerLive({ host: Config.succeed("/rpc") })), -- 2.49.1 From da597f763d9ad4fa9fc655ea797c1d3253f986c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sat, 7 Sep 2024 19:18:47 +0200 Subject: [PATCH 35/37] Build system --- package.json | 2 +- tsconfig.json | 10 +++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 9c8f21e..f845ce5 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,7 @@ } }, "scripts": { - "build": "tsup", + "build": "tsc", "lint:tsc": "tsc --noEmit", "clean:cache": "rm -f tsconfig.tsbuildinfo", "clean:dist": "rm -rf dist", diff --git a/tsconfig.json b/tsconfig.json index ef46c48..e46d674 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,9 +10,9 @@ // Bundler mode "moduleResolution": "bundler", - "allowImportingTsExtensions": true, + // "allowImportingTsExtensions": true, "verbatimModuleSyntax": true, - "noEmit": true, + // "noEmit": true, // Best practices "strict": true, @@ -22,7 +22,11 @@ // Some stricter flags (disabled by default) "noUnusedLocals": false, "noUnusedParameters": false, - "noPropertyAccessFromIndexSignature": false + "noPropertyAccessFromIndexSignature": false, + + // Build + "outDir": "./dist", + "declaration": true }, "include": ["./src"] -- 2.49.1 From 6e5c371b4087c0b30084bc6d71c0a1c73633710e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sat, 7 Sep 2024 19:24:33 +0200 Subject: [PATCH 36/37] JSONWebToken work --- src/JSONWebToken.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/JSONWebToken.ts b/src/JSONWebToken.ts index f088049..401ee6f 100644 --- a/src/JSONWebToken.ts +++ b/src/JSONWebToken.ts @@ -3,7 +3,9 @@ import type * as JWT from "jsonwebtoken" import { ImportError } from "./ImportError" -export class JSONWebToken extends Context.Tag("JSONWebToken")() {} + +export interface JSONWebTokenService { sign: ( payload: string | object | Buffer, secretOrPrivateKey: JWT.Secret, @@ -12,7 +14,7 @@ export class JSONWebToken extends Context.Tag("JSONWebToken"), + > verify: ( token: string, @@ -22,8 +24,8 @@ export class JSONWebToken extends Context.Tag("JSONWebToken"), -}>() {} + > +} const importJWT = Effect.tryPromise({ -- 2.49.1 From e23015835df51bcd31a2cb7a7a56e9ce1225dc09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sat, 7 Sep 2024 20:55:01 +0200 Subject: [PATCH 37/37] Version bump --- bun.lockb | Bin 107036 -> 107036 bytes package.json | 12 ++++++------ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/bun.lockb b/bun.lockb index 5bb451da1267e8181ca1994b61a2a92a0e90758c..9ff58ea702e4394881280da8bd5aa6670db3a7f0 100755 GIT binary patch delta 545 zcmV++0^a?c#0H$i29PcwYt}9|yr8|b2DNtj5(u+sR15ytABP_b2L}+cE5iT47qf<1u9MBla6nH~)&IoIoJqMwCJm-!rxoct*7q8h(3m4?6GE%q_u}&sB z0XdVg94nJx5(u-XI#3FeG#)6kAVnAtlXMa&lVB1EvuIQd0|6eBArdQ-U=j$Es8>*v zbR{U0U=j$kC|HF_K$$%G{VA@NNghg?n@YZxG_+LeO61BJ+kF)R@psx?xb;u0RHCwk z5V+aBv{KBD_281ulT#iRN|;O0_*i&q4YTc`Fb+U^Lea(u)+WCpR~Y?dtAdpL4E`b9 zlO5i;f_FBa^DR?tsGHwyy1SQt^jrUY3&9#~)HS3YNq2Bt4Qxp~+1fg@U9h+mK%cn7 zd##JG9llsk5cBzy-K^w2rmZSi|Jclr>KM0}tIyBc52PN6Tk_@Tk?l0Qooc~Fy7%Cc zRuU8H>v(IykF!<9I0QhxF`@NFa3nHOuVajQ(oy*t;GD5}O->~09s2HY10AH;qUrWd ztSpkibK>?0x0kr+AE;&&pHVfpRYu;+M>MgM&B<^;{~bXf4XLTcB6Ql$GHe)org`wI zQh;=v<|1zGvv4CAwk&fMxkjke8|fpQdW*FG&PpQT*L6#(nz5Y8A%Np|vwg~ASphPa z;UfVT0Wg>OBLRZ}HkW-Q0cQa*m*pe@O9MA9Gq*D(0n=IoH!d@`34j4N440!*0U)iGPm}i0VV-19{Y3v diff --git a/package.json b/package.json index f845ce5..52f0e92 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@thilawyn/thilalib", - "version": "0.1.16", + "version": "0.1.17", "type": "module", "files": [ "./dist" @@ -56,20 +56,20 @@ "type-fest": "^4.26.0" }, "devDependencies": { - "@effect/schema": "^0.72.2", - "@tanstack/form-core": "^0.30.0", + "@effect/schema": "^0.72.3", + "@tanstack/form-core": "^0.32.0", "@trpc/server": "^10.45.2", "@types/express": "^4.17.21", "@types/jsonwebtoken": "^9.0.6", "@types/ws": "^8.5.12", - "bun-types": "^1.1.26", + "bun-types": "^1.1.27", "effect": "^3.7.2", "express": "^4.19.2", "jsonwebtoken": "^9.0.2", - "mobx": "^6.13.1", + "mobx": "^6.13.2", "npm-check-updates": "^17.1.1", "npm-sort": "^0.0.4", - "openai": "^4.57.3", + "openai": "^4.58.1", "tsup": "^8.2.4", "tsx": "^4.19.0", "typescript": "^5.5.4", -- 2.49.1