From c5d2e77f1f2ac40841bbd4d52385bc378aca261b Mon Sep 17 00:00:00 2001 From: Matthew Sessions Date: Fri, 27 Feb 2026 14:38:14 -0700 Subject: [PATCH] migrate to v4 --- apps/effect-worker-api/package.json | 8 +- apps/effect-worker-api/src/handlers/health.ts | 20 +- apps/effect-worker-api/src/handlers/users.ts | 22 +- apps/effect-worker-api/src/index.ts | 22 +- apps/effect-worker-api/src/runtime.ts | 76 +- .../src/services/cloudflare.ts | 28 +- .../src/services/middleware.ts | 58 +- apps/effect-worker-api/tsconfig.json | 2 - apps/effect-worker-rpc/package.json | 9 +- apps/effect-worker-rpc/src/index.ts | 18 +- apps/effect-worker-rpc/src/runtime.ts | 98 +- .../src/services/cloudflare.ts | 42 +- .../src/services/middleware.ts | 48 +- apps/effect-worker-rpc/tsconfig.json | 2 - apps/react-app/.gitignore | 4 + apps/react-app/components.json | 21 + apps/react-app/index.html | 12 + apps/react-app/package.json | 43 + apps/react-app/src/components/ui/button.tsx | 64 + apps/react-app/src/lib/utils.ts | 6 + apps/react-app/src/main.tsx | 30 + apps/react-app/src/router.tsx | 11 + apps/react-app/src/routes/__root.tsx | 37 + apps/react-app/src/routes/index.tsx | 120 ++ apps/react-app/src/rpc/client.ts | 22 + apps/react-app/src/rpc/hooks.ts | 28 + apps/react-app/src/rpc/index.ts | 2 + apps/react-app/src/rpc/keys.ts | 5 + apps/react-app/src/styles.css | 124 ++ apps/react-app/tsconfig.json | 24 + apps/react-app/vite.config.ts | 26 + apps/react-app/worker/index.ts | 11 + apps/react-app/wrangler.jsonc | 9 + apps/tanstack-start/package.json | 3 +- apps/tanstack-start/src/routes/api/process.ts | 2 +- .../functions/example-effect-function.ts | 5 +- .../src/server/middleware/effect-runtime.ts | 56 +- apps/tanstack-start/src/server/types.ts | 4 +- designs/react-rpc-client.md | 385 ++++ package.json | 7 +- packages/cloudflare/LICENSE | 0 packages/cloudflare/README.md | 0 packages/cloudflare/package.json | 34 - packages/cloudflare/src/database.ts | 47 - packages/cloudflare/src/errors.ts | 32 - packages/cloudflare/src/fiber-ref.ts | 79 - packages/cloudflare/src/index.ts | 36 - packages/cloudflare/src/services.ts | 27 - packages/cloudflare/tsconfig.build.json | 23 - packages/cloudflare/tsconfig.json | 8 - packages/cloudflare/vitest.config.ts | 11 - packages/contracts/package.json | 6 +- packages/contracts/src/http/api.ts | 2 +- packages/contracts/src/http/groups/health.ts | 4 +- packages/contracts/src/http/groups/users.ts | 22 +- .../src/http/middleware/cloudflare.ts | 23 +- .../contracts/src/http/middleware/database.ts | 18 +- .../contracts/src/http/middleware/index.ts | 1 - packages/contracts/src/index.ts | 5 +- .../src/rpc/middleware/cloudflare.ts | 24 +- .../contracts/src/rpc/middleware/database.ts | 21 +- .../contracts/src/rpc/procedures/users.ts | 4 +- packages/contracts/src/services/cloudflare.ts | 32 + packages/contracts/src/services/index.ts | 8 + packages/db/package.json | 4 +- packages/db/src/index.ts | 10 + packages/db/src/pg-drizzle/index.ts | 107 ++ packages/db/src/pg-drizzle/patch.ts | 53 + packages/db/src/pg-drizzle/tag.ts | 20 + packages/db/src/queries/users.ts | 4 +- packages/domain/package.json | 3 +- packages/domain/src/errors/common.ts | 31 +- packages/domain/src/errors/index.ts | 2 +- packages/domain/src/errors/user.ts | 9 +- packages/domain/src/schemas/user.ts | 12 +- pnpm-lock.yaml | 1589 +++++++++++++---- pnpm-workspace.yaml | 5 + tsconfig.base.json | 2 - 78 files changed, 2776 insertions(+), 1056 deletions(-) create mode 100644 apps/react-app/.gitignore create mode 100644 apps/react-app/components.json create mode 100644 apps/react-app/index.html create mode 100644 apps/react-app/package.json create mode 100644 apps/react-app/src/components/ui/button.tsx create mode 100644 apps/react-app/src/lib/utils.ts create mode 100644 apps/react-app/src/main.tsx create mode 100644 apps/react-app/src/router.tsx create mode 100644 apps/react-app/src/routes/__root.tsx create mode 100644 apps/react-app/src/routes/index.tsx create mode 100644 apps/react-app/src/rpc/client.ts create mode 100644 apps/react-app/src/rpc/hooks.ts create mode 100644 apps/react-app/src/rpc/index.ts create mode 100644 apps/react-app/src/rpc/keys.ts create mode 100644 apps/react-app/src/styles.css create mode 100644 apps/react-app/tsconfig.json create mode 100644 apps/react-app/vite.config.ts create mode 100644 apps/react-app/worker/index.ts create mode 100644 apps/react-app/wrangler.jsonc create mode 100644 designs/react-rpc-client.md delete mode 100644 packages/cloudflare/LICENSE delete mode 100644 packages/cloudflare/README.md delete mode 100644 packages/cloudflare/package.json delete mode 100644 packages/cloudflare/src/database.ts delete mode 100644 packages/cloudflare/src/errors.ts delete mode 100644 packages/cloudflare/src/fiber-ref.ts delete mode 100644 packages/cloudflare/src/index.ts delete mode 100644 packages/cloudflare/src/services.ts delete mode 100644 packages/cloudflare/tsconfig.build.json delete mode 100644 packages/cloudflare/tsconfig.json delete mode 100644 packages/cloudflare/vitest.config.ts create mode 100644 packages/contracts/src/services/cloudflare.ts create mode 100644 packages/contracts/src/services/index.ts create mode 100644 packages/db/src/pg-drizzle/index.ts create mode 100644 packages/db/src/pg-drizzle/patch.ts create mode 100644 packages/db/src/pg-drizzle/tag.ts diff --git a/apps/effect-worker-api/package.json b/apps/effect-worker-api/package.json index 50d7156..22dd8b4 100644 --- a/apps/effect-worker-api/package.json +++ b/apps/effect-worker-api/package.json @@ -14,16 +14,10 @@ }, "dependencies": { "@repo/contracts": "workspace:^", - "@repo/cloudflare": "workspace:^", "@repo/db": "workspace:^", "@repo/domain": "workspace:^", - "@effect/experimental": "latest", - "@effect/platform": "latest", - "@effect/sql": "latest", - "@effect/sql-drizzle": "latest", - "@effect/sql-pg": "latest", "drizzle-orm": "^0.45.0", - "effect": "latest" + "effect": "catalog:" }, "devDependencies": { "@cloudflare/workers-types": "^4.20241127.0", diff --git a/apps/effect-worker-api/src/handlers/health.ts b/apps/effect-worker-api/src/handlers/health.ts index 565ed35..f46420f 100644 --- a/apps/effect-worker-api/src/handlers/health.ts +++ b/apps/effect-worker-api/src/handlers/health.ts @@ -3,7 +3,7 @@ * * @module */ -import { HttpApiBuilder } from "@effect/platform" +import { HttpApiBuilder } from "effect/unstable/httpapi" import { DateTime, Effect } from "effect" import { WorkerApi } from "@repo/contracts" @@ -14,14 +14,12 @@ export const HealthGroupLive = HttpApiBuilder.group( WorkerApi, "health", (handlers) => - Effect.gen(function* () { - return handlers.handle("check", () => - Effect.gen(function* () { - return { - status: "ok" as const, - timestamp: DateTime.unsafeNow() - } - }) - ) - }) + handlers.handle("check", () => + Effect.gen(function* () { + return { + status: "ok" as const, + timestamp: DateTime.nowUnsafe() + } + }) + ) ) diff --git a/apps/effect-worker-api/src/handlers/users.ts b/apps/effect-worker-api/src/handlers/users.ts index 0c7b368..ae10a4c 100644 --- a/apps/effect-worker-api/src/handlers/users.ts +++ b/apps/effect-worker-api/src/handlers/users.ts @@ -3,7 +3,7 @@ * * @module */ -import { HttpApiBuilder } from "@effect/platform" +import { HttpApiBuilder } from "effect/unstable/httpapi" import { Effect } from "effect" import { WorkerApi } from "@repo/contracts" import { UserQueries } from "@repo/db" @@ -15,15 +15,13 @@ export const UsersGroupLive = HttpApiBuilder.group( WorkerApi, "users", (handlers) => - Effect.gen(function* () { - return handlers - .handle("list", () => - Effect.gen(function* () { - const users = yield* UserQueries.findAllUsers - return { users, total: users.length } - }) - ) - .handle("get", ({ path: { id } }) => UserQueries.findUserById(id)) - .handle("create", ({ payload }) => UserQueries.createUser(payload)) - }) + handlers + .handle("list", () => + Effect.gen(function* () { + const users = yield* UserQueries.findAllUsers + return { users, total: users.length } + }) + ) + .handle("get", ({ params: { id } }) => UserQueries.findUserById(id)) + .handle("create", ({ payload }) => UserQueries.createUser(payload)) ) diff --git a/apps/effect-worker-api/src/index.ts b/apps/effect-worker-api/src/index.ts index a52d23b..125d9ee 100644 --- a/apps/effect-worker-api/src/index.ts +++ b/apps/effect-worker-api/src/index.ts @@ -5,25 +5,21 @@ * * @module */ -import { runtime, handleRequest, openApiSpec } from "@/runtime" -import { withCloudflareBindings } from "@/services" +import { pipe, ServiceMap } from "effect" +import { handler } from "@/runtime" +import { currentEnv, currentCtx } from "@/services/cloudflare" /** * Cloudflare Worker fetch handler. */ export default { async fetch(request: Request, env: Env, ctx: ExecutionContext) { - const url = new URL(request.url) + // Pass per-request Cloudflare bindings via ServiceMap context + const services = pipe( + ServiceMap.make(currentEnv, env), + ServiceMap.add(currentCtx, ctx) + ) - // Serve OpenAPI spec at /api/openapi.json - if (url.pathname === "/api/openapi.json") { - return Response.json(openApiSpec) - } - - // HTTP REST API - // Handle request with Cloudflare bindings available via FiberRef - const effect = handleRequest(request).pipe(withCloudflareBindings(env, ctx)) - - return runtime.runPromise(effect) + return handler(request, services) } } satisfies ExportedHandler diff --git a/apps/effect-worker-api/src/runtime.ts b/apps/effect-worker-api/src/runtime.ts index 2dce9ae..5e889a4 100644 --- a/apps/effect-worker-api/src/runtime.ts +++ b/apps/effect-worker-api/src/runtime.ts @@ -1,73 +1,37 @@ /** * Effect Runtime Configuration * - * Sets up the ManagedRuntime for handling HTTP requests. + * Sets up the HTTP handler using HttpRouter.toWebHandler. * * @module */ -import { Effect, Layer, ManagedRuntime } from "effect" -import { HttpApiBuilder, HttpServer, OpenApi } from "@effect/platform" -import * as ServerRequest from "@effect/platform/HttpServerRequest" -import * as ServerResponse from "@effect/platform/HttpServerResponse" +import { Layer } from "effect" +import { HttpRouter, HttpServer } from "effect/unstable/http" +import { HttpApiBuilder } from "effect/unstable/httpapi" import { WorkerApi } from "@repo/contracts" import { HttpGroupsLive } from "@/handlers" import { MiddlewareLive } from "@/services" /** - * API Layer combining static services. + * API routes layer. * - * These layers are memoized by ManagedRuntime - built once at startup. - * Middleware layers are provided here so their implementations are available, - * but the middleware effects run per-request. + * HttpApiBuilder.layer registers all API routes into the HttpRouter. + * The openapiPath option automatically serves the OpenAPI spec. */ -const ApiLayer = Layer.mergeAll( - HttpApiBuilder.api(WorkerApi).pipe(Layer.provide(HttpGroupsLive)), - HttpApiBuilder.Router.Live, - HttpApiBuilder.Middleware.layer, - HttpServer.layerContext -).pipe(Layer.provide(MiddlewareLive)) +const ApiRoutes = HttpApiBuilder.layer(WorkerApi, { + openapiPath: "/api/openapi.json" +}).pipe( + Layer.provide(HttpGroupsLive), + Layer.provide(MiddlewareLive) +) /** - * Shared runtime instance. + * Web handler created from the API routes. * - * Built once at module initialization. Layers are memoized, so subsequent - * calls to runPromise reuse the same service instances. + * Layers are memoized internally — built once at startup. + * Per-request services (env/ctx) are passed via the ServiceMap context + * parameter of the handler function. */ -export const runtime = ManagedRuntime.make(ApiLayer) - -/** - * Handle an incoming HTTP request. - * - * Returns an Effect that can be wrapped with request-scoped services - * (Cloudflare env/ctx) before execution. - */ -export const handleRequest = (request: Request) => - Effect.gen(function* () { - const app = yield* HttpApiBuilder.httpApp - const serverRequest = ServerRequest.fromWeb(request) - const url = new URL(request.url) - - const response = yield* app.pipe( - Effect.provideService(ServerRequest.HttpServerRequest, serverRequest), - Effect.scoped, - Effect.catchAll(() => - ServerResponse.json( - { - _tag: "NotFoundError", - path: url.pathname, - message: `Route not found: ${request.method} ${url.pathname}` - }, - { status: 404 } - ) - ) - ) - - return ServerResponse.toWeb(response) - }) - -/** - * OpenAPI specification for the API. - * - * Generated from the WorkerApi definition. - */ -export const openApiSpec = OpenApi.fromApi(WorkerApi) +export const { handler, dispose } = HttpRouter.toWebHandler( + ApiRoutes.pipe(Layer.provide(HttpServer.layerServices)) +) diff --git a/apps/effect-worker-api/src/services/cloudflare.ts b/apps/effect-worker-api/src/services/cloudflare.ts index 3d44cbb..2b875d6 100644 --- a/apps/effect-worker-api/src/services/cloudflare.ts +++ b/apps/effect-worker-api/src/services/cloudflare.ts @@ -1,22 +1,28 @@ /** * Cloudflare Bindings Service * - * FiberRef bridge for providing Cloudflare's `env` and `ExecutionContext` + * ServiceMap.Reference bridge for providing Cloudflare's `env` and `ExecutionContext` * to Effect handlers. * * @module */ -import { Effect, FiberRef } from "effect" +import { Effect, ServiceMap } from "effect" /** - * FiberRef holding the current request's Cloudflare environment bindings. + * Reference holding the current request's Cloudflare environment bindings. */ -export const currentEnv = FiberRef.unsafeMake(null) +export const currentEnv = ServiceMap.Reference( + "@app/api/currentEnv", + { defaultValue: () => null } +) /** - * FiberRef holding the current request's ExecutionContext. + * Reference holding the current request's ExecutionContext. */ -export const currentCtx = FiberRef.unsafeMake(null) +export const currentCtx = ServiceMap.Reference( + "@app/api/currentCtx", + { defaultValue: () => null } +) /** * Set Cloudflare bindings for the scope of an effect. @@ -33,8 +39,8 @@ export const currentCtx = FiberRef.unsafeMake(null) export const withCloudflareBindings = (env: Env, ctx: ExecutionContext) => (effect: Effect.Effect) => effect.pipe( - Effect.locally(currentEnv, env), - Effect.locally(currentCtx, ctx) + Effect.provideService(currentEnv, env), + Effect.provideService(currentCtx, ctx) ) /** @@ -47,13 +53,13 @@ export const waitUntil = ( effect: Effect.Effect ): Effect.Effect => Effect.gen(function* () { - const ctx = yield* FiberRef.get(currentCtx) + const ctx = yield* currentCtx if (ctx) { ctx.waitUntil( Effect.runPromise( effect.pipe( - Effect.tapErrorCause(Effect.logError), - Effect.catchAll(() => Effect.void) + Effect.tapCause(Effect.logError), + Effect.catch(() => Effect.void) ) ) ) diff --git a/apps/effect-worker-api/src/services/middleware.ts b/apps/effect-worker-api/src/services/middleware.ts index b5adf1d..c55dea3 100644 --- a/apps/effect-worker-api/src/services/middleware.ts +++ b/apps/effect-worker-api/src/services/middleware.ts @@ -1,32 +1,36 @@ /** * Middleware Implementations * - * App-specific implementations of middleware defined in @repo/api. + * App-specific implementations of middleware defined in @repo/contracts. + * + * In Effect v4, middleware with `provides` is a function that wraps the + * httpEffect and provides the required service to it. * * @module */ -import { Effect, FiberRef, Layer } from "effect"; +import { Effect, Layer } from "effect"; import { CloudflareBindingsMiddleware, CloudflareBindingsError, + CloudflareBindings, DatabaseMiddleware, DatabaseConnectionError, } from "@repo/contracts"; +import { PgDrizzle, makeDrizzle } from "@repo/db"; import { currentEnv, currentCtx } from "@/services/cloudflare"; -import { makeDrizzle } from "@repo/cloudflare"; /** * Live implementation of CloudflareBindingsMiddleware. * - * Reads env/ctx from FiberRef and provides them as the CloudflareBindings service. + * Reads env/ctx from ServiceMap.Reference and provides CloudflareBindings + * to the downstream handler effect. */ -export const CloudflareBindingsMiddlewareLive = Layer.effect( +export const CloudflareBindingsMiddlewareLive = Layer.succeed( CloudflareBindingsMiddleware, - Effect.gen(function* () { - // Return the middleware effect (runs per-request) - return Effect.gen(function* () { - const env = yield* FiberRef.get(currentEnv); - const ctx = yield* FiberRef.get(currentCtx); + (httpEffect) => + Effect.gen(function* () { + const env = yield* currentEnv; + const ctx = yield* currentCtx; if (env === null || ctx === null) { return yield* Effect.fail( @@ -37,24 +41,23 @@ export const CloudflareBindingsMiddlewareLive = Layer.effect( ); } - return { env, ctx }; - }); - }), + return yield* httpEffect.pipe( + Effect.provideService(CloudflareBindings, { env, ctx }), + ); + }), ); /** * Live implementation of DatabaseMiddleware. * - * Creates a scoped PgDrizzle instance per-request. - * The connection is automatically closed when the request scope ends. + * Creates a scoped PgDrizzle instance per-request and provides it + * to the downstream handler effect. */ -export const DatabaseMiddlewareLive = Layer.effect( +export const DatabaseMiddlewareLive = Layer.succeed( DatabaseMiddleware, - Effect.gen(function* () { - // Return the middleware effect (runs per-request) - return Effect.gen(function* () { - // Get connection string from Cloudflare env via FiberRef - const env = yield* FiberRef.get(currentEnv); + (httpEffect) => + Effect.gen(function* () { + const env = yield* currentEnv; if (env === null) { return yield* Effect.fail( new DatabaseConnectionError({ @@ -64,17 +67,20 @@ export const DatabaseMiddlewareLive = Layer.effect( ); } - return yield* makeDrizzle(env.HYPERDRIVE.connectionString); + const db = yield* makeDrizzle(env.HYPERDRIVE.connectionString); + + return yield* httpEffect.pipe( + Effect.provideService(PgDrizzle, db), + ); }).pipe( - Effect.catchAll((error) => + Effect.catch(() => Effect.fail( new DatabaseConnectionError({ - message: `Database connection failed: ${String(error)}`, + message: "Database connection failed", }), ), ), - ); - }), + ), ); /** diff --git a/apps/effect-worker-api/tsconfig.json b/apps/effect-worker-api/tsconfig.json index 57a4a89..4c123bf 100644 --- a/apps/effect-worker-api/tsconfig.json +++ b/apps/effect-worker-api/tsconfig.json @@ -23,8 +23,6 @@ "@/*": ["./src/*"], "@repo/contracts": ["../../packages/contracts/src/index.ts"], "@repo/contracts/*": ["../../packages/contracts/src/*"], - "@repo/cloudflare": ["../../packages/cloudflare/src/index.ts"], - "@repo/cloudflare/*": ["../../packages/cloudflare/src/*"], "@repo/db": ["../../packages/db/src/index.ts"], "@repo/db/*": ["../../packages/db/src/*"], "@repo/domain": ["../../packages/domain/src/index.ts"], diff --git a/apps/effect-worker-rpc/package.json b/apps/effect-worker-rpc/package.json index ceb774a..14d5fc1 100644 --- a/apps/effect-worker-rpc/package.json +++ b/apps/effect-worker-rpc/package.json @@ -13,18 +13,11 @@ "build": "tsc && wrangler deploy --dry-run" }, "dependencies": { - "@repo/cloudflare": "workspace:^", "@repo/db": "workspace:^", "@repo/domain": "workspace:^", "@repo/contracts": "workspace:^", - "@effect/experimental": "latest", - "@effect/platform": "latest", - "@effect/rpc": "latest", - "@effect/sql": "latest", - "@effect/sql-drizzle": "latest", - "@effect/sql-pg": "latest", "drizzle-orm": "^0.45.0", - "effect": "latest" + "effect": "catalog:" }, "devDependencies": { "@cloudflare/workers-types": "^4.20241127.0", diff --git a/apps/effect-worker-rpc/src/index.ts b/apps/effect-worker-rpc/src/index.ts index 73475b9..56ee16c 100644 --- a/apps/effect-worker-rpc/src/index.ts +++ b/apps/effect-worker-rpc/src/index.ts @@ -5,8 +5,9 @@ * * @module */ -import { rpcRuntime, handleRpcRequest } from "@/runtime" -import { withCloudflareBindings } from "@/services" +import { pipe, ServiceMap } from "effect" +import { rpcHandler } from "@/runtime" +import { currentEnv, currentCtx } from "@/services/cloudflare" /** * Cloudflare Worker fetch handler. @@ -20,13 +21,12 @@ export default { return Response.json({ status: "ok", service: "effect-worker-rpc" }) } - // RPC endpoint - if (url.pathname === "/rpc") { - const effect = handleRpcRequest(request).pipe(withCloudflareBindings(env, ctx)) - return rpcRuntime.runPromise(effect) - } + // Pass per-request Cloudflare bindings via ServiceMap context + const services = pipe( + ServiceMap.make(currentEnv, env), + ServiceMap.add(currentCtx, ctx) + ) - // Not found - return new Response("Not Found", { status: 404 }) + return rpcHandler(request, services) } } satisfies ExportedHandler diff --git a/apps/effect-worker-rpc/src/runtime.ts b/apps/effect-worker-rpc/src/runtime.ts index 6c5b98b..bb1a035 100644 --- a/apps/effect-worker-rpc/src/runtime.ts +++ b/apps/effect-worker-rpc/src/runtime.ts @@ -1,34 +1,13 @@ /** * RPC Runtime Configuration * - * This module sets up the RPC server for handling RPC requests. - * - * ## Request Flow - * - * ``` - * fetch(request, env, ctx) - * └─> withCloudflareBindings(env, ctx) - * └─> handleRpcRequest(request) - * └─> Middleware chain: - * ├─> RpcCloudflareMiddleware → provides env/ctx - * └─> RpcDatabaseMiddleware → provides drizzle - * └─> Handler accesses services via: - * - yield* CloudflareBindings - * - yield* DatabaseService - * ``` - * - * ## Why toHttpApp instead of toWebHandler? - * - * `toWebHandler` creates its own runtime internally, so FiberRefs set via - * `withCloudflareBindings` wouldn't be accessible. By using `toHttpApp`, - * we get an Effect that can be wrapped with `withCloudflareBindings` and - * run in our existing ManagedRuntime, preserving FiberRef access. + * Sets up the RPC server using HttpRouter.toWebHandler. * * @module */ -import { Effect, Layer, ManagedRuntime } from "effect" -import { HttpServer, HttpServerRequest, HttpServerResponse } from "@effect/platform" -import { RpcServer, RpcSerialization } from "@effect/rpc" +import { Layer } from "effect" +import { HttpRouter, HttpServer } from "effect/unstable/http" +import { RpcServer, RpcSerialization } from "effect/unstable/rpc" import { UsersRpc } from "@repo/contracts" import { UsersRpcHandlersLive } from "@/handlers" import { @@ -42,9 +21,6 @@ import { /** * Combined middleware layer. - * - * Middleware implementations are provided at the runtime level. - * These are needed by both handlers and RpcServer.toHttpApp. */ const RpcMiddlewareLive = Layer.mergeAll( RpcCloudflareMiddlewareLive, @@ -52,60 +28,34 @@ const RpcMiddlewareLive = Layer.mergeAll( ) /** - * Full RPC layer including handlers, middleware, serialization, and HTTP services. + * Protocol layer — HTTP transport with ndjson serialization. * - * Note: RpcMiddlewareLive is merged (not just provided) because toHttpApp - * requires the middleware services in its context, not just as dependencies. + * layerProtocolHttp requires RpcSerialization + HttpRouter. + * We provide RpcSerialization here; HttpRouter is provided by toWebHandler. */ -const RpcLayer = Layer.mergeAll( - UsersRpcHandlersLive, - RpcMiddlewareLive, - RpcSerialization.layerNdjson, - HttpServer.layerContext +const ProtocolLayer = RpcServer.layerProtocolHttp({ path: "/rpc" }).pipe( + Layer.provide(RpcSerialization.layerNdjson) ) /** - * Shared runtime for RPC requests. + * Full RPC routes layer. * - * Similar to HTTP runtime, layers are memoized at startup. - * Request-scoped services are provided via middleware. + * RpcServer.layer requires Protocol + handlers + middleware. + * After composition, only HttpRouter remains as a requirement + * (provided by HttpRouter.toWebHandler). */ -export const rpcRuntime = ManagedRuntime.make(RpcLayer) - -// ============================================================================ -// Request Handler -// ============================================================================ +const RpcRoutes = RpcServer.layer(UsersRpc).pipe( + Layer.provide(UsersRpcHandlersLive), + Layer.provide(RpcMiddlewareLive), + Layer.provide(ProtocolLayer) +) /** - * Handle an incoming RPC request. - * - * Returns an Effect that should be wrapped with `withCloudflareBindings` - * before execution to make env/ctx available to middleware. + * Web handler for RPC requests. * - * ## Usage - * - * ```typescript - * const effect = handleRpcRequest(request).pipe( - * withCloudflareBindings(env, ctx), - * ) - * return rpcRuntime.runPromise(effect) - * ``` + * Layers are memoized internally — built once at startup. + * Per-request services (env/ctx) are passed via the ServiceMap context. */ -export const handleRpcRequest = (request: Request) => - Effect.gen(function* () { - // Get the RPC HTTP app (yields an httpApp function) - const httpApp = yield* RpcServer.toHttpApp(UsersRpc, { - spanPrefix: "RpcServer" - }) - - // Convert web request to Effect platform request - const serverRequest = HttpServerRequest.fromWeb(request) - - // Handle and return web response - // httpApp is an Effect - const response = yield* httpApp.pipe( - Effect.provideService(HttpServerRequest.HttpServerRequest, serverRequest) - ) - - return HttpServerResponse.toWeb(response) - }).pipe(Effect.scoped) +export const { handler: rpcHandler, dispose } = HttpRouter.toWebHandler( + RpcRoutes.pipe(Layer.provide(HttpServer.layerServices)) +) diff --git a/apps/effect-worker-rpc/src/services/cloudflare.ts b/apps/effect-worker-rpc/src/services/cloudflare.ts index ecf16dc..448beb2 100644 --- a/apps/effect-worker-rpc/src/services/cloudflare.ts +++ b/apps/effect-worker-rpc/src/services/cloudflare.ts @@ -1,59 +1,53 @@ /** * Cloudflare Bindings Service * - * Re-exports FiberRef bridge from @repo/cloudflare. - * This provides app-local type inference for the Env type. + * ServiceMap.Reference bridge for providing Cloudflare's `env` and `ExecutionContext` + * to Effect handlers. * * @module */ -import { Effect, FiberRef } from "effect" +import { Effect, ServiceMap } from "effect" /** - * FiberRef holding the current request's Cloudflare environment bindings. + * Reference holding the current request's Cloudflare environment bindings. */ -export const currentEnv = FiberRef.unsafeMake(null) +export const currentEnv = ServiceMap.Reference( + "@app/rpc/currentEnv", + { defaultValue: () => null } +) /** - * FiberRef holding the current request's ExecutionContext. + * Reference holding the current request's ExecutionContext. */ -export const currentCtx = FiberRef.unsafeMake(null) +export const currentCtx = ServiceMap.Reference( + "@app/rpc/currentCtx", + { defaultValue: () => null } +) /** * Set Cloudflare bindings for the scope of an effect. - * - * Call this at the request boundary in index.ts: - * - * ```typescript - * const effect = handleRpcRequest(request).pipe( - * withCloudflareBindings(env, ctx), - * ) - * return rpcRuntime.runPromise(effect) - * ``` */ export const withCloudflareBindings = (env: Env, ctx: ExecutionContext) => (effect: Effect.Effect) => effect.pipe( - Effect.locally(currentEnv, env), - Effect.locally(currentCtx, ctx) + Effect.provideService(currentEnv, env), + Effect.provideService(currentCtx, ctx) ) /** * Schedule a background task that runs after the response is sent. - * - * Uses ctx.waitUntil() to keep the Worker alive while the effect runs. - * Errors are logged but don't affect the response. */ export const waitUntil = ( effect: Effect.Effect ): Effect.Effect => Effect.gen(function* () { - const ctx = yield* FiberRef.get(currentCtx) + const ctx = yield* currentCtx if (ctx) { ctx.waitUntil( Effect.runPromise( effect.pipe( - Effect.tapErrorCause(Effect.logError), - Effect.catchAll(() => Effect.void) + Effect.tapCause(Effect.logError), + Effect.catch(() => Effect.void) ) ) ) diff --git a/apps/effect-worker-rpc/src/services/middleware.ts b/apps/effect-worker-rpc/src/services/middleware.ts index 07f200d..c68a5c6 100644 --- a/apps/effect-worker-rpc/src/services/middleware.ts +++ b/apps/effect-worker-rpc/src/services/middleware.ts @@ -2,18 +2,20 @@ * RPC Middleware Implementations * * Provides the actual implementations for RPC middleware tags. - * These use FiberRefs to access request-scoped Cloudflare bindings. + * In Effect v4, middleware with `provides` is a function that wraps the + * RPC effect and provides the required service to it. * * @module */ -import { Effect, FiberRef, Layer } from "effect" +import { Effect, Layer } from "effect" import { + RpcCloudflareMiddleware, + RpcDatabaseMiddleware, + CloudflareBindings, CloudflareBindingsError, DatabaseConnectionError, - makeDrizzle, - -} from "@repo/cloudflare" -import { RpcCloudflareMiddleware, RpcDatabaseMiddleware } from "@repo/contracts" +} from "@repo/contracts" +import { PgDrizzle, makeDrizzle } from "@repo/db" import { currentEnv, currentCtx } from "@/services/cloudflare" // ============================================================================ @@ -23,15 +25,15 @@ import { currentEnv, currentCtx } from "@/services/cloudflare" /** * Live implementation of RpcCloudflareMiddleware. * - * Reads env/ctx from FiberRef and provides them as the CloudflareBindings service. + * Reads env/ctx from ServiceMap.Reference and provides CloudflareBindings + * to the downstream RPC handler effect. */ export const RpcCloudflareMiddlewareLive = Layer.succeed( RpcCloudflareMiddleware, - // Middleware function runs per-RPC-call - () => + (effect) => Effect.gen(function* () { - const env = yield* FiberRef.get(currentEnv) - const ctx = yield* FiberRef.get(currentCtx) + const env = yield* currentEnv + const ctx = yield* currentCtx if (env === null || ctx === null) { return yield* Effect.fail( @@ -42,7 +44,9 @@ export const RpcCloudflareMiddlewareLive = Layer.succeed( ) } - return { env, ctx } + return yield* effect.pipe( + Effect.provideService(CloudflareBindings, { env, ctx }) + ) }) ) @@ -53,16 +57,14 @@ export const RpcCloudflareMiddlewareLive = Layer.succeed( /** * Live implementation of RpcDatabaseMiddleware. * - * Creates a scoped database connection per-request. - * The connection is automatically closed when the request scope ends. + * Creates a scoped database connection per-request and provides PgDrizzle + * to the downstream RPC handler effect. */ export const RpcDatabaseMiddlewareLive = Layer.succeed( RpcDatabaseMiddleware, - // Middleware function runs per-RPC-call - () => + (effect) => Effect.gen(function* () { - // Get connection string from Cloudflare env via FiberRef - const env = yield* FiberRef.get(currentEnv) + const env = yield* currentEnv if (env === null) { return yield* Effect.fail( new DatabaseConnectionError({ @@ -72,14 +74,16 @@ export const RpcDatabaseMiddlewareLive = Layer.succeed( ) } + const db = yield* makeDrizzle(env.HYPERDRIVE.connectionString) - return yield* makeDrizzle(env.HYPERDRIVE.connectionString) + return yield* effect.pipe( + Effect.provideService(PgDrizzle, db) + ) }).pipe( - Effect.scoped, - Effect.catchAll((error) => + Effect.catch(() => Effect.fail( new DatabaseConnectionError({ - message: `Database connection failed: ${String(error)}` + message: "Database connection failed" }) ) ) diff --git a/apps/effect-worker-rpc/tsconfig.json b/apps/effect-worker-rpc/tsconfig.json index 8195214..d44fec9 100644 --- a/apps/effect-worker-rpc/tsconfig.json +++ b/apps/effect-worker-rpc/tsconfig.json @@ -13,8 +13,6 @@ "types": ["@cloudflare/workers-types", "node"], "paths": { "@/*": ["./src/*"], - "@repo/cloudflare": ["../../packages/cloudflare/src/index.ts"], - "@repo/cloudflare/*": ["../../packages/cloudflare/src/*"], "@repo/db": ["../../packages/db/src/index.ts"], "@repo/db/*": ["../../packages/db/src/*"], "@repo/domain": ["../../packages/domain/src/index.ts"], diff --git a/apps/react-app/.gitignore b/apps/react-app/.gitignore new file mode 100644 index 0000000..36abcf7 --- /dev/null +++ b/apps/react-app/.gitignore @@ -0,0 +1,4 @@ +node_modules +dist +.wrangler +src/routeTree.gen.ts diff --git a/apps/react-app/components.json b/apps/react-app/components.json new file mode 100644 index 0000000..58bb3a2 --- /dev/null +++ b/apps/react-app/components.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": false, + "tsx": true, + "tailwind": { + "config": "", + "css": "src/styles.css", + "baseColor": "zinc", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "iconLibrary": "lucide" +} diff --git a/apps/react-app/index.html b/apps/react-app/index.html new file mode 100644 index 0000000..5da656c --- /dev/null +++ b/apps/react-app/index.html @@ -0,0 +1,12 @@ + + + + + + React App + + +
+ + + diff --git a/apps/react-app/package.json b/apps/react-app/package.json new file mode 100644 index 0000000..e07d300 --- /dev/null +++ b/apps/react-app/package.json @@ -0,0 +1,43 @@ +{ + "name": "react-app", + "private": true, + "type": "module", + "scripts": { + "dev": "vite dev --port 3001", + "build": "vite build", + "preview": "vite preview", + "check": "tsc --noEmit", + "deploy": "pnpm run build && wrangler deploy", + "cf-typegen": "wrangler types --env-interface Env" + }, + "dependencies": { + "@tailwindcss/vite": "^4.1.18", + "@tanstack/react-query": "^5.90.18", + "@tanstack/react-query-devtools": "^5.91.2", + "@tanstack/react-router": "^1.150.0", + "@tanstack/react-router-devtools": "^1.150.0", + "@repo/contracts": "workspace:*", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "lucide-react": "^0.476.0", + "radix-ui": "^1.4.3", + "effect": "catalog:", + "react": "^19.2.3", + "react-dom": "^19.2.3", + "tailwind-merge": "^3.4.0", + "tailwindcss": "^4.1.18", + "tw-animate-css": "^1.4.0", + "vite-tsconfig-paths": "^5.1.4" + }, + "devDependencies": { + "@cloudflare/vite-plugin": "^1.21.0", + "@cloudflare/workers-types": "^4.20241127.0", + "@tanstack/router-plugin": "^1.150.0", + "@types/react": "^19.2.8", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^4.7.0", + "typescript": "^5.9.3", + "vite": "7.1.2", + "wrangler": "^4.59.2" + } +} diff --git a/apps/react-app/src/components/ui/button.tsx b/apps/react-app/src/components/ui/button.tsx new file mode 100644 index 0000000..b5ea4ab --- /dev/null +++ b/apps/react-app/src/components/ui/button.tsx @@ -0,0 +1,64 @@ +import * as React from "react" +import { cva, type VariantProps } from "class-variance-authority" +import { Slot } from "radix-ui" + +import { cn } from "@/lib/utils" + +const buttonVariants = cva( + "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", + { + variants: { + variant: { + default: "bg-primary text-primary-foreground hover:bg-primary/90", + destructive: + "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60", + outline: + "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50", + secondary: + "bg-secondary text-secondary-foreground hover:bg-secondary/80", + ghost: + "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50", + link: "text-primary underline-offset-4 hover:underline", + }, + size: { + default: "h-9 px-4 py-2 has-[>svg]:px-3", + xs: "h-6 gap-1 rounded-md px-2 text-xs has-[>svg]:px-1.5 [&_svg:not([class*='size-'])]:size-3", + sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5", + lg: "h-10 rounded-md px-6 has-[>svg]:px-4", + icon: "size-9", + "icon-xs": "size-6 rounded-md [&_svg:not([class*='size-'])]:size-3", + "icon-sm": "size-8", + "icon-lg": "size-10", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + } +) + +function Button({ + className, + variant = "default", + size = "default", + asChild = false, + ...props +}: React.ComponentProps<"button"> & + VariantProps & { + asChild?: boolean + }) { + const Comp = asChild ? Slot.Root : "button" + + return ( + + ) +} + +export { Button, buttonVariants } diff --git a/apps/react-app/src/lib/utils.ts b/apps/react-app/src/lib/utils.ts new file mode 100644 index 0000000..a5ef193 --- /dev/null +++ b/apps/react-app/src/lib/utils.ts @@ -0,0 +1,6 @@ +import { clsx, type ClassValue } from "clsx"; +import { twMerge } from "tailwind-merge"; + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)); +} diff --git a/apps/react-app/src/main.tsx b/apps/react-app/src/main.tsx new file mode 100644 index 0000000..bd9b607 --- /dev/null +++ b/apps/react-app/src/main.tsx @@ -0,0 +1,30 @@ +import { StrictMode } from "react"; +import { createRoot } from "react-dom/client"; +import { RouterProvider } from "@tanstack/react-router"; +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { createRouter } from "./router"; +import "./styles.css"; + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + staleTime: 1000 * 60, + }, + }, +}); + +const router = createRouter(queryClient); + +declare module "@tanstack/react-router" { + interface Register { + router: typeof router; + } +} + +createRoot(document.getElementById("root")!).render( + + + + + , +); diff --git a/apps/react-app/src/router.tsx b/apps/react-app/src/router.tsx new file mode 100644 index 0000000..5ffc489 --- /dev/null +++ b/apps/react-app/src/router.tsx @@ -0,0 +1,11 @@ +import type { QueryClient } from "@tanstack/react-query"; +import { createRouter as createTanStackRouter } from "@tanstack/react-router"; +import { routeTree } from "./routeTree.gen"; + +export function createRouter(queryClient: QueryClient) { + return createTanStackRouter({ + routeTree, + defaultPreload: "intent", + context: { queryClient }, + }); +} diff --git a/apps/react-app/src/routes/__root.tsx b/apps/react-app/src/routes/__root.tsx new file mode 100644 index 0000000..6f1aca6 --- /dev/null +++ b/apps/react-app/src/routes/__root.tsx @@ -0,0 +1,37 @@ +import type { QueryClient } from "@tanstack/react-query"; +import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; +import { + createRootRouteWithContext, + Link, + Outlet, +} from "@tanstack/react-router"; +import { TanStackRouterDevtools } from "@tanstack/react-router-devtools"; + +interface RouterContext { + queryClient: QueryClient; +} + +export const Route = createRootRouteWithContext()({ + component: RootComponent, +}); + +function RootComponent() { + return ( + <> + +
+ +
+ + + + ); +} diff --git a/apps/react-app/src/routes/index.tsx b/apps/react-app/src/routes/index.tsx new file mode 100644 index 0000000..c41287f --- /dev/null +++ b/apps/react-app/src/routes/index.tsx @@ -0,0 +1,120 @@ +import { useState } from "react" +import { createFileRoute } from "@tanstack/react-router" +import { Button } from "@/components/ui/button" +import { useUsers, useCreateUser } from "@/rpc" + +export const Route = createFileRoute("/")({ + component: HomePage, +}) + +function HomePage() { + return ( +
+
+

Users

+

+ Manage users via Effect RPC +

+
+ + +
+ ) +} + +function CreateUserForm() { + const [name, setName] = useState("") + const [email, setEmail] = useState("") + const createUser = useCreateUser() + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault() + if (!name.trim() || !email.trim()) return + createUser.mutate( + { name: name.trim(), email: email.trim() }, + { + onSuccess: () => { + setName("") + setEmail("") + }, + } + ) + } + + return ( +
+

Create User

+
+ setName(e.target.value)} + className="rounded-md border border-input bg-background px-3 py-2 text-sm placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring" + required + /> + setEmail(e.target.value)} + className="rounded-md border border-input bg-background px-3 py-2 text-sm placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring" + required + /> +
+ {createUser.error && ( +

+ {createUser.error instanceof Error + ? createUser.error.message + : "Failed to create user"} +

+ )} + +
+ ) +} + +function UsersList() { + const { data, isLoading, error } = useUsers() + + if (isLoading) { + return

Loading users...

+ } + + if (error) { + return ( +

+ {error instanceof Error ? error.message : "Failed to load users"} +

+ ) + } + + if (!data || data.users.length === 0) { + return ( +

No users yet. Create one above.

+ ) + } + + return ( +
+

+ All Users ({data.total}) +

+
+ {data.users.map((user) => ( +
+
+

{user.name}

+

{user.email}

+
+

+ {new Date(Number(user.createdAt)).toLocaleDateString()} +

+
+ ))} +
+
+ ) +} diff --git a/apps/react-app/src/rpc/client.ts b/apps/react-app/src/rpc/client.ts new file mode 100644 index 0000000..040c607 --- /dev/null +++ b/apps/react-app/src/rpc/client.ts @@ -0,0 +1,22 @@ +import { Effect, Layer, ManagedRuntime } from "effect" +import { RpcClient, RpcClientError, RpcSerialization } from "effect/unstable/rpc" +import { FetchHttpClient } from "effect/unstable/http" +import { UsersRpc } from "@repo/contracts/rpc" + +const RpcLayer = RpcClient.layerProtocolHttp({ url: "/rpc" }).pipe( + Layer.provide(RpcSerialization.layerNdjson), + Layer.provide(FetchHttpClient.layer) +) + +const runtime = ManagedRuntime.make(RpcLayer) + +type Client = RpcClient.FromGroup + +export function rpc( + fn: (client: Client) => Effect.Effect +): Promise { + const program = Effect.scoped( + Effect.flatMap(RpcClient.make(UsersRpc), fn) + ) + return runtime.runPromise(program) +} diff --git a/apps/react-app/src/rpc/hooks.ts b/apps/react-app/src/rpc/hooks.ts new file mode 100644 index 0000000..2bcd72f --- /dev/null +++ b/apps/react-app/src/rpc/hooks.ts @@ -0,0 +1,28 @@ +import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query" +import { rpc } from "./client" +import { userKeys } from "./keys" + +export function useUsers() { + return useQuery({ + queryKey: userKeys.list(), + queryFn: () => rpc((client) => client.listUsers()), + }) +} + +export function useUser(id: string) { + return useQuery({ + queryKey: userKeys.detail(id), + queryFn: () => rpc((client) => client.getUser({ id })), + }) +} + +export function useCreateUser() { + const queryClient = useQueryClient() + return useMutation({ + mutationFn: (data: { email: string; name: string }) => + rpc((client) => client.createUser(data)), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: userKeys.all }) + }, + }) +} diff --git a/apps/react-app/src/rpc/index.ts b/apps/react-app/src/rpc/index.ts new file mode 100644 index 0000000..1c0bd51 --- /dev/null +++ b/apps/react-app/src/rpc/index.ts @@ -0,0 +1,2 @@ +export { useUsers, useUser, useCreateUser } from "./hooks" +export { userKeys } from "./keys" diff --git a/apps/react-app/src/rpc/keys.ts b/apps/react-app/src/rpc/keys.ts new file mode 100644 index 0000000..d9e0afa --- /dev/null +++ b/apps/react-app/src/rpc/keys.ts @@ -0,0 +1,5 @@ +export const userKeys = { + all: ["users"] as const, + list: () => [...userKeys.all, "list"] as const, + detail: (id: string) => [...userKeys.all, "detail", id] as const, +} diff --git a/apps/react-app/src/styles.css b/apps/react-app/src/styles.css new file mode 100644 index 0000000..e6bb0c0 --- /dev/null +++ b/apps/react-app/src/styles.css @@ -0,0 +1,124 @@ +@import 'tailwindcss'; + +@import 'tw-animate-css'; + +@custom-variant dark (&:is(.dark *)); + +:root { + --background: oklch(1 0 0); + --foreground: oklch(0.141 0.005 285.823); + --card: oklch(1 0 0); + --card-foreground: oklch(0.141 0.005 285.823); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.141 0.005 285.823); + --primary: oklch(0.21 0.006 285.885); + --primary-foreground: oklch(0.985 0 0); + --secondary: oklch(0.967 0.001 286.375); + --secondary-foreground: oklch(0.21 0.006 285.885); + --muted: oklch(0.967 0.001 286.375); + --muted-foreground: oklch(0.552 0.016 285.938); + --accent: oklch(0.967 0.001 286.375); + --accent-foreground: oklch(0.21 0.006 285.885); + --destructive: oklch(0.577 0.245 27.325); + --destructive-foreground: oklch(0.577 0.245 27.325); + --border: oklch(0.92 0.004 286.32); + --input: oklch(0.92 0.004 286.32); + --ring: oklch(0.871 0.006 286.286); + --chart-1: oklch(0.646 0.222 41.116); + --chart-2: oklch(0.6 0.118 184.704); + --chart-3: oklch(0.398 0.07 227.392); + --chart-4: oklch(0.828 0.189 84.429); + --chart-5: oklch(0.769 0.188 70.08); + --radius: 0.625rem; + --sidebar: oklch(0.985 0 0); + --sidebar-foreground: oklch(0.141 0.005 285.823); + --sidebar-primary: oklch(0.21 0.006 285.885); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.967 0.001 286.375); + --sidebar-accent-foreground: oklch(0.21 0.006 285.885); + --sidebar-border: oklch(0.92 0.004 286.32); + --sidebar-ring: oklch(0.871 0.006 286.286); +} + +.dark { + --background: oklch(0.141 0.005 285.823); + --foreground: oklch(0.985 0 0); + --card: oklch(0.141 0.005 285.823); + --card-foreground: oklch(0.985 0 0); + --popover: oklch(0.141 0.005 285.823); + --popover-foreground: oklch(0.985 0 0); + --primary: oklch(0.985 0 0); + --primary-foreground: oklch(0.21 0.006 285.885); + --secondary: oklch(0.274 0.006 286.033); + --secondary-foreground: oklch(0.985 0 0); + --muted: oklch(0.274 0.006 286.033); + --muted-foreground: oklch(0.705 0.015 286.067); + --accent: oklch(0.274 0.006 286.033); + --accent-foreground: oklch(0.985 0 0); + --destructive: oklch(0.396 0.141 25.723); + --destructive-foreground: oklch(0.637 0.237 25.331); + --border: oklch(0.274 0.006 286.033); + --input: oklch(0.274 0.006 286.033); + --ring: oklch(0.442 0.017 285.786); + --chart-1: oklch(0.488 0.243 264.376); + --chart-2: oklch(0.696 0.17 162.48); + --chart-3: oklch(0.769 0.188 70.08); + --chart-4: oklch(0.627 0.265 303.9); + --chart-5: oklch(0.645 0.246 16.439); + --sidebar: oklch(0.21 0.006 285.885); + --sidebar-foreground: oklch(0.985 0 0); + --sidebar-primary: oklch(0.488 0.243 264.376); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.274 0.006 286.033); + --sidebar-accent-foreground: oklch(0.985 0 0); + --sidebar-border: oklch(0.274 0.006 286.033); + --sidebar-ring: oklch(0.442 0.017 285.786); +} + +@theme inline { + --color-background: var(--background); + --color-foreground: var(--foreground); + --color-card: var(--card); + --color-card-foreground: var(--card-foreground); + --color-popover: var(--popover); + --color-popover-foreground: var(--popover-foreground); + --color-primary: var(--primary); + --color-primary-foreground: var(--primary-foreground); + --color-secondary: var(--secondary); + --color-secondary-foreground: var(--secondary-foreground); + --color-muted: var(--muted); + --color-muted-foreground: var(--muted-foreground); + --color-accent: var(--accent); + --color-accent-foreground: var(--accent-foreground); + --color-destructive: var(--destructive); + --color-destructive-foreground: var(--destructive-foreground); + --color-border: var(--border); + --color-input: var(--input); + --color-ring: var(--ring); + --color-chart-1: var(--chart-1); + --color-chart-2: var(--chart-2); + --color-chart-3: var(--chart-3); + --color-chart-4: var(--chart-4); + --color-chart-5: var(--chart-5); + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); + --color-sidebar: var(--sidebar); + --color-sidebar-foreground: var(--sidebar-foreground); + --color-sidebar-primary: var(--sidebar-primary); + --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); + --color-sidebar-accent: var(--sidebar-accent); + --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); + --color-sidebar-border: var(--sidebar-border); + --color-sidebar-ring: var(--sidebar-ring); +} + +@layer base { + * { + @apply border-border outline-ring/50; + } + body { + @apply bg-background text-foreground; + } +} diff --git a/apps/react-app/tsconfig.json b/apps/react-app/tsconfig.json new file mode 100644 index 0000000..23acc5e --- /dev/null +++ b/apps/react-app/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "target": "ES2022", + "lib": ["ES2023", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "noEmit": true, + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedIndexedAccess": true, + "jsx": "react-jsx", + "types": ["vite/client", "@cloudflare/workers-types"], + "baseUrl": ".", + "paths": { + "@/*": ["./src/*"] + } + }, + "include": ["src", "worker"] +} diff --git a/apps/react-app/vite.config.ts b/apps/react-app/vite.config.ts new file mode 100644 index 0000000..722f9f4 --- /dev/null +++ b/apps/react-app/vite.config.ts @@ -0,0 +1,26 @@ +import { defineConfig } from "vite"; +import react from "@vitejs/plugin-react"; +import { cloudflare } from "@cloudflare/vite-plugin"; +import { TanStackRouterVite } from "@tanstack/router-plugin/vite"; +import tailwindcss from "@tailwindcss/vite"; +import viteTsConfigPaths from "vite-tsconfig-paths"; + +export default defineConfig({ + server: { + proxy: { + "/rpc": "http://localhost:8787", + }, + }, + plugins: [ + viteTsConfigPaths({ + projects: ["./tsconfig.json"], + }), + tailwindcss(), + TanStackRouterVite({ + target: "react", + autoCodeSplitting: true, + }), + react(), + cloudflare(), + ], +}); diff --git a/apps/react-app/worker/index.ts b/apps/react-app/worker/index.ts new file mode 100644 index 0000000..eec7157 --- /dev/null +++ b/apps/react-app/worker/index.ts @@ -0,0 +1,11 @@ +export default { + async fetch(request: Request): Promise { + const url = new URL(request.url); + + if (url.pathname.startsWith("/api/")) { + return Response.json({ message: "Hello from the API" }); + } + + return new Response("Not found", { status: 404 }); + }, +} satisfies ExportedHandler; diff --git a/apps/react-app/wrangler.jsonc b/apps/react-app/wrangler.jsonc new file mode 100644 index 0000000..b38ab02 --- /dev/null +++ b/apps/react-app/wrangler.jsonc @@ -0,0 +1,9 @@ +{ + "$schema": "node_modules/wrangler/config-schema.json", + "name": "react-app", + "main": "./worker/index.ts", + "compatibility_date": "2025-04-01", + "assets": { + "not_found_handling": "single-page-application" + } +} diff --git a/apps/tanstack-start/package.json b/apps/tanstack-start/package.json index 1b4c626..ee88b14 100644 --- a/apps/tanstack-start/package.json +++ b/apps/tanstack-start/package.json @@ -10,8 +10,7 @@ "cf-typegen": "wrangler types --env-interface Env" }, "dependencies": { - "@repo/cloudflare": "workspace:*", - "effect": "latest", + "effect": "catalog:", "@radix-ui/react-collapsible": "^1.1.12", "@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-dropdown-menu": "^2.1.16", diff --git a/apps/tanstack-start/src/routes/api/process.ts b/apps/tanstack-start/src/routes/api/process.ts index 06e1b67..ff31881 100644 --- a/apps/tanstack-start/src/routes/api/process.ts +++ b/apps/tanstack-start/src/routes/api/process.ts @@ -178,7 +178,7 @@ export const Route = createFileRoute("/api/process")({ Effect.succeed(errorResponse(error.message, 422)) ), // Handle unexpected errors (500) - Effect.catchAllDefect((defect) => + Effect.catchDefect((defect) => Effect.succeed( errorResponse( defect instanceof Error ? defect.message : "Internal server error", diff --git a/apps/tanstack-start/src/server/functions/example-effect-function.ts b/apps/tanstack-start/src/server/functions/example-effect-function.ts index 47ce6f6..63b7095 100644 --- a/apps/tanstack-start/src/server/functions/example-effect-function.ts +++ b/apps/tanstack-start/src/server/functions/example-effect-function.ts @@ -25,7 +25,10 @@ const validateWith = * Request schema */ const GreetingRequestSchema = S.Struct({ - name: S.String.pipe(S.minLength(1), S.maxLength(100)), + name: S.String.pipe( + S.check(S.isMinLength(1)), + S.check(S.isMaxLength(100)) + ), }); /** diff --git a/apps/tanstack-start/src/server/middleware/effect-runtime.ts b/apps/tanstack-start/src/server/middleware/effect-runtime.ts index db35d06..da2d288 100644 --- a/apps/tanstack-start/src/server/middleware/effect-runtime.ts +++ b/apps/tanstack-start/src/server/middleware/effect-runtime.ts @@ -11,17 +11,14 @@ * * @example Adding PgDrizzle (requires HYPERDRIVE binding in wrangler.jsonc): * ```typescript - * import { PgDrizzle, makeDrizzle } from "@repo/cloudflare" + * import { PgDrizzle, makePgDrizzleLayer } from "@repo/db" * - * const dbLayer = Layer.scoped( - * PgDrizzle, - * makeDrizzle(env.HYPERDRIVE.connectionString) - * ) + * const dbLayer = makePgDrizzleLayer(env.HYPERDRIVE.connectionString) * const servicesLayer = Layer.mergeAll(dbLayer, otherServiceLayer) * ``` */ import { createMiddleware } from "@tanstack/react-start" -import { Effect, Layer, Runtime } from "effect" +import { Effect, Layer, ManagedRuntime } from "effect" import { env } from "cloudflare:workers" import type { EffectServices } from "../types" @@ -51,42 +48,29 @@ export const effectRuntimeMiddleware = createMiddleware().server( // Add your service layers here and merge them: // // Example with PgDrizzle (uncomment when HYPERDRIVE is configured): - // const dbLayer = Layer.scoped( - // PgDrizzle, - // makeDrizzle(env.HYPERDRIVE.connectionString) - // ) + // const dbLayer = makePgDrizzleLayer(env.HYPERDRIVE.connectionString) // const servicesLayer = Layer.mergeAll(dbLayer, otherServiceLayer) // Empty layer - add your services above const servicesLayer = Layer.empty - return Effect.runPromise( - Effect.scoped( - Effect.gen(function* () { - const runtime = yield* Layer.toRuntime(servicesLayer) + const runtime = ManagedRuntime.make(servicesLayer) - const runEffect = ( - effect: Effect.Effect - ) => { - return Runtime.runPromise(runtime)(effect) - } + try { + const runEffect = ( + effect: Effect.Effect + ) => { + return runtime.runPromise(effect) + } - const nextResult = next({ - context: { - env, - runEffect, - }, - }) - - return yield* Effect.tryPromise({ - try: async () => await nextResult, - catch: (error) => { - console.error("Error occurred in middleware:", error) - throw error - }, - }) - }) - ) - ) + return await next({ + context: { + env, + runEffect, + }, + }) + } finally { + await runtime.dispose() + } } ) diff --git a/apps/tanstack-start/src/server/types.ts b/apps/tanstack-start/src/server/types.ts index 1ebe553..e0fca52 100644 --- a/apps/tanstack-start/src/server/types.ts +++ b/apps/tanstack-start/src/server/types.ts @@ -5,7 +5,7 @@ */ import type { Effect } from "effect" // Uncomment when adding PgDrizzle service: -// import type { PgDrizzle } from "@repo/cloudflare" +// import type { PgDrizzle } from "@repo/db" import { env } from "cloudflare:workers" /** @@ -19,7 +19,7 @@ export type CloudflareEnv = typeof env * Add your service types here as you create them: * @example * ```typescript - * import type { PgDrizzle } from "@repo/cloudflare" + * import type { PgDrizzle } from "@repo/db" * import type { MyCustomService } from "./services/my-service" * * export type EffectServices = PgDrizzle | MyCustomService diff --git a/designs/react-rpc-client.md b/designs/react-rpc-client.md new file mode 100644 index 0000000..a2ec8a4 --- /dev/null +++ b/designs/react-rpc-client.md @@ -0,0 +1,385 @@ +# React App RPC Client Design + +## Goal + +Wire up `apps/react-app` to call `apps/effect-worker-rpc` via Effect v4's RPC client, with TanStack Query managing all data fetching state (caching, refetching, loading/error states). + +## Current State + +**Server** (`apps/effect-worker-rpc`): +- HTTP RPC at path `/rpc` with ndjson serialization +- `UsersRpc` group: `getUser`, `listUsers`, `createUser` +- Both middlewares (`RpcDatabaseMiddleware`, `RpcCloudflareMiddleware`) have `requiredForClient: false` + +**Shared contracts** (`@repo/contracts`): +- `UsersRpc` group definition with schemas for payloads, success types, and errors +- Schemas: `UserRpcSchema`, `UsersListRpcSchema`, error schemas +- All importable from `@repo/contracts` + +**React app** (`apps/react-app`): +- TanStack Router + TanStack Query already configured +- `QueryClient` passed into router context (available in route loaders) + +--- + +## Approach: Effect RPC Client + TanStack Query Bridge + +Use Effect's typed RPC client for the transport layer and schema validation, then bridge into TanStack Query via a thin `runPromise` wrapper. This gives us: + +- Full type safety from shared contracts (payloads, responses, errors) +- Proper ndjson protocol handling (matching the server) +- Schema validation on responses +- TanStack Query handles caching, refetching, loading/error UI state + +### Why not raw fetch? + +The RPC server uses Effect's ndjson-framed protocol, not plain JSON REST. A raw fetch client would need to manually handle: +- ndjson framing and parsing +- Request ID generation and correlation +- Schema encoding/decoding (e.g., `DateTimeUtc`) +- Error type discrimination + +Effect's `RpcClient` handles all of this out of the box and gives us full type inference from the shared `UsersRpc` group. + +--- + +## Architecture + +``` +React Component + ↓ useQuery / useMutation +TanStack Query + ↓ queryFn / mutationFn +RPC Hook (runs Effect → Promise) + ↓ +Effect RpcClient (typed, schema-validated) + ↓ HTTP POST + ndjson +effect-worker-rpc (Cloudflare Worker) +``` + +--- + +## Implementation Plan + +### 1. Add dependencies to `apps/react-app` + +```json +{ + "dependencies": { + "effect": "catalog:", + "@repo/contracts": "workspace:*" + } +} +``` + +The `effect` package includes the RPC client, HTTP client, and serialization at `effect/unstable/rpc` and `effect/unstable/http`. No additional Effect packages needed. + +### 2. Create the RPC client module + +**`src/rpc/client.ts`** — Bootstraps the Effect RPC client as a singleton. + +```typescript +import { Effect, Layer, ManagedRuntime } from "effect" +import { RpcClient, RpcSerialization } from "effect/unstable/rpc" +import { HttpClient } from "effect/unstable/http" +import { UsersRpc } from "@repo/contracts" + +// Layer composition: RPC client with HTTP transport + ndjson +const UsersClientLive = Layer.unwrapEffect( + Effect.gen(function* () { + return RpcClient.make(UsersRpc) + }) +).pipe( + Layer.provide( + RpcClient.layerProtocolHttp({ + url: import.meta.env.VITE_RPC_URL ?? "/rpc" + }) + ), + Layer.provide(RpcSerialization.layerNdjson), + Layer.provide(HttpClient.layer) +) + +// ManagedRuntime for running Effects as Promises in the browser +const runtime = ManagedRuntime.make(UsersClientLive) + +// Export a function that gets the typed client and runs an RPC call +export const rpcClient = () => + runtime.runPromise( + RpcClient.make(UsersRpc) + ) +``` + +However, creating the client per-call is wasteful. Better pattern — expose individual call functions: + +```typescript +import { Effect, Layer, ManagedRuntime } from "effect" +import { RpcClient, RpcSerialization } from "effect/unstable/rpc" +import { HttpClient } from "effect/unstable/http" +import { UsersRpc } from "@repo/contracts" + +const RpcLive = Layer.scopedDiscard( + Effect.gen(function* () { + // Client is created once and cached by the runtime + }) +).pipe( + Layer.provideMerge( + RpcClient.layerProtocolHttp({ + url: import.meta.env.VITE_RPC_URL ?? "/rpc" + }) + ), + Layer.provideMerge(RpcSerialization.layerNdjson), + Layer.provideMerge(HttpClient.layer) +) + +const runtime = ManagedRuntime.make(RpcLive) + +// Run any Effect that needs the RPC client +export function runRpc( + effect: Effect.Effect +): Promise { + return runtime.runPromise(effect) as Promise +} +``` + +**Simpler recommended pattern** — just create the client once and expose call functions: + +```typescript +import { Effect, Layer, ManagedRuntime } from "effect" +import { RpcClient, RpcSerialization } from "effect/unstable/rpc" +import { HttpClient } from "effect/unstable/http" +import { UsersRpc } from "@repo/contracts" + +// Build the full layer +const RpcClientLive = RpcClient.layerProtocolHttp({ + url: import.meta.env.VITE_RPC_URL ?? "/rpc" +}).pipe( + Layer.provide(RpcSerialization.layerNdjson), + Layer.provide(HttpClient.layer) +) + +// ManagedRuntime that provides the Protocol layer +const runtime = ManagedRuntime.make(RpcClientLive) + +// Create a reusable client Effect +const makeClient = RpcClient.make(UsersRpc) + +// Helper: run an RPC call as a Promise +export function rpc( + fn: (client: RpcClient.RpcClient) => Effect.Effect +): Promise { + return runtime.runPromise( + Effect.gen(function* () { + const client = yield* makeClient + return yield* fn(client) + }) + ) +} +``` + +### 3. Create TanStack Query hooks + +**`src/rpc/hooks.ts`** — Typed hooks wrapping RPC calls in TanStack Query. + +```typescript +import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query" +import { rpc } from "./client" + +// Query keys +export const userKeys = { + all: ["users"] as const, + list: () => [...userKeys.all, "list"] as const, + detail: (id: string) => [...userKeys.all, "detail", id] as const, +} + +// Queries +export function useUsers() { + return useQuery({ + queryKey: userKeys.list(), + queryFn: () => rpc((client) => client.listUsers()), + }) +} + +export function useUser(id: string) { + return useQuery({ + queryKey: userKeys.detail(id), + queryFn: () => rpc((client) => client.getUser({ id })), + enabled: !!id, + }) +} + +// Mutations +export function useCreateUser() { + const queryClient = useQueryClient() + + return useMutation({ + mutationFn: (data: { email: string; name: string }) => + rpc((client) => client.createUser(data)), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: userKeys.all }) + }, + }) +} +``` + +### 4. Use in route components + +```typescript +import { createFileRoute } from "@tanstack/react-router" +import { useUsers, useCreateUser } from "@/rpc/hooks" + +export const Route = createFileRoute("/users")({ + component: UsersPage, +}) + +function UsersPage() { + const { data, isLoading, error } = useUsers() + const createUser = useCreateUser() + + if (isLoading) return
Loading...
+ if (error) return
Error: {error.message}
+ + return ( +
+

Users ({data?.total})

+
    + {data?.users.map((user) => ( +
  • {user.name} ({user.email})
  • + ))} +
+ +
+ ) +} +``` + +### 5. Route loader prefetching (optional) + +Since `queryClient` is in the router context, routes can prefetch data: + +```typescript +export const Route = createFileRoute("/users")({ + loader: ({ context: { queryClient } }) => + queryClient.ensureQueryData({ + queryKey: userKeys.list(), + queryFn: () => rpc((client) => client.listUsers()), + }), + component: UsersPage, +}) +``` + +--- + +## File Structure + +``` +apps/react-app/src/ + rpc/ + client.ts # Effect RPC client setup, ManagedRuntime, rpc() helper + hooks.ts # TanStack Query hooks (useUsers, useUser, useCreateUser) + keys.ts # Query key factories + index.ts # Re-exports + routes/ + users.tsx # Users list page + users.$userId.tsx # User detail page + ... +``` + +--- + +## Configuration + +### Environment variable + +The RPC URL defaults to `/rpc` (same-origin, handled by the worker). For local dev pointing at the separate RPC worker: + +```env +VITE_RPC_URL=http://localhost:8787/rpc +``` + +### CORS + +If `react-app` and `effect-worker-rpc` are on different origins in production, the RPC worker needs CORS headers. For same-origin deployment (both behind one Cloudflare Worker or using Service Bindings), this isn't needed. + +For local dev with separate ports, add CORS to the RPC worker's fetch handler: + +```typescript +// In effect-worker-rpc/src/index.ts +if (request.method === "OPTIONS") { + return new Response(null, { + headers: { + "Access-Control-Allow-Origin": "*", + "Access-Control-Allow-Methods": "POST, OPTIONS", + "Access-Control-Allow-Headers": "Content-Type", + } + }) +} +// ... existing handler, plus Access-Control-Allow-Origin on responses +``` + +--- + +## Error Handling + +Effect RPC errors are typed via the contract schemas. When an RPC call fails: + +1. **RPC domain errors** (e.g., `UserNotFound`, `DuplicateEmail`) — these are the `E` in `Effect`. They'll reject the Promise and appear in TanStack Query's `error` field. + +2. **Transport errors** (`RpcClientError`) — HTTP failures, network issues, ndjson parse errors. Also surface via TanStack Query's `error`. + +For better UX, the `rpc()` helper can be extended to normalize errors: + +```typescript +export function rpc( + fn: (client: RpcClient.RpcClient<...>) => Effect.Effect +): Promise
{ + return runtime.runPromise( + Effect.gen(function* () { + const client = yield* makeClient + return yield* fn(client) + }) + ) + // Errors propagate naturally — TanStack Query catches rejected promises +} +``` + +Components can then pattern-match on error `_tag`: + +```typescript +if (error && "_tag" in error) { + switch (error._tag) { + case "UserNotFound": return
User not found
+ case "DuplicateEmail": return
Email already taken
+ } +} +``` + +--- + +## Bundle Size Considerations + +Effect v4 is tree-shakeable. The browser bundle will include: +- `effect` core (Schema, Effect, Layer, ManagedRuntime) +- `effect/unstable/rpc` (RpcClient, RpcSerialization) +- `effect/unstable/http` (HttpClient — uses native fetch) + +The server-only modules (`PgDrizzle`, `@repo/db`, `@repo/cloudflare`) are NOT imported by the client. The client only imports `@repo/contracts` which contains schema definitions. + +Estimated addition: ~40-60KB gzipped for Effect core + RPC client + HTTP client. This is reasonable given the type safety and protocol correctness it provides. + +--- + +## Summary + +| Concern | Solution | +|---------|----------| +| Transport | Effect `RpcClient.layerProtocolHttp` (fetch-based) | +| Serialization | ndjson (matches server) | +| Type safety | Shared `UsersRpc` group from `@repo/contracts` | +| Schema validation | Automatic via Effect RPC client | +| Data fetching state | TanStack Query (`useQuery`, `useMutation`) | +| Caching | TanStack Query (hierarchical query keys) | +| Prefetching | Route loaders via `queryClient.ensureQueryData` | +| Runtime | `ManagedRuntime` singleton in browser | +| Error handling | Typed errors flow through to TanStack Query's `error` | diff --git a/package.json b/package.json index 9d9a319..007a3b2 100644 --- a/package.json +++ b/package.json @@ -7,17 +7,16 @@ "apps/*" ], "scripts": { - "build": "pnpm --filter '@repo/domain' run build && pnpm --filter '@repo/db' --filter '@repo/cloudflare' run build && pnpm --filter '@repo/contracts' run build", + "build": "pnpm --filter '@repo/domain' run build && pnpm --filter '@repo/db' run build && pnpm --filter '@repo/contracts' run build", "check": "pnpm -r run check", "clean": "pnpm --filter './packages/*' run clean", "test": "vitest", "coverage": "vitest --coverage" }, "devDependencies": { - "@effect/language-service": "latest", - "@effect/vitest": "latest", + "@effect/vitest": "catalog:", "@types/node": "^22.8.5", - "effect": "^3.10.7", + "effect": "catalog:", "tsx": "^4.19.2", "typescript": "^5.6.3", "vitest": "^3.2.0" diff --git a/packages/cloudflare/LICENSE b/packages/cloudflare/LICENSE deleted file mode 100644 index e69de29..0000000 diff --git a/packages/cloudflare/README.md b/packages/cloudflare/README.md deleted file mode 100644 index e69de29..0000000 diff --git a/packages/cloudflare/package.json b/packages/cloudflare/package.json deleted file mode 100644 index 9c301e3..0000000 --- a/packages/cloudflare/package.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "name": "@repo/cloudflare", - "version": "0.0.0", - "type": "module", - "license": "MIT", - "description": "Cloudflare Worker infrastructure for Effect", - "main": "./dist/index.js", - "types": "./dist/index.d.ts", - "exports": { - ".": { - "types": "./dist/index.d.ts", - "import": "./dist/index.js" - }, - "./*": { - "types": "./dist/*.d.ts", - "import": "./dist/*.js" - } - }, - "scripts": { - "build": "tsc -p tsconfig.build.json", - "check": "tsc --noEmit", - "test": "vitest", - "clean": "rm -rf dist" - }, - "dependencies": { - "@effect/experimental": "latest", - "@effect/platform": "latest", - "@effect/sql": "latest", - "@effect/sql-drizzle": "latest", - "@effect/sql-pg": "latest", - "drizzle-orm": "^0.45.0", - "effect": "latest" - } -} diff --git a/packages/cloudflare/src/database.ts b/packages/cloudflare/src/database.ts deleted file mode 100644 index 2e97bde..0000000 --- a/packages/cloudflare/src/database.ts +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Database Connection Factory - * - * Provides utilities for creating request-scoped database connections. - * Uses @effect/sql-drizzle's PgDrizzle directly for type-safe database access. - * - * @module - */ -import { Effect, Redacted } from "effect" -import { PgClient } from "@effect/sql-pg" -import { PgDrizzle, make as makePgDrizzle } from "@effect/sql-drizzle/Pg" -import * as Reactivity from "@effect/experimental/Reactivity" -import * as SqlClient from "@effect/sql/SqlClient" - - - -/** - * Creates a scoped PgDrizzle instance for request-scoped database access. - * - * Used by middleware to provide PgDrizzle to handlers. - * The connection is automatically closed when the scope ends. - * - * @param connectionString - PostgreSQL connection URL - * - * @example - * ```typescript - * // In middleware: - * return yield* makeDrizzle(connectionString) - * - * // In handlers: - * const drizzle = yield* PgDrizzle - * const users = yield* drizzle.select().from(usersTable) - * ``` - */ -export const makeDrizzle = (connectionString: string) => - Effect.gen(function* () { - const pgClient = yield* PgClient.make({ - url: Redacted.make(connectionString) - }).pipe(Effect.provide(Reactivity.layer)) - - return yield* makePgDrizzle({ - casing: "snake_case" - }).pipe(Effect.provideService(SqlClient.SqlClient, pgClient)) - }) - -// Re-export the PgDrizzle tag for convenience -export { PgDrizzle } diff --git a/packages/cloudflare/src/errors.ts b/packages/cloudflare/src/errors.ts deleted file mode 100644 index 4247850..0000000 --- a/packages/cloudflare/src/errors.ts +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Error Types - * - * Error classes for Cloudflare Worker services. - * These are shared between HTTP and RPC middleware. - * - * @module - */ -import { HttpApiSchema } from "@effect/platform" -import { Schema as S } from "effect" - -/** - * Error when Cloudflare bindings are not available. - * - * HTTP Status: 500 Internal Server Error - */ -export class CloudflareBindingsError extends S.TaggedError()( - "CloudflareBindingsError", - { message: S.String }, - HttpApiSchema.annotations({ status: 500 }) -) {} - -/** - * Error when database connection fails. - * - * HTTP Status: 503 Service Unavailable - */ -export class DatabaseConnectionError extends S.TaggedError()( - "DatabaseConnectionError", - { message: S.String }, - HttpApiSchema.annotations({ status: 503 }) -) {} diff --git a/packages/cloudflare/src/fiber-ref.ts b/packages/cloudflare/src/fiber-ref.ts deleted file mode 100644 index b82d8e0..0000000 --- a/packages/cloudflare/src/fiber-ref.ts +++ /dev/null @@ -1,79 +0,0 @@ -/** - * FiberRef Bridge - * - * Provides request-scoped access to Cloudflare's `env` and `ExecutionContext` - * bindings through Effect's FiberRef system. - * - * @module - */ -import { Effect, FiberRef } from "effect" - -/** - * ExecutionContext interface for Cloudflare Workers. - */ -export interface WorkerExecutionContext { - waitUntil(promise: Promise): void - passThroughOnException(): void -} - -/** - * FiberRef holding the current request's Cloudflare environment bindings. - * Type is `unknown` to allow apps to specify their own Env type. - */ -export const currentEnv = FiberRef.unsafeMake(null) - -/** - * FiberRef holding the current request's ExecutionContext. - */ -export const currentCtx = FiberRef.unsafeMake(null) - -/** - * Set Cloudflare bindings for the scope of an effect. - * - * Call this at the request boundary in your worker's fetch handler: - * - * ```typescript - * const effect = handleRequest(request).pipe( - * withCloudflareBindings(env, ctx), - * ) - * return runtime.runPromise(effect) - * ``` - * - * @param env - Cloudflare environment bindings - * @param ctx - Cloudflare ExecutionContext - */ -export const withCloudflareBindings = (env: Env, ctx: WorkerExecutionContext) => - (effect: Effect.Effect) => - effect.pipe( - Effect.locally(currentEnv, env), - Effect.locally(currentCtx, ctx) - ) - -/** - * Schedule a background task that runs after the response is sent. - * - * Uses ctx.waitUntil() to keep the Worker alive while the effect runs. - * Errors are logged but don't affect the response. - * - * ```typescript - * yield* waitUntil( - * Effect.log("Background task running...") - * ) - * ``` - */ -export const waitUntil = ( - effect: Effect.Effect -): Effect.Effect => - Effect.gen(function* () { - const ctx = yield* FiberRef.get(currentCtx) - if (ctx) { - ctx.waitUntil( - Effect.runPromise( - effect.pipe( - Effect.tapErrorCause(Effect.logError), - Effect.catchAll(() => Effect.void) - ) - ) - ) - } - }) diff --git a/packages/cloudflare/src/index.ts b/packages/cloudflare/src/index.ts deleted file mode 100644 index b255308..0000000 --- a/packages/cloudflare/src/index.ts +++ /dev/null @@ -1,36 +0,0 @@ -/** - * @repo/cloudflare - * - * Cloudflare Worker infrastructure for Effect. - * - * This package provides shared infrastructure for building Cloudflare Workers - * with Effect, including: - * - * - FiberRef bridge for request-scoped bindings - * - Service tag for CloudflareBindings - * - Error types for middleware - * - PgDrizzle connection factory - * - * @module - */ - -// FiberRef bridge -export { - currentEnv, - currentCtx, - withCloudflareBindings, - waitUntil, - type WorkerExecutionContext -} from "./fiber-ref" - -// Service tags -export { CloudflareBindings } from "./services" - -// Error types -export { CloudflareBindingsError, DatabaseConnectionError } from "./errors" - -// Database utilities -export { - makeDrizzle, - PgDrizzle -} from "./database" diff --git a/packages/cloudflare/src/services.ts b/packages/cloudflare/src/services.ts deleted file mode 100644 index e13a232..0000000 --- a/packages/cloudflare/src/services.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Service Tags - * - * Context tags for Cloudflare Worker services. - * These are shared between HTTP and RPC middleware. - * - * @module - */ -import { Context } from "effect" -import type { WorkerExecutionContext } from "./fiber-ref" - -/** - * CloudflareBindings service provides access to Cloudflare's env and ctx. - * - * Apps should cast the `env` to their specific Env type when accessing: - * - * ```typescript - * const { env, ctx } = yield* CloudflareBindings - * const myEnv = env as MyEnvType - * ``` - */ -export class CloudflareBindings extends Context.Tag( - "@repo/cloudflare/CloudflareBindings" -)< - CloudflareBindings, - { readonly env: unknown; readonly ctx: WorkerExecutionContext } ->() {} diff --git a/packages/cloudflare/tsconfig.build.json b/packages/cloudflare/tsconfig.build.json deleted file mode 100644 index a74fa6c..0000000 --- a/packages/cloudflare/tsconfig.build.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "compilerOptions": { - "strict": true, - "exactOptionalPropertyTypes": true, - "moduleDetection": "force", - "resolveJsonModule": true, - "esModuleInterop": true, - "skipLibCheck": true, - "moduleResolution": "Bundler", - "lib": ["ES2022", "DOM", "DOM.Iterable"], - "isolatedModules": true, - "forceConsistentCasingInFileNames": true, - "target": "ES2022", - "module": "ESNext", - "types": ["node"], - "rootDir": "src", - "outDir": "dist", - "declaration": true, - "declarationMap": true, - "sourceMap": true - }, - "include": ["src"] -} diff --git a/packages/cloudflare/tsconfig.json b/packages/cloudflare/tsconfig.json deleted file mode 100644 index 334e5ba..0000000 --- a/packages/cloudflare/tsconfig.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "compilerOptions": { - "types": ["node"], - "noEmit": true - }, - "include": ["src", "test"] -} diff --git a/packages/cloudflare/vitest.config.ts b/packages/cloudflare/vitest.config.ts deleted file mode 100644 index 1f3cd91..0000000 --- a/packages/cloudflare/vitest.config.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { mergeConfig, defineProject } from "vitest/config" -import configShared from "../../vitest.shared" - -export default mergeConfig( - configShared, - defineProject({ - test: { - include: ["test/**/*.test.ts"] - } - }) -) diff --git a/packages/contracts/package.json b/packages/contracts/package.json index a162bd7..ebb6016 100644 --- a/packages/contracts/package.json +++ b/packages/contracts/package.json @@ -31,10 +31,8 @@ "clean": "rm -rf dist" }, "dependencies": { - "@effect/platform": "latest", - "@effect/rpc": "latest", - "@repo/cloudflare": "workspace:^", + "@repo/db": "workspace:^", "@repo/domain": "workspace:^", - "effect": "latest" + "effect": "catalog:" } } diff --git a/packages/contracts/src/http/api.ts b/packages/contracts/src/http/api.ts index b07baac..98703bf 100644 --- a/packages/contracts/src/http/api.ts +++ b/packages/contracts/src/http/api.ts @@ -5,7 +5,7 @@ * * @module */ -import { HttpApi } from "@effect/platform" +import { HttpApi } from "effect/unstable/httpapi" import { HealthGroup, UsersGroup } from "./groups" import { CloudflareBindingsMiddleware } from "./middleware" diff --git a/packages/contracts/src/http/groups/health.ts b/packages/contracts/src/http/groups/health.ts index ce6d17e..7e27e69 100644 --- a/packages/contracts/src/http/groups/health.ts +++ b/packages/contracts/src/http/groups/health.ts @@ -5,7 +5,7 @@ * * @module */ -import { HttpApiEndpoint, HttpApiGroup } from "@effect/platform" +import { HttpApiEndpoint, HttpApiGroup } from "effect/unstable/httpapi" import { Schema as S } from "effect" /** @@ -21,5 +21,5 @@ export type HealthResponse = typeof HealthResponseSchema.Type * Health endpoint group definition. */ export const HealthGroup = HttpApiGroup.make("health") - .add(HttpApiEndpoint.get("check", "/").addSuccess(HealthResponseSchema)) + .add(HttpApiEndpoint.get("check", "/", { success: HealthResponseSchema })) .prefix("/health") diff --git a/packages/contracts/src/http/groups/users.ts b/packages/contracts/src/http/groups/users.ts index b2ef065..b4ae063 100644 --- a/packages/contracts/src/http/groups/users.ts +++ b/packages/contracts/src/http/groups/users.ts @@ -6,7 +6,7 @@ * * @module */ -import { HttpApiEndpoint, HttpApiGroup } from "@effect/platform" +import { HttpApiEndpoint, HttpApiGroup } from "effect/unstable/httpapi" import { Schema as S } from "effect" import { UserSchema, @@ -31,18 +31,20 @@ export type UsersList = typeof UsersListSchema.Type * DatabaseMiddleware provides request-scoped database connections. */ export const UsersGroup = HttpApiGroup.make("users") - .add(HttpApiEndpoint.get("list", "/").addSuccess(UsersListSchema)) + .add(HttpApiEndpoint.get("list", "/", { success: UsersListSchema })) .add( - HttpApiEndpoint.get("get", "/:id") - .setPath(UserIdPathSchema) - .addSuccess(UserSchema) - .addError(UserNotFoundError) + HttpApiEndpoint.get("get", "/:id", { + params: { id: UserIdPathSchema.fields.id }, + success: UserSchema, + error: UserNotFoundError + }) ) .add( - HttpApiEndpoint.post("create", "/") - .setPayload(CreateUserSchema) - .addSuccess(UserSchema) - .addError(UserCreationError) + HttpApiEndpoint.post("create", "/", { + payload: CreateUserSchema, + success: UserSchema, + error: UserCreationError + }) ) .middleware(DatabaseMiddleware) .prefix("/users") diff --git a/packages/contracts/src/http/middleware/cloudflare.ts b/packages/contracts/src/http/middleware/cloudflare.ts index e98c1d1..84594c2 100644 --- a/packages/contracts/src/http/middleware/cloudflare.ts +++ b/packages/contracts/src/http/middleware/cloudflare.ts @@ -6,11 +6,9 @@ * * @module */ -import { HttpApiMiddleware } from "@effect/platform" -import { - CloudflareBindings, - CloudflareBindingsError -} from "@repo/cloudflare" +import { HttpApiMiddleware } from "effect/unstable/httpapi" +import { CloudflareBindingsError } from "@repo/domain" +import { CloudflareBindings } from "../../services" /** * Middleware that provides CloudflareBindings to HTTP handlers. @@ -26,13 +24,12 @@ import { * * The implementation must be provided by the app via Layer. */ -export class CloudflareBindingsMiddleware extends HttpApiMiddleware.Tag()( - "@repo/api/CloudflareBindingsMiddleware", - { - failure: CloudflareBindingsError, - provides: CloudflareBindings - } -) {} +export class CloudflareBindingsMiddleware extends HttpApiMiddleware.Service< + CloudflareBindingsMiddleware, + { provides: CloudflareBindings } +>()("@repo/api/CloudflareBindingsMiddleware", { + error: CloudflareBindingsError +}) {} // Re-export for convenience -export { CloudflareBindings, CloudflareBindingsError } +export { CloudflareBindingsError } diff --git a/packages/contracts/src/http/middleware/database.ts b/packages/contracts/src/http/middleware/database.ts index 3e1d9a9..ee768bd 100644 --- a/packages/contracts/src/http/middleware/database.ts +++ b/packages/contracts/src/http/middleware/database.ts @@ -6,8 +6,9 @@ * * @module */ -import { HttpApiMiddleware } from "@effect/platform" -import { PgDrizzle, DatabaseConnectionError } from "@repo/cloudflare" +import { HttpApiMiddleware } from "effect/unstable/httpapi" +import { PgDrizzle } from "@repo/db/pg-drizzle/tag" +import { DatabaseConnectionError } from "@repo/domain" /** * Middleware that provides PgDrizzle to HTTP handlers. @@ -30,13 +31,12 @@ import { PgDrizzle, DatabaseConnectionError } from "@repo/cloudflare" * const users = yield* drizzle.select().from(usersTable) * ``` */ -export class DatabaseMiddleware extends HttpApiMiddleware.Tag()( - "@repo/api/DatabaseMiddleware", - { - failure: DatabaseConnectionError, - provides: PgDrizzle - } -) {} +export class DatabaseMiddleware extends HttpApiMiddleware.Service< + DatabaseMiddleware, + { provides: PgDrizzle } +>()("@repo/api/DatabaseMiddleware", { + error: DatabaseConnectionError +}) {} // Re-export for convenience export { DatabaseConnectionError } diff --git a/packages/contracts/src/http/middleware/index.ts b/packages/contracts/src/http/middleware/index.ts index a6561a7..316e7b9 100644 --- a/packages/contracts/src/http/middleware/index.ts +++ b/packages/contracts/src/http/middleware/index.ts @@ -7,7 +7,6 @@ * @module */ export { - CloudflareBindings, CloudflareBindingsError, CloudflareBindingsMiddleware } from "./cloudflare" diff --git a/packages/contracts/src/index.ts b/packages/contracts/src/index.ts index 4dd8299..05c3466 100644 --- a/packages/contracts/src/index.ts +++ b/packages/contracts/src/index.ts @@ -4,11 +4,12 @@ * API contract definitions for Effect Worker. * * This package provides shared API contracts for both HTTP and RPC: - * - HTTP API definitions using @effect/platform - * - RPC procedure definitions using @effect/rpc + * - HTTP API definitions using effect/unstable/httpapi + * - RPC procedure definitions using effect/unstable/rpc * - Middleware tags for both protocols * * @module */ export * from "./http" export * from "./rpc" +export * from "./services" diff --git a/packages/contracts/src/rpc/middleware/cloudflare.ts b/packages/contracts/src/rpc/middleware/cloudflare.ts index f92a330..e8dbfdf 100644 --- a/packages/contracts/src/rpc/middleware/cloudflare.ts +++ b/packages/contracts/src/rpc/middleware/cloudflare.ts @@ -2,15 +2,13 @@ * Cloudflare Bindings RPC Middleware Tag * * RpcMiddleware tag that provides CloudflareBindings to RPC handlers. - * The implementation is provided by the app using FiberRefs. + * The implementation is provided by the app using ServiceMap.Reference. * * @module */ -import { RpcMiddleware } from "@effect/rpc" -import { - CloudflareBindings, - CloudflareBindingsError -} from "@repo/cloudflare" +import { RpcMiddleware } from "effect/unstable/rpc" +import { CloudflareBindings } from "../../services" +import { CloudflareBindingsError } from "@repo/domain" /** * Middleware that provides CloudflareBindings to RPC handlers. @@ -24,10 +22,10 @@ import { * * Implementation is provided by the app layer. */ -export class RpcCloudflareMiddleware extends RpcMiddleware.Tag()( - "@repo/rpc/RpcCloudflareMiddleware", - { - failure: CloudflareBindingsError, - provides: CloudflareBindings - } -) {} +export class RpcCloudflareMiddleware extends RpcMiddleware.Service< + RpcCloudflareMiddleware, + { provides: CloudflareBindings } +>()("@repo/rpc/RpcCloudflareMiddleware", { + error: CloudflareBindingsError, + requiredForClient: false +}) {} diff --git a/packages/contracts/src/rpc/middleware/database.ts b/packages/contracts/src/rpc/middleware/database.ts index da90f9b..3c6f7b5 100644 --- a/packages/contracts/src/rpc/middleware/database.ts +++ b/packages/contracts/src/rpc/middleware/database.ts @@ -2,12 +2,13 @@ * Database RPC Middleware Tag * * RpcMiddleware tag that provides PgDrizzle to RPC handlers. - * The implementation is provided by the app using FiberRefs. + * The implementation is provided by the app using ServiceMap.Reference. * * @module */ -import { RpcMiddleware } from "@effect/rpc" -import { PgDrizzle, DatabaseConnectionError } from "@repo/cloudflare" +import { RpcMiddleware } from "effect/unstable/rpc" +import { PgDrizzle } from "@repo/db/pg-drizzle/tag" +import { DatabaseConnectionError } from "@repo/domain" /** * Middleware that provides PgDrizzle to RPC handlers. @@ -28,10 +29,10 @@ import { PgDrizzle, DatabaseConnectionError } from "@repo/cloudflare" * const users = yield* drizzle.select().from(usersTable) * ``` */ -export class RpcDatabaseMiddleware extends RpcMiddleware.Tag()( - "@repo/rpc/RpcDatabaseMiddleware", - { - failure: DatabaseConnectionError, - provides: PgDrizzle - } -) {} +export class RpcDatabaseMiddleware extends RpcMiddleware.Service< + RpcDatabaseMiddleware, + { provides: PgDrizzle } +>()("@repo/rpc/RpcDatabaseMiddleware", { + error: DatabaseConnectionError, + requiredForClient: false +}) {} diff --git a/packages/contracts/src/rpc/procedures/users.ts b/packages/contracts/src/rpc/procedures/users.ts index cfee8aa..d48aa77 100644 --- a/packages/contracts/src/rpc/procedures/users.ts +++ b/packages/contracts/src/rpc/procedures/users.ts @@ -6,7 +6,7 @@ * * @module */ -import { Rpc, RpcGroup } from "@effect/rpc" +import { Rpc, RpcGroup } from "effect/unstable/rpc" import { Schema as S } from "effect" import { RpcDatabaseMiddleware } from "../middleware" @@ -90,7 +90,7 @@ export const createUser = Rpc.make("createUser", { name: S.String }), success: UserRpcSchema, - error: S.Union(DuplicateEmailErrorSchema, ValidationErrorSchema) + error: S.Union([DuplicateEmailErrorSchema, ValidationErrorSchema]) }).middleware(RpcDatabaseMiddleware) // ============================================================================ diff --git a/packages/contracts/src/services/cloudflare.ts b/packages/contracts/src/services/cloudflare.ts new file mode 100644 index 0000000..6497486 --- /dev/null +++ b/packages/contracts/src/services/cloudflare.ts @@ -0,0 +1,32 @@ +/** + * Cloudflare Bindings Service Tag + * + * Context tag for Cloudflare Worker bindings. + * Shared between HTTP and RPC middleware. + * + * @module + */ +import { ServiceMap } from "effect" + +/** + * ExecutionContext interface for Cloudflare Workers. + */ +export interface WorkerExecutionContext { + waitUntil(promise: Promise): void + passThroughOnException(): void +} + +/** + * CloudflareBindings service provides access to Cloudflare's env and ctx. + * + * Apps should cast the `env` to their specific Env type when accessing: + * + * ```typescript + * const { env, ctx } = yield* CloudflareBindings + * const myEnv = env as MyEnvType + * ``` + */ +export class CloudflareBindings extends ServiceMap.Service< + CloudflareBindings, + { readonly env: unknown; readonly ctx: WorkerExecutionContext } +>()("@repo/cloudflare/CloudflareBindings") {} diff --git a/packages/contracts/src/services/index.ts b/packages/contracts/src/services/index.ts new file mode 100644 index 0000000..9e9a304 --- /dev/null +++ b/packages/contracts/src/services/index.ts @@ -0,0 +1,8 @@ +/** + * Services + * + * Shared service tags used by middleware and app implementations. + * + * @module + */ +export { CloudflareBindings, type WorkerExecutionContext } from "./cloudflare" diff --git a/packages/db/package.json b/packages/db/package.json index 6919807..6f46bad 100644 --- a/packages/db/package.json +++ b/packages/db/package.json @@ -28,8 +28,8 @@ }, "dependencies": { "drizzle-orm": "^0.45.0", - "effect": "latest", - "@effect/sql-drizzle": "latest", + "effect": "catalog:", + "@effect/sql-pg": "catalog:", "@repo/domain": "workspace:*" }, "devDependencies": { diff --git a/packages/db/src/index.ts b/packages/db/src/index.ts index 39a7f89..8b52399 100644 --- a/packages/db/src/index.ts +++ b/packages/db/src/index.ts @@ -14,3 +14,13 @@ export { users, type User as DbUser, type NewUser } from "./schema" // Query exports export * from "./queries" + +// PgDrizzle service and layers +export { + PgDrizzle, + PgDrizzleLive, + PgDrizzleLiveWithConfig, + makePgDrizzleLayer, + makeDrizzle, + makeRemoteCallback +} from "./pg-drizzle/index.js" diff --git a/packages/db/src/pg-drizzle/index.ts b/packages/db/src/pg-drizzle/index.ts new file mode 100644 index 0000000..8e7ffdc --- /dev/null +++ b/packages/db/src/pg-drizzle/index.ts @@ -0,0 +1,107 @@ +/** + * PgDrizzle Service + * + * Custom Effect v4 integration for Drizzle ORM with PostgreSQL. + * Replaces the removed @effect/sql-drizzle package. + * + * @module + */ +import "./patch.js" + +import { Effect, Layer, Redacted, ServiceMap } from "effect" +import { SqlClient } from "effect/unstable/sql" +import { PgClient } from "@effect/sql-pg" +import { drizzle, type RemoteCallback } from "drizzle-orm/pg-proxy" +import type { DrizzleConfig } from "drizzle-orm" + +export { PgDrizzle } from "./tag.js" +import { PgDrizzle } from "./tag.js" + +/** + * Creates the RemoteCallback that bridges Drizzle's pg-proxy to Effect's SqlClient. + */ +export const makeRemoteCallback: Effect.Effect = + Effect.gen(function* () { + const client = yield* SqlClient.SqlClient + return ((sql: string, params: any[], method: "all" | "execute") => { + const statement = client.unsafe(sql, params) + const baseEffect = + method === "execute" + ? Effect.map(statement.raw, (result) => ({ rows: [result] })) + : Effect.map(statement.values, (result) => ({ + rows: result as any[] + })) + return Effect.runPromise(baseEffect) as Promise<{ rows: any[] }> + }) satisfies RemoteCallback + }) + +/** + * Layer that provides PgDrizzle from an existing SqlClient in context. + */ +export const PgDrizzleLive: Layer.Layer = + Layer.effect( + PgDrizzle, + Effect.gen(function* () { + const callback = yield* makeRemoteCallback + return drizzle(callback, { casing: "snake_case" }) + }) + ) + +/** + * Layer that provides PgDrizzle with custom Drizzle config. + */ +export const PgDrizzleLiveWithConfig = ( + config: DrizzleConfig +): Layer.Layer => + Layer.effect( + PgDrizzle, + Effect.gen(function* () { + const callback = yield* makeRemoteCallback + return drizzle(callback, config) + }) + ) + +/** + * Creates a complete PgDrizzle layer from a connection string. + * Includes PgClient setup with proper lifecycle management. + */ +export const makePgDrizzleLayer = ( + connectionString: string, + config?: DrizzleConfig +): Layer.Layer => + (config + ? PgDrizzleLiveWithConfig(config) + : PgDrizzleLiveWithConfig({ casing: "snake_case" }) + ).pipe( + Layer.provide(PgClient.layer({ url: Redacted.make(connectionString) })) + ) as Layer.Layer + +/** + * Creates a PgRemoteDatabase instance from a connection string. + * + * The underlying SqlClient is tied to the current Scope — it stays alive + * for the duration of the enclosing scope (e.g. the request scope in + * middleware). + * + * Requires Scope in context to manage the PgClient lifecycle. + */ +export const makeDrizzle = ( + connectionString: string +) => + Effect.gen(function* () { + const services = yield* Layer.build( + PgClient.layer({ url: Redacted.make(connectionString) }) + ) + const client = ServiceMap.get(services, SqlClient.SqlClient) + const callback: RemoteCallback = (sql, params, method) => { + const statement = client.unsafe(sql, params) + const baseEffect = + method === "execute" + ? Effect.map(statement.raw, (result) => ({ rows: [result] })) + : Effect.map(statement.values, (result) => ({ + rows: result as any[] + })) + return Effect.runPromise(baseEffect) as Promise<{ rows: any[] }> + } + return drizzle(callback, { casing: "snake_case" }) + }) diff --git a/packages/db/src/pg-drizzle/patch.ts b/packages/db/src/pg-drizzle/patch.ts new file mode 100644 index 0000000..ebc8fc9 --- /dev/null +++ b/packages/db/src/pg-drizzle/patch.ts @@ -0,0 +1,53 @@ +/** + * Drizzle ORM Effect Patch + * + * Makes Drizzle query objects yieldable in Effect.gen() generators. + * This replaces the removed @effect/sql-drizzle package for Effect v4. + * + * @module + */ +import { Effect } from "effect" +import { pipeArguments } from "effect/Pipeable" +import { SingleShotGen } from "effect/Utils" +import { SqlError } from "effect/unstable/sql" +import { QueryPromise } from "drizzle-orm" +import { PgSelectBase } from "drizzle-orm/pg-core" + +declare module "drizzle-orm" { + export interface QueryPromise + extends Effect.Effect {} +} + +const proto = QueryPromise.prototype as any + +proto[Symbol.iterator] = function () { + return new SingleShotGen(this.asEffect()) +} + +proto.asEffect = function () { + return Effect.tryPromise({ + try: () => this.execute(), + catch: (cause) => new SqlError.SqlError({ cause }) + }) +} + +proto.pipe = function () { + return pipeArguments(this.asEffect(), arguments) +} + +const selectProto = PgSelectBase.prototype as any + +selectProto[Symbol.iterator] = function () { + return new SingleShotGen(this.asEffect()) +} + +selectProto.asEffect = function () { + return Effect.tryPromise({ + try: () => this.execute(), + catch: (cause) => new SqlError.SqlError({ cause }) + }) +} + +selectProto.pipe = function () { + return pipeArguments(this.asEffect(), arguments) +} diff --git a/packages/db/src/pg-drizzle/tag.ts b/packages/db/src/pg-drizzle/tag.ts new file mode 100644 index 0000000..9857443 --- /dev/null +++ b/packages/db/src/pg-drizzle/tag.ts @@ -0,0 +1,20 @@ +/** + * PgDrizzle Service Tag + * + * Lightweight module containing only the PgDrizzle service tag. + * Import from here (instead of the full pg-drizzle module) when you only need + * the tag for type-level references (e.g. middleware definitions), to avoid + * pulling in Node.js-only dependencies like pg. + * + * @module + */ +import { ServiceMap } from "effect" +import type { PgRemoteDatabase } from "drizzle-orm/pg-proxy" + +/** + * PgDrizzle service tag — provides a Drizzle PgRemoteDatabase instance. + */ +export class PgDrizzle extends ServiceMap.Service< + PgDrizzle, + PgRemoteDatabase +>()("@repo/db/PgDrizzle") {} diff --git a/packages/db/src/queries/users.ts b/packages/db/src/queries/users.ts index 31c2c1a..948d204 100644 --- a/packages/db/src/queries/users.ts +++ b/packages/db/src/queries/users.ts @@ -6,7 +6,7 @@ * @module */ import { DateTime, Effect } from "effect" -import { PgDrizzle } from "@effect/sql-drizzle/Pg" +import { PgDrizzle } from "../pg-drizzle/index.js" import { eq } from "drizzle-orm" import { users } from "../schema" @@ -31,7 +31,7 @@ const toDomainUser = (row: typeof users.$inferSelect): User => ({ id: toUserId(row.id), email: row.email as Email, name: row.name, - createdAt: DateTime.unsafeFromDate(row.createdAt) + createdAt: DateTime.fromDateUnsafe(row.createdAt) }) // ============================================================================ diff --git a/packages/domain/package.json b/packages/domain/package.json index 0ed5c0a..dd678f6 100644 --- a/packages/domain/package.json +++ b/packages/domain/package.json @@ -23,7 +23,6 @@ "clean": "rm -rf dist" }, "dependencies": { - "@effect/platform": "latest", - "effect": "latest" + "effect": "catalog:" } } diff --git a/packages/domain/src/errors/common.ts b/packages/domain/src/errors/common.ts index 6d658f6..9d6a6b0 100644 --- a/packages/domain/src/errors/common.ts +++ b/packages/domain/src/errors/common.ts @@ -5,7 +5,6 @@ * * @module */ -import { HttpApiSchema } from "@effect/platform" import { Schema as S } from "effect" /** @@ -13,13 +12,13 @@ import { Schema as S } from "effect" * * HTTP Status: 404 Not Found */ -export class NotFoundError extends S.TaggedError()( +export class NotFoundError extends S.TaggedErrorClass()( "NotFoundError", { path: S.String, message: S.String }, - HttpApiSchema.annotations({ status: 404 }) + { httpApiStatus: 404 } ) {} /** @@ -27,11 +26,33 @@ export class NotFoundError extends S.TaggedError()( * * HTTP Status: 400 Bad Request */ -export class ValidationError extends S.TaggedError()( +export class ValidationError extends S.TaggedErrorClass()( "ValidationError", { message: S.String, errors: S.Array(S.String) }, - HttpApiSchema.annotations({ status: 400 }) + { httpApiStatus: 400 } +) {} + +/** + * Error when Cloudflare bindings are not available. + * + * HTTP Status: 500 Internal Server Error + */ +export class CloudflareBindingsError extends S.TaggedErrorClass()( + "CloudflareBindingsError", + { message: S.String }, + { httpApiStatus: 500 } +) {} + +/** + * Error when database connection fails. + * + * HTTP Status: 503 Service Unavailable + */ +export class DatabaseConnectionError extends S.TaggedErrorClass()( + "DatabaseConnectionError", + { message: S.String }, + { httpApiStatus: 503 } ) {} diff --git a/packages/domain/src/errors/index.ts b/packages/domain/src/errors/index.ts index c70b736..3b31803 100644 --- a/packages/domain/src/errors/index.ts +++ b/packages/domain/src/errors/index.ts @@ -4,4 +4,4 @@ * @module */ export { UserCreationError, UserNotFoundError } from "./user" -export { NotFoundError, ValidationError } from "./common" +export { NotFoundError, ValidationError, CloudflareBindingsError, DatabaseConnectionError } from "./common" diff --git a/packages/domain/src/errors/user.ts b/packages/domain/src/errors/user.ts index 074602c..306700d 100644 --- a/packages/domain/src/errors/user.ts +++ b/packages/domain/src/errors/user.ts @@ -5,7 +5,6 @@ * * @module */ -import { HttpApiSchema } from "@effect/platform" import { Schema as S } from "effect" import { UserIdSchema } from "../schemas/user" @@ -14,13 +13,13 @@ import { UserIdSchema } from "../schemas/user" * * HTTP Status: 400 Bad Request */ -export class UserCreationError extends S.TaggedError()( +export class UserCreationError extends S.TaggedErrorClass()( "UserCreationError", { email: S.String, name: S.String }, - HttpApiSchema.annotations({ status: 400 }) + { httpApiStatus: 400 } ) {} /** @@ -28,11 +27,11 @@ export class UserCreationError extends S.TaggedError()( * * HTTP Status: 404 Not Found */ -export class UserNotFoundError extends S.TaggedError()( +export class UserNotFoundError extends S.TaggedErrorClass()( "UserNotFoundError", { id: UserIdSchema, message: S.String }, - HttpApiSchema.annotations({ status: 404 }) + { httpApiStatus: 404 } ) {} diff --git a/packages/domain/src/schemas/user.ts b/packages/domain/src/schemas/user.ts index 3a843ad..49ceec1 100644 --- a/packages/domain/src/schemas/user.ts +++ b/packages/domain/src/schemas/user.ts @@ -13,7 +13,7 @@ import { Schema as S } from "effect" * Format: usr_[alphanumeric] */ export const UserIdSchema = S.String.pipe( - S.pattern(/^usr_[a-zA-Z0-9]+$/), + S.check(S.isPattern(/^usr_[a-zA-Z0-9]+$/)), S.brand("UserId") ) export type UserId = typeof UserIdSchema.Type @@ -22,7 +22,7 @@ export type UserId = typeof UserIdSchema.Type * Email with basic format validation. */ export const EmailSchema = S.String.pipe( - S.pattern(/^[^\s@]+@[^\s@]+\.[^\s@]+$/), + S.check(S.isPattern(/^[^\s@]+@[^\s@]+\.[^\s@]+$/)), S.brand("Email") ) export type Email = typeof EmailSchema.Type @@ -43,11 +43,11 @@ export type User = typeof UserSchema.Type */ export const CreateUserSchema = S.Struct({ email: S.String.pipe( - S.pattern(/^[^\s@]+@[^\s@]+\.[^\s@]+$/, { - message: () => "Invalid email format" - }) + S.check(S.isPattern(/^[^\s@]+@[^\s@]+\.[^\s@]+$/, { + message: "Invalid email format" + })) ), - name: S.String.pipe(S.minLength(1, { message: () => "Name is required" })) + name: S.String.pipe(S.check(S.isMinLength(1, { message: "Name is required" }))) }) export type CreateUser = typeof CreateUserSchema.Type diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8ecfb03..7359649 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,22 +4,31 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false +catalogs: + default: + '@effect/sql-pg': + specifier: 4.0.0-beta.20 + version: 4.0.0-beta.20 + '@effect/vitest': + specifier: 4.0.0-beta.20 + version: 4.0.0-beta.20 + effect: + specifier: 4.0.0-beta.20 + version: 4.0.0-beta.20 + importers: .: devDependencies: - '@effect/language-service': - specifier: latest - version: 0.64.0 '@effect/vitest': - specifier: latest - version: 0.27.0(effect@3.19.14)(vitest@3.2.4(@types/node@22.19.3)(jiti@2.6.1)(jsdom@26.1.0)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)) + specifier: 'catalog:' + version: 4.0.0-beta.20(effect@4.0.0-beta.20)(vitest@3.2.4(@types/node@22.19.3)(jiti@2.6.1)(jsdom@26.1.0)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)) '@types/node': specifier: ^22.8.5 version: 22.19.3 effect: - specifier: ^3.10.7 - version: 3.19.14 + specifier: 'catalog:' + version: 4.0.0-beta.20 tsx: specifier: ^4.19.2 version: 4.21.0 @@ -32,24 +41,6 @@ importers: apps/effect-worker-api: dependencies: - '@effect/experimental': - specifier: latest - version: 0.58.0(@effect/platform@0.94.1(effect@3.19.14))(effect@3.19.14) - '@effect/platform': - specifier: latest - version: 0.94.1(effect@3.19.14) - '@effect/sql': - specifier: latest - version: 0.49.0(@effect/experimental@0.58.0(@effect/platform@0.94.1(effect@3.19.14))(effect@3.19.14))(@effect/platform@0.94.1(effect@3.19.14))(effect@3.19.14) - '@effect/sql-drizzle': - specifier: latest - version: 0.48.0(@effect/sql@0.49.0(@effect/experimental@0.58.0(@effect/platform@0.94.1(effect@3.19.14))(effect@3.19.14))(@effect/platform@0.94.1(effect@3.19.14))(effect@3.19.14))(drizzle-orm@0.45.1(@cloudflare/workers-types@4.20260103.0)(pg@8.16.3))(effect@3.19.14) - '@effect/sql-pg': - specifier: latest - version: 0.50.0(@effect/experimental@0.58.0(@effect/platform@0.94.1(effect@3.19.14))(effect@3.19.14))(@effect/platform@0.94.1(effect@3.19.14))(@effect/sql@0.49.0(@effect/experimental@0.58.0(@effect/platform@0.94.1(effect@3.19.14))(effect@3.19.14))(@effect/platform@0.94.1(effect@3.19.14))(effect@3.19.14))(effect@3.19.14) - '@repo/cloudflare': - specifier: workspace:^ - version: link:../../packages/cloudflare '@repo/contracts': specifier: workspace:^ version: link:../../packages/contracts @@ -61,10 +52,10 @@ importers: version: link:../../packages/domain drizzle-orm: specifier: ^0.45.0 - version: 0.45.1(@cloudflare/workers-types@4.20260103.0)(pg@8.16.3) + version: 0.45.1(@cloudflare/workers-types@4.20260103.0)(pg@8.19.0) effect: - specifier: latest - version: 3.19.14 + specifier: 'catalog:' + version: 4.0.0-beta.20 devDependencies: '@cloudflare/workers-types': specifier: ^4.20241127.0 @@ -84,27 +75,6 @@ importers: apps/effect-worker-rpc: dependencies: - '@effect/experimental': - specifier: latest - version: 0.58.0(@effect/platform@0.94.1(effect@3.19.14))(effect@3.19.14) - '@effect/platform': - specifier: latest - version: 0.94.1(effect@3.19.14) - '@effect/rpc': - specifier: latest - version: 0.73.0(@effect/platform@0.94.1(effect@3.19.14))(effect@3.19.14) - '@effect/sql': - specifier: latest - version: 0.49.0(@effect/experimental@0.58.0(@effect/platform@0.94.1(effect@3.19.14))(effect@3.19.14))(@effect/platform@0.94.1(effect@3.19.14))(effect@3.19.14) - '@effect/sql-drizzle': - specifier: latest - version: 0.48.0(@effect/sql@0.49.0(@effect/experimental@0.58.0(@effect/platform@0.94.1(effect@3.19.14))(effect@3.19.14))(@effect/platform@0.94.1(effect@3.19.14))(effect@3.19.14))(drizzle-orm@0.45.1(@cloudflare/workers-types@4.20260103.0)(pg@8.16.3))(effect@3.19.14) - '@effect/sql-pg': - specifier: latest - version: 0.50.0(@effect/experimental@0.58.0(@effect/platform@0.94.1(effect@3.19.14))(effect@3.19.14))(@effect/platform@0.94.1(effect@3.19.14))(@effect/sql@0.49.0(@effect/experimental@0.58.0(@effect/platform@0.94.1(effect@3.19.14))(effect@3.19.14))(@effect/platform@0.94.1(effect@3.19.14))(effect@3.19.14))(effect@3.19.14) - '@repo/cloudflare': - specifier: workspace:^ - version: link:../../packages/cloudflare '@repo/contracts': specifier: workspace:^ version: link:../../packages/contracts @@ -116,10 +86,10 @@ importers: version: link:../../packages/domain drizzle-orm: specifier: ^0.45.0 - version: 0.45.1(@cloudflare/workers-types@4.20260103.0)(pg@8.16.3) + version: 0.45.1(@cloudflare/workers-types@4.20260103.0)(pg@8.19.0) effect: - specifier: latest - version: 3.19.14 + specifier: 'catalog:' + version: 4.0.0-beta.20 devDependencies: '@cloudflare/workers-types': specifier: ^4.20241127.0 @@ -137,6 +107,88 @@ importers: specifier: ^4.55.0 version: 4.56.0(@cloudflare/workers-types@4.20260103.0) + apps/react-app: + dependencies: + '@repo/contracts': + specifier: workspace:* + version: link:../../packages/contracts + '@tailwindcss/vite': + specifier: ^4.1.18 + version: 4.1.18(vite@7.1.2(@types/node@22.19.7)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)) + '@tanstack/react-query': + specifier: ^5.90.18 + version: 5.90.18(react@19.2.3) + '@tanstack/react-query-devtools': + specifier: ^5.91.2 + version: 5.91.2(@tanstack/react-query@5.90.18(react@19.2.3))(react@19.2.3) + '@tanstack/react-router': + specifier: ^1.150.0 + version: 1.150.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@tanstack/react-router-devtools': + specifier: ^1.150.0 + version: 1.150.0(@tanstack/react-router@1.150.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(@tanstack/router-core@1.150.0)(csstype@3.2.3)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + class-variance-authority: + specifier: ^0.7.1 + version: 0.7.1 + clsx: + specifier: ^2.1.1 + version: 2.1.1 + effect: + specifier: 'catalog:' + version: 4.0.0-beta.20 + lucide-react: + specifier: ^0.476.0 + version: 0.476.0(react@19.2.3) + radix-ui: + specifier: ^1.4.3 + version: 1.4.3(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + react: + specifier: ^19.2.3 + version: 19.2.3 + react-dom: + specifier: ^19.2.3 + version: 19.2.3(react@19.2.3) + tailwind-merge: + specifier: ^3.4.0 + version: 3.4.0 + tailwindcss: + specifier: ^4.1.18 + version: 4.1.18 + tw-animate-css: + specifier: ^1.4.0 + version: 1.4.0 + vite-tsconfig-paths: + specifier: ^5.1.4 + version: 5.1.4(typescript@5.9.3)(vite@7.1.2(@types/node@22.19.7)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)) + devDependencies: + '@cloudflare/vite-plugin': + specifier: ^1.21.0 + version: 1.21.0(vite@7.1.2(@types/node@22.19.7)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2))(workerd@1.20260114.0)(wrangler@4.59.2) + '@cloudflare/workers-types': + specifier: ^4.20241127.0 + version: 4.20260103.0 + '@tanstack/router-plugin': + specifier: ^1.150.0 + version: 1.150.0(@tanstack/react-router@1.150.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(vite@7.1.2(@types/node@22.19.7)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)) + '@types/react': + specifier: ^19.2.8 + version: 19.2.8 + '@types/react-dom': + specifier: ^19.2.3 + version: 19.2.3(@types/react@19.2.8) + '@vitejs/plugin-react': + specifier: ^4.7.0 + version: 4.7.0(vite@7.1.2(@types/node@22.19.7)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)) + typescript: + specifier: ^5.9.3 + version: 5.9.3 + vite: + specifier: 7.1.2 + version: 7.1.2(@types/node@22.19.7)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) + wrangler: + specifier: ^4.59.2 + version: 4.59.2(@cloudflare/workers-types@4.20260103.0) + apps/tanstack-start: dependencies: '@radix-ui/react-collapsible': @@ -151,9 +203,6 @@ importers: '@radix-ui/react-slot': specifier: ^1.2.4 version: 1.2.4(@types/react@19.2.8)(react@19.2.3) - '@repo/cloudflare': - specifier: workspace:* - version: link:../../packages/cloudflare '@tailwindcss/vite': specifier: ^4.1.18 version: 4.1.18(vite@7.1.2(@types/node@22.19.7)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)) @@ -182,8 +231,8 @@ importers: specifier: ^2.1.1 version: 2.1.1 effect: - specifier: latest - version: 3.19.14 + specifier: 'catalog:' + version: 4.0.0-beta.20 lucide-react: specifier: ^0.476.0 version: 0.476.0(react@19.2.3) @@ -247,64 +296,34 @@ importers: version: 4.2.4 wrangler: specifier: ^4.59.2 - version: 4.59.2 - - packages/cloudflare: - dependencies: - '@effect/experimental': - specifier: latest - version: 0.58.0(@effect/platform@0.94.1(effect@3.19.14))(effect@3.19.14) - '@effect/platform': - specifier: latest - version: 0.94.1(effect@3.19.14) - '@effect/sql': - specifier: latest - version: 0.49.0(@effect/experimental@0.58.0(@effect/platform@0.94.1(effect@3.19.14))(effect@3.19.14))(@effect/platform@0.94.1(effect@3.19.14))(effect@3.19.14) - '@effect/sql-drizzle': - specifier: latest - version: 0.48.0(@effect/sql@0.49.0(@effect/experimental@0.58.0(@effect/platform@0.94.1(effect@3.19.14))(effect@3.19.14))(@effect/platform@0.94.1(effect@3.19.14))(effect@3.19.14))(drizzle-orm@0.45.1(@cloudflare/workers-types@4.20260103.0)(pg@8.16.3))(effect@3.19.14) - '@effect/sql-pg': - specifier: latest - version: 0.50.0(@effect/experimental@0.58.0(@effect/platform@0.94.1(effect@3.19.14))(effect@3.19.14))(@effect/platform@0.94.1(effect@3.19.14))(@effect/sql@0.49.0(@effect/experimental@0.58.0(@effect/platform@0.94.1(effect@3.19.14))(effect@3.19.14))(@effect/platform@0.94.1(effect@3.19.14))(effect@3.19.14))(effect@3.19.14) - drizzle-orm: - specifier: ^0.45.0 - version: 0.45.1(@cloudflare/workers-types@4.20260103.0)(pg@8.16.3) - effect: - specifier: latest - version: 3.19.14 + version: 4.59.2(@cloudflare/workers-types@4.20260103.0) packages/contracts: dependencies: - '@effect/platform': - specifier: latest - version: 0.94.1(effect@3.19.14) - '@effect/rpc': - specifier: latest - version: 0.73.0(@effect/platform@0.94.1(effect@3.19.14))(effect@3.19.14) - '@repo/cloudflare': + '@repo/db': specifier: workspace:^ - version: link:../cloudflare + version: link:../db '@repo/domain': specifier: workspace:^ version: link:../domain effect: - specifier: latest - version: 3.19.14 + specifier: 'catalog:' + version: 4.0.0-beta.20 packages/db: dependencies: - '@effect/sql-drizzle': - specifier: latest - version: 0.48.0(@effect/sql@0.49.0(@effect/experimental@0.58.0(@effect/platform@0.94.1(effect@3.19.14))(effect@3.19.14))(@effect/platform@0.94.1(effect@3.19.14))(effect@3.19.14))(drizzle-orm@0.45.1(@cloudflare/workers-types@4.20260103.0)(pg@8.16.3))(effect@3.19.14) + '@effect/sql-pg': + specifier: 'catalog:' + version: 4.0.0-beta.20(effect@4.0.0-beta.20) '@repo/domain': specifier: workspace:* version: link:../domain drizzle-orm: specifier: ^0.45.0 - version: 0.45.1(@cloudflare/workers-types@4.20260103.0)(pg@8.16.3) + version: 0.45.1(@cloudflare/workers-types@4.20260103.0)(pg@8.19.0) effect: - specifier: latest - version: 3.19.14 + specifier: 'catalog:' + version: 4.0.0-beta.20 devDependencies: drizzle-kit: specifier: ^0.31.8 @@ -312,12 +331,9 @@ importers: packages/domain: dependencies: - '@effect/platform': - specifier: latest - version: 0.94.1(effect@3.19.14) effect: - specifier: latest - version: 3.19.14 + specifier: 'catalog:' + version: 4.0.0-beta.20 packages: @@ -557,61 +573,16 @@ packages: '@drizzle-team/brocli@0.10.2': resolution: {integrity: sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w==} - '@effect/experimental@0.58.0': - resolution: {integrity: sha512-IEP9sapjF6rFy5TkoqDPc86st/fnqUfjT7Xa3pWJrFGr1hzaMXHo+mWsYOZS9LAOVKnpHuVziDK97EP5qsCHVA==} - peerDependencies: - '@effect/platform': ^0.94.0 - effect: ^3.19.13 - ioredis: ^5 - lmdb: ^3 - peerDependenciesMeta: - ioredis: - optional: true - lmdb: - optional: true - - '@effect/language-service@0.64.0': - resolution: {integrity: sha512-Ch6s0aAcrdaOWKmsXw0ys/MvYzz2DD5wf6qALnuyb8Tq1LO8livCUf2ax2cIWzPuEitRyvmIFb0byxKAYi1kcw==} - hasBin: true - - '@effect/platform@0.94.1': - resolution: {integrity: sha512-SlL8OMTogHmMNnFLnPAHHo3ua1yrB1LNQOVQMiZsqYu9g3216xjr0gn5WoDgCxUyOdZcseegMjWJ7dhm/2vnfg==} - peerDependencies: - effect: ^3.19.14 - - '@effect/rpc@0.73.0': - resolution: {integrity: sha512-iMPf6tTriz8sK0l5x4koFId8Hz5nFptHYg8WqyjHGIIVLTpZxuiSqhmXZG7FnAs5N2n6uCEws4wWGcIgXNUrFg==} - peerDependencies: - '@effect/platform': ^0.94.0 - effect: ^3.19.13 - - '@effect/sql-drizzle@0.48.0': - resolution: {integrity: sha512-igvoBrc3RwHq2IOVBAzT3rzMWTIronHa+aL1SFDZDDGIAY4BEmos+ahCAyPfhyW8UbYljJtL9PWJWHtXgYa9MA==} + '@effect/sql-pg@4.0.0-beta.20': + resolution: {integrity: sha512-likUKYpu92Dbf2RbqHw3zH/egoYiP2cDsNbGHe+p45lZHp9iySM/XNBRiw6O6eJ0zt9KM/qC19DYqIpUPNifkA==} peerDependencies: - '@effect/sql': ^0.49.0 - drizzle-orm: '>=0.43.1 <0.50' - effect: ^3.19.13 + effect: ^4.0.0-beta.20 - '@effect/sql-pg@0.50.0': - resolution: {integrity: sha512-TVpKkTbyBj21TFGiuylzzEGn6cR2jvIjiKI6LskhulSGnLOaM5O78xdf6ky7GTITXmVmZSSKS99C5Hn9cvngyw==} + '@effect/vitest@4.0.0-beta.20': + resolution: {integrity: sha512-xh9kQBrs4yXZN+5nbY969KPs7VkL84tILVdIh45BqHQ93gsxWbWBYSLy3V9fBc7ZCncknLHzWAix2POzU3uLbw==} peerDependencies: - '@effect/experimental': ^0.58.0 - '@effect/platform': ^0.94.0 - '@effect/sql': ^0.49.0 - effect: ^3.19.13 - - '@effect/sql@0.49.0': - resolution: {integrity: sha512-9UEKR+z+MrI/qMAmSvb/RiD9KlgIazjZUCDSpwNgm0lEK9/Q6ExEyfziiYFVCPiptp52cBw8uBHRic8hHnwqXA==} - peerDependencies: - '@effect/experimental': ^0.58.0 - '@effect/platform': ^0.94.0 - effect: ^3.19.13 - - '@effect/vitest@0.27.0': - resolution: {integrity: sha512-8bM7n9xlMUYw9GqPIVgXFwFm2jf27m/R7psI64PGpwU5+26iwyxp9eAXEsfT5S6lqztYfpQQ1Ubp5o6HfNYzJQ==} - peerDependencies: - effect: ^3.19.0 - vitest: ^3.2.0 + effect: ^4.0.0-beta.20 + vitest: ^3.0.0 '@emnapi/runtime@1.8.0': resolution: {integrity: sha512-Z82FDl1ByxqPEPrAYYeTQVlx2FSHPe1qwX465c+96IRS3fTdSYRoJcRxg3g2fEG5I69z1dSEWQlNRRr0/677mg==} @@ -1693,9 +1664,51 @@ packages: '@poppinss/exception@1.2.3': resolution: {integrity: sha512-dCED+QRChTVatE9ibtoaxc+WkdzOSjYTKi/+uacHWIsfodVfpsueo3+DKpgU5Px8qXjgmXkSvhXvSCz3fnP9lw==} + '@radix-ui/number@1.1.1': + resolution: {integrity: sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==} + '@radix-ui/primitive@1.1.3': resolution: {integrity: sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==} + '@radix-ui/react-accessible-icon@1.1.7': + resolution: {integrity: sha512-XM+E4WXl0OqUJFovy6GjmxxFyx9opfCAIUku4dlKRd5YEPqt4kALOkQOp0Of6reHuUkJuiPBEc5k0o4z4lTC8A==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-accordion@1.2.12': + resolution: {integrity: sha512-T4nygeh9YE9dLRPhAHSeOZi7HBXo+0kYIPJXayZfvWOWA0+n3dESrZbjfDPUABkUNym6Hd+f2IR113To8D2GPA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-alert-dialog@1.1.15': + resolution: {integrity: sha512-oTVLkEw5GpdRe29BqJ0LSDFWI3qu0vR1M0mUkOQWDIUnY/QIkLpgDMWuKxP94c2NAC2LGcgVhG1ImF3jkZ5wXw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-arrow@1.1.7': resolution: {integrity: sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==} peerDependencies: @@ -1709,6 +1722,45 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-aspect-ratio@1.1.7': + resolution: {integrity: sha512-Yq6lvO9HQyPwev1onK1daHCHqXVLzPhSVjmsNjCa2Zcxy2f7uJD2itDtxknv6FzAKCwD1qQkeVDmX/cev13n/g==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-avatar@1.1.10': + resolution: {integrity: sha512-V8piFfWapM5OmNCXTzVQY+E1rDa53zY+MQ4Y7356v4fFz6vqCyUtIz2rUD44ZEdwg78/jKmMJHj07+C/Z/rcog==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-checkbox@1.3.3': + resolution: {integrity: sha512-wBbpv+NQftHDdG86Qc0pIyXk5IR3tM8Vd0nWLKDcX8nNn4nXFOFwsKuqw2okA/1D/mpaAkmuyndrPJTYDNZtFw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-collapsible@1.1.12': resolution: {integrity: sha512-Uu+mSh4agx2ib1uIGPP4/CKNULyajb3p92LsVXmH2EHVMTfZWpll88XJ0j4W0z3f8NK1eYl1+Mf/szHPmcHzyA==} peerDependencies: @@ -1744,6 +1796,19 @@ packages: '@types/react': optional: true + '@radix-ui/react-context-menu@2.2.16': + resolution: {integrity: sha512-O8morBEW+HsVG28gYDZPTrT9UUovQUlJue5YO836tiTJhuIWBm/zQHc7j388sHWtdH/xUZurK9olD2+pcqx5ww==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-context@1.1.2': resolution: {integrity: sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==} peerDependencies: @@ -1823,17 +1888,21 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-id@1.1.1': - resolution: {integrity: sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==} + '@radix-ui/react-form@0.1.8': + resolution: {integrity: sha512-QM70k4Zwjttifr5a4sZFts9fn8FzHYvQ5PiB19O2HsYibaHSVt9fH9rzB0XZo/YcM+b7t/p7lYCT/F5eOeF5yQ==} peerDependencies: '@types/react': '*' + '@types/react-dom': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': optional: true + '@types/react-dom': + optional: true - '@radix-ui/react-menu@2.1.16': - resolution: {integrity: sha512-72F2T+PLlphrqLcAotYPp0uJMr5SjP5SL01wfEspJbru5Zs5vQaSHb4VB3ZMJPimgHHCHG7gMOeOB9H3Hdmtxg==} + '@radix-ui/react-hover-card@1.1.15': + resolution: {integrity: sha512-qgTkjNT1CfKMoP0rcasmlH2r1DAiYicWsDsufxl940sT2wHNEWWv6FMWIQXWhVdmC1d/HYfbhQx60KYyAtKxjg==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -1845,8 +1914,17 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-popper@1.2.8': - resolution: {integrity: sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==} + '@radix-ui/react-id@1.1.1': + resolution: {integrity: sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-label@2.1.7': + resolution: {integrity: sha512-YT1GqPSL8kJn20djelMX7/cTRp/Y9w5IZHvfxQTVHrOqa2yMl7i/UfMqKRU5V7mEyKTrUVgJXhNQPVCG8PBLoQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -1858,8 +1936,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-portal@1.1.9': - resolution: {integrity: sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==} + '@radix-ui/react-menu@2.1.16': + resolution: {integrity: sha512-72F2T+PLlphrqLcAotYPp0uJMr5SjP5SL01wfEspJbru5Zs5vQaSHb4VB3ZMJPimgHHCHG7gMOeOB9H3Hdmtxg==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -1871,8 +1949,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-presence@1.1.5': - resolution: {integrity: sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==} + '@radix-ui/react-menubar@1.1.16': + resolution: {integrity: sha512-EB1FktTz5xRRi2Er974AUQZWg2yVBb1yjip38/lgwtCVRd3a+maUoGHN/xs9Yv8SY8QwbSEb+YrxGadVWbEutA==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -1884,8 +1962,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-primitive@2.1.3': - resolution: {integrity: sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==} + '@radix-ui/react-navigation-menu@1.2.14': + resolution: {integrity: sha512-YB9mTFQvCOAQMHU+C/jVl96WmuWeltyUEpRJJky51huhds5W2FQr1J8D/16sQlf0ozxkPK8uF3niQMdUwZPv5w==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -1897,8 +1975,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-roving-focus@1.1.11': - resolution: {integrity: sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==} + '@radix-ui/react-one-time-password-field@0.1.8': + resolution: {integrity: sha512-ycS4rbwURavDPVjCb5iS3aG4lURFDILi6sKI/WITUMZ13gMmn/xGjpLoqBAalhJaDk8I3UbCM5GzKHrnzwHbvg==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -1910,69 +1988,347 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-slot@1.2.3': - resolution: {integrity: sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==} + '@radix-ui/react-password-toggle-field@0.1.3': + resolution: {integrity: sha512-/UuCrDBWravcaMix4TdT+qlNdVwOM1Nck9kWx/vafXsdfj1ChfhOdfi3cy9SGBpWgTXwYCuboT/oYpJy3clqfw==} peerDependencies: '@types/react': '*' + '@types/react-dom': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': optional: true + '@types/react-dom': + optional: true - '@radix-ui/react-slot@1.2.4': - resolution: {integrity: sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==} + '@radix-ui/react-popover@1.1.15': + resolution: {integrity: sha512-kr0X2+6Yy/vJzLYJUPCZEc8SfQcf+1COFoAqauJm74umQhta9M7lNJHP7QQS3vkvcGLQUbWpMzwrXYwrYztHKA==} peerDependencies: '@types/react': '*' + '@types/react-dom': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': optional: true + '@types/react-dom': + optional: true - '@radix-ui/react-use-callback-ref@1.1.1': - resolution: {integrity: sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==} + '@radix-ui/react-popper@1.2.8': + resolution: {integrity: sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==} peerDependencies: '@types/react': '*' + '@types/react-dom': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': optional: true + '@types/react-dom': + optional: true - '@radix-ui/react-use-controllable-state@1.2.2': - resolution: {integrity: sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==} + '@radix-ui/react-portal@1.1.9': + resolution: {integrity: sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==} peerDependencies: '@types/react': '*' + '@types/react-dom': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': optional: true + '@types/react-dom': + optional: true - '@radix-ui/react-use-effect-event@0.0.2': - resolution: {integrity: sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==} + '@radix-ui/react-presence@1.1.5': + resolution: {integrity: sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==} peerDependencies: '@types/react': '*' + '@types/react-dom': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': optional: true + '@types/react-dom': + optional: true - '@radix-ui/react-use-escape-keydown@1.1.1': - resolution: {integrity: sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==} + '@radix-ui/react-primitive@2.1.3': + resolution: {integrity: sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==} peerDependencies: '@types/react': '*' + '@types/react-dom': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': optional: true + '@types/react-dom': + optional: true - '@radix-ui/react-use-layout-effect@1.1.1': - resolution: {integrity: sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==} + '@radix-ui/react-progress@1.1.7': + resolution: {integrity: sha512-vPdg/tF6YC/ynuBIJlk1mm7Le0VgW6ub6J2UWnTQ7/D23KXcPI1qy+0vBkgKgd38RCMJavBXpB83HPNFMTb0Fg==} peerDependencies: '@types/react': '*' + '@types/react-dom': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': optional: true - + '@types/react-dom': + optional: true + + '@radix-ui/react-radio-group@1.3.8': + resolution: {integrity: sha512-VBKYIYImA5zsxACdisNQ3BjCBfmbGH3kQlnFVqlWU4tXwjy7cGX8ta80BcrO+WJXIn5iBylEH3K6ZTlee//lgQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-roving-focus@1.1.11': + resolution: {integrity: sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-scroll-area@1.2.10': + resolution: {integrity: sha512-tAXIa1g3sM5CGpVT0uIbUx/U3Gs5N8T52IICuCtObaos1S8fzsrPXG5WObkQN3S6NVl6wKgPhAIiBGbWnvc97A==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-select@2.2.6': + resolution: {integrity: sha512-I30RydO+bnn2PQztvo25tswPH+wFBjehVGtmagkU78yMdwTwVf12wnAOF+AeP8S2N8xD+5UPbGhkUfPyvT+mwQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-separator@1.1.7': + resolution: {integrity: sha512-0HEb8R9E8A+jZjvmFCy/J4xhbXy3TV+9XSnGJ3KvTtjlIUy/YQ/p6UYZvi7YbeoeXdyU9+Y3scizK6hkY37baA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-slider@1.3.6': + resolution: {integrity: sha512-JPYb1GuM1bxfjMRlNLE+BcmBC8onfCi60Blk7OBqi2MLTFdS+8401U4uFjnwkOr49BLmXxLC6JHkvAsx5OJvHw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-slot@1.2.3': + resolution: {integrity: sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-slot@1.2.4': + resolution: {integrity: sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-switch@1.2.6': + resolution: {integrity: sha512-bByzr1+ep1zk4VubeEVViV592vu2lHE2BZY5OnzehZqOOgogN80+mNtCqPkhn2gklJqOpxWgPoYTSnhBCqpOXQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-tabs@1.1.13': + resolution: {integrity: sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-toast@1.2.15': + resolution: {integrity: sha512-3OSz3TacUWy4WtOXV38DggwxoqJK4+eDkNMl5Z/MJZaoUPaP4/9lf81xXMe1I2ReTAptverZUpbPY4wWwWyL5g==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-toggle-group@1.1.11': + resolution: {integrity: sha512-5umnS0T8JQzQT6HbPyO7Hh9dgd82NmS36DQr+X/YJ9ctFNCiiQd6IJAYYZ33LUwm8M+taCz5t2ui29fHZc4Y6Q==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-toggle@1.1.10': + resolution: {integrity: sha512-lS1odchhFTeZv3xwHH31YPObmJn8gOg7Lq12inrr0+BH/l3Tsq32VfjqH1oh80ARM3mlkfMic15n0kg4sD1poQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-toolbar@1.1.11': + resolution: {integrity: sha512-4ol06/1bLoFu1nwUqzdD4Y5RZ9oDdKeiHIsntug54Hcr1pgaHiPqHFEaXI1IFP/EsOfROQZ8Mig9VTIRza6Tjg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-tooltip@1.2.8': + resolution: {integrity: sha512-tY7sVt1yL9ozIxvmbtN5qtmH2krXcBCfjEiCgKGLqunJHvgvZG2Pcl2oQ3kbcZARb1BGEHdkLzcYGO8ynVlieg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-use-callback-ref@1.1.1': + resolution: {integrity: sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-controllable-state@1.2.2': + resolution: {integrity: sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-effect-event@0.0.2': + resolution: {integrity: sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-escape-keydown@1.1.1': + resolution: {integrity: sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-is-hydrated@0.1.0': + resolution: {integrity: sha512-U+UORVEq+cTnRIaostJv9AGdV3G6Y+zbVd+12e18jQ5A3c0xL03IhnHuiU4UV69wolOQp5GfR58NW/EgdQhwOA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-layout-effect@1.1.1': + resolution: {integrity: sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-previous@1.1.1': + resolution: {integrity: sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@radix-ui/react-use-rect@1.1.1': resolution: {integrity: sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==} peerDependencies: @@ -1991,6 +2347,19 @@ packages: '@types/react': optional: true + '@radix-ui/react-visually-hidden@1.2.3': + resolution: {integrity: sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/rect@1.1.1': resolution: {integrity: sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==} @@ -2787,8 +3156,8 @@ packages: sqlite3: optional: true - effect@3.19.14: - resolution: {integrity: sha512-3vwdq0zlvQOxXzXNKRIPKTqZNMyGCdaFUBfMPqpsyzZDre67kgC1EEHDV4EoQTovJ4w5fmJW756f86kkuz7WFA==} + effect@4.0.0-beta.20: + resolution: {integrity: sha512-wVkh3cXtaaxyRWnjTHE4rnE6FTQ+a6SLKnrgME7yLsllmOXDnsMYRv81Z1BTLYLkIjvsMAP2UaMi/V4rnFZ+AQ==} electron-to-chromium@1.5.267: resolution: {integrity: sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==} @@ -2867,9 +3236,9 @@ packages: exsolve@1.0.8: resolution: {integrity: sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==} - fast-check@3.23.2: - resolution: {integrity: sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==} - engines: {node: '>=8.0.0'} + fast-check@4.5.3: + resolution: {integrity: sha512-IE9csY7lnhxBnA8g/WI5eg/hygA6MGWJMSNfFRrBlXUciADEhS1EDB0SIsMSvzubzIlOBbVITSsypCsW717poA==} + engines: {node: '>=12.17.0'} fdir@6.5.0: resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} @@ -2949,6 +3318,10 @@ packages: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} engines: {node: '>=0.10.0'} + ini@6.0.0: + resolution: {integrity: sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ==} + engines: {node: ^20.17.0 || >=22.9.0} + is-arrayish@0.3.4: resolution: {integrity: sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==} @@ -3012,6 +3385,9 @@ packages: resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} engines: {node: '>=6'} + kubernetes-types@1.30.0: + resolution: {integrity: sha512-Dew1okvhM/SQcIa2rcgujNndZwU8VnSapDgdxlYoB84ZlpAD43U6KLAFqYo17ykSFGHNPrg0qry0bP+GJd9v7Q==} + lightningcss-android-arm64@1.30.2: resolution: {integrity: sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==} engines: {node: '>= 12.0.0'} @@ -3178,14 +3554,14 @@ packages: resolution: {integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==} engines: {node: '>= 14.16'} - pg-cloudflare@1.2.7: - resolution: {integrity: sha512-YgCtzMH0ptvZJslLM1ffsY4EuGaU0cx4XSdXLRFae8bPP4dS5xL1tNB3k2o/N64cHJpwU7dxKli/nZ2lUa5fLg==} + pg-cloudflare@1.3.0: + resolution: {integrity: sha512-6lswVVSztmHiRtD6I8hw4qP/nDm1EJbKMRhf3HCYaqud7frGysPv7FYJ5noZQdhQtN2xJnimfMtvQq21pdbzyQ==} - pg-connection-string@2.9.1: - resolution: {integrity: sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w==} + pg-connection-string@2.11.0: + resolution: {integrity: sha512-kecgoJwhOpxYU21rZjULrmrBJ698U2RxXofKVzOn5UDj61BPj/qMb7diYUR1nLScCDbrztQFl1TaQZT0t1EtzQ==} - pg-cursor@2.15.3: - resolution: {integrity: sha512-eHw63TsiGtFEfAd7tOTZ+TLy+i/2ePKS20H84qCQ+aQ60pve05Okon9tKMC+YN3j6XyeFoHnaim7Lt9WVafQsA==} + pg-cursor@2.18.0: + resolution: {integrity: sha512-WkMubzXP+FWDIC6XfA9pZwJHO0rmUwbNXUNFfBshp9amnCraQslVLYqEuWA+7qemtyz+v3zybcvcX//Dq5WpxQ==} peerDependencies: pg: ^8 @@ -3197,13 +3573,13 @@ packages: resolution: {integrity: sha512-BM/Thnrw5jm2kKLE5uJkXqqExRUY/toLHda65XgFTBTFYZyopbKjBe29Ii3RbkvlsMoFwD+tHeGaCjjv0gHlyw==} engines: {node: '>=4'} - pg-pool@3.10.1: - resolution: {integrity: sha512-Tu8jMlcX+9d8+QVzKIvM/uJtp07PKr82IUOYEphaWcoBhIYkoHpLXN3qO59nAI11ripznDsEzEv8nUxBVWajGg==} + pg-pool@3.12.0: + resolution: {integrity: sha512-eIJ0DES8BLaziFHW7VgJEBPi5hg3Nyng5iKpYtj3wbcAUV9A1wLgWiY7ajf/f/oO1wfxt83phXPY8Emztg7ITg==} peerDependencies: pg: '>=8.0' - pg-protocol@1.10.3: - resolution: {integrity: sha512-6DIBgBQaTKDJyxnXaLiLR8wBpQQcGWuAESkRBX/t6OwA8YsqP+iVSiond2EDy6Y/dsGk8rh/jtax3js5NeV7JQ==} + pg-protocol@1.12.0: + resolution: {integrity: sha512-uOANXNRACNdElMXJ0tPz6RBM0XQ61nONGAwlt8da5zs/iUOOCLBQOHSXnrC6fMsvtjxbOJrZZl5IScGv+7mpbg==} pg-types@2.2.0: resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==} @@ -3213,8 +3589,8 @@ packages: resolution: {integrity: sha512-o2XFanIMy/3+mThw69O8d4n1E5zsLhdO+OPqswezu7Z5ekP4hYDqlDjlmOpYMbzY2Br0ufCwJLdDIXeNVwcWFg==} engines: {node: '>=10'} - pg@8.16.3: - resolution: {integrity: sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw==} + pg@8.19.0: + resolution: {integrity: sha512-QIcLGi508BAHkQ3pJNptsFz5WQMlpGbuBGBaIaXsWK8mel2kQ/rThYI+DbgjUvZrIr7MiuEuc9LcChJoEZK1xQ==} engines: {node: '>= 16.0.0'} peerDependencies: pg-native: '>=3.0.1' @@ -3288,8 +3664,21 @@ packages: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} - pure-rand@6.1.0: - resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==} + pure-rand@7.0.1: + resolution: {integrity: sha512-oTUZM/NAZS8p7ANR3SHh30kXB+zK2r2BPcEn/awJIbOvq82WoMN4p62AWWp3Hhw50G0xMsw1mhIBLqHw64EcNQ==} + + radix-ui@1.4.3: + resolution: {integrity: sha512-aWizCQiyeAenIdUbqEpXgRA1ya65P13NKn/W8rWkcN0OPkRDxdBVLWnIEDsS2RpwCK2nobI7oMUSmexzTDyAmA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true react-dom@19.2.3: resolution: {integrity: sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==} @@ -3503,6 +3892,9 @@ packages: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} + toml@3.0.0: + resolution: {integrity: sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==} + tough-cookie@5.1.2: resolution: {integrity: sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==} engines: {node: '>=16'} @@ -3585,8 +3977,8 @@ packages: peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - uuid@11.1.0: - resolution: {integrity: sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==} + uuid@13.0.0: + resolution: {integrity: sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==} hasBin: true vite-node@2.1.9: @@ -4056,7 +4448,7 @@ snapshots: miniflare: 4.20260114.0 unenv: 2.0.0-rc.24 vite: 7.1.2(@types/node@22.19.7)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) - wrangler: 4.59.2 + wrangler: 4.59.2(@cloudflare/workers-types@4.20260103.0) ws: 8.18.0 transitivePeerDependencies: - bufferutil @@ -4121,60 +4513,23 @@ snapshots: '@drizzle-team/brocli@0.10.2': {} - '@effect/experimental@0.58.0(@effect/platform@0.94.1(effect@3.19.14))(effect@3.19.14)': + '@effect/sql-pg@4.0.0-beta.20(effect@4.0.0-beta.20)': dependencies: - '@effect/platform': 0.94.1(effect@3.19.14) - effect: 3.19.14 - uuid: 11.1.0 + effect: 4.0.0-beta.20 + pg: 8.19.0 + pg-connection-string: 2.11.0 + pg-cursor: 2.18.0(pg@8.19.0) + pg-pool: 3.12.0(pg@8.19.0) + pg-types: 4.1.0 + transitivePeerDependencies: + - pg-native - '@effect/language-service@0.64.0': {} + '@effect/vitest@4.0.0-beta.20(effect@4.0.0-beta.20)(vitest@3.2.4(@types/node@22.19.3)(jiti@2.6.1)(jsdom@26.1.0)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2))': + dependencies: + effect: 4.0.0-beta.20 + vitest: 3.2.4(@types/node@22.19.3)(jiti@2.6.1)(jsdom@26.1.0)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) - '@effect/platform@0.94.1(effect@3.19.14)': - dependencies: - effect: 3.19.14 - find-my-way-ts: 0.1.6 - msgpackr: 1.11.8 - multipasta: 0.2.7 - - '@effect/rpc@0.73.0(@effect/platform@0.94.1(effect@3.19.14))(effect@3.19.14)': - dependencies: - '@effect/platform': 0.94.1(effect@3.19.14) - effect: 3.19.14 - msgpackr: 1.11.8 - - '@effect/sql-drizzle@0.48.0(@effect/sql@0.49.0(@effect/experimental@0.58.0(@effect/platform@0.94.1(effect@3.19.14))(effect@3.19.14))(@effect/platform@0.94.1(effect@3.19.14))(effect@3.19.14))(drizzle-orm@0.45.1(@cloudflare/workers-types@4.20260103.0)(pg@8.16.3))(effect@3.19.14)': - dependencies: - '@effect/sql': 0.49.0(@effect/experimental@0.58.0(@effect/platform@0.94.1(effect@3.19.14))(effect@3.19.14))(@effect/platform@0.94.1(effect@3.19.14))(effect@3.19.14) - drizzle-orm: 0.45.1(@cloudflare/workers-types@4.20260103.0)(pg@8.16.3) - effect: 3.19.14 - - '@effect/sql-pg@0.50.0(@effect/experimental@0.58.0(@effect/platform@0.94.1(effect@3.19.14))(effect@3.19.14))(@effect/platform@0.94.1(effect@3.19.14))(@effect/sql@0.49.0(@effect/experimental@0.58.0(@effect/platform@0.94.1(effect@3.19.14))(effect@3.19.14))(@effect/platform@0.94.1(effect@3.19.14))(effect@3.19.14))(effect@3.19.14)': - dependencies: - '@effect/experimental': 0.58.0(@effect/platform@0.94.1(effect@3.19.14))(effect@3.19.14) - '@effect/platform': 0.94.1(effect@3.19.14) - '@effect/sql': 0.49.0(@effect/experimental@0.58.0(@effect/platform@0.94.1(effect@3.19.14))(effect@3.19.14))(@effect/platform@0.94.1(effect@3.19.14))(effect@3.19.14) - effect: 3.19.14 - pg: 8.16.3 - pg-connection-string: 2.9.1 - pg-cursor: 2.15.3(pg@8.16.3) - pg-pool: 3.10.1(pg@8.16.3) - pg-types: 4.1.0 - transitivePeerDependencies: - - pg-native - - '@effect/sql@0.49.0(@effect/experimental@0.58.0(@effect/platform@0.94.1(effect@3.19.14))(effect@3.19.14))(@effect/platform@0.94.1(effect@3.19.14))(effect@3.19.14)': - dependencies: - '@effect/experimental': 0.58.0(@effect/platform@0.94.1(effect@3.19.14))(effect@3.19.14) - '@effect/platform': 0.94.1(effect@3.19.14) - effect: 3.19.14 - uuid: 11.1.0 - - '@effect/vitest@0.27.0(effect@3.19.14)(vitest@3.2.4(@types/node@22.19.3)(jiti@2.6.1)(jsdom@26.1.0)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2))': - dependencies: - effect: 3.19.14 - vitest: 3.2.4(@types/node@22.19.3)(jiti@2.6.1)(jsdom@26.1.0)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) - - '@emnapi/runtime@1.8.0': + '@emnapi/runtime@1.8.0': dependencies: tslib: 2.8.1 optional: true @@ -4817,8 +5172,50 @@ snapshots: '@poppinss/exception@1.2.3': {} + '@radix-ui/number@1.1.1': {} + '@radix-ui/primitive@1.1.3': {} + '@radix-ui/react-accessible-icon@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.8 + '@types/react-dom': 19.2.3(@types/react@19.2.8) + + '@radix-ui/react-accordion@1.2.12(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-collapsible': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.8)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.8 + '@types/react-dom': 19.2.3(@types/react@19.2.8) + + '@radix-ui/react-alert-dialog@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.8)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.8 + '@types/react-dom': 19.2.3(@types/react@19.2.8) + '@radix-ui/react-arrow@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) @@ -4828,95 +5225,428 @@ snapshots: '@types/react': 19.2.8 '@types/react-dom': 19.2.3(@types/react@19.2.8) - '@radix-ui/react-collapsible@1.1.12(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + '@radix-ui/react-aspect-ratio@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.8 + '@types/react-dom': 19.2.3(@types/react@19.2.8) + + '@radix-ui/react-avatar@1.1.10(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/react-context': 1.1.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-use-is-hydrated': 0.1.0(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.8)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.8 + '@types/react-dom': 19.2.3(@types/react@19.2.8) + + '@radix-ui/react-checkbox@1.3.3(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.8)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.8 + '@types/react-dom': 19.2.3(@types/react@19.2.8) + + '@radix-ui/react-collapsible@1.1.12(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.8)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.8 + '@types/react-dom': 19.2.3(@types/react@19.2.8) + + '@radix-ui/react-collection@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.8)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.8 + '@types/react-dom': 19.2.3(@types/react@19.2.8) + + '@radix-ui/react-compose-refs@1.1.2(@types/react@19.2.8)(react@19.2.3)': + dependencies: + react: 19.2.3 + optionalDependencies: + '@types/react': 19.2.8 + + '@radix-ui/react-context-menu@2.2.16(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-context': 1.1.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-menu': 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.8)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.8 + '@types/react-dom': 19.2.3(@types/react@19.2.8) + + '@radix-ui/react-context@1.1.2(@types/react@19.2.8)(react@19.2.3)': + dependencies: + react: 19.2.3 + optionalDependencies: + '@types/react': 19.2.8 + + '@radix-ui/react-dialog@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.8)(react@19.2.3) + aria-hidden: 1.2.6 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + react-remove-scroll: 2.7.2(@types/react@19.2.8)(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.8 + '@types/react-dom': 19.2.3(@types/react@19.2.8) + + '@radix-ui/react-direction@1.1.1(@types/react@19.2.8)(react@19.2.3)': + dependencies: + react: 19.2.3 + optionalDependencies: + '@types/react': 19.2.8 + + '@radix-ui/react-dismissable-layer@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.2.8)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.8 + '@types/react-dom': 19.2.3(@types/react@19.2.8) + + '@radix-ui/react-dropdown-menu@2.1.16(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-menu': 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.8)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.8 + '@types/react-dom': 19.2.3(@types/react@19.2.8) + + '@radix-ui/react-focus-guards@1.1.3(@types/react@19.2.8)(react@19.2.3)': + dependencies: + react: 19.2.3 + optionalDependencies: + '@types/react': 19.2.8 + + '@radix-ui/react-focus-scope@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.8)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.8 + '@types/react-dom': 19.2.3(@types/react@19.2.8) + + '@radix-ui/react-form@0.1.8(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-label': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.8 + '@types/react-dom': 19.2.3(@types/react@19.2.8) + + '@radix-ui/react-hover-card@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.8)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.8 + '@types/react-dom': 19.2.3(@types/react@19.2.8) + + '@radix-ui/react-id@1.1.1(@types/react@19.2.8)(react@19.2.3)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.8)(react@19.2.3) + react: 19.2.3 + optionalDependencies: + '@types/react': 19.2.8 + + '@radix-ui/react-label@2.1.7(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.8 + '@types/react-dom': 19.2.3(@types/react@19.2.8) + + '@radix-ui/react-menu@2.1.16(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.8)(react@19.2.3) + aria-hidden: 1.2.6 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + react-remove-scroll: 2.7.2(@types/react@19.2.8)(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.8 + '@types/react-dom': 19.2.3(@types/react@19.2.8) + + '@radix-ui/react-menubar@1.1.16(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-menu': 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.8)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.8 + '@types/react-dom': 19.2.3(@types/react@19.2.8) + + '@radix-ui/react-navigation-menu@1.2.14(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.8 + '@types/react-dom': 19.2.3(@types/react@19.2.8) + + '@radix-ui/react-one-time-password-field@0.1.8(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/number': 1.1.1 + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-use-is-hydrated': 0.1.0(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.8)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.8 + '@types/react-dom': 19.2.3(@types/react@19.2.8) + + '@radix-ui/react-password-toggle-field@0.1.3(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-use-is-hydrated': 0.1.0(@types/react@19.2.8)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.8 + '@types/react-dom': 19.2.3(@types/react@19.2.8) + + '@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: '@radix-ui/primitive': 1.1.3 '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.8)(react@19.2.3) '@radix-ui/react-context': 1.1.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@radix-ui/react-id': 1.1.1(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.8)(react@19.2.3) '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.8)(react@19.2.3) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.8)(react@19.2.3) + aria-hidden: 1.2.6 react: 19.2.3 react-dom: 19.2.3(react@19.2.3) + react-remove-scroll: 2.7.2(@types/react@19.2.8)(react@19.2.3) optionalDependencies: '@types/react': 19.2.8 '@types/react-dom': 19.2.3(@types/react@19.2.8) - '@radix-ui/react-collection@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + '@radix-ui/react-popper@1.2.8(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: + '@floating-ui/react-dom': 2.1.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-arrow': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.8)(react@19.2.3) '@radix-ui/react-context': 1.1.2(@types/react@19.2.8)(react@19.2.3) '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-use-rect': 1.1.1(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/rect': 1.1.1 react: 19.2.3 react-dom: 19.2.3(react@19.2.3) optionalDependencies: '@types/react': 19.2.8 '@types/react-dom': 19.2.3(@types/react@19.2.8) - '@radix-ui/react-compose-refs@1.1.2(@types/react@19.2.8)(react@19.2.3)': + '@radix-ui/react-portal@1.1.9(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.8)(react@19.2.3) react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) optionalDependencies: '@types/react': 19.2.8 + '@types/react-dom': 19.2.3(@types/react@19.2.8) - '@radix-ui/react-context@1.1.2(@types/react@19.2.8)(react@19.2.3)': + '@radix-ui/react-presence@1.1.5(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.8)(react@19.2.3) react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) optionalDependencies: '@types/react': 19.2.8 + '@types/react-dom': 19.2.3(@types/react@19.2.8) - '@radix-ui/react-dialog@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + '@radix-ui/react-primitive@2.1.3(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.8)(react@19.2.3) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.8)(react@19.2.3) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.8)(react@19.2.3) - '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.8)(react@19.2.3) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@radix-ui/react-slot': 1.2.3(@types/react@19.2.8)(react@19.2.3) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.8)(react@19.2.3) - aria-hidden: 1.2.6 react: 19.2.3 react-dom: 19.2.3(react@19.2.3) - react-remove-scroll: 2.7.2(@types/react@19.2.8)(react@19.2.3) optionalDependencies: '@types/react': 19.2.8 '@types/react-dom': 19.2.3(@types/react@19.2.8) - '@radix-ui/react-direction@1.1.1(@types/react@19.2.8)(react@19.2.3)': + '@radix-ui/react-progress@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: + '@radix-ui/react-context': 1.1.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) optionalDependencies: '@types/react': 19.2.8 + '@types/react-dom': 19.2.3(@types/react@19.2.8) - '@radix-ui/react-dismissable-layer@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + '@radix-ui/react-radio-group@1.3.8(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: '@radix-ui/primitive': 1.1.3 '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.8)(react@19.2.3) - '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.8)(react@19.2.3) react: 19.2.3 react-dom: 19.2.3(react@19.2.3) optionalDependencies: '@types/react': 19.2.8 '@types/react-dom': 19.2.3(@types/react@19.2.8) - '@radix-ui/react-dropdown-menu@2.1.16(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + '@radix-ui/react-roving-focus@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.8)(react@19.2.3) '@radix-ui/react-context': 1.1.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.8)(react@19.2.3) '@radix-ui/react-id': 1.1.1(@types/react@19.2.8)(react@19.2.3) - '@radix-ui/react-menu': 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.8)(react@19.2.3) '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.8)(react@19.2.3) react: 19.2.3 react-dom: 19.2.3(react@19.2.3) @@ -4924,32 +5654,26 @@ snapshots: '@types/react': 19.2.8 '@types/react-dom': 19.2.3(@types/react@19.2.8) - '@radix-ui/react-focus-guards@1.1.3(@types/react@19.2.8)(react@19.2.3)': - dependencies: - react: 19.2.3 - optionalDependencies: - '@types/react': 19.2.8 - - '@radix-ui/react-focus-scope@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + '@radix-ui/react-scroll-area@1.2.10(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: + '@radix-ui/number': 1.1.1 + '@radix-ui/primitive': 1.1.3 '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.8)(react@19.2.3) react: 19.2.3 react-dom: 19.2.3(react@19.2.3) optionalDependencies: '@types/react': 19.2.8 '@types/react-dom': 19.2.3(@types/react@19.2.8) - '@radix-ui/react-id@1.1.1(@types/react@19.2.8)(react@19.2.3)': - dependencies: - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.8)(react@19.2.3) - react: 19.2.3 - optionalDependencies: - '@types/react': 19.2.8 - - '@radix-ui/react-menu@2.1.16(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + '@radix-ui/react-select@2.2.6(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: + '@radix-ui/number': 1.1.1 '@radix-ui/primitive': 1.1.3 '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.8)(react@19.2.3) @@ -4961,11 +5685,13 @@ snapshots: '@radix-ui/react-id': 1.1.1(@types/react@19.2.8)(react@19.2.3) '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@radix-ui/react-slot': 1.2.3(@types/react@19.2.8)(react@19.2.3) '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) aria-hidden: 1.2.6 react: 19.2.3 react-dom: 19.2.3(react@19.2.3) @@ -4974,83 +5700,159 @@ snapshots: '@types/react': 19.2.8 '@types/react-dom': 19.2.3(@types/react@19.2.8) - '@radix-ui/react-popper@1.2.8(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + '@radix-ui/react-separator@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: - '@floating-ui/react-dom': 2.1.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-arrow': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.8)(react@19.2.3) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.8)(react@19.2.3) '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.8)(react@19.2.3) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.8)(react@19.2.3) - '@radix-ui/react-use-rect': 1.1.1(@types/react@19.2.8)(react@19.2.3) - '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.8)(react@19.2.3) - '@radix-ui/rect': 1.1.1 react: 19.2.3 react-dom: 19.2.3(react@19.2.3) optionalDependencies: '@types/react': 19.2.8 '@types/react-dom': 19.2.3(@types/react@19.2.8) - '@radix-ui/react-portal@1.1.9(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + '@radix-ui/react-slider@1.3.6(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: + '@radix-ui/number': 1.1.1 + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.8)(react@19.2.3) '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.8)(react@19.2.3) '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.8)(react@19.2.3) react: 19.2.3 react-dom: 19.2.3(react@19.2.3) optionalDependencies: '@types/react': 19.2.8 '@types/react-dom': 19.2.3(@types/react@19.2.8) - '@radix-ui/react-presence@1.1.5(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + '@radix-ui/react-slot@1.2.3(@types/react@19.2.8)(react@19.2.3)': dependencies: '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.8)(react@19.2.3) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.8)(react@19.2.3) + react: 19.2.3 + optionalDependencies: + '@types/react': 19.2.8 + + '@radix-ui/react-slot@1.2.4(@types/react@19.2.8)(react@19.2.3)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.8)(react@19.2.3) + react: 19.2.3 + optionalDependencies: + '@types/react': 19.2.8 + + '@radix-ui/react-switch@1.2.6(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.8)(react@19.2.3) react: 19.2.3 react-dom: 19.2.3(react@19.2.3) optionalDependencies: '@types/react': 19.2.8 '@types/react-dom': 19.2.3(@types/react@19.2.8) - '@radix-ui/react-primitive@2.1.3(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + '@radix-ui/react-tabs@1.1.13(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-context': 1.1.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.8)(react@19.2.3) react: 19.2.3 react-dom: 19.2.3(react@19.2.3) optionalDependencies: '@types/react': 19.2.8 '@types/react-dom': 19.2.3(@types/react@19.2.8) - '@radix-ui/react-roving-focus@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + '@radix-ui/react-toast@1.2.15(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: '@radix-ui/primitive': 1.1.3 '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.8)(react@19.2.3) '@radix-ui/react-context': 1.1.2(@types/react@19.2.8)(react@19.2.3) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.8)(react@19.2.3) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.8)(react@19.2.3) '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) react: 19.2.3 react-dom: 19.2.3(react@19.2.3) optionalDependencies: '@types/react': 19.2.8 '@types/react-dom': 19.2.3(@types/react@19.2.8) - '@radix-ui/react-slot@1.2.3(@types/react@19.2.8)(react@19.2.3)': + '@radix-ui/react-toggle-group@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-context': 1.1.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-toggle': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.8)(react@19.2.3) react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) optionalDependencies: '@types/react': 19.2.8 + '@types/react-dom': 19.2.3(@types/react@19.2.8) - '@radix-ui/react-slot@1.2.4(@types/react@19.2.8)(react@19.2.3)': + '@radix-ui/react-toggle@1.1.10(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.8)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.8 + '@types/react-dom': 19.2.3(@types/react@19.2.8) + + '@radix-ui/react-toolbar@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-context': 1.1.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-separator': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-toggle-group': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.8 + '@types/react-dom': 19.2.3(@types/react@19.2.8) + + '@radix-ui/react-tooltip@1.2.8(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: + '@radix-ui/primitive': 1.1.3 '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) optionalDependencies: '@types/react': 19.2.8 + '@types/react-dom': 19.2.3(@types/react@19.2.8) '@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.2.8)(react@19.2.3)': dependencies: @@ -5080,12 +5882,25 @@ snapshots: optionalDependencies: '@types/react': 19.2.8 + '@radix-ui/react-use-is-hydrated@0.1.0(@types/react@19.2.8)(react@19.2.3)': + dependencies: + react: 19.2.3 + use-sync-external-store: 1.6.0(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.8 + '@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.2.8)(react@19.2.3)': dependencies: react: 19.2.3 optionalDependencies: '@types/react': 19.2.8 + '@radix-ui/react-use-previous@1.1.1(@types/react@19.2.8)(react@19.2.3)': + dependencies: + react: 19.2.3 + optionalDependencies: + '@types/react': 19.2.8 + '@radix-ui/react-use-rect@1.1.1(@types/react@19.2.8)(react@19.2.3)': dependencies: '@radix-ui/rect': 1.1.1 @@ -5100,6 +5915,15 @@ snapshots: optionalDependencies: '@types/react': 19.2.8 + '@radix-ui/react-visually-hidden@1.2.3(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.8 + '@types/react-dom': 19.2.3(@types/react@19.2.8) + '@radix-ui/rect@1.1.1': {} '@rolldown/pluginutils@1.0.0-beta.27': {} @@ -5859,15 +6683,23 @@ snapshots: transitivePeerDependencies: - supports-color - drizzle-orm@0.45.1(@cloudflare/workers-types@4.20260103.0)(pg@8.16.3): + drizzle-orm@0.45.1(@cloudflare/workers-types@4.20260103.0)(pg@8.19.0): optionalDependencies: '@cloudflare/workers-types': 4.20260103.0 - pg: 8.16.3 + pg: 8.19.0 - effect@3.19.14: + effect@4.0.0-beta.20: dependencies: '@standard-schema/spec': 1.1.0 - fast-check: 3.23.2 + fast-check: 4.5.3 + find-my-way-ts: 0.1.6 + ini: 6.0.0 + kubernetes-types: 1.30.0 + msgpackr: 1.11.8 + multipasta: 0.2.7 + toml: 3.0.0 + uuid: 13.0.0 + yaml: 2.8.2 electron-to-chromium@1.5.267: {} @@ -6048,9 +6880,9 @@ snapshots: exsolve@1.0.8: {} - fast-check@3.23.2: + fast-check@4.5.3: dependencies: - pure-rand: 6.1.0 + pure-rand: 7.0.1 fdir@6.5.0(picomatch@4.0.3): optionalDependencies: @@ -6121,6 +6953,8 @@ snapshots: dependencies: safer-buffer: 2.1.2 + ini@6.0.0: {} + is-arrayish@0.3.4: {} is-binary-path@2.1.0: @@ -6182,6 +7016,8 @@ snapshots: kleur@4.1.5: {} + kubernetes-types@1.30.0: {} + lightningcss-android-arm64@1.30.2: optional: true @@ -6342,24 +7178,24 @@ snapshots: pathval@2.0.1: {} - pg-cloudflare@1.2.7: + pg-cloudflare@1.3.0: optional: true - pg-connection-string@2.9.1: {} + pg-connection-string@2.11.0: {} - pg-cursor@2.15.3(pg@8.16.3): + pg-cursor@2.18.0(pg@8.19.0): dependencies: - pg: 8.16.3 + pg: 8.19.0 pg-int8@1.0.1: {} pg-numeric@1.0.2: {} - pg-pool@3.10.1(pg@8.16.3): + pg-pool@3.12.0(pg@8.19.0): dependencies: - pg: 8.16.3 + pg: 8.19.0 - pg-protocol@1.10.3: {} + pg-protocol@1.12.0: {} pg-types@2.2.0: dependencies: @@ -6379,15 +7215,15 @@ snapshots: postgres-interval: 3.0.0 postgres-range: 1.1.4 - pg@8.16.3: + pg@8.19.0: dependencies: - pg-connection-string: 2.9.1 - pg-pool: 3.10.1(pg@8.16.3) - pg-protocol: 1.10.3 + pg-connection-string: 2.11.0 + pg-pool: 3.12.0(pg@8.19.0) + pg-protocol: 1.12.0 pg-types: 2.2.0 pgpass: 1.0.5 optionalDependencies: - pg-cloudflare: 1.2.7 + pg-cloudflare: 1.3.0 pgpass@1.0.5: dependencies: @@ -6437,7 +7273,70 @@ snapshots: punycode@2.3.1: {} - pure-rand@6.1.0: {} + pure-rand@7.0.1: {} + + radix-ui@1.4.3(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3): + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-accessible-icon': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-accordion': 1.2.12(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-alert-dialog': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-arrow': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-aspect-ratio': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-avatar': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-checkbox': 1.3.3(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-collapsible': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-context-menu': 2.2.16(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-dropdown-menu': 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-form': 0.1.8(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-hover-card': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-label': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-menu': 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-menubar': 1.1.16(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-navigation-menu': 1.2.14(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-one-time-password-field': 0.1.8(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-password-toggle-field': 0.1.3(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-popover': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-progress': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-radio-group': 1.3.8(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-scroll-area': 1.2.10(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-select': 2.2.6(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-separator': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-slider': 1.3.6(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-switch': 1.2.6(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-tabs': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-toast': 1.2.15(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-toggle': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-toggle-group': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-toolbar': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-tooltip': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-use-is-hydrated': 0.1.0(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.8 + '@types/react-dom': 19.2.3(@types/react@19.2.8) react-dom@19.2.3(react@19.2.3): dependencies: @@ -6672,6 +7571,8 @@ snapshots: dependencies: is-number: 7.0.0 + toml@3.0.0: {} + tough-cookie@5.1.2: dependencies: tldts: 6.1.86 @@ -6739,7 +7640,7 @@ snapshots: dependencies: react: 19.2.3 - uuid@11.1.0: {} + uuid@13.0.0: {} vite-node@2.1.9(@types/node@22.19.3)(lightningcss@1.30.2): dependencies: @@ -7069,7 +7970,7 @@ snapshots: - bufferutil - utf-8-validate - wrangler@4.59.2: + wrangler@4.59.2(@cloudflare/workers-types@4.20260103.0): dependencies: '@cloudflare/kv-asset-handler': 0.4.2 '@cloudflare/unenv-preset': 2.10.0(unenv@2.0.0-rc.24)(workerd@1.20260114.0) @@ -7080,6 +7981,7 @@ snapshots: unenv: 2.0.0-rc.24 workerd: 1.20260114.0 optionalDependencies: + '@cloudflare/workers-types': 4.20260103.0 fsevents: 2.3.3 transitivePeerDependencies: - bufferutil @@ -7102,8 +8004,7 @@ snapshots: yallist@3.1.1: {} - yaml@2.8.2: - optional: true + yaml@2.8.2: {} youch-core@0.3.3: dependencies: diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 6f00273..6e5a86b 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,3 +1,8 @@ packages: - packages/* - apps/* + +catalog: + effect: 4.0.0-beta.20 + "@effect/sql-pg": 4.0.0-beta.20 + "@effect/vitest": 4.0.0-beta.20 diff --git a/tsconfig.base.json b/tsconfig.base.json index c41bee1..577af81 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -21,8 +21,6 @@ "target": "ES2022", "module": "ESNext", "paths": { - "@repo/cloudflare": ["./packages/cloudflare/src/index.ts"], - "@repo/cloudflare/*": ["./packages/cloudflare/src/*"], "@repo/contracts": ["./packages/contracts/src/index.ts"], "@repo/contracts/*": ["./packages/contracts/src/*"], "@repo/db": ["./packages/db/src/index.ts"],