effect-start 0.14.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/package.json +8 -9
  2. package/src/Commander.test.ts +507 -245
  3. package/src/ContentNegotiation.test.ts +603 -0
  4. package/src/ContentNegotiation.ts +542 -0
  5. package/src/Entity.test.ts +592 -0
  6. package/src/Entity.ts +362 -0
  7. package/src/FileRouter.ts +16 -12
  8. package/src/{FileRouterCodegen.test.ts → FileRouterCodegen.todo.ts} +384 -219
  9. package/src/FileRouterCodegen.ts +6 -6
  10. package/src/FileRouterPattern.test.ts +93 -62
  11. package/src/FileRouter_files.test.ts +5 -5
  12. package/src/FileRouter_path.test.ts +121 -69
  13. package/src/FileRouter_tree.test.ts +62 -56
  14. package/src/FileSystemExtra.test.ts +46 -30
  15. package/src/Http.test.ts +319 -0
  16. package/src/Http.ts +167 -0
  17. package/src/HttpAppExtra.test.ts +39 -20
  18. package/src/HttpAppExtra.ts +0 -1
  19. package/src/HttpUtils.test.ts +35 -18
  20. package/src/HttpUtils.ts +2 -0
  21. package/src/PathPattern.test.ts +648 -0
  22. package/src/PathPattern.ts +485 -0
  23. package/src/Route.ts +266 -1069
  24. package/src/RouteBody.test.ts +234 -0
  25. package/src/RouteBody.ts +193 -0
  26. package/src/RouteHook.test.ts +40 -0
  27. package/src/RouteHook.ts +106 -0
  28. package/src/RouteHttp.test.ts +2906 -0
  29. package/src/RouteHttp.ts +427 -0
  30. package/src/RouteHttpTracer.ts +92 -0
  31. package/src/RouteMount.test.ts +481 -0
  32. package/src/RouteMount.ts +470 -0
  33. package/src/RouteSchema.test.ts +427 -0
  34. package/src/RouteSchema.ts +423 -0
  35. package/src/RouteTree.test.ts +494 -0
  36. package/src/RouteTree.ts +219 -0
  37. package/src/RouteTrie.test.ts +322 -0
  38. package/src/RouteTrie.ts +224 -0
  39. package/src/RouterPattern.test.ts +569 -548
  40. package/src/RouterPattern.ts +7 -7
  41. package/src/Start.ts +3 -3
  42. package/src/StreamExtra.ts +21 -1
  43. package/src/TuplePathPattern.ts +64 -0
  44. package/src/Values.test.ts +263 -0
  45. package/src/Values.ts +76 -0
  46. package/src/bun/BunBundle.test.ts +36 -42
  47. package/src/bun/BunBundle.ts +2 -2
  48. package/src/bun/BunBundle_imports.test.ts +4 -6
  49. package/src/bun/BunHttpServer.test.ts +183 -6
  50. package/src/bun/BunHttpServer.ts +72 -32
  51. package/src/bun/BunHttpServer_web.ts +18 -6
  52. package/src/bun/BunImportTrackerPlugin.test.ts +3 -3
  53. package/src/bun/BunRoute.test.ts +124 -442
  54. package/src/bun/BunRoute.ts +146 -286
  55. package/src/{BundleHttp.test.ts → bundler/BundleHttp.test.ts} +34 -60
  56. package/src/{BundleHttp.ts → bundler/BundleHttp.ts} +1 -2
  57. package/src/client/index.ts +1 -1
  58. package/src/{Effect_HttpRouter.test.ts → effect/HttpRouter.test.ts} +69 -90
  59. package/src/experimental/EncryptedCookies.test.ts +125 -64
  60. package/src/experimental/SseHttpResponse.ts +0 -1
  61. package/src/hyper/Hyper.ts +89 -0
  62. package/src/{HyperHtml.test.ts → hyper/HyperHtml.test.ts} +13 -13
  63. package/src/{HyperHtml.ts → hyper/HyperHtml.ts} +2 -2
  64. package/src/{jsx.d.ts → hyper/jsx.d.ts} +1 -1
  65. package/src/index.ts +3 -4
  66. package/src/middlewares/BasicAuthMiddleware.test.ts +29 -19
  67. package/src/{NodeFileSystem.ts → node/FileSystem.ts} +6 -2
  68. package/src/testing/TestHttpClient.test.ts +26 -26
  69. package/src/testing/TestLogger.test.ts +27 -14
  70. package/src/testing/TestLogger.ts +15 -9
  71. package/src/x/datastar/Datastar.test.ts +47 -48
  72. package/src/x/datastar/Datastar.ts +1 -1
  73. package/src/x/tailwind/TailwindPlugin.test.ts +56 -58
  74. package/src/x/tailwind/plugin.ts +1 -1
  75. package/src/FileHttpRouter.test.ts +0 -239
  76. package/src/FileHttpRouter.ts +0 -194
  77. package/src/Hyper.ts +0 -194
  78. package/src/Route.test.ts +0 -1370
  79. package/src/RouteRender.ts +0 -40
  80. package/src/Router.test.ts +0 -375
  81. package/src/Router.ts +0 -255
  82. package/src/bun/BunRoute_bundles.test.ts +0 -219
  83. /package/src/{Bundle.ts → bundler/Bundle.ts} +0 -0
  84. /package/src/{BundleFiles.ts → bundler/BundleFiles.ts} +0 -0
  85. /package/src/{HyperNode.ts → hyper/HyperNode.ts} +0 -0
  86. /package/src/{jsx-runtime.ts → hyper/jsx-runtime.ts} +0 -0
  87. /package/src/{NodeUtils.ts → node/Utils.ts} +0 -0
@@ -1,40 +0,0 @@
1
- import * as HttpServerResponse from "@effect/platform/HttpServerResponse"
2
- import * as Effect from "effect/Effect"
3
- import * as HyperHtml from "./HyperHtml.ts"
4
- import * as Route from "./Route.ts"
5
-
6
- /**
7
- * Renders a route handler to an HttpServerResponse.
8
- * Converts the raw handler value to a response based on the route's media type.
9
- */
10
- export function render<E, R>(
11
- route: Route.Route<any, any, Route.RouteHandler<any, E, R>, any>,
12
- context: Route.RouteContext,
13
- ): Effect.Effect<HttpServerResponse.HttpServerResponse, E, R> {
14
- return Effect.gen(function*() {
15
- const raw = yield* route.handler(context)
16
-
17
- // Allow handlers to return HttpServerResponse directly (e.g. BunRoute proxy)
18
- if (HttpServerResponse.isServerResponse(raw)) {
19
- return raw
20
- }
21
-
22
- switch (route.media) {
23
- case "text/plain":
24
- return HttpServerResponse.text(raw as string)
25
-
26
- case "text/html":
27
- if (Route.isGenericJsxObject(raw)) {
28
- return HttpServerResponse.html(HyperHtml.renderToString(raw))
29
- }
30
- return HttpServerResponse.html(raw as string)
31
-
32
- case "application/json":
33
- return HttpServerResponse.unsafeJson(raw)
34
-
35
- case "*":
36
- default:
37
- return HttpServerResponse.text(String(raw))
38
- }
39
- })
40
- }
@@ -1,375 +0,0 @@
1
- import * as t from "bun:test"
2
- import * as Effect from "effect/Effect"
3
- import * as Route from "./Route.ts"
4
- import * as Router from "./Router.ts"
5
-
6
- t.describe("Router", () => {
7
- t.describe("mount", () => {
8
- t.test("creates router with single route", () => {
9
- const router = Router.mount("/hello", Route.text("Hello World"))
10
-
11
- t.expect(router.entries).toHaveLength(1)
12
- t.expect(router.entries[0].path).toBe("/hello")
13
- t.expect(router.entries[0].route.set).toHaveLength(1)
14
- })
15
-
16
- t.test("chains multiple routes", () => {
17
- const router = Router
18
- .mount("/hello", Route.text("Hello"))
19
- .mount("/world", Route.text("World"))
20
-
21
- t.expect(router.entries).toHaveLength(2)
22
- t.expect(router.entries[0].path).toBe("/hello")
23
- t.expect(router.entries[1].path).toBe("/world")
24
- })
25
-
26
- t.test("merges routes at same path", () => {
27
- const router = Router
28
- .mount("/api", Route.get(Route.json({ method: "get" })))
29
- .mount("/api", Route.post(Route.json({ method: "post" })))
30
-
31
- t.expect(router.entries).toHaveLength(1)
32
- t.expect(router.entries[0].path).toBe("/api")
33
- })
34
- })
35
-
36
- t.describe("mounts", () => {
37
- t.test("exposes mounted routes as Record", () => {
38
- const router = Router
39
- .mount("/hello", Route.text("Hello"))
40
- .mount("/world", Route.text("World"))
41
-
42
- t.expect(router.mounts["/hello"]).toBeDefined()
43
- t.expect(router.mounts["/world"]).toBeDefined()
44
- t.expect(router.mounts["/hello"].set).toHaveLength(1)
45
- })
46
-
47
- t.test("mounts contain routes with layers applied", async () => {
48
- const layer = Route.layer(
49
- Route.html(function*(c) {
50
- const inner = yield* c.next()
51
- return `<wrap>${inner}</wrap>`
52
- }),
53
- )
54
-
55
- const router = Router
56
- .use(layer)
57
- .mount("/page", Route.html(Effect.succeed("content")))
58
-
59
- const mountedRoute = router.mounts["/page"]
60
- t.expect(mountedRoute).toBeDefined()
61
- t.expect(mountedRoute.set).toHaveLength(1)
62
-
63
- const route = mountedRoute.set[0]
64
- const mockContext: Route.RouteContext = {
65
- request: {} as any,
66
- url: new URL("http://localhost/page"),
67
- slots: {},
68
- next: () => Effect.void,
69
- }
70
-
71
- const result = await Effect.runPromise(
72
- route.handler(mockContext) as Effect.Effect<unknown>,
73
- )
74
-
75
- t.expect(result).toBe("<wrap>content</wrap>")
76
- })
77
- })
78
-
79
- t.describe("use", () => {
80
- t.test("adds global layer", () => {
81
- const layer = Route.layer(
82
- Route.html(function*(c) {
83
- const inner = yield* c.next()
84
- return `<html><body>${inner}</body></html>`
85
- }),
86
- )
87
-
88
- const router = Router.use(layer)
89
-
90
- t.expect(router.globalLayers).toHaveLength(1)
91
- t.expect(router.entries).toHaveLength(0)
92
- })
93
-
94
- t.test("applies layer to subsequently mounted routes", () => {
95
- const layer = Route.layer(
96
- Route.html(function*(c) {
97
- const inner = yield* c.next()
98
- return `<html><body>${inner}</body></html>`
99
- }),
100
- )
101
-
102
- const router = Router
103
- .use(layer)
104
- .mount("/", Route.text("Hello world!"))
105
-
106
- t.expect(router.globalLayers).toHaveLength(1)
107
- t.expect(router.entries).toHaveLength(1)
108
- t.expect(router.entries[0].layers).toHaveLength(1)
109
- })
110
-
111
- t.test("layer only applies to routes mounted after use()", async () => {
112
- const layer = Route.layer(
113
- Route.html(function*(c) {
114
- const inner = yield* c.next()
115
- return `<wrap>${inner}</wrap>`
116
- }),
117
- )
118
-
119
- const router = Router
120
- .mount("/before", Route.html(Effect.succeed("before-content")))
121
- .use(layer)
122
- .mount("/after", Route.html(Effect.succeed("after-content")))
123
-
124
- const mockContext = (path: string): Route.RouteContext => ({
125
- request: {} as any,
126
- url: new URL(`http://localhost${path}`),
127
- slots: {},
128
- next: () => Effect.void,
129
- })
130
-
131
- const beforeRoute = router.mounts["/before"].set[0]
132
- const afterRoute = router.mounts["/after"].set[0]
133
-
134
- const beforeResult = await Effect.runPromise(
135
- beforeRoute.handler(mockContext("/before")) as Effect.Effect<unknown>,
136
- )
137
- const afterResult = await Effect.runPromise(
138
- afterRoute.handler(mockContext("/after")) as Effect.Effect<unknown>,
139
- )
140
-
141
- t.expect(beforeResult).toBe("before-content")
142
- t.expect(afterResult).toBe("<wrap>after-content</wrap>")
143
- })
144
- })
145
-
146
- t.describe("layer application - runtime behavior", () => {
147
- t.test("layer handler wraps route handler", async () => {
148
- const layer = Route.layer(
149
- Route.html(function*(c) {
150
- const inner = yield* c.next()
151
- return `<wrap>${inner}</wrap>`
152
- }),
153
- )
154
-
155
- const router = Router
156
- .use(layer)
157
- .mount("/page", Route.html(Effect.succeed("content")))
158
-
159
- const mountedRoute = router.mounts["/page"]
160
- const route = mountedRoute.set[0]
161
-
162
- const mockContext: Route.RouteContext = {
163
- request: {} as any,
164
- url: new URL("http://localhost/page"),
165
- slots: {},
166
- next: () => Effect.succeed("unused"),
167
- }
168
-
169
- const result = await Effect.runPromise(
170
- route.handler(mockContext) as Effect.Effect<unknown>,
171
- )
172
-
173
- t.expect(result).toBe("<wrap>content</wrap>")
174
- })
175
-
176
- t.test("multiple layers are applied in order", async () => {
177
- const outerLayer = Route.layer(
178
- Route.html(function*(c) {
179
- const inner = yield* c.next()
180
- return `<outer>${inner}</outer>`
181
- }),
182
- )
183
-
184
- const innerLayer = Route.layer(
185
- Route.html(function*(c) {
186
- const inner = yield* c.next()
187
- return `<inner>${inner}</inner>`
188
- }),
189
- )
190
-
191
- const router = Router
192
- .use(outerLayer)
193
- .use(innerLayer)
194
- .mount("/page", Route.html(Effect.succeed("content")))
195
-
196
- const mountedRoute = router.mounts["/page"]
197
- const route = mountedRoute.set[0]
198
-
199
- const mockContext: Route.RouteContext = {
200
- request: {} as any,
201
- url: new URL("http://localhost/page"),
202
- slots: {},
203
- next: () => Effect.succeed("unused"),
204
- }
205
-
206
- const result = await Effect.runPromise(
207
- route.handler(mockContext) as Effect.Effect<unknown>,
208
- )
209
-
210
- t.expect(result).toBe("<outer><inner>content</inner></outer>")
211
- })
212
-
213
- t.test("layer only applies to matching media type", async () => {
214
- const htmlLayer = Route.layer(
215
- Route.html(function*(c) {
216
- const inner = yield* c.next()
217
- return `<wrap>${inner}</wrap>`
218
- }),
219
- )
220
-
221
- const router = Router
222
- .use(htmlLayer)
223
- .mount("/api", Route.json({ data: "value" }))
224
-
225
- const mountedRoute = router.mounts["/api"]
226
- const route = mountedRoute.set[0]
227
-
228
- const mockContext: Route.RouteContext = {
229
- request: {} as any,
230
- url: new URL("http://localhost/api"),
231
- slots: {},
232
- next: () => Effect.succeed("unused"),
233
- }
234
-
235
- const result = await Effect.runPromise(
236
- route.handler(mockContext) as Effect.Effect<unknown>,
237
- )
238
-
239
- t.expect(result).toEqual({ data: "value" })
240
- })
241
-
242
- t.test("route without layer is not wrapped", async () => {
243
- const router = Router.mount("/hello", Route.text("Hello"))
244
-
245
- const mountedRoute = router.mounts["/hello"]
246
- const route = mountedRoute.set[0]
247
-
248
- const mockContext: Route.RouteContext = {
249
- request: {} as any,
250
- url: new URL("http://localhost/hello"),
251
- slots: {},
252
- next: () => Effect.succeed("unused"),
253
- }
254
-
255
- const result = await Effect.runPromise(
256
- route.handler(mockContext) as Effect.Effect<unknown>,
257
- )
258
-
259
- t.expect(result).toBe("Hello")
260
- })
261
- })
262
-
263
- t.describe("type inference", () => {
264
- t.test("infers never for routes without requirements", () => {
265
- const router = Router.mount("/hello", Route.text("Hello"))
266
-
267
- type RouterError = Router.Router.Error<typeof router>
268
- type RouterRequirements = Router.Router.Requirements<typeof router>
269
-
270
- const _checkError: RouterError = undefined as never
271
- const _checkRequirements: RouterRequirements = undefined as never
272
-
273
- t.expect(true).toBe(true)
274
- })
275
-
276
- t.test("infers error type from route handler", () => {
277
- class MyError {
278
- readonly _tag = "MyError"
279
- }
280
-
281
- const router = Router.mount(
282
- "/fail",
283
- Route.text(Effect.fail(new MyError())),
284
- )
285
-
286
- type RouterError = Router.Router.Error<typeof router>
287
-
288
- const _checkError: MyError extends RouterError ? true : false = true
289
-
290
- t.expect(true).toBe(true)
291
- })
292
-
293
- t.test("infers context type from route handler", () => {
294
- class MyService extends Effect.Tag("MyService")<
295
- MyService,
296
- { getValue(): string }
297
- >() {}
298
-
299
- const router = Router.mount(
300
- "/service",
301
- Route.text(
302
- Effect.gen(function*() {
303
- const svc = yield* MyService
304
- return svc.getValue()
305
- }),
306
- ),
307
- )
308
-
309
- type RouterRequirements = Router.Router.Requirements<typeof router>
310
-
311
- const _checkRequirements: MyService extends RouterRequirements ? true
312
- : false = true
313
-
314
- t.expect(true).toBe(true)
315
- })
316
-
317
- t.test("unions error types from multiple routes", () => {
318
- class ErrorA {
319
- readonly _tag = "ErrorA"
320
- }
321
- class ErrorB {
322
- readonly _tag = "ErrorB"
323
- }
324
-
325
- const router = Router
326
- .mount("/a", Route.text(Effect.fail(new ErrorA())))
327
- .mount("/b", Route.text(Effect.fail(new ErrorB())))
328
-
329
- type RouterError = Router.Router.Error<typeof router>
330
-
331
- const _checkA: ErrorA extends RouterError ? true : false = true
332
- const _checkB: ErrorB extends RouterError ? true : false = true
333
-
334
- t.expect(true).toBe(true)
335
- })
336
-
337
- t.test("unions context types from multiple routes", () => {
338
- class ServiceA extends Effect.Tag("ServiceA")<
339
- ServiceA,
340
- { getA(): string }
341
- >() {}
342
- class ServiceB extends Effect.Tag("ServiceB")<
343
- ServiceB,
344
- { getB(): string }
345
- >() {}
346
-
347
- const router = Router
348
- .mount(
349
- "/a",
350
- Route.text(
351
- Effect.gen(function*() {
352
- const svc = yield* ServiceA
353
- return svc.getA()
354
- }),
355
- ),
356
- )
357
- .mount(
358
- "/b",
359
- Route.text(
360
- Effect.gen(function*() {
361
- const svc = yield* ServiceB
362
- return svc.getB()
363
- }),
364
- ),
365
- )
366
-
367
- type RouterRequirements = Router.Router.Requirements<typeof router>
368
-
369
- const _checkA: ServiceA extends RouterRequirements ? true : false = true
370
- const _checkB: ServiceB extends RouterRequirements ? true : false = true
371
-
372
- t.expect(true).toBe(true)
373
- })
374
- })
375
- })
package/src/Router.ts DELETED
@@ -1,255 +0,0 @@
1
- import * as Data from "effect/Data"
2
- import * as Pipeable from "effect/Pipeable"
3
- import * as Predicate from "effect/Predicate"
4
- import * as Route from "./Route.ts"
5
-
6
- type RouterModule = typeof import("./Router.ts")
7
-
8
- type Self =
9
- | Router.Any
10
- | RouterModule
11
- | undefined
12
-
13
- export type RouterErrorReason =
14
- | "UnsupportedPattern"
15
- | "ProxyError"
16
-
17
- export class RouterError extends Data.TaggedError("RouterError")<{
18
- reason: RouterErrorReason
19
- pattern: string
20
- message: string
21
- }> {}
22
-
23
- const TypeId: unique symbol = Symbol.for(
24
- "effect-start/Router",
25
- )
26
-
27
- export type RouterEntry = {
28
- path: `/${string}`
29
- route: Route.RouteSet.Default
30
- layers: Route.RouteLayer[]
31
- }
32
-
33
- type Methods = {
34
- use: typeof use
35
- mount: typeof mount
36
- }
37
-
38
- export interface Router<
39
- out E = never,
40
- out R = never,
41
- > extends Pipeable.Pipeable, Methods {
42
- [TypeId]: typeof TypeId
43
- readonly entries: readonly RouterEntry[]
44
- readonly globalLayers: readonly Route.RouteLayer[]
45
- readonly mounts: Record<`/${string}`, Route.RouteSet.Default>
46
- readonly _E: () => E
47
- readonly _R: () => R
48
- }
49
-
50
- export namespace Router {
51
- export type Any = Router<any, any>
52
- export type Error<T> = T extends Router<infer E, any> ? E : never
53
- export type Requirements<T> = T extends Router<any, infer R> ? R : never
54
- }
55
-
56
- const Proto: Methods & {
57
- [TypeId]: typeof TypeId
58
- pipe: Pipeable.Pipeable["pipe"]
59
- } = {
60
- [TypeId]: TypeId,
61
-
62
- pipe() {
63
- return Pipeable.pipeArguments(this, arguments)
64
- },
65
-
66
- use,
67
- mount,
68
- }
69
-
70
- type ExtractRouteSetError<T> = T extends Route.RouteSet<infer Routes, any>
71
- ? Routes[number] extends Route.Route<any, any, infer H, any>
72
- ? H extends Route.RouteHandler<any, infer E, any> ? E : never
73
- : never
74
- : never
75
-
76
- type ExtractRouteSetContext<T> = T extends Route.RouteSet<infer Routes, any>
77
- ? Routes[number] extends Route.Route<any, any, infer H, any>
78
- ? H extends Route.RouteHandler<any, any, infer R> ? R : never
79
- : never
80
- : never
81
-
82
- function addRoute<
83
- E,
84
- R,
85
- RouteE,
86
- RouteR,
87
- >(
88
- builder: Router<E, R>,
89
- path: `/${string}`,
90
- route: Route.RouteSet.Default,
91
- ): Router<E | RouteE, R | RouteR> {
92
- const existingEntry = builder.entries.find((e) => e.path === path)
93
- if (existingEntry) {
94
- const updatedEntry: RouterEntry = {
95
- ...existingEntry,
96
- route: Route.merge(existingEntry.route, route),
97
- }
98
- return make(
99
- builder.entries.map((e) => (e.path === path ? updatedEntry : e)),
100
- builder.globalLayers,
101
- )
102
- }
103
-
104
- const newEntry: RouterEntry = {
105
- path,
106
- route,
107
- layers: [...builder.globalLayers],
108
- }
109
-
110
- return make([...builder.entries, newEntry], builder.globalLayers)
111
- }
112
-
113
- function addGlobalLayer<E, R>(
114
- builder: Router<E, R>,
115
- layerRoute: Route.RouteLayer,
116
- ): Router<E, R> {
117
- const newGlobalLayers = [...builder.globalLayers, layerRoute]
118
- return make(builder.entries, newGlobalLayers)
119
- }
120
-
121
- function findMatchingLayerRoutes(
122
- route: Route.Route.Default,
123
- layers: readonly Route.RouteLayer[],
124
- ): Route.Route.Default[] {
125
- const matchingRoutes: Route.Route.Default[] = []
126
- for (const layer of layers) {
127
- for (const layerRoute of layer.set) {
128
- if (Route.matches(layerRoute, route)) {
129
- matchingRoutes.push(layerRoute)
130
- }
131
- }
132
- }
133
- return matchingRoutes
134
- }
135
-
136
- function wrapWithLayerRoute(
137
- innerRoute: Route.Route.Default,
138
- layerRoute: Route.Route.Default,
139
- ): Route.Route.Default {
140
- const handler: Route.RouteHandler = (context) => {
141
- const contextWithNext: Route.RouteContext = {
142
- ...context,
143
- next: () => innerRoute.handler(context),
144
- }
145
- return layerRoute.handler(contextWithNext)
146
- }
147
-
148
- return Route.make({
149
- method: layerRoute.method,
150
- media: layerRoute.media,
151
- handler,
152
- schemas: {},
153
- })
154
- }
155
-
156
- function applyLayersToRoute(
157
- route: Route.Route.Default,
158
- layers: readonly Route.RouteLayer[],
159
- ): Route.Route.Default {
160
- const matchingLayerRoutes = findMatchingLayerRoutes(route, layers)
161
- let wrappedRoute = route
162
-
163
- for (const layerRoute of matchingLayerRoutes.reverse()) {
164
- wrappedRoute = wrapWithLayerRoute(wrappedRoute, layerRoute)
165
- }
166
-
167
- return wrappedRoute
168
- }
169
-
170
- function applyLayersToRouteSet(
171
- routeSet: Route.RouteSet.Default,
172
- layers: readonly Route.RouteLayer[],
173
- ): Route.RouteSet.Default {
174
- if (layers.length === 0) {
175
- return routeSet
176
- }
177
-
178
- const wrappedRoutes = routeSet.set.map((route) =>
179
- applyLayersToRoute(route, layers)
180
- )
181
-
182
- return {
183
- set: wrappedRoutes,
184
- schema: routeSet.schema,
185
- } as unknown as Route.RouteSet.Default
186
- }
187
-
188
- export function make<E, R>(
189
- entries: readonly RouterEntry[],
190
- globalLayers: readonly Route.RouteLayer[] = [],
191
- ): Router<E, R> {
192
- const mounts: Record<`/${string}`, Route.RouteSet.Default> = {}
193
-
194
- for (const entry of entries) {
195
- if (entry.route.set.length > 0) {
196
- mounts[entry.path] = applyLayersToRouteSet(entry.route, entry.layers)
197
- }
198
- }
199
-
200
- return Object.assign(
201
- Object.create(Proto),
202
- {
203
- entries,
204
- globalLayers,
205
- mounts,
206
- },
207
- )
208
- }
209
-
210
- export function isRouter(input: unknown): input is Router.Any {
211
- return Predicate.hasProperty(input, TypeId)
212
- }
213
-
214
- export function use<
215
- S extends Self,
216
- >(
217
- this: S,
218
- layerRoute: Route.RouteLayer,
219
- ): S extends Router<infer E, infer R> ? Router<E, R>
220
- : Router<never, never>
221
- {
222
- const router = isRouter(this)
223
- ? this
224
- : make<never, never>([], [])
225
-
226
- return addGlobalLayer(router, layerRoute) as any
227
- }
228
-
229
- export function mount<
230
- S extends Self,
231
- Routes extends Route.Route.Tuple,
232
- Schemas extends Route.RouteSchemas,
233
- >(
234
- this: S,
235
- path: `/${string}`,
236
- route: Route.RouteSet<Routes, Schemas>,
237
- ): S extends Router<infer E, infer R> ? Router<
238
- E | ExtractRouteSetError<Route.RouteSet<Routes, Schemas>>,
239
- R | ExtractRouteSetContext<Route.RouteSet<Routes, Schemas>>
240
- >
241
- : Router<
242
- ExtractRouteSetError<Route.RouteSet<Routes, Schemas>>,
243
- ExtractRouteSetContext<Route.RouteSet<Routes, Schemas>>
244
- >
245
- {
246
- const router = isRouter(this)
247
- ? this
248
- : make<never, never>([], [])
249
-
250
- return addRoute(
251
- router,
252
- path,
253
- route as Route.RouteSet.Default,
254
- ) as any
255
- }