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
|
@@ -0,0 +1,603 @@
|
|
|
1
|
+
import * as Headers from "@effect/platform/Headers"
|
|
2
|
+
import * as test from "bun:test"
|
|
3
|
+
import * as ContentNegotiation from "./ContentNegotiation.ts"
|
|
4
|
+
|
|
5
|
+
test.describe("ContentNegotiation.media", () => {
|
|
6
|
+
test.it("returns empty array when no types provided", () => {
|
|
7
|
+
const result = ContentNegotiation.media("text/html", [])
|
|
8
|
+
test
|
|
9
|
+
.expect(result)
|
|
10
|
+
.toEqual([])
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
test.it("returns matching type", () => {
|
|
14
|
+
const result = ContentNegotiation.media(
|
|
15
|
+
"application/json",
|
|
16
|
+
["text/html", "application/json"],
|
|
17
|
+
)
|
|
18
|
+
test
|
|
19
|
+
.expect(result)
|
|
20
|
+
.toEqual(["application/json"])
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
test.it("returns types sorted by quality", () => {
|
|
24
|
+
const result = ContentNegotiation.media(
|
|
25
|
+
"text/html;q=0.5, application/json;q=0.9",
|
|
26
|
+
["text/html", "application/json"],
|
|
27
|
+
)
|
|
28
|
+
test
|
|
29
|
+
.expect(result)
|
|
30
|
+
.toEqual(["application/json", "text/html"])
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
test.it("returns empty array when no matching type", () => {
|
|
34
|
+
const result = ContentNegotiation.media(
|
|
35
|
+
"text/plain",
|
|
36
|
+
["text/html", "application/json"],
|
|
37
|
+
)
|
|
38
|
+
test
|
|
39
|
+
.expect(result)
|
|
40
|
+
.toEqual([])
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
test.it("handles wildcard subtype", () => {
|
|
44
|
+
const result = ContentNegotiation.media(
|
|
45
|
+
"text/*",
|
|
46
|
+
["application/json", "text/html", "text/plain"],
|
|
47
|
+
)
|
|
48
|
+
test
|
|
49
|
+
.expect(result)
|
|
50
|
+
.toEqual(["text/html", "text/plain"])
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
test.it("prefers exact match over wildcard", () => {
|
|
54
|
+
const result = ContentNegotiation.media(
|
|
55
|
+
"text/*, text/html",
|
|
56
|
+
["text/plain", "text/html"],
|
|
57
|
+
)
|
|
58
|
+
test
|
|
59
|
+
.expect(result)
|
|
60
|
+
.toEqual(["text/html", "text/plain"])
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
test.it("handles complex accept header", () => {
|
|
64
|
+
const result = ContentNegotiation.media(
|
|
65
|
+
"text/html, application/*;q=0.2, image/jpeg;q=0.8",
|
|
66
|
+
["image/jpeg", "application/json", "text/html"],
|
|
67
|
+
)
|
|
68
|
+
test
|
|
69
|
+
.expect(result)
|
|
70
|
+
.toEqual(["text/html", "image/jpeg", "application/json"])
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
test.it("returns type as provided (preserves original string)", () => {
|
|
74
|
+
const result = ContentNegotiation.media(
|
|
75
|
+
"application/json",
|
|
76
|
+
["text/HTML", "Application/JSON"],
|
|
77
|
+
)
|
|
78
|
+
test
|
|
79
|
+
.expect(result)
|
|
80
|
+
.toEqual(["Application/JSON"])
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
test.it("handles */* wildcard", () => {
|
|
84
|
+
const result = ContentNegotiation.media(
|
|
85
|
+
"*/*",
|
|
86
|
+
["text/html", "application/json"],
|
|
87
|
+
)
|
|
88
|
+
test
|
|
89
|
+
.expect(result)
|
|
90
|
+
.toEqual(["text/html", "application/json"])
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
test.it("returns empty array for invalid accept header", () => {
|
|
94
|
+
const result = ContentNegotiation.media(
|
|
95
|
+
"invalid",
|
|
96
|
+
["text/html", "application/json"],
|
|
97
|
+
)
|
|
98
|
+
test
|
|
99
|
+
.expect(result)
|
|
100
|
+
.toEqual([])
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
test.it("returns all accepted types when available not provided", () => {
|
|
104
|
+
const result = ContentNegotiation.media(
|
|
105
|
+
"text/html, application/json;q=0.9, text/plain;q=0.5",
|
|
106
|
+
)
|
|
107
|
+
test
|
|
108
|
+
.expect(result)
|
|
109
|
+
.toEqual(["text/html", "application/json", "text/plain"])
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
test.it("returns empty array for empty accept header", () => {
|
|
113
|
+
const result = ContentNegotiation.media("", [
|
|
114
|
+
"text/html",
|
|
115
|
+
"application/json",
|
|
116
|
+
])
|
|
117
|
+
test
|
|
118
|
+
.expect(result)
|
|
119
|
+
.toEqual([])
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
test.it("excludes types with q=0", () => {
|
|
123
|
+
const result = ContentNegotiation.media(
|
|
124
|
+
"text/html, application/json;q=0",
|
|
125
|
+
["text/html", "application/json"],
|
|
126
|
+
)
|
|
127
|
+
test
|
|
128
|
+
.expect(result)
|
|
129
|
+
.toEqual(["text/html"])
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
test.it("matches media type with parameters", () => {
|
|
133
|
+
const result = ContentNegotiation.media(
|
|
134
|
+
"text/html;level=1",
|
|
135
|
+
["text/html;level=1", "text/html;level=2", "text/html"],
|
|
136
|
+
)
|
|
137
|
+
test
|
|
138
|
+
.expect(result)
|
|
139
|
+
.toEqual(["text/html;level=1"])
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
test.it("prefers more specific wildcard match", () => {
|
|
143
|
+
const result = ContentNegotiation.media(
|
|
144
|
+
"text/*;q=0.5, */*;q=0.1",
|
|
145
|
+
["text/html", "application/json"],
|
|
146
|
+
)
|
|
147
|
+
test
|
|
148
|
+
.expect(result)
|
|
149
|
+
.toEqual(["text/html", "application/json"])
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
test.describe("wildcard in available types", () => {
|
|
153
|
+
test.it("text/* matches text/event-stream", () => {
|
|
154
|
+
const result = ContentNegotiation.media(
|
|
155
|
+
"text/event-stream",
|
|
156
|
+
["text/*", "application/json"],
|
|
157
|
+
)
|
|
158
|
+
test
|
|
159
|
+
.expect(result)
|
|
160
|
+
.toEqual(["text/*"])
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
test.it("text/* matches text/markdown", () => {
|
|
164
|
+
const result = ContentNegotiation.media(
|
|
165
|
+
"text/markdown",
|
|
166
|
+
["text/*"],
|
|
167
|
+
)
|
|
168
|
+
test
|
|
169
|
+
.expect(result)
|
|
170
|
+
.toEqual(["text/*"])
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
test.it("text/* matches text/plain", () => {
|
|
174
|
+
const result = ContentNegotiation.media(
|
|
175
|
+
"text/plain",
|
|
176
|
+
["text/*"],
|
|
177
|
+
)
|
|
178
|
+
test
|
|
179
|
+
.expect(result)
|
|
180
|
+
.toEqual(["text/*"])
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
test.it("text/* does not match application/json", () => {
|
|
184
|
+
const result = ContentNegotiation.media(
|
|
185
|
+
"application/json",
|
|
186
|
+
["text/*"],
|
|
187
|
+
)
|
|
188
|
+
test
|
|
189
|
+
.expect(result)
|
|
190
|
+
.toEqual([])
|
|
191
|
+
})
|
|
192
|
+
|
|
193
|
+
test.it("prefers exact match over wildcard available type", () => {
|
|
194
|
+
const result = ContentNegotiation.media(
|
|
195
|
+
"text/html",
|
|
196
|
+
["text/*", "text/html"],
|
|
197
|
+
)
|
|
198
|
+
test
|
|
199
|
+
.expect(result)
|
|
200
|
+
.toEqual(["text/html", "text/*"])
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
test.it("text/* matches multiple text types in Accept", () => {
|
|
204
|
+
const result = ContentNegotiation.media(
|
|
205
|
+
"text/html, text/plain",
|
|
206
|
+
["text/*"],
|
|
207
|
+
)
|
|
208
|
+
test
|
|
209
|
+
.expect(result)
|
|
210
|
+
.toEqual(["text/*"])
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
test.it("application/* matches application/xml", () => {
|
|
214
|
+
const result = ContentNegotiation.media(
|
|
215
|
+
"application/xml",
|
|
216
|
+
["application/*", "text/html"],
|
|
217
|
+
)
|
|
218
|
+
test
|
|
219
|
+
.expect(result)
|
|
220
|
+
.toEqual(["application/*"])
|
|
221
|
+
})
|
|
222
|
+
|
|
223
|
+
test.it("*/* in available matches any type", () => {
|
|
224
|
+
const result = ContentNegotiation.media(
|
|
225
|
+
"image/png",
|
|
226
|
+
["*/*"],
|
|
227
|
+
)
|
|
228
|
+
test
|
|
229
|
+
.expect(result)
|
|
230
|
+
.toEqual(["*/*"])
|
|
231
|
+
})
|
|
232
|
+
|
|
233
|
+
test.it("combines client and server wildcards", () => {
|
|
234
|
+
// Client wants text/*, server offers text/*
|
|
235
|
+
const result = ContentNegotiation.media(
|
|
236
|
+
"text/*",
|
|
237
|
+
["text/*"],
|
|
238
|
+
)
|
|
239
|
+
test
|
|
240
|
+
.expect(result)
|
|
241
|
+
.toEqual(["text/*"])
|
|
242
|
+
})
|
|
243
|
+
|
|
244
|
+
test.it("quality values still apply with wildcard available", () => {
|
|
245
|
+
const result = ContentNegotiation.media(
|
|
246
|
+
"text/html;q=0.5, text/event-stream;q=0.9",
|
|
247
|
+
["text/*"],
|
|
248
|
+
)
|
|
249
|
+
test
|
|
250
|
+
.expect(result)
|
|
251
|
+
.toEqual(["text/*"])
|
|
252
|
+
})
|
|
253
|
+
})
|
|
254
|
+
})
|
|
255
|
+
|
|
256
|
+
test.describe("ContentNegotiation.language", () => {
|
|
257
|
+
test.it("returns empty array when no languages provided", () => {
|
|
258
|
+
const result = ContentNegotiation.language("en", [])
|
|
259
|
+
test
|
|
260
|
+
.expect(result)
|
|
261
|
+
.toEqual([])
|
|
262
|
+
})
|
|
263
|
+
|
|
264
|
+
test.it("returns matching language", () => {
|
|
265
|
+
const result = ContentNegotiation.language("fr", ["en", "fr"])
|
|
266
|
+
test
|
|
267
|
+
.expect(result)
|
|
268
|
+
.toEqual(["fr"])
|
|
269
|
+
})
|
|
270
|
+
|
|
271
|
+
test.it("returns languages sorted by quality", () => {
|
|
272
|
+
const result = ContentNegotiation.language(
|
|
273
|
+
"en;q=0.5, fr;q=0.9",
|
|
274
|
+
["en", "fr"],
|
|
275
|
+
)
|
|
276
|
+
test
|
|
277
|
+
.expect(result)
|
|
278
|
+
.toEqual(["fr", "en"])
|
|
279
|
+
})
|
|
280
|
+
|
|
281
|
+
test.it("returns empty array when no matching language", () => {
|
|
282
|
+
const result = ContentNegotiation.language("de", ["en", "fr"])
|
|
283
|
+
test
|
|
284
|
+
.expect(result)
|
|
285
|
+
.toEqual([])
|
|
286
|
+
})
|
|
287
|
+
|
|
288
|
+
test.it("handles language prefix match", () => {
|
|
289
|
+
const result = ContentNegotiation.language("en", ["en-US", "en-GB", "fr"])
|
|
290
|
+
test
|
|
291
|
+
.expect(result)
|
|
292
|
+
.toEqual(["en-US", "en-GB"])
|
|
293
|
+
})
|
|
294
|
+
|
|
295
|
+
test.it("handles language with region", () => {
|
|
296
|
+
const result = ContentNegotiation.language("en-US", ["en", "en-US", "fr"])
|
|
297
|
+
test
|
|
298
|
+
.expect(result)
|
|
299
|
+
.toEqual(["en-US"])
|
|
300
|
+
})
|
|
301
|
+
|
|
302
|
+
test.it("prefers exact match over prefix match", () => {
|
|
303
|
+
const result = ContentNegotiation.language(
|
|
304
|
+
"en-US, en;q=0.9",
|
|
305
|
+
["en", "en-US"],
|
|
306
|
+
)
|
|
307
|
+
test
|
|
308
|
+
.expect(result)
|
|
309
|
+
.toEqual(["en-US", "en"])
|
|
310
|
+
})
|
|
311
|
+
|
|
312
|
+
test.it("handles complex accept-language header", () => {
|
|
313
|
+
const result = ContentNegotiation.language(
|
|
314
|
+
"en;q=0.8, es, pt",
|
|
315
|
+
["en", "es", "pt"],
|
|
316
|
+
)
|
|
317
|
+
test
|
|
318
|
+
.expect(result)
|
|
319
|
+
.toEqual(["es", "pt", "en"])
|
|
320
|
+
})
|
|
321
|
+
|
|
322
|
+
test.it("handles * wildcard", () => {
|
|
323
|
+
const result = ContentNegotiation.language("*", ["en", "fr"])
|
|
324
|
+
test
|
|
325
|
+
.expect(result)
|
|
326
|
+
.toEqual(["en", "fr"])
|
|
327
|
+
})
|
|
328
|
+
|
|
329
|
+
test.it("returns all accepted languages when available not provided", () => {
|
|
330
|
+
const result = ContentNegotiation.language("en-US, fr;q=0.8, de;q=0.5")
|
|
331
|
+
test
|
|
332
|
+
.expect(result)
|
|
333
|
+
.toEqual(["en-us", "fr", "de"])
|
|
334
|
+
})
|
|
335
|
+
|
|
336
|
+
test.it("returns empty array for empty accept-language header", () => {
|
|
337
|
+
const result = ContentNegotiation.language("", ["en", "fr"])
|
|
338
|
+
test
|
|
339
|
+
.expect(result)
|
|
340
|
+
.toEqual([])
|
|
341
|
+
})
|
|
342
|
+
|
|
343
|
+
test.it("matches case-insensitively", () => {
|
|
344
|
+
const result = ContentNegotiation.language("EN-US", ["en-us", "fr"])
|
|
345
|
+
test
|
|
346
|
+
.expect(result)
|
|
347
|
+
.toEqual(["en-us"])
|
|
348
|
+
})
|
|
349
|
+
})
|
|
350
|
+
|
|
351
|
+
test.describe("ContentNegotiation.encoding", () => {
|
|
352
|
+
test.it("returns empty array when no encodings provided", () => {
|
|
353
|
+
const result = ContentNegotiation.encoding("gzip", [])
|
|
354
|
+
test
|
|
355
|
+
.expect(result)
|
|
356
|
+
.toEqual([])
|
|
357
|
+
})
|
|
358
|
+
|
|
359
|
+
test.it("returns matching encoding", () => {
|
|
360
|
+
const result = ContentNegotiation.encoding("deflate", ["gzip", "deflate"])
|
|
361
|
+
test
|
|
362
|
+
.expect(result)
|
|
363
|
+
.toEqual(["deflate"])
|
|
364
|
+
})
|
|
365
|
+
|
|
366
|
+
test.it("returns encodings sorted by quality", () => {
|
|
367
|
+
const result = ContentNegotiation.encoding(
|
|
368
|
+
"gzip;q=0.5, deflate;q=0.9",
|
|
369
|
+
["gzip", "deflate"],
|
|
370
|
+
)
|
|
371
|
+
test
|
|
372
|
+
.expect(result)
|
|
373
|
+
.toEqual(["deflate", "gzip"])
|
|
374
|
+
})
|
|
375
|
+
|
|
376
|
+
test.it(
|
|
377
|
+
"returns empty array when no matching encoding (except identity)",
|
|
378
|
+
() => {
|
|
379
|
+
const result = ContentNegotiation.encoding("br", ["gzip", "deflate"])
|
|
380
|
+
test
|
|
381
|
+
.expect(result)
|
|
382
|
+
.toEqual([])
|
|
383
|
+
},
|
|
384
|
+
)
|
|
385
|
+
|
|
386
|
+
test.it("handles wildcard", () => {
|
|
387
|
+
const result = ContentNegotiation.encoding("*", ["gzip", "deflate"])
|
|
388
|
+
test
|
|
389
|
+
.expect(result)
|
|
390
|
+
.toEqual(["gzip", "deflate"])
|
|
391
|
+
})
|
|
392
|
+
|
|
393
|
+
test.it("handles identity encoding as implicit fallback", () => {
|
|
394
|
+
const result = ContentNegotiation.encoding("br", ["identity", "gzip"])
|
|
395
|
+
test
|
|
396
|
+
.expect(result)
|
|
397
|
+
.toEqual(["identity"])
|
|
398
|
+
})
|
|
399
|
+
|
|
400
|
+
test.it("handles complex accept-encoding header", () => {
|
|
401
|
+
const result = ContentNegotiation.encoding(
|
|
402
|
+
"gzip;q=1.0, identity;q=0.5, *;q=0",
|
|
403
|
+
["deflate", "gzip", "identity"],
|
|
404
|
+
)
|
|
405
|
+
test
|
|
406
|
+
.expect(result)
|
|
407
|
+
.toEqual(["gzip", "identity"])
|
|
408
|
+
})
|
|
409
|
+
|
|
410
|
+
test.it("returns all accepted encodings when available not provided", () => {
|
|
411
|
+
const result = ContentNegotiation.encoding("gzip, deflate;q=0.8, br;q=0.5")
|
|
412
|
+
test
|
|
413
|
+
.expect(result)
|
|
414
|
+
.toEqual(["gzip", "deflate", "br", "identity"])
|
|
415
|
+
})
|
|
416
|
+
|
|
417
|
+
test.it("returns empty array for empty accept-encoding header", () => {
|
|
418
|
+
const result = ContentNegotiation.encoding("", ["gzip", "deflate"])
|
|
419
|
+
test
|
|
420
|
+
.expect(result)
|
|
421
|
+
.toEqual([])
|
|
422
|
+
})
|
|
423
|
+
|
|
424
|
+
test.it("excludes identity when identity;q=0", () => {
|
|
425
|
+
const result = ContentNegotiation.encoding(
|
|
426
|
+
"gzip, identity;q=0",
|
|
427
|
+
["gzip", "identity"],
|
|
428
|
+
)
|
|
429
|
+
test
|
|
430
|
+
.expect(result)
|
|
431
|
+
.toEqual(["gzip"])
|
|
432
|
+
})
|
|
433
|
+
|
|
434
|
+
test.it("excludes unspecified encodings when *;q=0", () => {
|
|
435
|
+
const result = ContentNegotiation.encoding(
|
|
436
|
+
"gzip, *;q=0",
|
|
437
|
+
["gzip", "deflate", "br"],
|
|
438
|
+
)
|
|
439
|
+
test
|
|
440
|
+
.expect(result)
|
|
441
|
+
.toEqual(["gzip"])
|
|
442
|
+
})
|
|
443
|
+
})
|
|
444
|
+
|
|
445
|
+
test.describe("ContentNegotiation.charset", () => {
|
|
446
|
+
test.it("returns empty array when no charsets provided", () => {
|
|
447
|
+
const result = ContentNegotiation.charset("utf-8", [])
|
|
448
|
+
test
|
|
449
|
+
.expect(result)
|
|
450
|
+
.toEqual([])
|
|
451
|
+
})
|
|
452
|
+
|
|
453
|
+
test.it("returns matching charset", () => {
|
|
454
|
+
const result = ContentNegotiation.charset(
|
|
455
|
+
"iso-8859-1",
|
|
456
|
+
["utf-8", "iso-8859-1"],
|
|
457
|
+
)
|
|
458
|
+
test
|
|
459
|
+
.expect(result)
|
|
460
|
+
.toEqual(["iso-8859-1"])
|
|
461
|
+
})
|
|
462
|
+
|
|
463
|
+
test.it("returns charsets sorted by quality", () => {
|
|
464
|
+
const result = ContentNegotiation.charset(
|
|
465
|
+
"utf-8;q=0.5, iso-8859-1;q=0.9",
|
|
466
|
+
["utf-8", "iso-8859-1"],
|
|
467
|
+
)
|
|
468
|
+
test
|
|
469
|
+
.expect(result)
|
|
470
|
+
.toEqual(["iso-8859-1", "utf-8"])
|
|
471
|
+
})
|
|
472
|
+
|
|
473
|
+
test.it("returns empty array when no matching charset", () => {
|
|
474
|
+
const result = ContentNegotiation.charset(
|
|
475
|
+
"utf-16",
|
|
476
|
+
["utf-8", "iso-8859-1"],
|
|
477
|
+
)
|
|
478
|
+
test
|
|
479
|
+
.expect(result)
|
|
480
|
+
.toEqual([])
|
|
481
|
+
})
|
|
482
|
+
|
|
483
|
+
test.it("handles wildcard", () => {
|
|
484
|
+
const result = ContentNegotiation.charset("*", ["utf-8", "iso-8859-1"])
|
|
485
|
+
test
|
|
486
|
+
.expect(result)
|
|
487
|
+
.toEqual(["utf-8", "iso-8859-1"])
|
|
488
|
+
})
|
|
489
|
+
|
|
490
|
+
test.it("handles complex accept-charset header", () => {
|
|
491
|
+
const result = ContentNegotiation.charset(
|
|
492
|
+
"utf-8, iso-8859-1;q=0.8, utf-7;q=0.2",
|
|
493
|
+
["utf-7", "iso-8859-1", "utf-8"],
|
|
494
|
+
)
|
|
495
|
+
test
|
|
496
|
+
.expect(result)
|
|
497
|
+
.toEqual(["utf-8", "iso-8859-1", "utf-7"])
|
|
498
|
+
})
|
|
499
|
+
|
|
500
|
+
test.it("returns all accepted charsets when available not provided", () => {
|
|
501
|
+
const result = ContentNegotiation.charset(
|
|
502
|
+
"utf-8, iso-8859-1;q=0.8, utf-7;q=0.2",
|
|
503
|
+
)
|
|
504
|
+
test
|
|
505
|
+
.expect(result)
|
|
506
|
+
.toEqual(["utf-8", "iso-8859-1", "utf-7"])
|
|
507
|
+
})
|
|
508
|
+
|
|
509
|
+
test.it("returns empty array for empty accept-charset header", () => {
|
|
510
|
+
const result = ContentNegotiation.charset("", ["utf-8", "iso-8859-1"])
|
|
511
|
+
test
|
|
512
|
+
.expect(result)
|
|
513
|
+
.toEqual([])
|
|
514
|
+
})
|
|
515
|
+
|
|
516
|
+
test.it("matches case-insensitively", () => {
|
|
517
|
+
const result = ContentNegotiation.charset("UTF-8", ["utf-8", "iso-8859-1"])
|
|
518
|
+
test
|
|
519
|
+
.expect(result)
|
|
520
|
+
.toEqual(["utf-8"])
|
|
521
|
+
})
|
|
522
|
+
})
|
|
523
|
+
|
|
524
|
+
test.describe("ContentNegotiation.headerMedia", () => {
|
|
525
|
+
test.it("parses Accept header from Headers object", () => {
|
|
526
|
+
const headers = Headers.fromInput({ accept: "text/html, application/json" })
|
|
527
|
+
const result = ContentNegotiation.headerMedia(headers, [
|
|
528
|
+
"text/html",
|
|
529
|
+
"application/json",
|
|
530
|
+
])
|
|
531
|
+
test
|
|
532
|
+
.expect(result)
|
|
533
|
+
.toEqual(["text/html", "application/json"])
|
|
534
|
+
})
|
|
535
|
+
|
|
536
|
+
test.it("returns empty array when Accept header is missing", () => {
|
|
537
|
+
const headers = Headers.fromInput({})
|
|
538
|
+
const result = ContentNegotiation.headerMedia(headers, ["text/html"])
|
|
539
|
+
test
|
|
540
|
+
.expect(result)
|
|
541
|
+
.toEqual([])
|
|
542
|
+
})
|
|
543
|
+
})
|
|
544
|
+
|
|
545
|
+
test.describe("ContentNegotiation.headerLanguage", () => {
|
|
546
|
+
test.it("parses Accept-Language header from Headers object", () => {
|
|
547
|
+
const headers = Headers.fromInput({ "accept-language": "en, fr;q=0.8" })
|
|
548
|
+
const result = ContentNegotiation.headerLanguage(headers, ["en", "fr"])
|
|
549
|
+
test
|
|
550
|
+
.expect(result)
|
|
551
|
+
.toEqual(["en", "fr"])
|
|
552
|
+
})
|
|
553
|
+
|
|
554
|
+
test.it("returns empty array when Accept-Language header is missing", () => {
|
|
555
|
+
const headers = Headers.fromInput({})
|
|
556
|
+
const result = ContentNegotiation.headerLanguage(headers, ["en"])
|
|
557
|
+
test
|
|
558
|
+
.expect(result)
|
|
559
|
+
.toEqual([])
|
|
560
|
+
})
|
|
561
|
+
})
|
|
562
|
+
|
|
563
|
+
test.describe("ContentNegotiation.headerEncoding", () => {
|
|
564
|
+
test.it("parses Accept-Encoding header from Headers object", () => {
|
|
565
|
+
const headers = Headers.fromInput({ "accept-encoding": "gzip, deflate" })
|
|
566
|
+
const result = ContentNegotiation.headerEncoding(headers, [
|
|
567
|
+
"gzip",
|
|
568
|
+
"deflate",
|
|
569
|
+
])
|
|
570
|
+
test
|
|
571
|
+
.expect(result)
|
|
572
|
+
.toEqual(["gzip", "deflate"])
|
|
573
|
+
})
|
|
574
|
+
|
|
575
|
+
test.it("returns empty array when Accept-Encoding header is missing", () => {
|
|
576
|
+
const headers = Headers.fromInput({})
|
|
577
|
+
const result = ContentNegotiation.headerEncoding(headers, ["gzip"])
|
|
578
|
+
test
|
|
579
|
+
.expect(result)
|
|
580
|
+
.toEqual([])
|
|
581
|
+
})
|
|
582
|
+
})
|
|
583
|
+
|
|
584
|
+
test.describe("ContentNegotiation.headerCharset", () => {
|
|
585
|
+
test.it("parses Accept-Charset header from Headers object", () => {
|
|
586
|
+
const headers = Headers.fromInput({ "accept-charset": "utf-8, iso-8859-1" })
|
|
587
|
+
const result = ContentNegotiation.headerCharset(headers, [
|
|
588
|
+
"utf-8",
|
|
589
|
+
"iso-8859-1",
|
|
590
|
+
])
|
|
591
|
+
test
|
|
592
|
+
.expect(result)
|
|
593
|
+
.toEqual(["utf-8", "iso-8859-1"])
|
|
594
|
+
})
|
|
595
|
+
|
|
596
|
+
test.it("returns empty array when Accept-Charset header is missing", () => {
|
|
597
|
+
const headers = Headers.fromInput({})
|
|
598
|
+
const result = ContentNegotiation.headerCharset(headers, ["utf-8"])
|
|
599
|
+
test
|
|
600
|
+
.expect(result)
|
|
601
|
+
.toEqual([])
|
|
602
|
+
})
|
|
603
|
+
})
|