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.
- package/package.json +8 -9
- package/src/Commander.test.ts +507 -245
- package/src/ContentNegotiation.test.ts +603 -0
- package/src/ContentNegotiation.ts +542 -0
- package/src/Entity.test.ts +592 -0
- package/src/Entity.ts +362 -0
- package/src/FileRouter.ts +16 -12
- package/src/{FileRouterCodegen.test.ts → FileRouterCodegen.todo.ts} +384 -219
- package/src/FileRouterCodegen.ts +6 -6
- package/src/FileRouterPattern.test.ts +93 -62
- package/src/FileRouter_files.test.ts +5 -5
- package/src/FileRouter_path.test.ts +121 -69
- package/src/FileRouter_tree.test.ts +62 -56
- package/src/FileSystemExtra.test.ts +46 -30
- package/src/Http.test.ts +319 -0
- package/src/Http.ts +167 -0
- package/src/HttpAppExtra.test.ts +39 -20
- package/src/HttpAppExtra.ts +0 -1
- package/src/HttpUtils.test.ts +35 -18
- package/src/HttpUtils.ts +2 -0
- package/src/PathPattern.test.ts +648 -0
- package/src/PathPattern.ts +485 -0
- package/src/Route.ts +266 -1069
- package/src/RouteBody.test.ts +234 -0
- package/src/RouteBody.ts +193 -0
- package/src/RouteHook.test.ts +40 -0
- package/src/RouteHook.ts +106 -0
- package/src/RouteHttp.test.ts +2906 -0
- package/src/RouteHttp.ts +427 -0
- package/src/RouteHttpTracer.ts +92 -0
- package/src/RouteMount.test.ts +481 -0
- package/src/RouteMount.ts +470 -0
- package/src/RouteSchema.test.ts +427 -0
- package/src/RouteSchema.ts +423 -0
- package/src/RouteTree.test.ts +494 -0
- package/src/RouteTree.ts +219 -0
- package/src/RouteTrie.test.ts +322 -0
- package/src/RouteTrie.ts +224 -0
- package/src/RouterPattern.test.ts +569 -548
- package/src/RouterPattern.ts +7 -7
- package/src/Start.ts +3 -3
- package/src/StreamExtra.ts +21 -1
- package/src/TuplePathPattern.ts +64 -0
- package/src/Values.test.ts +263 -0
- package/src/Values.ts +76 -0
- package/src/bun/BunBundle.test.ts +36 -42
- package/src/bun/BunBundle.ts +2 -2
- package/src/bun/BunBundle_imports.test.ts +4 -6
- package/src/bun/BunHttpServer.test.ts +183 -6
- package/src/bun/BunHttpServer.ts +72 -32
- package/src/bun/BunHttpServer_web.ts +18 -6
- package/src/bun/BunImportTrackerPlugin.test.ts +3 -3
- package/src/bun/BunRoute.test.ts +124 -442
- package/src/bun/BunRoute.ts +146 -286
- package/src/{BundleHttp.test.ts → bundler/BundleHttp.test.ts} +34 -60
- package/src/{BundleHttp.ts → bundler/BundleHttp.ts} +1 -2
- package/src/client/index.ts +1 -1
- package/src/{Effect_HttpRouter.test.ts → effect/HttpRouter.test.ts} +69 -90
- package/src/experimental/EncryptedCookies.test.ts +125 -64
- package/src/experimental/SseHttpResponse.ts +0 -1
- package/src/hyper/Hyper.ts +89 -0
- package/src/{HyperHtml.test.ts → hyper/HyperHtml.test.ts} +13 -13
- package/src/{HyperHtml.ts → hyper/HyperHtml.ts} +2 -2
- package/src/{jsx.d.ts → hyper/jsx.d.ts} +1 -1
- package/src/index.ts +3 -4
- package/src/middlewares/BasicAuthMiddleware.test.ts +29 -19
- package/src/{NodeFileSystem.ts → node/FileSystem.ts} +6 -2
- package/src/testing/TestHttpClient.test.ts +26 -26
- package/src/testing/TestLogger.test.ts +27 -14
- package/src/testing/TestLogger.ts +15 -9
- package/src/x/datastar/Datastar.test.ts +47 -48
- package/src/x/datastar/Datastar.ts +1 -1
- package/src/x/tailwind/TailwindPlugin.test.ts +56 -58
- package/src/x/tailwind/plugin.ts +1 -1
- package/src/FileHttpRouter.test.ts +0 -239
- package/src/FileHttpRouter.ts +0 -194
- package/src/Hyper.ts +0 -194
- package/src/Route.test.ts +0 -1370
- package/src/RouteRender.ts +0 -40
- package/src/Router.test.ts +0 -375
- package/src/Router.ts +0 -255
- package/src/bun/BunRoute_bundles.test.ts +0 -219
- /package/src/{Bundle.ts → bundler/Bundle.ts} +0 -0
- /package/src/{BundleFiles.ts → bundler/BundleFiles.ts} +0 -0
- /package/src/{HyperNode.ts → hyper/HyperNode.ts} +0 -0
- /package/src/{jsx-runtime.ts → hyper/jsx-runtime.ts} +0 -0
- /package/src/{NodeUtils.ts → node/Utils.ts} +0 -0
package/src/bun/BunRoute.test.ts
CHANGED
|
@@ -1,480 +1,162 @@
|
|
|
1
|
-
import * as
|
|
2
|
-
import * as HttpServerResponse from "@effect/platform/HttpServerResponse"
|
|
3
|
-
import type { HTMLBundle } from "bun"
|
|
4
|
-
import * as t from "bun:test"
|
|
1
|
+
import * as test from "bun:test"
|
|
5
2
|
import * as Effect from "effect/Effect"
|
|
6
3
|
import * as Layer from "effect/Layer"
|
|
7
|
-
import * as
|
|
4
|
+
import * as Option from "effect/Option"
|
|
8
5
|
import * as Route from "../Route.ts"
|
|
9
|
-
import * as Router from "../Router.ts"
|
|
10
6
|
import * as BunHttpServer from "./BunHttpServer.ts"
|
|
11
7
|
import * as BunRoute from "./BunRoute.ts"
|
|
12
8
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
})
|
|
18
|
-
|
|
19
|
-
t.test("allows full-segment params", () => {
|
|
20
|
-
const result = BunRoute.validateBunPattern("/users/[id]")
|
|
21
|
-
t.expect(result._tag).toBe("None")
|
|
22
|
-
})
|
|
23
|
-
|
|
24
|
-
t.test("allows rest params", () => {
|
|
25
|
-
const result = BunRoute.validateBunPattern("/docs/[...path]")
|
|
26
|
-
t.expect(result._tag).toBe("None")
|
|
27
|
-
})
|
|
28
|
-
|
|
29
|
-
t.test("rejects prefixed params", () => {
|
|
30
|
-
const result = BunRoute.validateBunPattern("/users/pk_[id]")
|
|
31
|
-
assert.strictEqual(result._tag, "Some")
|
|
32
|
-
t.expect(result.value.reason).toBe("UnsupportedPattern")
|
|
33
|
-
t.expect(result.value.pattern).toBe("/users/pk_[id]")
|
|
34
|
-
})
|
|
35
|
-
|
|
36
|
-
t.test("rejects suffixed params", () => {
|
|
37
|
-
const result = BunRoute.validateBunPattern("/users/[id]_details")
|
|
38
|
-
assert.strictEqual(result._tag, "Some")
|
|
39
|
-
t.expect(result.value.reason).toBe("UnsupportedPattern")
|
|
40
|
-
})
|
|
41
|
-
|
|
42
|
-
t.test("rejects dot suffix on params", () => {
|
|
43
|
-
const result = BunRoute.validateBunPattern("/api/[id].json")
|
|
44
|
-
assert.strictEqual(result._tag, "Some")
|
|
45
|
-
t.expect(result.value.reason).toBe("UnsupportedPattern")
|
|
46
|
-
})
|
|
47
|
-
|
|
48
|
-
t.test("rejects tilde suffix on params", () => {
|
|
49
|
-
const result = BunRoute.validateBunPattern("/api/[id]~test")
|
|
50
|
-
assert.strictEqual(result._tag, "Some")
|
|
51
|
-
t.expect(result.value.reason).toBe("UnsupportedPattern")
|
|
52
|
-
})
|
|
53
|
-
|
|
54
|
-
t.test("allows optional params (implemented via two patterns)", () => {
|
|
55
|
-
const result = BunRoute.validateBunPattern("/users/[[id]]")
|
|
56
|
-
t.expect(result._tag).toBe("None")
|
|
57
|
-
})
|
|
58
|
-
|
|
59
|
-
t.test("allows optional rest params (implemented via two patterns)", () => {
|
|
60
|
-
const result = BunRoute.validateBunPattern("/docs/[[...path]]")
|
|
61
|
-
t.expect(result._tag).toBe("None")
|
|
62
|
-
})
|
|
63
|
-
})
|
|
64
|
-
|
|
65
|
-
t.describe(`${BunRoute.routesFromRouter.name}`, () => {
|
|
66
|
-
t.test("fails with RouterError for unsupported patterns", async () => {
|
|
67
|
-
const result = await Effect.runPromise(
|
|
68
|
-
BunRoute
|
|
69
|
-
.routesFromRouter(
|
|
70
|
-
Router.mount("/users/pk_[id]", Route.text("user")),
|
|
71
|
-
)
|
|
72
|
-
.pipe(
|
|
73
|
-
Effect.either,
|
|
74
|
-
Effect.provide(BunHttpServer.layer({ port: 0 })),
|
|
75
|
-
),
|
|
76
|
-
)
|
|
77
|
-
|
|
78
|
-
assert.strictEqual(result._tag, "Left")
|
|
79
|
-
t.expect(result.left._tag).toBe("RouterError")
|
|
80
|
-
t.expect(result.left.reason).toBe("UnsupportedPattern")
|
|
81
|
-
})
|
|
82
|
-
|
|
83
|
-
t.test("converts text route to fetch handler", async () => {
|
|
84
|
-
const fetch = await makeFetch(
|
|
85
|
-
Router.mount("/hello", Route.text("Hello World")),
|
|
86
|
-
)
|
|
87
|
-
|
|
88
|
-
const response = await fetch("/hello")
|
|
89
|
-
|
|
90
|
-
t.expect(response.status).toBe(200)
|
|
91
|
-
t.expect(await response.text()).toBe("Hello World")
|
|
92
|
-
})
|
|
93
|
-
|
|
94
|
-
t.test("converts json route to fetch handler", async () => {
|
|
95
|
-
const fetch = await makeFetch(
|
|
96
|
-
Router.mount("/api/data", Route.json({ message: "ok", count: 42 })),
|
|
97
|
-
)
|
|
98
|
-
|
|
99
|
-
const response = await fetch("/api/data")
|
|
100
|
-
|
|
101
|
-
t.expect(response.status).toBe(200)
|
|
102
|
-
t.expect(await response.json()).toEqual({ message: "ok", count: 42 })
|
|
103
|
-
})
|
|
9
|
+
const layerServer = Layer.scoped(
|
|
10
|
+
BunHttpServer.BunHttpServer,
|
|
11
|
+
BunHttpServer.make({ port: 0 }),
|
|
12
|
+
)
|
|
104
13
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
const response = await fetch("/page")
|
|
111
|
-
|
|
112
|
-
t.expect(response.status).toBe(200)
|
|
113
|
-
t.expect(await response.text()).toBe("<h1>Title</h1>")
|
|
114
|
-
})
|
|
14
|
+
const testLayer = (routes: ReturnType<typeof Route.tree>) =>
|
|
15
|
+
Layer.provideMerge(
|
|
16
|
+
BunHttpServer.layerRoutes(routes),
|
|
17
|
+
layerServer,
|
|
18
|
+
)
|
|
115
19
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
),
|
|
20
|
+
test.describe(BunRoute.htmlBundle, () => {
|
|
21
|
+
test.test("wraps child content with layout", async () => {
|
|
22
|
+
const routes = Route.tree({
|
|
23
|
+
"/": Route.get(
|
|
24
|
+
BunRoute.htmlBundle(() => import("../../static/LayoutSlots.html")),
|
|
25
|
+
Route.html("<p>Hello World</p>"),
|
|
123
26
|
),
|
|
124
|
-
)
|
|
125
|
-
|
|
126
|
-
const
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
.mount("/users/[id]", Route.text("user"))
|
|
137
|
-
.mount("/docs/[...path]", Route.text("docs")),
|
|
138
|
-
)
|
|
139
|
-
|
|
140
|
-
t.expect(routes["/users/:id"]).toBeDefined()
|
|
141
|
-
t.expect(routes["/docs/*"]).toBeDefined()
|
|
142
|
-
t.expect(routes["/users/[id]"]).toBeUndefined()
|
|
143
|
-
t.expect(routes["/docs/[...path]"]).toBeUndefined()
|
|
144
|
-
})
|
|
145
|
-
|
|
146
|
-
t.test("creates proxy and internal routes for BunRoute", async () => {
|
|
147
|
-
const mockBundle = { index: "index.html" } as HTMLBundle
|
|
148
|
-
const bunRoute = BunRoute.html(() => Promise.resolve(mockBundle))
|
|
149
|
-
|
|
150
|
-
const routes = await makeBunRoutes(
|
|
151
|
-
Router.mount("/app", bunRoute),
|
|
152
|
-
)
|
|
153
|
-
|
|
154
|
-
const internalPath = Object.keys(routes).find((k) =>
|
|
155
|
-
k.includes(".BunRoute-")
|
|
156
|
-
)
|
|
157
|
-
|
|
158
|
-
t.expect(internalPath).toBeDefined()
|
|
159
|
-
t.expect(routes[internalPath!]).toBe(mockBundle)
|
|
160
|
-
t.expect(typeof routes["/app"]).toBe("function")
|
|
161
|
-
})
|
|
162
|
-
|
|
163
|
-
t.test("handles mixed BunRoute and regular routes", async () => {
|
|
164
|
-
const mockBundle = { index: "index.html" } as HTMLBundle
|
|
165
|
-
const bunRoute = BunRoute.html(() => Promise.resolve(mockBundle))
|
|
166
|
-
|
|
167
|
-
const routes = await makeBunRoutes(
|
|
168
|
-
Router
|
|
169
|
-
.mount("/app", bunRoute)
|
|
170
|
-
.mount("/api/health", Route.json({ ok: true })),
|
|
171
|
-
)
|
|
172
|
-
|
|
173
|
-
const internalPath = Object.keys(routes).find((k) =>
|
|
174
|
-
k.includes(".BunRoute-")
|
|
175
|
-
)
|
|
176
|
-
|
|
177
|
-
t.expect(internalPath).toBeDefined()
|
|
178
|
-
t.expect(routes[internalPath!]).toBe(mockBundle)
|
|
179
|
-
t.expect(typeof routes["/app"]).toBe("function")
|
|
180
|
-
t.expect(routes["/api/health"]).toBeDefined()
|
|
181
|
-
t.expect(typeof routes["/api/health"]).toBe("object")
|
|
182
|
-
})
|
|
183
|
-
|
|
184
|
-
t.test("groups multiple methods under same path", async () => {
|
|
185
|
-
const fetch = await makeFetch(
|
|
186
|
-
Router.mount(
|
|
187
|
-
"/resource",
|
|
188
|
-
Route
|
|
189
|
-
.get(Route.text("get"))
|
|
190
|
-
.post(Route.text("post"))
|
|
191
|
-
.delete(Route.text("delete")),
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
const response = await Effect.runPromise(
|
|
30
|
+
Effect.scoped(
|
|
31
|
+
Effect
|
|
32
|
+
.gen(function*() {
|
|
33
|
+
const bunServer = yield* BunHttpServer.BunHttpServer
|
|
34
|
+
return yield* Effect.promise(() =>
|
|
35
|
+
fetch(`http://localhost:${bunServer.server.port}/`)
|
|
36
|
+
)
|
|
37
|
+
})
|
|
38
|
+
.pipe(Effect.provide(testLayer(routes))),
|
|
192
39
|
),
|
|
193
40
|
)
|
|
194
41
|
|
|
195
|
-
|
|
196
|
-
const
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
t.expect(await postRes.text()).toBe("post")
|
|
201
|
-
t.expect(await delRes.text()).toBe("delete")
|
|
202
|
-
})
|
|
203
|
-
})
|
|
204
|
-
|
|
205
|
-
t.describe("fetch handler Response", () => {
|
|
206
|
-
t.test("returns Response instance", async () => {
|
|
207
|
-
const fetch = await makeFetch(
|
|
208
|
-
Router.mount("/test", Route.text("test")),
|
|
209
|
-
)
|
|
210
|
-
|
|
211
|
-
const response = await fetch("/test")
|
|
212
|
-
|
|
213
|
-
t.expect(response).toBeInstanceOf(Response)
|
|
214
|
-
})
|
|
215
|
-
|
|
216
|
-
t.test("text response has correct content-type", async () => {
|
|
217
|
-
const fetch = await makeFetch(
|
|
218
|
-
Router.mount("/text", Route.text("hello")),
|
|
219
|
-
)
|
|
220
|
-
|
|
221
|
-
const response = await fetch("/text")
|
|
222
|
-
|
|
223
|
-
t.expect(response.headers.get("content-type")).toContain("text/plain")
|
|
224
|
-
})
|
|
225
|
-
|
|
226
|
-
t.test("json response has correct content-type", async () => {
|
|
227
|
-
const fetch = await makeFetch(
|
|
228
|
-
Router.mount("/json", Route.json({ data: 1 })),
|
|
229
|
-
)
|
|
230
|
-
|
|
231
|
-
const response = await fetch("/json")
|
|
232
|
-
|
|
233
|
-
t.expect(response.headers.get("content-type")).toContain("application/json")
|
|
42
|
+
test.expect(response.status).toBe(200)
|
|
43
|
+
const html = await response.text()
|
|
44
|
+
test.expect(html).toContain("<p>Hello World</p>")
|
|
45
|
+
test.expect(html).toContain("<body>")
|
|
46
|
+
test.expect(html).toContain("</body>")
|
|
234
47
|
})
|
|
235
48
|
|
|
236
|
-
|
|
237
|
-
const
|
|
238
|
-
|
|
49
|
+
test.test("replaces %yield% with child content", async () => {
|
|
50
|
+
const routes = Route.tree({
|
|
51
|
+
"/page": Route.get(
|
|
52
|
+
BunRoute.htmlBundle(() => import("../../static/LayoutSlots.html")),
|
|
53
|
+
Route.html("<div>Page Content</div>"),
|
|
54
|
+
),
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
const response = await Effect.runPromise(
|
|
58
|
+
Effect.scoped(
|
|
59
|
+
Effect
|
|
60
|
+
.gen(function*() {
|
|
61
|
+
const bunServer = yield* BunHttpServer.BunHttpServer
|
|
62
|
+
return yield* Effect.promise(() =>
|
|
63
|
+
fetch(`http://localhost:${bunServer.server.port}/page`)
|
|
64
|
+
)
|
|
65
|
+
})
|
|
66
|
+
.pipe(Effect.provide(testLayer(routes))),
|
|
67
|
+
),
|
|
239
68
|
)
|
|
240
69
|
|
|
241
|
-
const
|
|
242
|
-
|
|
243
|
-
|
|
70
|
+
const html = await response.text()
|
|
71
|
+
test.expect(html).toContain("<div>Page Content</div>")
|
|
72
|
+
test.expect(html).not.toContain("%children%")
|
|
244
73
|
})
|
|
245
74
|
|
|
246
|
-
|
|
247
|
-
const
|
|
248
|
-
|
|
75
|
+
test.test("works with use() for wildcard routes", async () => {
|
|
76
|
+
const routes = Route.tree({
|
|
77
|
+
"*": Route.use(
|
|
78
|
+
BunRoute.htmlBundle(() => import("../../static/LayoutSlots.html")),
|
|
79
|
+
),
|
|
80
|
+
"/:path*": Route.get(Route.html("<section>Catch All</section>")),
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
const response = await Effect.runPromise(
|
|
84
|
+
Effect.scoped(
|
|
85
|
+
Effect
|
|
86
|
+
.gen(function*() {
|
|
87
|
+
const bunServer = yield* BunHttpServer.BunHttpServer
|
|
88
|
+
return yield* Effect.promise(() =>
|
|
89
|
+
fetch(`http://localhost:${bunServer.server.port}/any/path`)
|
|
90
|
+
)
|
|
91
|
+
})
|
|
92
|
+
.pipe(Effect.provide(testLayer(routes))),
|
|
93
|
+
),
|
|
249
94
|
)
|
|
250
95
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
const text = await response.text()
|
|
255
|
-
t.expect(text).toBe("readable body")
|
|
256
|
-
t.expect(response.bodyUsed).toBe(true)
|
|
96
|
+
test.expect(response.status).toBe(200)
|
|
97
|
+
const html = await response.text()
|
|
98
|
+
test.expect(html).toContain("<section>Catch All</section>")
|
|
257
99
|
})
|
|
258
100
|
|
|
259
|
-
|
|
260
|
-
const
|
|
261
|
-
|
|
101
|
+
test.test("has format: html descriptor", async () => {
|
|
102
|
+
const routes = Route.tree({
|
|
103
|
+
"/": Route.get(
|
|
104
|
+
BunRoute.htmlBundle(() => import("../../static/LayoutSlots.html")),
|
|
105
|
+
Route.html("<p>content</p>"),
|
|
106
|
+
),
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
const response = await Effect.runPromise(
|
|
110
|
+
Effect.scoped(
|
|
111
|
+
Effect
|
|
112
|
+
.gen(function*() {
|
|
113
|
+
const bunServer = yield* BunHttpServer.BunHttpServer
|
|
114
|
+
return yield* Effect.promise(() =>
|
|
115
|
+
fetch(`http://localhost:${bunServer.server.port}/`)
|
|
116
|
+
)
|
|
117
|
+
})
|
|
118
|
+
.pipe(Effect.provide(testLayer(routes))),
|
|
119
|
+
),
|
|
262
120
|
)
|
|
263
121
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
t.expect(response.status).toBe(200)
|
|
122
|
+
test.expect(response.status).toBe(200)
|
|
123
|
+
const contentType = response.headers.get("content-type")
|
|
124
|
+
test.expect(contentType).toContain("text/html")
|
|
268
125
|
})
|
|
269
126
|
})
|
|
270
127
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
.
|
|
282
|
-
.mount("/child", Route.text("child content"))
|
|
283
|
-
|
|
284
|
-
const fetch = await makeFetch(router)
|
|
285
|
-
const response = await fetch("/child")
|
|
286
|
-
|
|
287
|
-
t.expect(response.headers.get("X-Layer-Applied")).toBe("true")
|
|
288
|
-
t.expect(await response.text()).toBe("child content")
|
|
289
|
-
})
|
|
290
|
-
|
|
291
|
-
t.test("middleware only applies to children, not siblings", async () => {
|
|
292
|
-
const addHeader = HttpMiddleware.make((app) =>
|
|
293
|
-
Effect.gen(function*() {
|
|
294
|
-
const response = yield* app
|
|
295
|
-
return HttpServerResponse.setHeader(response, "X-Layer-Applied", "true")
|
|
296
|
-
})
|
|
297
|
-
) as Route.HttpMiddlewareFunction
|
|
298
|
-
|
|
299
|
-
const router = Router
|
|
300
|
-
.mount("/outside", Route.text("outside content"))
|
|
301
|
-
.use(Route.layer(Route.http(addHeader)))
|
|
302
|
-
.mount("/inside", Route.text("inside content"))
|
|
303
|
-
|
|
304
|
-
const fetch = await makeFetch(router)
|
|
305
|
-
|
|
306
|
-
const insideResponse = await fetch("/inside")
|
|
307
|
-
t.expect(insideResponse.headers.get("X-Layer-Applied")).toBe("true")
|
|
308
|
-
|
|
309
|
-
const outsideResponse = await fetch("/outside")
|
|
310
|
-
t.expect(outsideResponse.headers.get("X-Layer-Applied")).toBeNull()
|
|
128
|
+
test.describe(BunRoute.validateBunPattern, () => {
|
|
129
|
+
test.test("returns none for valid patterns", () => {
|
|
130
|
+
test
|
|
131
|
+
.expect(Option.isNone(BunRoute.validateBunPattern("/users")))
|
|
132
|
+
.toBe(true)
|
|
133
|
+
test
|
|
134
|
+
.expect(Option.isNone(BunRoute.validateBunPattern("/users/[id]")))
|
|
135
|
+
.toBe(true)
|
|
136
|
+
test
|
|
137
|
+
.expect(Option.isNone(BunRoute.validateBunPattern("/[...rest]")))
|
|
138
|
+
.toBe(true)
|
|
311
139
|
})
|
|
312
140
|
|
|
313
|
-
|
|
314
|
-
const
|
|
315
|
-
|
|
316
|
-
const response = yield* app
|
|
317
|
-
return HttpServerResponse.setHeader(response, "X-First", "1")
|
|
318
|
-
})
|
|
319
|
-
) as Route.HttpMiddlewareFunction
|
|
320
|
-
|
|
321
|
-
const addHeader2 = HttpMiddleware.make((app) =>
|
|
322
|
-
Effect.gen(function*() {
|
|
323
|
-
const response = yield* app
|
|
324
|
-
return HttpServerResponse.setHeader(response, "X-Second", "2")
|
|
325
|
-
})
|
|
326
|
-
) as Route.HttpMiddlewareFunction
|
|
327
|
-
|
|
328
|
-
const router = Router
|
|
329
|
-
.use(Route.layer(Route.http(addHeader1), Route.http(addHeader2)))
|
|
330
|
-
.mount("/test", Route.text("test"))
|
|
331
|
-
|
|
332
|
-
const fetch = await makeFetch(router)
|
|
333
|
-
const response = await fetch("/test")
|
|
334
|
-
|
|
335
|
-
t.expect(response.headers.get("X-First")).toBe("1")
|
|
336
|
-
t.expect(response.headers.get("X-Second")).toBe("2")
|
|
141
|
+
test.test("returns error for prefixed params", () => {
|
|
142
|
+
const result = BunRoute.validateBunPattern("/pk_[id]")
|
|
143
|
+
test.expect(Option.isSome(result)).toBe(true)
|
|
337
144
|
})
|
|
338
145
|
|
|
339
|
-
|
|
340
|
-
const
|
|
341
|
-
|
|
342
|
-
const response = yield* app
|
|
343
|
-
return HttpServerResponse.setHeader(response, "X-Outer", "outer")
|
|
344
|
-
})
|
|
345
|
-
) as Route.HttpMiddlewareFunction
|
|
346
|
-
|
|
347
|
-
const innerHeader = HttpMiddleware.make((app) =>
|
|
348
|
-
Effect.gen(function*() {
|
|
349
|
-
const response = yield* app
|
|
350
|
-
return HttpServerResponse.setHeader(response, "X-Inner", "inner")
|
|
351
|
-
})
|
|
352
|
-
) as Route.HttpMiddlewareFunction
|
|
353
|
-
|
|
354
|
-
const router = Router
|
|
355
|
-
.use(Route.layer(Route.http(outerHeader)))
|
|
356
|
-
.use(Route.layer(Route.http(innerHeader)))
|
|
357
|
-
.mount("/nested", Route.text("nested"))
|
|
358
|
-
|
|
359
|
-
const fetch = await makeFetch(router)
|
|
360
|
-
const response = await fetch("/nested")
|
|
361
|
-
|
|
362
|
-
t.expect(response.headers.get("X-Outer")).toBe("outer")
|
|
363
|
-
t.expect(response.headers.get("X-Inner")).toBe("inner")
|
|
146
|
+
test.test("returns error for suffixed params", () => {
|
|
147
|
+
const result = BunRoute.validateBunPattern("/[id]_suffix")
|
|
148
|
+
test.expect(Option.isSome(result)).toBe(true)
|
|
364
149
|
})
|
|
365
150
|
})
|
|
366
151
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
)
|
|
372
|
-
|
|
373
|
-
const router = Router
|
|
374
|
-
.use(Route.layer(layoutBundle))
|
|
375
|
-
.mount("/page", Route.html(Effect.succeed("<p>Child Content</p>")))
|
|
376
|
-
|
|
377
|
-
await Effect.runPromise(
|
|
378
|
-
Effect
|
|
379
|
-
.gen(function*() {
|
|
380
|
-
const bunServer = yield* BunHttpServer.BunHttpServer
|
|
381
|
-
const routes = yield* BunRoute.routesFromRouter(router)
|
|
382
|
-
bunServer.addRoutes(routes)
|
|
383
|
-
|
|
384
|
-
const baseUrl =
|
|
385
|
-
`http://${bunServer.server.hostname}:${bunServer.server.port}`
|
|
386
|
-
const response = yield* Effect.promise(() => fetch(`${baseUrl}/page`))
|
|
387
|
-
|
|
388
|
-
const html = yield* Effect.promise(() => response.text())
|
|
389
|
-
|
|
390
|
-
t.expect(html).toContain("<body>")
|
|
391
|
-
t.expect(html).toContain("<p>Child Content</p>")
|
|
392
|
-
t.expect(html).not.toContain("%yield%")
|
|
393
|
-
t.expect(html).not.toContain("%slots.")
|
|
394
|
-
})
|
|
395
|
-
.pipe(
|
|
396
|
-
Effect.scoped,
|
|
397
|
-
Effect.provide(BunHttpServer.layer({ port: 0 })),
|
|
398
|
-
),
|
|
399
|
-
)
|
|
152
|
+
test.describe(BunRoute.isHtmlBundle, () => {
|
|
153
|
+
test.test("returns false for non-objects", () => {
|
|
154
|
+
test.expect(BunRoute.isHtmlBundle(null)).toBe(false)
|
|
155
|
+
test.expect(BunRoute.isHtmlBundle(undefined)).toBe(false)
|
|
156
|
+
test.expect(BunRoute.isHtmlBundle("string")).toBe(false)
|
|
400
157
|
})
|
|
401
158
|
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
import("../../static/LayoutSlots.html")
|
|
405
|
-
)
|
|
406
|
-
|
|
407
|
-
const router = Router.mount("/layout", layoutBundle)
|
|
408
|
-
|
|
409
|
-
await Effect.runPromise(
|
|
410
|
-
Effect
|
|
411
|
-
.gen(function*() {
|
|
412
|
-
const bunServer = yield* BunHttpServer.BunHttpServer
|
|
413
|
-
const routes = yield* BunRoute.routesFromRouter(router)
|
|
414
|
-
bunServer.addRoutes(routes)
|
|
415
|
-
|
|
416
|
-
const baseUrl =
|
|
417
|
-
`http://${bunServer.server.hostname}:${bunServer.server.port}`
|
|
418
|
-
const response = yield* Effect.promise(() =>
|
|
419
|
-
fetch(`${baseUrl}/layout`)
|
|
420
|
-
)
|
|
421
|
-
|
|
422
|
-
const html = yield* Effect.promise(() => response.text())
|
|
423
|
-
|
|
424
|
-
t.expect(html).toContain("<title></title>")
|
|
425
|
-
t.expect(html).toContain("<body>")
|
|
426
|
-
t.expect(html).not.toContain("%yield%")
|
|
427
|
-
t.expect(html).not.toContain("%slots.")
|
|
428
|
-
})
|
|
429
|
-
.pipe(
|
|
430
|
-
Effect.scoped,
|
|
431
|
-
Effect.provide(BunHttpServer.layer({ port: 0 })),
|
|
432
|
-
),
|
|
433
|
-
)
|
|
159
|
+
test.test("returns true for object with index property", () => {
|
|
160
|
+
test.expect(BunRoute.isHtmlBundle({ index: "index.html" })).toBe(true)
|
|
434
161
|
})
|
|
435
162
|
})
|
|
436
|
-
|
|
437
|
-
type FetchFn = (path: string, init?: { method?: string }) => Promise<Response>
|
|
438
|
-
|
|
439
|
-
type HandlerFn = (
|
|
440
|
-
req: Request,
|
|
441
|
-
server: unknown,
|
|
442
|
-
) => Response | Promise<Response>
|
|
443
|
-
|
|
444
|
-
async function makeBunRoutes(
|
|
445
|
-
router: Router.Router.Any,
|
|
446
|
-
): Promise<BunRoute.BunRoutes> {
|
|
447
|
-
return Effect.runPromise(
|
|
448
|
-
BunRoute.routesFromRouter(router).pipe(
|
|
449
|
-
Effect.provide(BunHttpServer.layer({ port: 0 })),
|
|
450
|
-
),
|
|
451
|
-
)
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
async function makeFetch(router: Router.Router.Any): Promise<FetchFn> {
|
|
455
|
-
const routes = await makeBunRoutes(router)
|
|
456
|
-
const mockServer = {} as import("bun").Server<unknown>
|
|
457
|
-
|
|
458
|
-
return async (path, init) => {
|
|
459
|
-
const method = init?.method ?? "GET"
|
|
460
|
-
const handler = routes[path]
|
|
461
|
-
|
|
462
|
-
if (!handler) {
|
|
463
|
-
throw new Error(`No handler for path: ${path}`)
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
if (typeof handler === "function") {
|
|
467
|
-
return handler(new Request(`http://localhost${path}`, init), mockServer)
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
const methodHandler = (handler as Record<string, HandlerFn>)[method]
|
|
471
|
-
if (!methodHandler) {
|
|
472
|
-
throw new Error(`No handler for ${method} ${path}`)
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
return methodHandler(
|
|
476
|
-
new Request(`http://localhost${path}`, init),
|
|
477
|
-
mockServer,
|
|
478
|
-
)
|
|
479
|
-
}
|
|
480
|
-
}
|