effect-start 0.9.0 → 0.10.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 +12 -13
- package/src/BundleHttp.test.ts +1 -1
- package/src/Commander.test.ts +15 -15
- package/src/Commander.ts +58 -88
- package/src/EncryptedCookies.test.ts +4 -4
- package/src/FileHttpRouter.test.ts +81 -12
- package/src/FileHttpRouter.ts +115 -26
- package/src/FileRouter.ts +60 -162
- package/src/FileRouterCodegen.test.ts +250 -64
- package/src/FileRouterCodegen.ts +13 -56
- package/src/FileRouterPattern.test.ts +116 -0
- package/src/FileRouterPattern.ts +59 -0
- package/src/FileRouter_path.test.ts +63 -102
- package/src/FileSystemExtra.test.ts +226 -0
- package/src/FileSystemExtra.ts +24 -60
- package/src/HttpUtils.test.ts +68 -0
- package/src/HttpUtils.ts +15 -0
- package/src/HyperHtml.ts +24 -5
- package/src/JsModule.test.ts +1 -1
- package/src/NodeFileSystem.ts +764 -0
- package/src/Random.ts +59 -0
- package/src/Route.test.ts +471 -0
- package/src/Route.ts +298 -153
- package/src/RouteRender.ts +38 -0
- package/src/Router.ts +11 -33
- package/src/RouterPattern.test.ts +629 -0
- package/src/RouterPattern.ts +391 -0
- package/src/Start.ts +14 -52
- package/src/bun/BunBundle.test.ts +0 -3
- package/src/bun/BunHttpServer.ts +246 -0
- package/src/bun/BunHttpServer_web.ts +384 -0
- package/src/bun/BunRoute.test.ts +341 -0
- package/src/bun/BunRoute.ts +326 -0
- package/src/bun/BunRoute_bundles.test.ts +218 -0
- package/src/bun/BunRuntime.ts +33 -0
- package/src/bun/BunTailwindPlugin.test.ts +1 -1
- package/src/bun/_empty.html +1 -0
- package/src/bun/index.ts +2 -1
- package/src/testing.ts +12 -3
- package/src/Datastar.test.ts +0 -267
- package/src/Datastar.ts +0 -68
- package/src/bun/BunFullstackServer.ts +0 -45
- package/src/bun/BunFullstackServer_httpServer.ts +0 -541
- package/src/jsx-datastar.d.ts +0 -63
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
import type * as Route from "./Route.ts"
|
|
2
|
+
|
|
3
|
+
export type ParamDelimiter = "_" | "-" | "." | "," | ";" | "!" | "@" | "~"
|
|
4
|
+
export type ParamPrefix = `${string}${ParamDelimiter}` | ""
|
|
5
|
+
export type ParamSuffix = `${ParamDelimiter}${string}` | ""
|
|
6
|
+
|
|
7
|
+
export type LiteralSegment<
|
|
8
|
+
Value extends string = string,
|
|
9
|
+
> = {
|
|
10
|
+
_tag: "LiteralSegment"
|
|
11
|
+
value: Value
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export type ParamSegment<
|
|
15
|
+
Name extends string = string,
|
|
16
|
+
Optional extends boolean = boolean,
|
|
17
|
+
Prefix extends ParamPrefix = "",
|
|
18
|
+
Suffix extends ParamSuffix = "",
|
|
19
|
+
> = {
|
|
20
|
+
_tag: "ParamSegment"
|
|
21
|
+
name: Name
|
|
22
|
+
optional?: Optional
|
|
23
|
+
prefix?: Prefix
|
|
24
|
+
suffix?: Suffix
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export type RestSegment<
|
|
28
|
+
Name extends string = string,
|
|
29
|
+
Optional extends boolean = boolean,
|
|
30
|
+
> = {
|
|
31
|
+
_tag: "RestSegment"
|
|
32
|
+
name: Name
|
|
33
|
+
optional?: Optional
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export type Segment =
|
|
37
|
+
| LiteralSegment
|
|
38
|
+
| ParamSegment<
|
|
39
|
+
string,
|
|
40
|
+
boolean,
|
|
41
|
+
ParamPrefix,
|
|
42
|
+
ParamSuffix
|
|
43
|
+
>
|
|
44
|
+
| RestSegment
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Parses a route path string into a tuple of Segment types at compile time.
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```ts
|
|
51
|
+
* type Usage = Segments<"/users/[id]/posts/[...rest]">
|
|
52
|
+
* type Expected = [
|
|
53
|
+
* LiteralSegment<"users">,
|
|
54
|
+
* ParamSegment<"id", false>,
|
|
55
|
+
* LiteralSegment<"posts">,
|
|
56
|
+
* RestSegment<"rest", false>
|
|
57
|
+
* ]
|
|
58
|
+
* ```
|
|
59
|
+
*
|
|
60
|
+
* Supports:
|
|
61
|
+
* - Literals: `users` → `LiteralSegment<"users">`
|
|
62
|
+
* - Params: `[id]` → `ParamSegment<"id", false>`
|
|
63
|
+
* - Params (optional): `[[id]]` → `ParamSegment<"id", true>`
|
|
64
|
+
* - Rest: `[...rest]` → `RestSegment<"rest", false>`
|
|
65
|
+
* - Rest (optional): `[[...rest]]` → `RestSegment<"rest", true>`
|
|
66
|
+
* - {Pre,Suf}fixed params: `prefix_[id]_suffix` → `ParamSegment<"id", false, "prefix_", "_suffix">`
|
|
67
|
+
* - Malformed segments: `pk_[id]foo` → `undefined` (suffix must start with delimiter)
|
|
68
|
+
*
|
|
69
|
+
* @limit Paths with more than 48 segments in TypeScript 5.9.3 will fail with
|
|
70
|
+
* `TS2589: Type instantiation is excessively deep and possibly infinite`.
|
|
71
|
+
*/
|
|
72
|
+
export type Segments<Path extends string> = Path extends `/${infer PathRest}`
|
|
73
|
+
? Segments<PathRest>
|
|
74
|
+
: Path extends `${infer Head}/${infer Tail}`
|
|
75
|
+
? [ExtractSegment<Head>, ...Segments<Tail>]
|
|
76
|
+
: Path extends "" ? []
|
|
77
|
+
: [ExtractSegment<Path>]
|
|
78
|
+
|
|
79
|
+
const PARAM_PATTERN =
|
|
80
|
+
/^(?<prefix>.*[^a-zA-Z0-9])?\[(?<name>[^\]]+)\](?<suffix>[^a-zA-Z0-9].*)?$/
|
|
81
|
+
|
|
82
|
+
export function parseSegment(segment: string): Segment | null {
|
|
83
|
+
if (
|
|
84
|
+
segment.startsWith("[[...")
|
|
85
|
+
&& segment.endsWith("]]")
|
|
86
|
+
) {
|
|
87
|
+
return {
|
|
88
|
+
_tag: "RestSegment",
|
|
89
|
+
name: segment.slice(5, -2),
|
|
90
|
+
optional: true,
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (
|
|
95
|
+
segment.startsWith("[...")
|
|
96
|
+
&& segment.endsWith("]")
|
|
97
|
+
) {
|
|
98
|
+
return {
|
|
99
|
+
_tag: "RestSegment",
|
|
100
|
+
name: segment.slice(4, -1),
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (
|
|
105
|
+
segment.startsWith("[[")
|
|
106
|
+
&& segment.endsWith("]]")
|
|
107
|
+
) {
|
|
108
|
+
return {
|
|
109
|
+
_tag: "ParamSegment",
|
|
110
|
+
name: segment.slice(2, -2),
|
|
111
|
+
optional: true,
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const match = segment.match(PARAM_PATTERN)
|
|
116
|
+
if (match?.groups) {
|
|
117
|
+
const { prefix, name, suffix } = match.groups
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
_tag: "ParamSegment",
|
|
121
|
+
name,
|
|
122
|
+
prefix: (prefix as ParamPrefix) || undefined,
|
|
123
|
+
suffix: (suffix as ParamSuffix) || undefined,
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (/^[\p{L}\p{N}._~-]+$/u.test(segment)) {
|
|
128
|
+
return { _tag: "LiteralSegment", value: segment }
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return null
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export function parse(pattern: string): Segment[] {
|
|
135
|
+
const segments = pattern.split("/").filter(Boolean).map(parseSegment)
|
|
136
|
+
|
|
137
|
+
if (segments.some((seg) => seg === null)) {
|
|
138
|
+
throw new Error(
|
|
139
|
+
`Invalid path segment in "${pattern}": contains invalid characters or format`,
|
|
140
|
+
)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return segments as Segment[]
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export function formatSegment(seg: Segment): string {
|
|
147
|
+
switch (seg._tag) {
|
|
148
|
+
case "LiteralSegment":
|
|
149
|
+
return seg.value
|
|
150
|
+
case "ParamSegment": {
|
|
151
|
+
const param = seg.optional ? `[[${seg.name}]]` : `[${seg.name}]`
|
|
152
|
+
return (seg.prefix ?? "") + param + (seg.suffix ?? "")
|
|
153
|
+
}
|
|
154
|
+
case "RestSegment":
|
|
155
|
+
return seg.optional ? `[[...${seg.name}]]` : `[...${seg.name}]`
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export function format(segments: Segment[]): `/${string}` {
|
|
160
|
+
const joined = segments.map(formatSegment).join("/")
|
|
161
|
+
return (joined ? `/${joined}` : "/") as `/${string}`
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function buildPaths(
|
|
165
|
+
segments: Segment[],
|
|
166
|
+
mapper: (seg: Segment) => string,
|
|
167
|
+
restWildcard: string,
|
|
168
|
+
): string[] {
|
|
169
|
+
const optionalRestIndex = segments.findIndex(
|
|
170
|
+
(s) => s._tag === "RestSegment" && s.optional,
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
if (optionalRestIndex !== -1) {
|
|
174
|
+
const before = segments.slice(0, optionalRestIndex)
|
|
175
|
+
const beforeJoined = before.map(mapper).join("/")
|
|
176
|
+
const basePath = beforeJoined ? "/" + beforeJoined : "/"
|
|
177
|
+
const withWildcard = basePath === "/"
|
|
178
|
+
? restWildcard
|
|
179
|
+
: basePath + restWildcard
|
|
180
|
+
return [basePath, withWildcard]
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const joined = segments.map(mapper).join("/")
|
|
184
|
+
return [joined ? "/" + joined : "/"]
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function colonParamSegment(segment: Segment): string {
|
|
188
|
+
switch (segment._tag) {
|
|
189
|
+
case "LiteralSegment":
|
|
190
|
+
return segment.value
|
|
191
|
+
case "ParamSegment": {
|
|
192
|
+
const param = `:${segment.name}${segment.optional ? "?" : ""}`
|
|
193
|
+
return (segment.prefix ?? "") + param + (segment.suffix ?? "")
|
|
194
|
+
}
|
|
195
|
+
case "RestSegment":
|
|
196
|
+
return "*"
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Converts to colon-style path pattern (used by Hono, Bun, itty-router).
|
|
202
|
+
*
|
|
203
|
+
* - `[param]` → `:param`
|
|
204
|
+
* - `[[param]]` → `:param?`
|
|
205
|
+
* - `[...param]` → `*`
|
|
206
|
+
* - `[[...param]]` → `/`, `/*`
|
|
207
|
+
* - `pk_[id]` → `pk_:id`
|
|
208
|
+
*/
|
|
209
|
+
export function toColon(path: Route.RoutePattern): string[] {
|
|
210
|
+
return buildPaths(parse(path), colonParamSegment, "/*")
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export const toHono = toColon
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Converts to Express path pattern.
|
|
217
|
+
*
|
|
218
|
+
* - `[param]` → `:param`
|
|
219
|
+
* - `[[param]]` → `{/:param}`
|
|
220
|
+
* - `[...param]` → `/*param`
|
|
221
|
+
* - `[[...param]]` → `/`, `/*param`
|
|
222
|
+
* - `pk_[id]` → `pk_:id`
|
|
223
|
+
*/
|
|
224
|
+
export function toExpress(path: Route.RoutePattern): string[] {
|
|
225
|
+
const segments = parse(path)
|
|
226
|
+
const optionalRestIndex = segments.findIndex(
|
|
227
|
+
(s) => s._tag === "RestSegment" && s.optional,
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
const mapper = (segment: Segment): string => {
|
|
231
|
+
switch (segment._tag) {
|
|
232
|
+
case "LiteralSegment":
|
|
233
|
+
return segment.value
|
|
234
|
+
case "ParamSegment": {
|
|
235
|
+
const param = `:${segment.name}`
|
|
236
|
+
return (segment.prefix ?? "") + param + (segment.suffix ?? "")
|
|
237
|
+
}
|
|
238
|
+
case "RestSegment":
|
|
239
|
+
return `*${segment.name}`
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (optionalRestIndex !== -1) {
|
|
244
|
+
const before = segments.slice(0, optionalRestIndex)
|
|
245
|
+
const rest = segments[optionalRestIndex]
|
|
246
|
+
if (rest._tag !== "RestSegment") throw new Error("unreachable")
|
|
247
|
+
const restName = rest.name
|
|
248
|
+
const beforeJoined = before.map(mapper).join("/")
|
|
249
|
+
const basePath = beforeJoined ? "/" + beforeJoined : "/"
|
|
250
|
+
const withWildcard = basePath === "/"
|
|
251
|
+
? `/*${restName}`
|
|
252
|
+
: basePath + `/*${restName}`
|
|
253
|
+
return [basePath, withWildcard]
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
let result = ""
|
|
257
|
+
for (let i = 0; i < segments.length; i++) {
|
|
258
|
+
const segment = segments[i]
|
|
259
|
+
const isFirst = i === 0
|
|
260
|
+
switch (segment._tag) {
|
|
261
|
+
case "LiteralSegment":
|
|
262
|
+
result += "/" + segment.value
|
|
263
|
+
break
|
|
264
|
+
case "ParamSegment":
|
|
265
|
+
if (segment.optional && !segment.prefix && !segment.suffix) {
|
|
266
|
+
result += isFirst
|
|
267
|
+
? "/{/:$name}".replace("$name", segment.name)
|
|
268
|
+
: `{/:${segment.name}}`
|
|
269
|
+
} else {
|
|
270
|
+
const param = `:${segment.name}`
|
|
271
|
+
result += "/"
|
|
272
|
+
+ (segment.prefix ?? "")
|
|
273
|
+
+ param
|
|
274
|
+
+ (segment.suffix ?? "")
|
|
275
|
+
}
|
|
276
|
+
break
|
|
277
|
+
case "RestSegment":
|
|
278
|
+
result += `/*${segment.name}`
|
|
279
|
+
break
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return [result || "/"]
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Converts to Effect HttpRouter/find-my-way path pattern.
|
|
287
|
+
*
|
|
288
|
+
* - `[param]` → `:param`
|
|
289
|
+
* - `[[param]]` → `:param?` (must be final segment)
|
|
290
|
+
* - `[...param]` → `*`
|
|
291
|
+
* - `[[...param]]` → `/`, `/*`
|
|
292
|
+
* - `pk_[id]` → `pk_:id`
|
|
293
|
+
*/
|
|
294
|
+
export function toEffect(path: Route.RoutePattern): string[] {
|
|
295
|
+
return buildPaths(parse(path), colonParamSegment, "/*")
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Converts to URLPattern path pattern.
|
|
300
|
+
*
|
|
301
|
+
* - `[param]` → `:param`
|
|
302
|
+
* - `[[param]]` → `:param?`
|
|
303
|
+
* - `[...param]` → `:param+`
|
|
304
|
+
* - `[[...param]]` → `:param*`
|
|
305
|
+
* - `pk_[id]` → `pk_:id`
|
|
306
|
+
*/
|
|
307
|
+
export function toURLPattern(path: Route.RoutePattern): string[] {
|
|
308
|
+
const segments = parse(path)
|
|
309
|
+
const joined = segments
|
|
310
|
+
.map((segment) => {
|
|
311
|
+
switch (segment._tag) {
|
|
312
|
+
case "LiteralSegment":
|
|
313
|
+
return segment.value
|
|
314
|
+
case "ParamSegment": {
|
|
315
|
+
const param = `:${segment.name}${segment.optional ? "?" : ""}`
|
|
316
|
+
return (segment.prefix ?? "") + param + (segment.suffix ?? "")
|
|
317
|
+
}
|
|
318
|
+
case "RestSegment":
|
|
319
|
+
return `:${segment.name}${segment.optional ? "*" : "+"}`
|
|
320
|
+
}
|
|
321
|
+
})
|
|
322
|
+
.join("/")
|
|
323
|
+
return [joined ? "/" + joined : "/"]
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Converts to Remix path pattern.
|
|
328
|
+
*
|
|
329
|
+
* - `[param]` → `$param`
|
|
330
|
+
* - `[[param]]` → `($param)`
|
|
331
|
+
* - `[...param]` → `$`
|
|
332
|
+
* - `[[...param]]` → `/`, `$`
|
|
333
|
+
* - `pk_[id]` → (not supported, emits `pk_$id`)
|
|
334
|
+
*/
|
|
335
|
+
export function toRemix(path: Route.RoutePattern): string[] {
|
|
336
|
+
const segments = parse(path)
|
|
337
|
+
const optionalRestIndex = segments.findIndex(
|
|
338
|
+
(s) => s._tag === "RestSegment" && s.optional,
|
|
339
|
+
)
|
|
340
|
+
|
|
341
|
+
const mapper = (segment: Segment): string => {
|
|
342
|
+
switch (segment._tag) {
|
|
343
|
+
case "LiteralSegment":
|
|
344
|
+
return segment.value
|
|
345
|
+
case "ParamSegment": {
|
|
346
|
+
const param = segment.optional
|
|
347
|
+
? `($${segment.name})`
|
|
348
|
+
: `$${segment.name}`
|
|
349
|
+
return (segment.prefix ?? "") + param + (segment.suffix ?? "")
|
|
350
|
+
}
|
|
351
|
+
case "RestSegment":
|
|
352
|
+
return "$"
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
if (optionalRestIndex !== -1) {
|
|
357
|
+
const before = segments.slice(0, optionalRestIndex)
|
|
358
|
+
const beforeJoined = before.map(mapper).join("/")
|
|
359
|
+
const basePath = beforeJoined ? "/" + beforeJoined : "/"
|
|
360
|
+
const withWildcard = basePath === "/" ? "$" : basePath + "/$"
|
|
361
|
+
return [basePath, withWildcard]
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
const joined = segments.map(mapper).join("/")
|
|
365
|
+
return [joined ? "/" + joined : "/"]
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
export const toBun = toColon
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* @deprecated Use toEffectHttpRouterPath instead
|
|
372
|
+
*/
|
|
373
|
+
export function toHttpPath(path: Route.RoutePattern): string {
|
|
374
|
+
return toEffect(path)[0]
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
type ExtractSegment<S extends string> = S extends `[[...${infer Name}]]`
|
|
378
|
+
? RestSegment<Name, true>
|
|
379
|
+
: S extends `[...${infer Name}]` ? RestSegment<Name, false>
|
|
380
|
+
: S extends `[[${infer Name}]]` ? ParamSegment<Name, true, "", "">
|
|
381
|
+
: S extends
|
|
382
|
+
`${infer Pre
|
|
383
|
+
extends `${string}${ParamDelimiter}`}[${infer Name}]${infer Suf}`
|
|
384
|
+
? Suf extends `${infer Delim extends ParamDelimiter}${infer SufRest}`
|
|
385
|
+
? ParamSegment<Name, false, Pre, `${Delim}${SufRest}`>
|
|
386
|
+
: Suf extends "" ? ParamSegment<Name, false, Pre, "">
|
|
387
|
+
: undefined
|
|
388
|
+
: S extends `[${infer Name}]${infer Suf extends `${ParamDelimiter}${string}`}`
|
|
389
|
+
? ParamSegment<Name, false, "", Suf>
|
|
390
|
+
: S extends `[${infer Name}]` ? ParamSegment<Name, false, "", "">
|
|
391
|
+
: LiteralSegment<S>
|
package/src/Start.ts
CHANGED
|
@@ -1,48 +1,24 @@
|
|
|
1
|
-
import * as BunContext from "@effect/platform-bun/BunContext"
|
|
2
|
-
import * as BunHttpServer from "@effect/platform-bun/BunHttpServer"
|
|
3
|
-
import * as BunRuntime from "@effect/platform-bun/BunRuntime"
|
|
4
1
|
import * as FetchHttpClient from "@effect/platform/FetchHttpClient"
|
|
2
|
+
import * as FileSystem from "@effect/platform/FileSystem"
|
|
5
3
|
import * as HttpClient from "@effect/platform/HttpClient"
|
|
6
4
|
import * as HttpRouter from "@effect/platform/HttpRouter"
|
|
7
5
|
import * as HttpServer from "@effect/platform/HttpServer"
|
|
6
|
+
import * as Config from "effect/Config"
|
|
8
7
|
import * as Effect from "effect/Effect"
|
|
9
8
|
import * as Function from "effect/Function"
|
|
10
9
|
import * as Layer from "effect/Layer"
|
|
10
|
+
import * as Option from "effect/Option"
|
|
11
11
|
import * as BunBundle from "./bun/BunBundle.ts"
|
|
12
|
+
import * as BunHttpServer from "./bun/BunHttpServer.ts"
|
|
13
|
+
import * as BunRoute from "./bun/BunRoute.ts"
|
|
14
|
+
import * as BunRuntime from "./bun/BunRuntime.ts"
|
|
12
15
|
import * as Bundle from "./Bundle.ts"
|
|
13
16
|
import * as BundleHttp from "./BundleHttp.ts"
|
|
14
|
-
import * as FileRouter from "./FileRouter.ts"
|
|
15
17
|
import * as HttpAppExtra from "./HttpAppExtra.ts"
|
|
18
|
+
import * as NodeFileSystem from "./NodeFileSystem.ts"
|
|
16
19
|
import * as Router from "./Router.ts"
|
|
17
20
|
import * as StartApp from "./StartApp.ts"
|
|
18
21
|
|
|
19
|
-
// TODO: we probably want to remove this API to avoid
|
|
20
|
-
// multiple entrypoints for routers and bundles.
|
|
21
|
-
// We could handle endpoints routing in {@link layer}
|
|
22
|
-
// or {@link serve}.
|
|
23
|
-
// Serve probably makes more sense because it's an entrypoint
|
|
24
|
-
// for serving an HTTP server
|
|
25
|
-
export function router(options: {
|
|
26
|
-
load: () => Promise<Router.RouteManifest>
|
|
27
|
-
path: string
|
|
28
|
-
}) {
|
|
29
|
-
return Layer.provideMerge(
|
|
30
|
-
// add it to BundleHttp
|
|
31
|
-
Layer.effectDiscard(
|
|
32
|
-
Effect.gen(function*() {
|
|
33
|
-
const httpRouter = yield* HttpRouter.Default
|
|
34
|
-
const startRouter = yield* Router.Router
|
|
35
|
-
|
|
36
|
-
yield* httpRouter.concat(startRouter.httpRouter)
|
|
37
|
-
}),
|
|
38
|
-
),
|
|
39
|
-
Layer.merge(
|
|
40
|
-
Router.layerPromise(options.load),
|
|
41
|
-
FileRouter.layer(options),
|
|
42
|
-
),
|
|
43
|
-
)
|
|
44
|
-
}
|
|
45
|
-
|
|
46
22
|
export function bundleClient(config: BunBundle.BuildOptions | string) {
|
|
47
23
|
const clientLayer = Layer.effect(
|
|
48
24
|
Bundle.ClientBundle,
|
|
@@ -88,9 +64,10 @@ export function serve<ROut, E>(
|
|
|
88
64
|
ROut,
|
|
89
65
|
E,
|
|
90
66
|
| HttpServer.HttpServer
|
|
91
|
-
| HttpRouter.Default
|
|
92
67
|
| HttpClient.HttpClient
|
|
93
|
-
|
|
|
68
|
+
| HttpRouter.Default
|
|
69
|
+
| FileSystem.FileSystem
|
|
70
|
+
| BunHttpServer.BunServer
|
|
94
71
|
>
|
|
95
72
|
}>,
|
|
96
73
|
) {
|
|
@@ -102,29 +79,14 @@ export function serve<ROut, E>(
|
|
|
102
79
|
)
|
|
103
80
|
|
|
104
81
|
return Function.pipe(
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
const middleware = yield* middlewareService.middleware
|
|
108
|
-
|
|
109
|
-
const finalMiddleware = Function.flow(
|
|
110
|
-
HttpAppExtra.handleErrors,
|
|
111
|
-
middleware,
|
|
112
|
-
)
|
|
113
|
-
|
|
114
|
-
return Function.pipe(
|
|
115
|
-
HttpRouter
|
|
116
|
-
.Default
|
|
117
|
-
.serve(finalMiddleware),
|
|
118
|
-
HttpServer.withLogAddress,
|
|
119
|
-
)
|
|
120
|
-
})),
|
|
82
|
+
BunHttpServer.layerRoutes(),
|
|
83
|
+
HttpServer.withLogAddress,
|
|
121
84
|
Layer.provide(appLayer),
|
|
122
85
|
Layer.provide([
|
|
123
86
|
FetchHttpClient.layer,
|
|
124
87
|
HttpRouter.Default.Live,
|
|
125
|
-
BunHttpServer.
|
|
126
|
-
|
|
127
|
-
}),
|
|
88
|
+
BunHttpServer.layerServer(),
|
|
89
|
+
NodeFileSystem.layer,
|
|
128
90
|
StartApp.layer(),
|
|
129
91
|
]),
|
|
130
92
|
Layer.launch,
|
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
import * as BunContext from "@effect/platform-bun/BunContext"
|
|
2
|
-
import * as BunHttpServer from "@effect/platform-bun/BunHttpServer"
|
|
3
1
|
import * as HttpRouter from "@effect/platform/HttpRouter"
|
|
4
|
-
import * as HttpServer from "@effect/platform/HttpServer"
|
|
5
2
|
import * as t from "bun:test"
|
|
6
3
|
import * as Effect from "effect/Effect"
|
|
7
4
|
import * as Layer from "effect/Layer"
|