diff --git a/.changeset/odd-ways-dress.md b/.changeset/odd-ways-dress.md new file mode 100644 index 0000000000..7d8c8630f6 --- /dev/null +++ b/.changeset/odd-ways-dress.md @@ -0,0 +1,5 @@ +--- +"@effect/platform": patch +--- + +Add `HttpRouter.concatAll`. diff --git a/.changeset/ten-ties-guess.md b/.changeset/ten-ties-guess.md new file mode 100644 index 0000000000..dbe5659c7e --- /dev/null +++ b/.changeset/ten-ties-guess.md @@ -0,0 +1,5 @@ +--- +"@effect/platform": patch +--- + +Fix `HttpRouter.concat` - add mounts from both routers. diff --git a/packages/platform-node/test/HttpServer.test.ts b/packages/platform-node/test/HttpServer.test.ts index ddd3840b85..610bdaeb50 100644 --- a/packages/platform-node/test/HttpServer.test.ts +++ b/packages/platform-node/test/HttpServer.test.ts @@ -701,4 +701,44 @@ describe("HttpServer", () => { const res = yield* HttpClientRequest.get("/").pipe(client) assert.deepStrictEqual(res.status, 500) }).pipe(Effect.provide(NodeHttpServer.layerTest))) + + const routerA = HttpRouter.empty.pipe( + HttpRouter.get("/a", HttpServerResponse.text("a")), + HttpRouter.mountApp("/ma", HttpServerResponse.text("ma")) + ) + + const routerB = HttpRouter.empty.pipe( + HttpRouter.get("/b", HttpServerResponse.text("b")), + HttpRouter.mountApp("/mb", HttpServerResponse.text("mb")) + ) + + it.scoped("concat", () => + Effect.gen(function*() { + yield* HttpRouter.concat(routerA, routerB).pipe(HttpServer.serveEffect()) + const [responseA, responseMountA, responseB, responseMountB] = yield* Effect.all([ + HttpClientRequest.get("/a"), + HttpClientRequest.get("/ma"), + HttpClientRequest.get("/b"), + HttpClientRequest.get("/mb") + ]) + expect(yield* responseA.text).toEqual("a") + expect(yield* responseMountA.text).toEqual("ma") + expect(yield* responseB.text).toEqual("b") + expect(yield* responseMountB.text).toEqual("mb") + }).pipe(Effect.provide(NodeHttpServer.layerTest))) + + it.scoped("concatAll", () => + Effect.gen(function*() { + yield* HttpRouter.concatAll(routerA, routerB).pipe(HttpServer.serveEffect()) + const [responseA, responseMountA, responseB, responseMountB] = yield* Effect.all([ + HttpClientRequest.get("/a"), + HttpClientRequest.get("/ma"), + HttpClientRequest.get("/b"), + HttpClientRequest.get("/mb") + ]) + expect(yield* responseA.text).toEqual("a") + expect(yield* responseMountA.text).toEqual("ma") + expect(yield* responseB.text).toEqual("b") + expect(yield* responseMountB.text).toEqual("mb") + }).pipe(Effect.provide(NodeHttpServer.layerTest))) }) diff --git a/packages/platform/dtslint/HttpRouter.ts b/packages/platform/dtslint/HttpRouter.ts new file mode 100644 index 0000000000..baa7937497 --- /dev/null +++ b/packages/platform/dtslint/HttpRouter.ts @@ -0,0 +1,8 @@ +import * as HttpRouter from "@effect/platform/HttpRouter" + +declare const router1: HttpRouter.HttpRouter<"E1", "R1"> +declare const router2: HttpRouter.HttpRouter<"E2", "R2"> +declare const router3: HttpRouter.HttpRouter<"E3", "R3"> + +// $ExpectType HttpRouter<"E1" | "E2" | "E3", "R1" | "R2" | "R3"> +HttpRouter.concatAll(router1, router2, router3) diff --git a/packages/platform/dtslint/tsconfig.json b/packages/platform/dtslint/tsconfig.json new file mode 100644 index 0000000000..6b2c70900f --- /dev/null +++ b/packages/platform/dtslint/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../../tsconfig.base.json", + "include": ["."], + "compilerOptions": { + "incremental": false, + "composite": false, + "noUnusedLocals": false + } +} diff --git a/packages/platform/package.json b/packages/platform/package.json index cdacdc7400..a0af958edc 100644 --- a/packages/platform/package.json +++ b/packages/platform/package.json @@ -34,6 +34,7 @@ "build-esm": "tsc -b tsconfig.build.json", "build-cjs": "babel build/esm --plugins @babel/transform-export-namespace-from --plugins @babel/transform-modules-commonjs --out-dir build/cjs --source-maps", "build-annotate": "babel build/esm --plugins annotate-pure-calls --out-dir build/esm --source-maps", + "dtslint": "dtslint dtslint", "check": "tsc -b tsconfig.json", "test": "vitest", "coverage": "vitest --coverage" diff --git a/packages/platform/src/HttpRouter.ts b/packages/platform/src/HttpRouter.ts index fbb4ac44f6..a9c5447569 100644 --- a/packages/platform/src/HttpRouter.ts +++ b/packages/platform/src/HttpRouter.ts @@ -379,6 +379,14 @@ export const concat: { > } = internal.concat +/** + * @since 1.0.0 + * @category combinators + */ +export const concatAll: >>( + ...routers: Routers +) => [Routers[number]] extends [HttpRouter] ? HttpRouter : never = internal.concatAll + /** * @since 1.0.0 * @category routing diff --git a/packages/platform/src/internal/httpRouter.ts b/packages/platform/src/internal/httpRouter.ts index 671cad187d..973dae2ca1 100644 --- a/packages/platform/src/internal/httpRouter.ts +++ b/packages/platform/src/internal/httpRouter.ts @@ -351,7 +351,25 @@ export const concat = dual< that: Router.HttpRouter ) => (self: Router.HttpRouter) => Router.HttpRouter, (self: Router.HttpRouter, that: Router.HttpRouter) => Router.HttpRouter ->(2, (self, that) => new RouterImpl(Chunk.appendAll(self.routes, that.routes) as any, self.mounts)) +>(2, (self, that) => concatAll(self, that)) + +/** @internal */ +export const concatAll = >, E, R>( + ...routers: Routers +) => + new RouterImpl( + routers.reduce((cur, acc) => Chunk.appendAll(cur, acc.routes), Chunk.empty>()), + routers.reduce( + (cur, acc) => Chunk.appendAll(cur, acc.mounts), + Chunk.empty< + readonly [ + prefix: string, + httpApp: App.Default, + options?: { readonly includePrefix?: boolean | undefined } | undefined + ] + >() + ) + ) as any const removeTrailingSlash = ( path: Router.PathInput