effect-start 0.14.0 → 0.15.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 +500 -0
- package/src/ContentNegotiation.ts +535 -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 +24 -0
- package/src/Http.ts +25 -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 +483 -0
- package/src/Route.ts +258 -1073
- package/src/RouteBody.test.ts +182 -0
- package/src/RouteBody.ts +106 -0
- package/src/RouteHook.test.ts +40 -0
- package/src/RouteHook.ts +105 -0
- package/src/RouteHttp.test.ts +443 -0
- package/src/RouteHttp.ts +219 -0
- package/src/RouteMount.test.ts +468 -0
- package/src/RouteMount.ts +313 -0
- package/src/RouteSchema.test.ts +81 -0
- package/src/RouteSchema.ts +44 -0
- package/src/RouteTree.test.ts +346 -0
- package/src/RouteTree.ts +165 -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/TuplePathPattern.ts +64 -0
- package/src/Values.ts +16 -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 +56 -32
- package/src/bun/BunHttpServer_web.ts +18 -6
- package/src/bun/BunImportTrackerPlugin.test.ts +3 -3
- package/src/bun/BunRoute.ts +29 -210
- 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 +2 -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 -11
- 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.test.ts +0 -480
- 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,535 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RFC 7231 Content Negotiation compatible with Express/Node.js ecosystem.
|
|
3
|
+
* Based on {@link https://github.com/jshttp/negotiator}
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type * as Headers from "@effect/platform/Headers"
|
|
7
|
+
|
|
8
|
+
interface ParsedSpec {
|
|
9
|
+
value: string
|
|
10
|
+
q: number
|
|
11
|
+
s: number
|
|
12
|
+
o: number
|
|
13
|
+
i: number
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const simpleMediaTypeRegExp = /^\s*([^\s\/;]+)\/([^;\s]+)\s*(?:;(.*))?$/
|
|
17
|
+
const simpleLanguageRegExp = /^\s*([^\s\-;]+)(?:-([^\s;]+))?\s*(?:;(.*))?$/
|
|
18
|
+
const simpleEncodingRegExp = /^\s*([^\s;]+)\s*(?:;(.*))?$/
|
|
19
|
+
const simpleCharsetRegExp = /^\s*([^\s;]+)\s*(?:;(.*))?$/
|
|
20
|
+
|
|
21
|
+
function parseQuality(params: string | undefined): number {
|
|
22
|
+
if (!params) return 1
|
|
23
|
+
const match = params.match(/q\s*=\s*([0-9.]+)/)
|
|
24
|
+
if (!match) return 1
|
|
25
|
+
const q = parseFloat(match[1])
|
|
26
|
+
return isNaN(q) ? 1 : Math.min(Math.max(q, 0), 1)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function splitMediaTypeParams(
|
|
30
|
+
params: string,
|
|
31
|
+
): { params: Record<string, string>; q: number } {
|
|
32
|
+
const result: Record<string, string> = {}
|
|
33
|
+
let q = 1
|
|
34
|
+
|
|
35
|
+
const parts = params.split(";")
|
|
36
|
+
for (const part of parts) {
|
|
37
|
+
const trimmed = part.trim()
|
|
38
|
+
const eqIndex = trimmed.indexOf("=")
|
|
39
|
+
if (eqIndex === -1) continue
|
|
40
|
+
|
|
41
|
+
const key = trimmed.slice(0, eqIndex).trim().toLowerCase()
|
|
42
|
+
let value = trimmed.slice(eqIndex + 1).trim()
|
|
43
|
+
|
|
44
|
+
if (value.startsWith("\"") && value.endsWith("\"")) {
|
|
45
|
+
value = value.slice(1, -1)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (key === "q") {
|
|
49
|
+
q = parseFloat(value)
|
|
50
|
+
if (isNaN(q)) q = 1
|
|
51
|
+
q = Math.min(Math.max(q, 0), 1)
|
|
52
|
+
} else {
|
|
53
|
+
result[key] = value
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return { params: result, q }
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function parseAccept(
|
|
61
|
+
accept: string,
|
|
62
|
+
): Array<
|
|
63
|
+
{
|
|
64
|
+
type: string
|
|
65
|
+
subtype: string
|
|
66
|
+
params: Record<string, string>
|
|
67
|
+
q: number
|
|
68
|
+
o: number
|
|
69
|
+
}
|
|
70
|
+
> {
|
|
71
|
+
const specs: Array<{
|
|
72
|
+
type: string
|
|
73
|
+
subtype: string
|
|
74
|
+
params: Record<string, string>
|
|
75
|
+
q: number
|
|
76
|
+
o: number
|
|
77
|
+
}> = []
|
|
78
|
+
const parts = accept.split(",")
|
|
79
|
+
|
|
80
|
+
for (let o = 0; o < parts.length; o++) {
|
|
81
|
+
const part = parts[o].trim()
|
|
82
|
+
if (!part) continue
|
|
83
|
+
|
|
84
|
+
const match = simpleMediaTypeRegExp.exec(part)
|
|
85
|
+
if (!match) continue
|
|
86
|
+
|
|
87
|
+
const type = match[1].toLowerCase()
|
|
88
|
+
const subtype = match[2].toLowerCase()
|
|
89
|
+
const { params, q } = match[3]
|
|
90
|
+
? splitMediaTypeParams(match[3])
|
|
91
|
+
: { params: {}, q: 1 }
|
|
92
|
+
|
|
93
|
+
if (q > 0) {
|
|
94
|
+
specs.push({ type, subtype, params, q, o })
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return specs
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function specifyMediaType(
|
|
102
|
+
type: string,
|
|
103
|
+
subtype: string,
|
|
104
|
+
params: Record<string, string>,
|
|
105
|
+
spec: {
|
|
106
|
+
type: string
|
|
107
|
+
subtype: string
|
|
108
|
+
params: Record<string, string>
|
|
109
|
+
q: number
|
|
110
|
+
o: number
|
|
111
|
+
},
|
|
112
|
+
): { q: number; s: number; o: number } | null {
|
|
113
|
+
let s = 0
|
|
114
|
+
|
|
115
|
+
if (spec.type === type) {
|
|
116
|
+
s |= 4
|
|
117
|
+
} else if (spec.type !== "*") {
|
|
118
|
+
return null
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (spec.subtype === subtype) {
|
|
122
|
+
s |= 2
|
|
123
|
+
} else if (spec.subtype !== "*") {
|
|
124
|
+
return null
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const specParams = Object.keys(spec.params)
|
|
128
|
+
if (specParams.length > 0) {
|
|
129
|
+
if (
|
|
130
|
+
specParams.every(
|
|
131
|
+
(key) =>
|
|
132
|
+
spec.params[key].toLowerCase() === (params[key] || "").toLowerCase(),
|
|
133
|
+
)
|
|
134
|
+
) {
|
|
135
|
+
s |= 1
|
|
136
|
+
} else {
|
|
137
|
+
return null
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return { q: spec.q, s, o: spec.o }
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function getMediaTypePriority(
|
|
145
|
+
mediaType: string,
|
|
146
|
+
accepted: Array<
|
|
147
|
+
{
|
|
148
|
+
type: string
|
|
149
|
+
subtype: string
|
|
150
|
+
params: Record<string, string>
|
|
151
|
+
q: number
|
|
152
|
+
o: number
|
|
153
|
+
}
|
|
154
|
+
>,
|
|
155
|
+
index: number,
|
|
156
|
+
): ParsedSpec {
|
|
157
|
+
let best: { q: number; s: number; o: number } | null = null
|
|
158
|
+
|
|
159
|
+
const match = simpleMediaTypeRegExp.exec(mediaType)
|
|
160
|
+
if (!match) {
|
|
161
|
+
return { value: mediaType, q: 0, s: 0, o: -1, i: index }
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const type = match[1].toLowerCase()
|
|
165
|
+
const subtype = match[2].toLowerCase()
|
|
166
|
+
const { params } = match[3]
|
|
167
|
+
? splitMediaTypeParams(match[3])
|
|
168
|
+
: { params: {} }
|
|
169
|
+
|
|
170
|
+
for (const spec of accepted) {
|
|
171
|
+
const result = specifyMediaType(type, subtype, params, spec)
|
|
172
|
+
if (
|
|
173
|
+
result
|
|
174
|
+
&& (best === null
|
|
175
|
+
|| result.s > best.s
|
|
176
|
+
|| (result.s === best.s && result.q > best.q)
|
|
177
|
+
|| (result.s === best.s && result.q === best.q && result.o < best.o))
|
|
178
|
+
) {
|
|
179
|
+
best = result
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return {
|
|
184
|
+
value: mediaType,
|
|
185
|
+
q: best?.q ?? 0,
|
|
186
|
+
s: best?.s ?? 0,
|
|
187
|
+
o: best?.o ?? -1,
|
|
188
|
+
i: index,
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function parseAcceptLanguage(
|
|
193
|
+
accept: string,
|
|
194
|
+
): Array<{ prefix: string; suffix: string | undefined; q: number; o: number }> {
|
|
195
|
+
const specs: Array<{
|
|
196
|
+
prefix: string
|
|
197
|
+
suffix: string | undefined
|
|
198
|
+
q: number
|
|
199
|
+
o: number
|
|
200
|
+
}> = []
|
|
201
|
+
const parts = accept.split(",")
|
|
202
|
+
|
|
203
|
+
for (let o = 0; o < parts.length; o++) {
|
|
204
|
+
const part = parts[o].trim()
|
|
205
|
+
if (!part) continue
|
|
206
|
+
|
|
207
|
+
const match = simpleLanguageRegExp.exec(part)
|
|
208
|
+
if (!match) continue
|
|
209
|
+
|
|
210
|
+
const prefix = match[1].toLowerCase()
|
|
211
|
+
const suffix = match[2]?.toLowerCase()
|
|
212
|
+
const q = parseQuality(match[3])
|
|
213
|
+
|
|
214
|
+
if (q > 0) {
|
|
215
|
+
specs.push({ prefix, suffix, q, o })
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return specs
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function specifyLanguage(
|
|
223
|
+
language: string,
|
|
224
|
+
spec: { prefix: string; suffix: string | undefined; q: number; o: number },
|
|
225
|
+
): { q: number; s: number; o: number } | null {
|
|
226
|
+
const match = simpleLanguageRegExp.exec(language)
|
|
227
|
+
if (!match) return null
|
|
228
|
+
|
|
229
|
+
const prefix = match[1].toLowerCase()
|
|
230
|
+
const suffix = match[2]?.toLowerCase()
|
|
231
|
+
|
|
232
|
+
if (spec.prefix === "*") {
|
|
233
|
+
return { q: spec.q, s: 0, o: spec.o }
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (spec.prefix !== prefix) {
|
|
237
|
+
return null
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (spec.suffix === undefined) {
|
|
241
|
+
return { q: spec.q, s: suffix ? 2 : 4, o: spec.o }
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (spec.suffix === suffix) {
|
|
245
|
+
return { q: spec.q, s: 4, o: spec.o }
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
return null
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function getLanguagePriority(
|
|
252
|
+
language: string,
|
|
253
|
+
accepted: Array<
|
|
254
|
+
{ prefix: string; suffix: string | undefined; q: number; o: number }
|
|
255
|
+
>,
|
|
256
|
+
index: number,
|
|
257
|
+
): ParsedSpec {
|
|
258
|
+
let best: { q: number; s: number; o: number } | null = null
|
|
259
|
+
|
|
260
|
+
for (const spec of accepted) {
|
|
261
|
+
const result = specifyLanguage(language, spec)
|
|
262
|
+
if (
|
|
263
|
+
result
|
|
264
|
+
&& (best === null
|
|
265
|
+
|| result.s > best.s
|
|
266
|
+
|| (result.s === best.s && result.q > best.q)
|
|
267
|
+
|| (result.s === best.s && result.q === best.q && result.o < best.o))
|
|
268
|
+
) {
|
|
269
|
+
best = result
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
return {
|
|
274
|
+
value: language,
|
|
275
|
+
q: best?.q ?? 0,
|
|
276
|
+
s: best?.s ?? 0,
|
|
277
|
+
o: best?.o ?? -1,
|
|
278
|
+
i: index,
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function parseAcceptEncoding(
|
|
283
|
+
accept: string,
|
|
284
|
+
): Array<{ encoding: string; q: number; o: number }> {
|
|
285
|
+
const specs: Array<{ encoding: string; q: number; o: number }> = []
|
|
286
|
+
const parts = accept.split(",")
|
|
287
|
+
let hasIdentity = false
|
|
288
|
+
|
|
289
|
+
for (let o = 0; o < parts.length; o++) {
|
|
290
|
+
const part = parts[o].trim()
|
|
291
|
+
if (!part) continue
|
|
292
|
+
|
|
293
|
+
const match = simpleEncodingRegExp.exec(part)
|
|
294
|
+
if (!match) continue
|
|
295
|
+
|
|
296
|
+
const encoding = match[1].toLowerCase()
|
|
297
|
+
const q = parseQuality(match[2])
|
|
298
|
+
|
|
299
|
+
if (encoding === "identity") hasIdentity = true
|
|
300
|
+
if (encoding === "*") hasIdentity = true
|
|
301
|
+
|
|
302
|
+
if (q > 0) {
|
|
303
|
+
specs.push({ encoding, q, o })
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
if (!hasIdentity) {
|
|
308
|
+
specs.push({ encoding: "identity", q: 0.0001, o: specs.length })
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
return specs
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
function specifyEncoding(
|
|
315
|
+
encoding: string,
|
|
316
|
+
spec: { encoding: string; q: number; o: number },
|
|
317
|
+
): { q: number; s: number; o: number } | null {
|
|
318
|
+
const e = encoding.toLowerCase()
|
|
319
|
+
const s = spec.encoding
|
|
320
|
+
|
|
321
|
+
if (s === "*" || s === e) {
|
|
322
|
+
return { q: spec.q, s: s === e ? 1 : 0, o: spec.o }
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
return null
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function getEncodingPriority(
|
|
329
|
+
encoding: string,
|
|
330
|
+
accepted: Array<{ encoding: string; q: number; o: number }>,
|
|
331
|
+
index: number,
|
|
332
|
+
): ParsedSpec {
|
|
333
|
+
let best: { q: number; s: number; o: number } | null = null
|
|
334
|
+
|
|
335
|
+
for (const spec of accepted) {
|
|
336
|
+
const result = specifyEncoding(encoding, spec)
|
|
337
|
+
if (
|
|
338
|
+
result
|
|
339
|
+
&& (best === null
|
|
340
|
+
|| result.s > best.s
|
|
341
|
+
|| (result.s === best.s && result.q > best.q)
|
|
342
|
+
|| (result.s === best.s && result.q === best.q && result.o < best.o))
|
|
343
|
+
) {
|
|
344
|
+
best = result
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
return {
|
|
349
|
+
value: encoding,
|
|
350
|
+
q: best?.q ?? 0,
|
|
351
|
+
s: best?.s ?? 0,
|
|
352
|
+
o: best?.o ?? -1,
|
|
353
|
+
i: index,
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
function parseAcceptCharset(
|
|
358
|
+
accept: string,
|
|
359
|
+
): Array<{ charset: string; q: number; o: number }> {
|
|
360
|
+
const specs: Array<{ charset: string; q: number; o: number }> = []
|
|
361
|
+
const parts = accept.split(",")
|
|
362
|
+
|
|
363
|
+
for (let o = 0; o < parts.length; o++) {
|
|
364
|
+
const part = parts[o].trim()
|
|
365
|
+
if (!part) continue
|
|
366
|
+
|
|
367
|
+
const match = simpleCharsetRegExp.exec(part)
|
|
368
|
+
if (!match) continue
|
|
369
|
+
|
|
370
|
+
const charset = match[1].toLowerCase()
|
|
371
|
+
const q = parseQuality(match[2])
|
|
372
|
+
|
|
373
|
+
if (q > 0) {
|
|
374
|
+
specs.push({ charset, q, o })
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
return specs
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
function specifyCharset(
|
|
382
|
+
charset: string,
|
|
383
|
+
spec: { charset: string; q: number; o: number },
|
|
384
|
+
): { q: number; s: number; o: number } | null {
|
|
385
|
+
const c = charset.toLowerCase()
|
|
386
|
+
const s = spec.charset
|
|
387
|
+
|
|
388
|
+
if (s === "*" || s === c) {
|
|
389
|
+
return { q: spec.q, s: s === c ? 1 : 0, o: spec.o }
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
return null
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
function getCharsetPriority(
|
|
396
|
+
charset: string,
|
|
397
|
+
accepted: Array<{ charset: string; q: number; o: number }>,
|
|
398
|
+
index: number,
|
|
399
|
+
): ParsedSpec {
|
|
400
|
+
let best: { q: number; s: number; o: number } | null = null
|
|
401
|
+
|
|
402
|
+
for (const spec of accepted) {
|
|
403
|
+
const result = specifyCharset(charset, spec)
|
|
404
|
+
if (
|
|
405
|
+
result
|
|
406
|
+
&& (best === null
|
|
407
|
+
|| result.s > best.s
|
|
408
|
+
|| (result.s === best.s && result.q > best.q)
|
|
409
|
+
|| (result.s === best.s && result.q === best.q && result.o < best.o))
|
|
410
|
+
) {
|
|
411
|
+
best = result
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
return {
|
|
416
|
+
value: charset,
|
|
417
|
+
q: best?.q ?? 0,
|
|
418
|
+
s: best?.s ?? 0,
|
|
419
|
+
o: best?.o ?? -1,
|
|
420
|
+
i: index,
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
function compareSpecs(a: ParsedSpec, b: ParsedSpec): number {
|
|
425
|
+
return (
|
|
426
|
+
b.q - a.q
|
|
427
|
+
|| b.s - a.s
|
|
428
|
+
|| a.o - b.o
|
|
429
|
+
|| a.i - b.i
|
|
430
|
+
)
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
export function media(accept: string, available?: string[]): string[] {
|
|
434
|
+
const parsed = parseAccept(accept)
|
|
435
|
+
if (parsed.length === 0) {
|
|
436
|
+
return []
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
if (!available) {
|
|
440
|
+
return parsed.sort((a, b) => b.q - a.q || a.o - b.o).map((p) =>
|
|
441
|
+
`${p.type}/${p.subtype}`
|
|
442
|
+
)
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
const priorities = available.map((t, i) => getMediaTypePriority(t, parsed, i))
|
|
446
|
+
const sorted = priorities.filter((p) => p.q > 0).sort(compareSpecs)
|
|
447
|
+
|
|
448
|
+
return sorted.map((p) => p.value)
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
export function language(accept: string, available?: string[]): string[] {
|
|
452
|
+
const parsed = parseAcceptLanguage(accept)
|
|
453
|
+
if (parsed.length === 0) {
|
|
454
|
+
return []
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
if (!available) {
|
|
458
|
+
return parsed.sort((a, b) => b.q - a.q || a.o - b.o).map((p) =>
|
|
459
|
+
p.suffix ? `${p.prefix}-${p.suffix}` : p.prefix
|
|
460
|
+
)
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
const priorities = available.map((l, i) => getLanguagePriority(l, parsed, i))
|
|
464
|
+
const sorted = priorities.filter((p) => p.q > 0).sort(compareSpecs)
|
|
465
|
+
|
|
466
|
+
return sorted.map((p) => p.value)
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
export function encoding(accept: string, available?: string[]): string[] {
|
|
470
|
+
const parsed = parseAcceptEncoding(accept)
|
|
471
|
+
if (parsed.length === 0) {
|
|
472
|
+
return []
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
if (!available) {
|
|
476
|
+
return parsed.sort((a, b) => b.q - a.q || a.o - b.o).map((p) => p.encoding)
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
const priorities = available.map((e, i) => getEncodingPriority(e, parsed, i))
|
|
480
|
+
const sorted = priorities.filter((p) => p.q > 0).sort(compareSpecs)
|
|
481
|
+
|
|
482
|
+
return sorted.map((p) => p.value)
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
export function charset(accept: string, available?: string[]): string[] {
|
|
486
|
+
const parsed = parseAcceptCharset(accept)
|
|
487
|
+
if (parsed.length === 0) {
|
|
488
|
+
return []
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
if (!available) {
|
|
492
|
+
return parsed.sort((a, b) => b.q - a.q || a.o - b.o).map((p) => p.charset)
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
const priorities = available.map((c, i) => getCharsetPriority(c, parsed, i))
|
|
496
|
+
const sorted = priorities.filter((p) => p.q > 0).sort(compareSpecs)
|
|
497
|
+
|
|
498
|
+
return sorted.map((p) => p.value)
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
export function headerMedia(
|
|
502
|
+
headers: Headers.Headers,
|
|
503
|
+
available?: string[],
|
|
504
|
+
): string[] {
|
|
505
|
+
const accept = headers["accept"]
|
|
506
|
+
if (!accept) return []
|
|
507
|
+
return media(accept, available)
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
export function headerLanguage(
|
|
511
|
+
headers: Headers.Headers,
|
|
512
|
+
available?: string[],
|
|
513
|
+
): string[] {
|
|
514
|
+
const accept = headers["accept-language"]
|
|
515
|
+
if (!accept) return []
|
|
516
|
+
return language(accept, available)
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
export function headerEncoding(
|
|
520
|
+
headers: Headers.Headers,
|
|
521
|
+
available?: string[],
|
|
522
|
+
): string[] {
|
|
523
|
+
const accept = headers["accept-encoding"]
|
|
524
|
+
if (!accept) return []
|
|
525
|
+
return encoding(accept, available)
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
export function headerCharset(
|
|
529
|
+
headers: Headers.Headers,
|
|
530
|
+
available?: string[],
|
|
531
|
+
): string[] {
|
|
532
|
+
const accept = headers["accept-charset"]
|
|
533
|
+
if (!accept) return []
|
|
534
|
+
return charset(accept, available)
|
|
535
|
+
}
|
package/src/FileRouter.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
1
2
|
import type { PlatformError } from "@effect/platform/Error"
|
|
2
3
|
import * as FileSystem from "@effect/platform/FileSystem"
|
|
3
4
|
import * as Array from "effect/Array"
|
|
@@ -12,11 +13,9 @@ import * as NUrl from "node:url"
|
|
|
12
13
|
import * as FileRouterCodegen from "./FileRouterCodegen.ts"
|
|
13
14
|
import * as FileRouterPattern from "./FileRouterPattern.ts"
|
|
14
15
|
import * as FileSystemExtra from "./FileSystemExtra.ts"
|
|
15
|
-
import * as Route from "./Route.ts"
|
|
16
|
-
import * as Router from "./Router.ts"
|
|
17
16
|
|
|
18
17
|
export type RouteModule = {
|
|
19
|
-
default:
|
|
18
|
+
default: RouteSet.RouteSet.Default
|
|
20
19
|
}
|
|
21
20
|
|
|
22
21
|
export type LazyRoute = {
|
|
@@ -187,7 +186,9 @@ export function fromManifest(
|
|
|
187
186
|
manifest: Manifest,
|
|
188
187
|
): Effect.Effect<Router.Router.Any> {
|
|
189
188
|
return Effect.gen(function*() {
|
|
190
|
-
const
|
|
189
|
+
const mounts: Record<`/${string}`, RouteSet.RouteSet.Default> = {}
|
|
190
|
+
|
|
191
|
+
yield* Effect.forEach(
|
|
191
192
|
manifest.routes,
|
|
192
193
|
(lazyRoute) =>
|
|
193
194
|
Effect.gen(function*() {
|
|
@@ -199,19 +200,22 @@ export function fromManifest(
|
|
|
199
200
|
)
|
|
200
201
|
: []
|
|
201
202
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
.filter(Route.isRouteLayer)
|
|
203
|
+
// Start with the route from the route module
|
|
204
|
+
let mergedRouteSet: RouteSet.RouteSet.Default = routeModule.default
|
|
205
205
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
206
|
+
// Concatenate each layer's routes into the routeSet
|
|
207
|
+
for (const m of layerModules) {
|
|
208
|
+
const layerRouteSet = (m as any).default
|
|
209
|
+
if (RouteSet.isRouteSet(layerRouteSet)) {
|
|
210
|
+
mergedRouteSet = Route.merge(layerRouteSet, mergedRouteSet)
|
|
211
|
+
}
|
|
210
212
|
}
|
|
213
|
+
|
|
214
|
+
mounts[lazyRoute.path] = mergedRouteSet
|
|
211
215
|
}),
|
|
212
216
|
)
|
|
213
217
|
|
|
214
|
-
return Router.make(
|
|
218
|
+
return Router.make(mounts, RouteSet.make())
|
|
215
219
|
})
|
|
216
220
|
}
|
|
217
221
|
|