effect-start 0.17.2 → 0.19.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/dist/Development.d.ts +7 -2
- package/dist/Development.js +12 -6
- package/dist/PlatformRuntime.d.ts +4 -0
- package/dist/PlatformRuntime.js +9 -0
- package/dist/Route.d.ts +6 -2
- package/dist/Route.js +22 -0
- package/dist/RouteHttp.d.ts +1 -1
- package/dist/RouteHttp.js +12 -19
- package/dist/RouteMount.d.ts +2 -1
- package/dist/Start.d.ts +1 -5
- package/dist/Start.js +1 -8
- package/dist/Unique.d.ts +50 -0
- package/dist/Unique.js +187 -0
- package/dist/bun/BunHttpServer.js +5 -6
- package/dist/bun/BunRoute.d.ts +1 -1
- package/dist/bun/BunRoute.js +2 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/node/Effectify.d.ts +209 -0
- package/dist/node/Effectify.js +19 -0
- package/dist/node/FileSystem.d.ts +3 -5
- package/dist/node/FileSystem.js +42 -62
- package/dist/node/PlatformError.d.ts +46 -0
- package/dist/node/PlatformError.js +43 -0
- package/dist/testing/TestLogger.js +1 -1
- package/package.json +10 -5
- package/src/Development.ts +13 -18
- package/src/PlatformRuntime.ts +11 -0
- package/src/Route.ts +31 -2
- package/src/RouteHttp.ts +15 -31
- package/src/RouteMount.ts +1 -1
- package/src/Start.ts +1 -15
- package/src/Unique.ts +232 -0
- package/src/bun/BunHttpServer.ts +6 -9
- package/src/bun/BunRoute.ts +3 -3
- package/src/index.ts +1 -0
- package/src/node/Effectify.ts +262 -0
- package/src/node/FileSystem.ts +59 -97
- package/src/node/PlatformError.ts +102 -0
- package/src/testing/TestLogger.ts +1 -1
- package/dist/Random.d.ts +0 -5
- package/dist/Random.js +0 -49
- package/src/Commander.test.ts +0 -1639
- package/src/ContentNegotiation.test.ts +0 -603
- package/src/Development.test.ts +0 -119
- package/src/Entity.test.ts +0 -592
- package/src/FileRouterPattern.test.ts +0 -147
- package/src/FileRouter_files.test.ts +0 -64
- package/src/FileRouter_path.test.ts +0 -145
- package/src/FileRouter_tree.test.ts +0 -132
- package/src/Http.test.ts +0 -319
- package/src/HttpAppExtra.test.ts +0 -103
- package/src/HttpUtils.test.ts +0 -85
- package/src/PathPattern.test.ts +0 -648
- package/src/Random.ts +0 -59
- package/src/RouteBody.test.ts +0 -232
- package/src/RouteHook.test.ts +0 -40
- package/src/RouteHttp.test.ts +0 -2909
- package/src/RouteMount.test.ts +0 -481
- package/src/RouteSchema.test.ts +0 -427
- package/src/RouteSse.test.ts +0 -249
- package/src/RouteTree.test.ts +0 -494
- package/src/RouteTrie.test.ts +0 -322
- package/src/RouterPattern.test.ts +0 -676
- package/src/Values.test.ts +0 -263
- package/src/bun/BunBundle.test.ts +0 -268
- package/src/bun/BunBundle_imports.test.ts +0 -48
- package/src/bun/BunHttpServer.test.ts +0 -251
- package/src/bun/BunImportTrackerPlugin.test.ts +0 -77
- package/src/bun/BunRoute.test.ts +0 -162
- package/src/bundler/BundleHttp.test.ts +0 -132
- package/src/effect/HttpRouter.test.ts +0 -548
- package/src/experimental/EncryptedCookies.test.ts +0 -488
- package/src/hyper/HyperHtml.test.ts +0 -209
- package/src/hyper/HyperRoute.test.tsx +0 -197
- package/src/middlewares/BasicAuthMiddleware.test.ts +0 -84
- package/src/testing/TestHttpClient.test.ts +0 -83
- package/src/testing/TestLogger.test.ts +0 -51
- package/src/x/datastar/Datastar.test.ts +0 -266
- package/src/x/tailwind/TailwindPlugin.test.ts +0 -333
package/src/RouteTree.test.ts
DELETED
|
@@ -1,494 +0,0 @@
|
|
|
1
|
-
import * as test from "bun:test"
|
|
2
|
-
import * as Route from "./Route.ts"
|
|
3
|
-
import * as RouteTree from "./RouteTree.ts"
|
|
4
|
-
|
|
5
|
-
test.describe("layer route", () => {
|
|
6
|
-
test.it("merges LayerRoute into other routes", () => {
|
|
7
|
-
const tree = RouteTree.make({
|
|
8
|
-
"*": Route.use(Route.filter({ context: { authenticated: true } })),
|
|
9
|
-
"/users": Route.get(Route.text("users")),
|
|
10
|
-
})
|
|
11
|
-
|
|
12
|
-
type TreeRoutes = RouteTree.Routes<typeof tree>
|
|
13
|
-
|
|
14
|
-
test
|
|
15
|
-
.expectTypeOf<TreeRoutes>()
|
|
16
|
-
.toExtend<{
|
|
17
|
-
"/users": Route.Route.Tuple
|
|
18
|
-
}>()
|
|
19
|
-
|
|
20
|
-
// "*" key should not exist in the resulting type
|
|
21
|
-
test
|
|
22
|
-
.expectTypeOf<TreeRoutes>()
|
|
23
|
-
.not
|
|
24
|
-
.toHaveProperty("*")
|
|
25
|
-
|
|
26
|
-
// layer route should be first in the tuple (method: "*")
|
|
27
|
-
test
|
|
28
|
-
.expectTypeOf<TreeRoutes["/users"][0]>()
|
|
29
|
-
.toExtend<Route.Route.With<{ method: "*" }>>()
|
|
30
|
-
|
|
31
|
-
// actual route should be second (method: "GET")
|
|
32
|
-
test
|
|
33
|
-
.expectTypeOf<TreeRoutes["/users"][1]>()
|
|
34
|
-
.toExtend<Route.Route.With<{ method: "GET" }>>()
|
|
35
|
-
})
|
|
36
|
-
|
|
37
|
-
test.it("prepends LayerRoute to all other routes when walking", () => {
|
|
38
|
-
const tree = RouteTree.make({
|
|
39
|
-
"*": Route.use(Route.filter({ context: { layer: true } })),
|
|
40
|
-
"/users": Route.get(Route.text("users")),
|
|
41
|
-
"/admin": Route.post(Route.json({ ok: true })),
|
|
42
|
-
})
|
|
43
|
-
|
|
44
|
-
test.expect(Route.descriptor(RouteTree.walk(tree))).toEqual([
|
|
45
|
-
{ path: "/admin", method: "*" },
|
|
46
|
-
{ path: "/admin", method: "POST", format: "json" },
|
|
47
|
-
{ path: "/users", method: "*" },
|
|
48
|
-
{ path: "/users", method: "GET", format: "text" },
|
|
49
|
-
])
|
|
50
|
-
})
|
|
51
|
-
|
|
52
|
-
test.it("prepends multiple LayerRoutes to all other routes", () => {
|
|
53
|
-
const tree = RouteTree.make({
|
|
54
|
-
"*": Route
|
|
55
|
-
.use(Route.filter({ context: { first: true } }))
|
|
56
|
-
.use(Route.filter({ context: { second: true } })),
|
|
57
|
-
"/users": Route.get(Route.text("users")),
|
|
58
|
-
})
|
|
59
|
-
|
|
60
|
-
test.expect(Route.descriptor(RouteTree.walk(tree))).toEqual([
|
|
61
|
-
{ path: "/users", method: "*" },
|
|
62
|
-
{ path: "/users", method: "*" },
|
|
63
|
-
{ path: "/users", method: "GET", format: "text" },
|
|
64
|
-
])
|
|
65
|
-
})
|
|
66
|
-
|
|
67
|
-
test.it("only allows method '*' routes under '*' key", () => {
|
|
68
|
-
const _tree = RouteTree.make({
|
|
69
|
-
// @ts-expect-error - LayerRoute must have method "*"
|
|
70
|
-
"*": Route.get(Route.text("invalid")),
|
|
71
|
-
"/users": Route.get(Route.text("users")),
|
|
72
|
-
})
|
|
73
|
-
})
|
|
74
|
-
|
|
75
|
-
test.it("lookup finds LayerRoute first", () => {
|
|
76
|
-
const tree = RouteTree.make({
|
|
77
|
-
"*": Route.use(Route.filter({ context: { layer: true } })),
|
|
78
|
-
"/users": Route.get(Route.text("users")),
|
|
79
|
-
})
|
|
80
|
-
|
|
81
|
-
const result = RouteTree.lookup(tree, "GET", "/users")
|
|
82
|
-
test.expect(result).not.toBeNull()
|
|
83
|
-
test.expect(Route.descriptor(result!.route).method).toBe("*")
|
|
84
|
-
})
|
|
85
|
-
|
|
86
|
-
test.it("works without LayerRoute (no '*' key)", () => {
|
|
87
|
-
const tree = RouteTree.make({
|
|
88
|
-
"/users": Route.get(Route.text("users")),
|
|
89
|
-
"/admin": Route.post(Route.json({ ok: true })),
|
|
90
|
-
})
|
|
91
|
-
|
|
92
|
-
test
|
|
93
|
-
.expect(Route.descriptor(RouteTree.walk(tree)).map((d) => d.path))
|
|
94
|
-
.toEqual(["/admin", "/users"])
|
|
95
|
-
})
|
|
96
|
-
})
|
|
97
|
-
|
|
98
|
-
test.describe(RouteTree.make, () => {
|
|
99
|
-
test.it("makes", () => {
|
|
100
|
-
const routes = RouteTree.make({
|
|
101
|
-
"/admin": Route
|
|
102
|
-
.use(
|
|
103
|
-
Route.filter({
|
|
104
|
-
context: {
|
|
105
|
-
isAdmin: true,
|
|
106
|
-
},
|
|
107
|
-
}),
|
|
108
|
-
)
|
|
109
|
-
.get(
|
|
110
|
-
Route.text("admin home"),
|
|
111
|
-
),
|
|
112
|
-
|
|
113
|
-
"/users": Route
|
|
114
|
-
.get(
|
|
115
|
-
Route.text("users list"),
|
|
116
|
-
),
|
|
117
|
-
})
|
|
118
|
-
|
|
119
|
-
test
|
|
120
|
-
.expectTypeOf<RouteTree.Routes<typeof routes>>()
|
|
121
|
-
.toExtend<{
|
|
122
|
-
"/admin": unknown
|
|
123
|
-
"/users": unknown
|
|
124
|
-
}>()
|
|
125
|
-
})
|
|
126
|
-
|
|
127
|
-
test.it("flattens nested route trees with prefixed paths", () => {
|
|
128
|
-
const apiTree = RouteTree.make({
|
|
129
|
-
"/users": Route.get(Route.json({ users: [] })),
|
|
130
|
-
"/posts": Route.get(Route.json({ posts: [] })),
|
|
131
|
-
})
|
|
132
|
-
|
|
133
|
-
const tree = RouteTree.make({
|
|
134
|
-
"/": Route.get(Route.text("home")),
|
|
135
|
-
"/api": apiTree,
|
|
136
|
-
})
|
|
137
|
-
|
|
138
|
-
type TreeRoutes = RouteTree.Routes<typeof tree>
|
|
139
|
-
|
|
140
|
-
test
|
|
141
|
-
.expectTypeOf<TreeRoutes["/"]>()
|
|
142
|
-
.toExtend<Route.Route.Tuple>()
|
|
143
|
-
|
|
144
|
-
test
|
|
145
|
-
.expectTypeOf<TreeRoutes["/"][0]>()
|
|
146
|
-
.toExtend<
|
|
147
|
-
Route.Route.With<
|
|
148
|
-
{ method: "GET"; format: "text" }
|
|
149
|
-
>
|
|
150
|
-
>()
|
|
151
|
-
|
|
152
|
-
test
|
|
153
|
-
.expectTypeOf<TreeRoutes["/api/users"]>()
|
|
154
|
-
.toExtend<Route.Route.Tuple>()
|
|
155
|
-
|
|
156
|
-
test
|
|
157
|
-
.expectTypeOf<TreeRoutes["/api/users"][0]>()
|
|
158
|
-
.toExtend<
|
|
159
|
-
Route.Route.With<
|
|
160
|
-
{ method: "GET"; format: "json" }
|
|
161
|
-
>
|
|
162
|
-
>()
|
|
163
|
-
|
|
164
|
-
test
|
|
165
|
-
.expectTypeOf<TreeRoutes["/api/posts"]>()
|
|
166
|
-
.toExtend<Route.Route.Tuple>()
|
|
167
|
-
|
|
168
|
-
test
|
|
169
|
-
.expectTypeOf<TreeRoutes["/api/posts"][0]>()
|
|
170
|
-
.toExtend<
|
|
171
|
-
Route.Route.With<
|
|
172
|
-
{ method: "GET"; format: "json" }
|
|
173
|
-
>
|
|
174
|
-
>()
|
|
175
|
-
})
|
|
176
|
-
|
|
177
|
-
test.it("walks nested route trees with prefixed paths", () => {
|
|
178
|
-
const apiTree = RouteTree.make({
|
|
179
|
-
"/users": Route.get(Route.json({ users: [] })),
|
|
180
|
-
"/posts": Route.post(Route.json({ ok: true })),
|
|
181
|
-
})
|
|
182
|
-
|
|
183
|
-
const tree = RouteTree.make({
|
|
184
|
-
"/": Route.get(Route.text("home")),
|
|
185
|
-
"/api": apiTree,
|
|
186
|
-
})
|
|
187
|
-
|
|
188
|
-
test.expect(Route.descriptor(RouteTree.walk(tree))).toEqual([
|
|
189
|
-
{ path: "/", method: "GET", format: "text" },
|
|
190
|
-
{ path: "/api/posts", method: "POST", format: "json" },
|
|
191
|
-
{ path: "/api/users", method: "GET", format: "json" },
|
|
192
|
-
])
|
|
193
|
-
})
|
|
194
|
-
|
|
195
|
-
test.it("deeply nested route trees", () => {
|
|
196
|
-
const v1Tree = RouteTree.make({
|
|
197
|
-
"/health": Route.get(Route.text("ok")),
|
|
198
|
-
})
|
|
199
|
-
|
|
200
|
-
const apiTree = RouteTree.make({
|
|
201
|
-
"/v1": v1Tree,
|
|
202
|
-
})
|
|
203
|
-
|
|
204
|
-
const tree = RouteTree.make({
|
|
205
|
-
"/api": apiTree,
|
|
206
|
-
})
|
|
207
|
-
|
|
208
|
-
test
|
|
209
|
-
.expect(Route.descriptor(RouteTree.walk(tree)).map((d) => d.path))
|
|
210
|
-
.toEqual(["/api/v1/health"])
|
|
211
|
-
})
|
|
212
|
-
|
|
213
|
-
test.it("lookup works with nested trees", () => {
|
|
214
|
-
const apiTree = RouteTree.make({
|
|
215
|
-
"/users": Route.get(Route.json({ users: [] })),
|
|
216
|
-
"/users/:id": Route.get(Route.json({ user: null })),
|
|
217
|
-
})
|
|
218
|
-
|
|
219
|
-
const tree = RouteTree.make({
|
|
220
|
-
"/": Route.get(Route.text("home")),
|
|
221
|
-
"/api": apiTree,
|
|
222
|
-
})
|
|
223
|
-
|
|
224
|
-
const home = RouteTree.lookup(tree, "GET", "/")
|
|
225
|
-
test.expect(Route.descriptor(home!.route).path).toBe("/")
|
|
226
|
-
|
|
227
|
-
const users = RouteTree.lookup(tree, "GET", "/api/users")
|
|
228
|
-
test.expect(Route.descriptor(users!.route).path).toBe("/api/users")
|
|
229
|
-
|
|
230
|
-
const user = RouteTree.lookup(tree, "GET", "/api/users/123")
|
|
231
|
-
test.expect(Route.descriptor(user!.route).path).toBe("/api/users/:id")
|
|
232
|
-
test.expect(user!.params).toEqual({ id: "123" })
|
|
233
|
-
})
|
|
234
|
-
})
|
|
235
|
-
|
|
236
|
-
test.describe(RouteTree.walk, () => {
|
|
237
|
-
test.it("walks in sorted order: by depth, static before params", () => {
|
|
238
|
-
// routes defined in random order
|
|
239
|
-
const routes = RouteTree.make({
|
|
240
|
-
"/users/:userId": Route.get(Route.text("user detail")),
|
|
241
|
-
"/admin/stats": Route.get(Route.html("admin stats")),
|
|
242
|
-
"/": Route.get(Route.text("home")),
|
|
243
|
-
"/admin/users": Route.post(Route.json({ ok: true })),
|
|
244
|
-
"/users": Route.get(Route.text("users list")),
|
|
245
|
-
"/admin": Route.use(Route.filter({ context: { admin: true } })),
|
|
246
|
-
})
|
|
247
|
-
|
|
248
|
-
// expected order:
|
|
249
|
-
// depth 0: /
|
|
250
|
-
// depth 1: /admin, /users (alphabetical)
|
|
251
|
-
// depth 2: /admin/stats, /admin/users, /users/:userId (static first, param last)
|
|
252
|
-
test
|
|
253
|
-
.expect(Route.descriptor(RouteTree.walk(routes)).map((d) => d.path))
|
|
254
|
-
.toEqual([
|
|
255
|
-
"/",
|
|
256
|
-
"/admin",
|
|
257
|
-
"/users",
|
|
258
|
-
"/admin/stats",
|
|
259
|
-
"/admin/users",
|
|
260
|
-
"/users/:userId",
|
|
261
|
-
])
|
|
262
|
-
})
|
|
263
|
-
|
|
264
|
-
test.it("static < :param < :param? < :param+ < :param*", () => {
|
|
265
|
-
const routes = RouteTree.make({
|
|
266
|
-
"/:path*": Route.get(Route.text("catch all")),
|
|
267
|
-
"/about": Route.get(Route.text("about")),
|
|
268
|
-
"/:path+": Route.get(Route.text("one or more")),
|
|
269
|
-
"/:page": Route.get(Route.text("single param")),
|
|
270
|
-
"/:page?": Route.get(Route.text("optional param")),
|
|
271
|
-
})
|
|
272
|
-
|
|
273
|
-
test
|
|
274
|
-
.expect(Route.descriptor(RouteTree.walk(routes)).map((d) => d.path))
|
|
275
|
-
.toEqual([
|
|
276
|
-
"/about",
|
|
277
|
-
"/:page",
|
|
278
|
-
"/:page?",
|
|
279
|
-
"/:path+",
|
|
280
|
-
"/:path*",
|
|
281
|
-
])
|
|
282
|
-
})
|
|
283
|
-
|
|
284
|
-
test.it("greedy routes come after all non-greedy across depth", () => {
|
|
285
|
-
const routes = RouteTree.make({
|
|
286
|
-
"/:path*": Route.get(Route.text("catch all")),
|
|
287
|
-
"/users/:id": Route.get(Route.text("user detail")),
|
|
288
|
-
"/users": Route.get(Route.text("users")),
|
|
289
|
-
"/users/:id/posts/:postId": Route.get(Route.text("post detail")),
|
|
290
|
-
})
|
|
291
|
-
|
|
292
|
-
test
|
|
293
|
-
.expect(Route.descriptor(RouteTree.walk(routes)).map((d) => d.path))
|
|
294
|
-
.toEqual([
|
|
295
|
-
"/users",
|
|
296
|
-
"/users/:id",
|
|
297
|
-
"/users/:id/posts/:postId",
|
|
298
|
-
"/:path*",
|
|
299
|
-
])
|
|
300
|
-
})
|
|
301
|
-
|
|
302
|
-
test.it("greedy routes sorted by greedy position", () => {
|
|
303
|
-
const routes = RouteTree.make({
|
|
304
|
-
"/:path*": Route.get(Route.text("root catch all")),
|
|
305
|
-
"/api/:rest*": Route.get(Route.text("api catch all")),
|
|
306
|
-
"/api/v1/:rest*": Route.get(Route.text("api v1 catch all")),
|
|
307
|
-
})
|
|
308
|
-
|
|
309
|
-
test
|
|
310
|
-
.expect(Route.descriptor(RouteTree.walk(routes)).map((d) => d.path))
|
|
311
|
-
.toEqual([
|
|
312
|
-
"/api/v1/:rest*",
|
|
313
|
-
"/api/:rest*",
|
|
314
|
-
"/:path*",
|
|
315
|
-
])
|
|
316
|
-
})
|
|
317
|
-
|
|
318
|
-
test.it("greedy routes with same position sorted by prefix then type", () => {
|
|
319
|
-
const routes = RouteTree.make({
|
|
320
|
-
"/docs/:path*": Route.get(Route.text("docs catch all")),
|
|
321
|
-
"/api/:rest+": Route.get(Route.text("api one or more")),
|
|
322
|
-
"/api/:rest*": Route.get(Route.text("api catch all")),
|
|
323
|
-
})
|
|
324
|
-
|
|
325
|
-
// /api before /docs (alphabetical), then + before * for same prefix
|
|
326
|
-
test
|
|
327
|
-
.expect(Route.descriptor(RouteTree.walk(routes)).map((d) => d.path))
|
|
328
|
-
.toEqual([
|
|
329
|
-
"/api/:rest+",
|
|
330
|
-
"/api/:rest*",
|
|
331
|
-
"/docs/:path*",
|
|
332
|
-
])
|
|
333
|
-
})
|
|
334
|
-
})
|
|
335
|
-
|
|
336
|
-
test.describe(RouteTree.lookup, () => {
|
|
337
|
-
test.it("matches static paths", () => {
|
|
338
|
-
const tree = RouteTree.make({
|
|
339
|
-
"/users": Route.get(Route.text("users list")),
|
|
340
|
-
"/admin": Route.get(Route.text("admin")),
|
|
341
|
-
})
|
|
342
|
-
|
|
343
|
-
const result = RouteTree.lookup(tree, "GET", "/users")
|
|
344
|
-
test.expect(result).not.toBeNull()
|
|
345
|
-
test.expect(result!.params).toEqual({})
|
|
346
|
-
test.expect(Route.descriptor(result!.route).path).toBe("/users")
|
|
347
|
-
})
|
|
348
|
-
|
|
349
|
-
test.it("extracts path parameters", () => {
|
|
350
|
-
const tree = RouteTree.make({
|
|
351
|
-
"/users/:id": Route.get(Route.text("user detail")),
|
|
352
|
-
})
|
|
353
|
-
|
|
354
|
-
const result = RouteTree.lookup(tree, "GET", "/users/123")
|
|
355
|
-
test.expect(result).not.toBeNull()
|
|
356
|
-
test.expect(result!.params).toEqual({ id: "123" })
|
|
357
|
-
})
|
|
358
|
-
|
|
359
|
-
test.it("filters by HTTP method", () => {
|
|
360
|
-
const tree = RouteTree.make({
|
|
361
|
-
"/users": Route.get(Route.text("get users")),
|
|
362
|
-
"/admin": Route.post(Route.text("post admin")),
|
|
363
|
-
})
|
|
364
|
-
|
|
365
|
-
const getResult = RouteTree.lookup(tree, "GET", "/users")
|
|
366
|
-
test.expect(getResult).not.toBeNull()
|
|
367
|
-
|
|
368
|
-
const postOnGet = RouteTree.lookup(tree, "POST", "/users")
|
|
369
|
-
test.expect(postOnGet).toBeNull()
|
|
370
|
-
|
|
371
|
-
const postResult = RouteTree.lookup(tree, "POST", "/admin")
|
|
372
|
-
test.expect(postResult).not.toBeNull()
|
|
373
|
-
})
|
|
374
|
-
|
|
375
|
-
test.it("wildcard method matches any method", () => {
|
|
376
|
-
const tree = RouteTree.make({
|
|
377
|
-
"/api": Route.use(Route.filter({ context: { api: true } })),
|
|
378
|
-
})
|
|
379
|
-
|
|
380
|
-
const getResult = RouteTree.lookup(tree, "GET", "/api")
|
|
381
|
-
test.expect(getResult).not.toBeNull()
|
|
382
|
-
|
|
383
|
-
const postResult = RouteTree.lookup(tree, "POST", "/api")
|
|
384
|
-
test.expect(postResult).not.toBeNull()
|
|
385
|
-
|
|
386
|
-
const deleteResult = RouteTree.lookup(tree, "DELETE", "/api")
|
|
387
|
-
test.expect(deleteResult).not.toBeNull()
|
|
388
|
-
})
|
|
389
|
-
|
|
390
|
-
test.it("static routes take priority over param routes", () => {
|
|
391
|
-
const tree = RouteTree.make({
|
|
392
|
-
"/users/:id": Route.get(Route.text("user by id")),
|
|
393
|
-
"/users/me": Route.get(Route.text("current user")),
|
|
394
|
-
})
|
|
395
|
-
|
|
396
|
-
const result = RouteTree.lookup(tree, "GET", "/users/me")
|
|
397
|
-
test.expect(result).not.toBeNull()
|
|
398
|
-
test.expect(Route.descriptor(result!.route).path).toBe("/users/me")
|
|
399
|
-
test.expect(result!.params).toEqual({})
|
|
400
|
-
})
|
|
401
|
-
|
|
402
|
-
test.it("matches greedy params with +", () => {
|
|
403
|
-
const tree = RouteTree.make({
|
|
404
|
-
"/docs/:path+": Route.get(Route.text("docs")),
|
|
405
|
-
})
|
|
406
|
-
|
|
407
|
-
const result = RouteTree.lookup(tree, "GET", "/docs/api/v1/users")
|
|
408
|
-
test.expect(result).not.toBeNull()
|
|
409
|
-
test.expect(result!.params).toEqual({ path: "api/v1/users" })
|
|
410
|
-
|
|
411
|
-
const noMatch = RouteTree.lookup(tree, "GET", "/docs")
|
|
412
|
-
test.expect(noMatch).toBeNull()
|
|
413
|
-
})
|
|
414
|
-
|
|
415
|
-
test.it("matches greedy params with *", () => {
|
|
416
|
-
const tree = RouteTree.make({
|
|
417
|
-
"/files/:path*": Route.get(Route.text("files")),
|
|
418
|
-
})
|
|
419
|
-
|
|
420
|
-
const withPath = RouteTree.lookup(tree, "GET", "/files/a/b/c")
|
|
421
|
-
test.expect(withPath).not.toBeNull()
|
|
422
|
-
test.expect(withPath!.params).toEqual({ path: "a/b/c" })
|
|
423
|
-
|
|
424
|
-
const withoutPath = RouteTree.lookup(tree, "GET", "/files")
|
|
425
|
-
test.expect(withoutPath).not.toBeNull()
|
|
426
|
-
test.expect(withoutPath!.params).toEqual({})
|
|
427
|
-
})
|
|
428
|
-
|
|
429
|
-
test.it("returns null for no match", () => {
|
|
430
|
-
const tree = RouteTree.make({
|
|
431
|
-
"/users": Route.get(Route.text("users")),
|
|
432
|
-
})
|
|
433
|
-
|
|
434
|
-
const result = RouteTree.lookup(tree, "GET", "/not-found")
|
|
435
|
-
test.expect(result).toBeNull()
|
|
436
|
-
})
|
|
437
|
-
|
|
438
|
-
test.it("matches optional params with ?", () => {
|
|
439
|
-
const tree = RouteTree.make({
|
|
440
|
-
"/files/:name?": Route.get(Route.text("files")),
|
|
441
|
-
})
|
|
442
|
-
|
|
443
|
-
const withParam = RouteTree.lookup(tree, "GET", "/files/readme")
|
|
444
|
-
test.expect(withParam).not.toBeNull()
|
|
445
|
-
test.expect(withParam!.params).toEqual({ name: "readme" })
|
|
446
|
-
|
|
447
|
-
const withoutParam = RouteTree.lookup(tree, "GET", "/files")
|
|
448
|
-
test.expect(withoutParam).not.toBeNull()
|
|
449
|
-
test.expect(withoutParam!.params).toEqual({})
|
|
450
|
-
})
|
|
451
|
-
|
|
452
|
-
test.it("respects route priority for complex trees", () => {
|
|
453
|
-
const tree = RouteTree.make({
|
|
454
|
-
"/:path*": Route.get(Route.text("catch all")),
|
|
455
|
-
"/api/:rest+": Route.get(Route.text("api wildcard")),
|
|
456
|
-
"/api/users": Route.get(Route.text("api users")),
|
|
457
|
-
"/api/users/:id": Route.get(Route.text("api user detail")),
|
|
458
|
-
})
|
|
459
|
-
|
|
460
|
-
const staticMatch = RouteTree.lookup(tree, "GET", "/api/users")
|
|
461
|
-
test.expect(Route.descriptor(staticMatch!.route).path).toBe("/api/users")
|
|
462
|
-
|
|
463
|
-
const paramMatch = RouteTree.lookup(tree, "GET", "/api/users/123")
|
|
464
|
-
test.expect(Route.descriptor(paramMatch!.route).path).toBe("/api/users/:id")
|
|
465
|
-
test.expect(paramMatch!.params).toEqual({ id: "123" })
|
|
466
|
-
|
|
467
|
-
const greedyMatch = RouteTree.lookup(tree, "GET", "/api/something/else")
|
|
468
|
-
test.expect(Route.descriptor(greedyMatch!.route).path).toBe("/api/:rest+")
|
|
469
|
-
test.expect(greedyMatch!.params).toEqual({ rest: "something/else" })
|
|
470
|
-
|
|
471
|
-
const catchAll = RouteTree.lookup(tree, "GET", "/random/path")
|
|
472
|
-
test.expect(Route.descriptor(catchAll!.route).path).toBe("/:path*")
|
|
473
|
-
})
|
|
474
|
-
|
|
475
|
-
test.it("static routes take priority over optional param routes", () => {
|
|
476
|
-
const tree = RouteTree.make({
|
|
477
|
-
"/files/:name?": Route.get(Route.text("files optional")),
|
|
478
|
-
"/files/latest": Route.get(Route.text("files latest")),
|
|
479
|
-
})
|
|
480
|
-
|
|
481
|
-
const staticMatch = RouteTree.lookup(tree, "GET", "/files/latest")
|
|
482
|
-
test.expect(Route.descriptor(staticMatch!.route).path).toBe("/files/latest")
|
|
483
|
-
|
|
484
|
-
const optionalMatch = RouteTree.lookup(tree, "GET", "/files/other")
|
|
485
|
-
test.expect(Route.descriptor(optionalMatch!.route).path).toBe(
|
|
486
|
-
"/files/:name?",
|
|
487
|
-
)
|
|
488
|
-
test.expect(optionalMatch!.params).toEqual({ name: "other" })
|
|
489
|
-
|
|
490
|
-
const noParam = RouteTree.lookup(tree, "GET", "/files")
|
|
491
|
-
test.expect(Route.descriptor(noParam!.route).path).toBe("/files/:name?")
|
|
492
|
-
test.expect(noParam!.params).toEqual({})
|
|
493
|
-
})
|
|
494
|
-
})
|