effect-start 0.19.0 → 0.20.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/README.md +3 -3
- package/dist/Development.d.ts +3 -3
- package/dist/Development.js +3 -2
- package/dist/Effectify.d.ts +212 -0
- package/dist/Effectify.js +19 -0
- package/dist/FilePathPattern.d.ts +29 -0
- package/dist/FilePathPattern.js +86 -0
- package/dist/FileRouter.d.ts +39 -41
- package/dist/FileRouter.js +104 -158
- package/dist/FileRouterCodegen.d.ts +7 -8
- package/dist/FileRouterCodegen.js +97 -66
- package/dist/PlatformError.d.ts +46 -0
- package/dist/PlatformError.js +43 -0
- package/dist/PlatformRuntime.d.ts +23 -0
- package/dist/PlatformRuntime.js +42 -0
- package/dist/RouteBody.d.ts +1 -1
- package/dist/Start.d.ts +34 -3
- package/dist/Start.js +31 -6
- package/dist/bun/BunPlatformHttpServer.d.ts +10 -0
- package/dist/bun/BunPlatformHttpServer.js +53 -0
- package/dist/bun/BunRoute.d.ts +3 -5
- package/dist/bun/BunRoute.js +9 -17
- package/dist/bun/BunRuntime.d.ts +2 -1
- package/dist/bun/BunRuntime.js +10 -5
- package/dist/bun/BunServer.d.ts +33 -0
- package/dist/bun/BunServer.js +133 -0
- package/dist/bun/BunServerRequest.d.ts +60 -0
- package/dist/bun/BunServerRequest.js +252 -0
- package/dist/bun/index.d.ts +1 -1
- package/dist/bun/index.js +1 -1
- package/dist/datastar/actions/fetch.d.ts +30 -0
- package/dist/datastar/actions/fetch.js +411 -0
- package/dist/datastar/actions/peek.d.ts +1 -0
- package/dist/datastar/actions/peek.js +14 -0
- package/dist/datastar/actions/setAll.d.ts +1 -0
- package/dist/datastar/actions/setAll.js +13 -0
- package/dist/datastar/actions/toggleAll.d.ts +1 -0
- package/dist/datastar/actions/toggleAll.js +13 -0
- package/dist/datastar/attributes/attr.d.ts +1 -0
- package/dist/datastar/attributes/attr.js +49 -0
- package/dist/datastar/attributes/bind.d.ts +1 -0
- package/dist/datastar/attributes/bind.js +183 -0
- package/dist/datastar/attributes/class.d.ts +1 -0
- package/dist/datastar/attributes/class.js +50 -0
- package/dist/datastar/attributes/computed.d.ts +1 -0
- package/dist/datastar/attributes/computed.js +27 -0
- package/dist/datastar/attributes/effect.d.ts +1 -0
- package/dist/datastar/attributes/effect.js +10 -0
- package/dist/datastar/attributes/indicator.d.ts +1 -0
- package/dist/datastar/attributes/indicator.js +32 -0
- package/dist/datastar/attributes/init.d.ts +1 -0
- package/dist/datastar/attributes/init.js +27 -0
- package/dist/datastar/attributes/jsonSignals.d.ts +1 -0
- package/dist/datastar/attributes/jsonSignals.js +31 -0
- package/dist/datastar/attributes/on.d.ts +1 -0
- package/dist/datastar/attributes/on.js +59 -0
- package/dist/datastar/attributes/onIntersect.d.ts +1 -0
- package/dist/datastar/attributes/onIntersect.js +54 -0
- package/dist/datastar/attributes/onInterval.d.ts +1 -0
- package/dist/datastar/attributes/onInterval.js +31 -0
- package/dist/datastar/attributes/onSignalPatch.d.ts +1 -0
- package/dist/datastar/attributes/onSignalPatch.js +44 -0
- package/dist/datastar/attributes/ref.d.ts +1 -0
- package/dist/datastar/attributes/ref.js +11 -0
- package/dist/datastar/attributes/show.d.ts +1 -0
- package/dist/datastar/attributes/show.js +32 -0
- package/dist/datastar/attributes/signals.d.ts +1 -0
- package/dist/datastar/attributes/signals.js +18 -0
- package/dist/datastar/attributes/style.d.ts +1 -0
- package/dist/datastar/attributes/style.js +56 -0
- package/dist/datastar/attributes/text.d.ts +1 -0
- package/dist/datastar/attributes/text.js +27 -0
- package/dist/datastar/engine.d.ts +156 -0
- package/dist/datastar/engine.js +971 -0
- package/dist/datastar/index.d.ts +24 -0
- package/dist/datastar/index.js +24 -0
- package/dist/datastar/load.d.ts +24 -0
- package/dist/datastar/load.js +24 -0
- package/dist/datastar/utils.d.ts +51 -0
- package/dist/datastar/utils.js +205 -0
- package/dist/datastar/watchers/patchElements.d.ts +1 -0
- package/dist/datastar/watchers/patchElements.js +420 -0
- package/dist/datastar/watchers/patchSignals.d.ts +1 -0
- package/dist/datastar/watchers/patchSignals.js +15 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/node/NodeFileSystem.d.ts +7 -0
- package/dist/node/NodeFileSystem.js +420 -0
- package/dist/node/NodeUtils.d.ts +2 -0
- package/dist/node/NodeUtils.js +20 -0
- package/dist/x/tailwind/plugin.js +1 -1
- package/package.json +11 -7
- package/src/Development.ts +26 -25
- package/src/{node/Effectify.ts → Effectify.ts} +10 -3
- package/src/FilePathPattern.ts +115 -0
- package/src/FileRouter.ts +178 -255
- package/src/FileRouterCodegen.ts +135 -92
- package/src/{node/PlatformError.ts → PlatformError.ts} +34 -19
- package/src/PlatformRuntime.ts +97 -0
- package/src/RouteBody.ts +1 -1
- package/src/RouteHttp.ts +3 -1
- package/src/Start.ts +62 -14
- package/src/bun/BunPlatformHttpServer.ts +88 -0
- package/src/bun/BunRoute.ts +12 -22
- package/src/bun/BunRuntime.ts +21 -5
- package/src/bun/BunServer.ts +228 -0
- package/src/bun/index.ts +1 -1
- package/src/datastar/README.md +18 -0
- package/src/datastar/actions/fetch.ts +609 -0
- package/src/datastar/actions/peek.ts +17 -0
- package/src/datastar/actions/setAll.ts +20 -0
- package/src/datastar/actions/toggleAll.ts +20 -0
- package/src/datastar/attributes/attr.ts +50 -0
- package/src/datastar/attributes/bind.ts +220 -0
- package/src/datastar/attributes/class.ts +57 -0
- package/src/datastar/attributes/computed.ts +33 -0
- package/src/datastar/attributes/effect.ts +11 -0
- package/src/datastar/attributes/indicator.ts +39 -0
- package/src/datastar/attributes/init.ts +35 -0
- package/src/datastar/attributes/jsonSignals.ts +38 -0
- package/src/datastar/attributes/on.ts +71 -0
- package/src/datastar/attributes/onIntersect.ts +65 -0
- package/src/datastar/attributes/onInterval.ts +39 -0
- package/src/datastar/attributes/onSignalPatch.ts +63 -0
- package/src/datastar/attributes/ref.ts +12 -0
- package/src/datastar/attributes/show.ts +33 -0
- package/src/datastar/attributes/signals.ts +22 -0
- package/src/datastar/attributes/style.ts +63 -0
- package/src/datastar/attributes/text.ts +30 -0
- package/src/datastar/engine.ts +1341 -0
- package/src/datastar/index.ts +25 -0
- package/src/datastar/utils.ts +286 -0
- package/src/datastar/watchers/patchElements.ts +554 -0
- package/src/datastar/watchers/patchSignals.ts +15 -0
- package/src/index.ts +1 -1
- package/src/node/{FileSystem.ts → NodeFileSystem.ts} +2 -2
- package/src/node/{Utils.ts → NodeUtils.ts} +2 -0
- package/src/x/tailwind/plugin.ts +1 -1
- package/src/FileRouterCodegen.todo.ts +0 -1133
- package/src/FileRouterPattern.ts +0 -59
- package/src/RouterPattern.ts +0 -416
- package/src/StartApp.ts +0 -47
- package/src/bun/BunHttpServer.ts +0 -303
- /package/src/bun/{BunHttpServer_web.ts → BunServerRequest.ts} +0 -0
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import * as HttpApp from "@effect/platform/HttpApp"
|
|
2
|
+
import * as HttpServer from "@effect/platform/HttpServer"
|
|
3
|
+
import * as HttpServerError from "@effect/platform/HttpServerError"
|
|
4
|
+
import * as HttpServerRequest from "@effect/platform/HttpServerRequest"
|
|
5
|
+
import type * as Bun from "bun"
|
|
6
|
+
import * as Effect from "effect/Effect"
|
|
7
|
+
import * as FiberSet from "effect/FiberSet"
|
|
8
|
+
import type * as Scope from "effect/Scope"
|
|
9
|
+
import * as BunServer from "./BunServer.ts"
|
|
10
|
+
import * as BunServerRequest from "./BunServerRequest.ts"
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* From times when we used @effect/platform
|
|
14
|
+
* Not used any more internally. Kept for the future,
|
|
15
|
+
* in case someone will need it for whatever reason. [2026]
|
|
16
|
+
*/
|
|
17
|
+
export const make: Effect.Effect<
|
|
18
|
+
HttpServer.HttpServer,
|
|
19
|
+
never,
|
|
20
|
+
Scope.Scope | BunServer.BunServer
|
|
21
|
+
> = Effect.gen(function*() {
|
|
22
|
+
const bunServer = yield* BunServer.BunServer
|
|
23
|
+
|
|
24
|
+
return HttpServer.make({
|
|
25
|
+
address: {
|
|
26
|
+
_tag: "TcpAddress",
|
|
27
|
+
port: bunServer.server.port!,
|
|
28
|
+
hostname: bunServer.server.hostname!,
|
|
29
|
+
},
|
|
30
|
+
serve(httpApp, middleware) {
|
|
31
|
+
return Effect.gen(function*() {
|
|
32
|
+
const runFork = yield* FiberSet.makeRuntime<never>()
|
|
33
|
+
const runtime = yield* Effect.runtime<never>()
|
|
34
|
+
const app = HttpApp.toHandled(
|
|
35
|
+
httpApp,
|
|
36
|
+
(request, response) =>
|
|
37
|
+
Effect.sync(() => {
|
|
38
|
+
;(request as BunServerRequest.ServerRequestImpl).resolve(
|
|
39
|
+
BunServerRequest.makeResponse(request, response, runtime),
|
|
40
|
+
)
|
|
41
|
+
}),
|
|
42
|
+
middleware,
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
function handler(
|
|
46
|
+
request: Request,
|
|
47
|
+
server: Bun.Server<BunServerRequest.WebSocketContext>,
|
|
48
|
+
) {
|
|
49
|
+
return new Promise<Response>((resolve, _reject) => {
|
|
50
|
+
const fiber = runFork(Effect.provideService(
|
|
51
|
+
app,
|
|
52
|
+
HttpServerRequest.HttpServerRequest,
|
|
53
|
+
new BunServerRequest.ServerRequestImpl(
|
|
54
|
+
request,
|
|
55
|
+
resolve,
|
|
56
|
+
removeHost(request.url),
|
|
57
|
+
server,
|
|
58
|
+
),
|
|
59
|
+
))
|
|
60
|
+
request.signal.addEventListener("abort", () => {
|
|
61
|
+
runFork(
|
|
62
|
+
fiber.interruptAsFork(HttpServerError.clientAbortFiberId),
|
|
63
|
+
)
|
|
64
|
+
}, { once: true })
|
|
65
|
+
})
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
yield* Effect.acquireRelease(
|
|
69
|
+
Effect.sync(() => {
|
|
70
|
+
bunServer.pushHandler(handler)
|
|
71
|
+
}),
|
|
72
|
+
() =>
|
|
73
|
+
Effect.sync(() => {
|
|
74
|
+
bunServer.popHandler()
|
|
75
|
+
}),
|
|
76
|
+
)
|
|
77
|
+
})
|
|
78
|
+
},
|
|
79
|
+
})
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
const removeHost = (url: string) => {
|
|
83
|
+
if (url[0] === "/") {
|
|
84
|
+
return url
|
|
85
|
+
}
|
|
86
|
+
const index = url.indexOf("/", url.indexOf("//") + 2)
|
|
87
|
+
return index === -1 ? "/" : url.slice(index)
|
|
88
|
+
}
|
package/src/bun/BunRoute.ts
CHANGED
|
@@ -4,12 +4,12 @@ import * as Data from "effect/Data"
|
|
|
4
4
|
import * as Effect from "effect/Effect"
|
|
5
5
|
import * as Option from "effect/Option"
|
|
6
6
|
import * as Entity from "../Entity.ts"
|
|
7
|
+
import * as FilePathPattern from "../FilePathPattern.ts"
|
|
7
8
|
import * as Hyper from "../hyper/Hyper.ts"
|
|
8
9
|
import * as HyperHtml from "../hyper/HyperHtml.ts"
|
|
9
|
-
import * as Unique from "../Unique.ts"
|
|
10
10
|
import * as Route from "../Route.ts"
|
|
11
|
-
import * as
|
|
12
|
-
import * as
|
|
11
|
+
import * as Unique from "../Unique.ts"
|
|
12
|
+
import * as BunServer from "./BunServer.ts"
|
|
13
13
|
|
|
14
14
|
const INTERNAL_FETCH_HEADER = "x-effect-start-internal-fetch"
|
|
15
15
|
|
|
@@ -60,7 +60,7 @@ export function htmlBundle(
|
|
|
60
60
|
{ request: Request },
|
|
61
61
|
string,
|
|
62
62
|
BunRouteError,
|
|
63
|
-
|
|
63
|
+
BunServer.BunServer
|
|
64
64
|
>,
|
|
65
65
|
]
|
|
66
66
|
> {
|
|
@@ -68,7 +68,7 @@ export function htmlBundle(
|
|
|
68
68
|
BunDescriptors & { format: "html" } & { request: Request },
|
|
69
69
|
string,
|
|
70
70
|
BunRouteError,
|
|
71
|
-
|
|
71
|
+
BunServer.BunServer
|
|
72
72
|
> = (context, next) =>
|
|
73
73
|
Effect.gen(function*() {
|
|
74
74
|
const originalRequest = context.request
|
|
@@ -87,7 +87,7 @@ export function htmlBundle(
|
|
|
87
87
|
)
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
-
const bunServer = yield*
|
|
90
|
+
const bunServer = yield* BunServer.BunServer
|
|
91
91
|
const url = new URL(originalRequest.url)
|
|
92
92
|
|
|
93
93
|
const internalPath = `${bunPrefix}${url.pathname}`
|
|
@@ -152,7 +152,7 @@ export function htmlBundle(
|
|
|
152
152
|
{ request: Request },
|
|
153
153
|
string,
|
|
154
154
|
BunRouteError,
|
|
155
|
-
|
|
155
|
+
BunServer.BunServer
|
|
156
156
|
>(handler, descriptors)
|
|
157
157
|
|
|
158
158
|
return Route.set(
|
|
@@ -181,9 +181,7 @@ export type BunRoutes = Record<string, BunServerRouteHandler>
|
|
|
181
181
|
* - /exact - Exact match
|
|
182
182
|
* - /users/:id - Full-segment named param
|
|
183
183
|
* - /path/* - Directory wildcard
|
|
184
|
-
* -
|
|
185
|
-
* - /[[id]] - Optional param (implemented via `/` and `/:id`)
|
|
186
|
-
* - /[[...rest]] - Optional rest param (implemented via `/` and `/*`)
|
|
184
|
+
* - /[[404]] - Catch-all / Rest
|
|
187
185
|
*
|
|
188
186
|
* Unsupported patterns (cannot be implemented in Bun):
|
|
189
187
|
* - /pk_[id] - Prefix before param
|
|
@@ -196,24 +194,16 @@ export type BunRoutes = Record<string, BunServerRouteHandler>
|
|
|
196
194
|
export function validateBunPattern(
|
|
197
195
|
pattern: string,
|
|
198
196
|
): Option.Option<BunRouteError> {
|
|
199
|
-
const
|
|
200
|
-
|
|
201
|
-
const unsupported = Array.findFirst(segments, (seg) => {
|
|
202
|
-
if (seg._tag === "ParamSegment") {
|
|
203
|
-
return seg.prefix !== undefined || seg.suffix !== undefined
|
|
204
|
-
}
|
|
197
|
+
const segs = FilePathPattern.segments(pattern)
|
|
205
198
|
|
|
206
|
-
|
|
207
|
-
})
|
|
199
|
+
const invalid = Array.findFirst(segs, (seg) => seg._tag === "InvalidSegment")
|
|
208
200
|
|
|
209
|
-
if (Option.isSome(
|
|
201
|
+
if (Option.isSome(invalid)) {
|
|
210
202
|
return Option.some(
|
|
211
203
|
new BunRouteError({
|
|
212
204
|
reason: "UnsupportedPattern",
|
|
213
205
|
pattern,
|
|
214
|
-
message:
|
|
215
|
-
`Pattern "${pattern}" uses prefixed/suffixed params (prefix_[param] or [param]_suffix) `
|
|
216
|
-
+ `which cannot be implemented in Bun.serve.`,
|
|
206
|
+
message: `Pattern "${pattern}" contains invalid segment.`,
|
|
217
207
|
}),
|
|
218
208
|
)
|
|
219
209
|
}
|
package/src/bun/BunRuntime.ts
CHANGED
|
@@ -1,11 +1,24 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import type * as Fiber from "effect/Fiber"
|
|
2
|
+
import * as GlobalValue from "effect/GlobalValue"
|
|
3
|
+
import * as MutableRef from "effect/MutableRef"
|
|
4
|
+
import * as PlatformRuntime from "../PlatformRuntime.ts"
|
|
3
5
|
|
|
4
|
-
|
|
6
|
+
const mainFiber = GlobalValue.globalValue(
|
|
7
|
+
Symbol.for("effect-start/BunRuntime/existingFiber"),
|
|
8
|
+
() =>
|
|
9
|
+
MutableRef.make<Fiber.RuntimeFiber<unknown, unknown> | undefined>(
|
|
10
|
+
undefined,
|
|
11
|
+
),
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
export const runMain = PlatformRuntime.makeRunMain(({
|
|
5
15
|
fiber,
|
|
6
16
|
teardown,
|
|
7
17
|
}) => {
|
|
8
|
-
const
|
|
18
|
+
const prevFiber = MutableRef.get(mainFiber)
|
|
19
|
+
|
|
20
|
+
MutableRef.set(mainFiber, fiber)
|
|
21
|
+
|
|
9
22
|
let receivedSignal = false
|
|
10
23
|
|
|
11
24
|
fiber.addObserver((exit) => {
|
|
@@ -13,7 +26,6 @@ export const runMain = makeRunMain(({
|
|
|
13
26
|
process.removeListener("SIGINT", onSigint)
|
|
14
27
|
process.removeListener("SIGTERM", onSigint)
|
|
15
28
|
}
|
|
16
|
-
clearInterval(keepAlive)
|
|
17
29
|
teardown(exit, (code) => {
|
|
18
30
|
if (receivedSignal || code !== 0) {
|
|
19
31
|
process.exit(code)
|
|
@@ -30,4 +42,8 @@ export const runMain = makeRunMain(({
|
|
|
30
42
|
|
|
31
43
|
process.on("SIGINT", onSigint)
|
|
32
44
|
process.on("SIGTERM", onSigint)
|
|
45
|
+
|
|
46
|
+
if (prevFiber) {
|
|
47
|
+
prevFiber.unsafeInterruptAsFork(prevFiber.id())
|
|
48
|
+
}
|
|
33
49
|
})
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import * as Socket from "@effect/platform/Socket"
|
|
2
|
+
import * as Bun from "bun"
|
|
3
|
+
import * as Config from "effect/Config"
|
|
4
|
+
import * as Context from "effect/Context"
|
|
5
|
+
import * as Deferred from "effect/Deferred"
|
|
6
|
+
import * as Effect from "effect/Effect"
|
|
7
|
+
import * as Exit from "effect/Exit"
|
|
8
|
+
import * as Layer from "effect/Layer"
|
|
9
|
+
import * as Option from "effect/Option"
|
|
10
|
+
import * as Runtime from "effect/Runtime"
|
|
11
|
+
import type * as Scope from "effect/Scope"
|
|
12
|
+
import * as PathPattern from "../PathPattern.ts"
|
|
13
|
+
import * as PlataformRuntime from "../PlatformRuntime.ts"
|
|
14
|
+
import * as Route from "../Route.ts"
|
|
15
|
+
import * as RouteHttp from "../RouteHttp.ts"
|
|
16
|
+
import * as RouteMount from "../RouteMount.ts"
|
|
17
|
+
import * as RouteTree from "../RouteTree.ts"
|
|
18
|
+
import * as BunRoute from "./BunRoute.ts"
|
|
19
|
+
import * as BunServerRequest from "./BunServerRequest.ts"
|
|
20
|
+
|
|
21
|
+
type FetchHandler = (
|
|
22
|
+
request: Request,
|
|
23
|
+
server: Bun.Server<BunServerRequest.WebSocketContext>,
|
|
24
|
+
) => Response | Promise<Response>
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Basically `Omit<Bun.Serve.Options, "fetch" | "error" | "websocket">`
|
|
28
|
+
* TypeScript 5.9 cannot verify discriminated union types used in
|
|
29
|
+
* {@link Bun.serve} so we need to define them explicitly.
|
|
30
|
+
*/
|
|
31
|
+
interface BunServeOptions {
|
|
32
|
+
readonly port?: number
|
|
33
|
+
readonly hostname?: string
|
|
34
|
+
readonly reusePort?: boolean
|
|
35
|
+
readonly ipv6Only?: boolean
|
|
36
|
+
readonly idleTimeout?: number
|
|
37
|
+
readonly development?: boolean
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export type BunServer = {
|
|
41
|
+
readonly server: Bun.Server<BunServerRequest.WebSocketContext>
|
|
42
|
+
readonly pushHandler: (fetch: FetchHandler) => void
|
|
43
|
+
readonly popHandler: () => void
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export const BunServer = Context.GenericTag<BunServer>(
|
|
47
|
+
"effect-start/BunServer",
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
export const make = (
|
|
51
|
+
options: BunServeOptions,
|
|
52
|
+
): Effect.Effect<
|
|
53
|
+
BunServer,
|
|
54
|
+
never,
|
|
55
|
+
Scope.Scope
|
|
56
|
+
> =>
|
|
57
|
+
Effect.gen(function*() {
|
|
58
|
+
const routes = yield* Effect.serviceOption(Route.Routes).pipe(
|
|
59
|
+
Effect.andThen(Option.getOrUndefined),
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
const port = yield* Config.number("PORT").pipe(
|
|
63
|
+
Effect.catchTag("ConfigError", () => {
|
|
64
|
+
return PlataformRuntime.isAgentHarness()
|
|
65
|
+
? Effect.succeed(0) // random port
|
|
66
|
+
: Effect.succeed(3000)
|
|
67
|
+
}),
|
|
68
|
+
)
|
|
69
|
+
const hostname = yield* Config.string("HOSTNAME").pipe(
|
|
70
|
+
Effect.catchTag("ConfigError", () => Effect.succeed(undefined)),
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
const handlerStack: Array<FetchHandler> = [
|
|
74
|
+
function(_request, _server) {
|
|
75
|
+
return new Response("not found", { status: 404 })
|
|
76
|
+
},
|
|
77
|
+
]
|
|
78
|
+
|
|
79
|
+
const service = BunServer.of({
|
|
80
|
+
// During the construction we need to create a service imlpementation
|
|
81
|
+
// first so we can provide it in the runtime that will be used in web
|
|
82
|
+
// handlers. After we create the runtime, we set it below so it's always
|
|
83
|
+
// available at runtime.
|
|
84
|
+
// An alternative approach would be to use Bun.Server.reload but I prefer
|
|
85
|
+
// to avoid it since it's badly documented and has bunch of bugs.
|
|
86
|
+
server: undefined as any,
|
|
87
|
+
pushHandler(fetch) {
|
|
88
|
+
handlerStack.push(fetch)
|
|
89
|
+
reload()
|
|
90
|
+
},
|
|
91
|
+
popHandler() {
|
|
92
|
+
handlerStack.pop()
|
|
93
|
+
reload()
|
|
94
|
+
},
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
const runtime = yield* Effect.runtime().pipe(
|
|
98
|
+
Effect.andThen(Runtime.provideService(BunServer, service)),
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
let currentRoutes: BunRoute.BunRoutes = routes
|
|
102
|
+
? yield* walkBunRoutes(runtime, routes)
|
|
103
|
+
: {}
|
|
104
|
+
|
|
105
|
+
const websocket: Bun.WebSocketHandler<BunServerRequest.WebSocketContext> = {
|
|
106
|
+
open(ws) {
|
|
107
|
+
Deferred.unsafeDone(ws.data.deferred, Exit.succeed(ws))
|
|
108
|
+
},
|
|
109
|
+
message(ws, message) {
|
|
110
|
+
ws.data.run(message)
|
|
111
|
+
},
|
|
112
|
+
close(ws, code, closeReason) {
|
|
113
|
+
Deferred.unsafeDone(
|
|
114
|
+
ws.data.closeDeferred,
|
|
115
|
+
Socket.defaultCloseCodeIsError(code)
|
|
116
|
+
? Exit.fail(
|
|
117
|
+
new Socket.SocketCloseError({
|
|
118
|
+
reason: "Close",
|
|
119
|
+
code,
|
|
120
|
+
closeReason,
|
|
121
|
+
}),
|
|
122
|
+
)
|
|
123
|
+
: Exit.void,
|
|
124
|
+
)
|
|
125
|
+
},
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const server = Bun.serve({
|
|
129
|
+
port,
|
|
130
|
+
hostname,
|
|
131
|
+
...options,
|
|
132
|
+
routes: currentRoutes,
|
|
133
|
+
fetch: handlerStack[0],
|
|
134
|
+
websocket,
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
// @ts-expect-error
|
|
138
|
+
service.server = server
|
|
139
|
+
|
|
140
|
+
yield* Effect.addFinalizer(() =>
|
|
141
|
+
Effect.sync(() => {
|
|
142
|
+
server.stop()
|
|
143
|
+
})
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
const reload = () => {
|
|
147
|
+
server.reload({
|
|
148
|
+
fetch: handlerStack[handlerStack.length - 1],
|
|
149
|
+
routes: currentRoutes,
|
|
150
|
+
websocket,
|
|
151
|
+
})
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const bunServer = BunServer.of({
|
|
155
|
+
server,
|
|
156
|
+
pushHandler(fetch) {
|
|
157
|
+
handlerStack.push(fetch)
|
|
158
|
+
reload()
|
|
159
|
+
},
|
|
160
|
+
popHandler() {
|
|
161
|
+
handlerStack.pop()
|
|
162
|
+
reload()
|
|
163
|
+
},
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
return bunServer
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Provides HttpServer using BunServer under the hood.
|
|
171
|
+
*/
|
|
172
|
+
export const layer = (
|
|
173
|
+
options?: BunServeOptions,
|
|
174
|
+
): Layer.Layer<BunServer> =>
|
|
175
|
+
Layer.scoped(
|
|
176
|
+
BunServer,
|
|
177
|
+
make(options ?? {}),
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
export const withLogAddress = <A, E, R>(
|
|
181
|
+
layer: Layer.Layer<A, E, R>,
|
|
182
|
+
) =>
|
|
183
|
+
Layer
|
|
184
|
+
.effectDiscard(
|
|
185
|
+
BunServer.pipe(
|
|
186
|
+
Effect.andThen(server =>
|
|
187
|
+
Effect.log(
|
|
188
|
+
`Listening on ${server.server.hostname}:${server.server.port}`,
|
|
189
|
+
)
|
|
190
|
+
),
|
|
191
|
+
),
|
|
192
|
+
)
|
|
193
|
+
.pipe(
|
|
194
|
+
Layer.provideMerge(layer),
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
function walkBunRoutes(
|
|
198
|
+
runtime: Runtime.Runtime<BunServer>,
|
|
199
|
+
tree: RouteTree.RouteTree,
|
|
200
|
+
) {
|
|
201
|
+
return Effect.gen(function*() {
|
|
202
|
+
const bunRoutes: BunRoute.BunRoutes = {}
|
|
203
|
+
const pathGroups = new Map<string, RouteMount.MountedRoute[]>()
|
|
204
|
+
const toWebHandler = RouteHttp.toWebHandlerRuntime(runtime)
|
|
205
|
+
|
|
206
|
+
for (const route of RouteTree.walk(tree)) {
|
|
207
|
+
const bunDescriptors = BunRoute.descriptors(route)
|
|
208
|
+
if (bunDescriptors) {
|
|
209
|
+
const htmlBundle = yield* Effect.promise(bunDescriptors.bunLoad)
|
|
210
|
+
bunRoutes[`${bunDescriptors.bunPrefix}/*`] = htmlBundle
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const path = Route.descriptor(route).path
|
|
214
|
+
const group = pathGroups.get(path) ?? []
|
|
215
|
+
group.push(route)
|
|
216
|
+
pathGroups.set(path, group)
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
for (const [path, routes] of pathGroups) {
|
|
220
|
+
const handler = toWebHandler(routes)
|
|
221
|
+
for (const bunPath of PathPattern.toBun(path)) {
|
|
222
|
+
bunRoutes[bunPath] = handler
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return bunRoutes
|
|
227
|
+
})
|
|
228
|
+
}
|
package/src/bun/index.ts
CHANGED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Datastar
|
|
2
|
+
|
|
3
|
+
This is a port of [Datastar](https://github.com/starfederation/datastar) of magnificent Star Federation.
|
|
4
|
+
|
|
5
|
+
We experimentally place it inside Effect Start to have tighter integration with it and provide
|
|
6
|
+
great out-of-the-box experience. After cleaning up the code, we're at around ~90kb of source code.
|
|
7
|
+
We can probably cut it down by another ~10kb if we remove DataStar expression and use JS functions directly.
|
|
8
|
+
|
|
9
|
+
Based on `812cbe9` (2025-02-05) following changes were made:
|
|
10
|
+
|
|
11
|
+
- Path aliases converted to relative imports: `@engine/*` → `./engine/*`, etc.
|
|
12
|
+
- Flattened `plugins/` directory: `plugins/actions/` → `actions/`, etc.
|
|
13
|
+
- Deleted the `ALIAS` type declaration: removed `globals.d.ts`,
|
|
14
|
+
no alias conditional in `utils/text.ts` & `applyAttributePlugin` in `engine.ts`.
|
|
15
|
+
- Removed plugin header comments.
|
|
16
|
+
- Updated type declaration to conform to `erasableSyntaxOnly`:
|
|
17
|
+
- Converted `enum ReactiveFlags` and `enum EffectFlags` to `const` objects with `as const`
|
|
18
|
+
- Added type aliases `ReactiveFlags_X` to replace `ReactiveFlags.X` namespace types
|