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.
Files changed (44) hide show
  1. package/package.json +12 -13
  2. package/src/BundleHttp.test.ts +1 -1
  3. package/src/Commander.test.ts +15 -15
  4. package/src/Commander.ts +58 -88
  5. package/src/EncryptedCookies.test.ts +4 -4
  6. package/src/FileHttpRouter.test.ts +81 -12
  7. package/src/FileHttpRouter.ts +115 -26
  8. package/src/FileRouter.ts +60 -162
  9. package/src/FileRouterCodegen.test.ts +250 -64
  10. package/src/FileRouterCodegen.ts +13 -56
  11. package/src/FileRouterPattern.test.ts +116 -0
  12. package/src/FileRouterPattern.ts +59 -0
  13. package/src/FileRouter_path.test.ts +63 -102
  14. package/src/FileSystemExtra.test.ts +226 -0
  15. package/src/FileSystemExtra.ts +24 -60
  16. package/src/HttpUtils.test.ts +68 -0
  17. package/src/HttpUtils.ts +15 -0
  18. package/src/HyperHtml.ts +24 -5
  19. package/src/JsModule.test.ts +1 -1
  20. package/src/NodeFileSystem.ts +764 -0
  21. package/src/Random.ts +59 -0
  22. package/src/Route.test.ts +471 -0
  23. package/src/Route.ts +298 -153
  24. package/src/RouteRender.ts +38 -0
  25. package/src/Router.ts +11 -33
  26. package/src/RouterPattern.test.ts +629 -0
  27. package/src/RouterPattern.ts +391 -0
  28. package/src/Start.ts +14 -52
  29. package/src/bun/BunBundle.test.ts +0 -3
  30. package/src/bun/BunHttpServer.ts +246 -0
  31. package/src/bun/BunHttpServer_web.ts +384 -0
  32. package/src/bun/BunRoute.test.ts +341 -0
  33. package/src/bun/BunRoute.ts +326 -0
  34. package/src/bun/BunRoute_bundles.test.ts +218 -0
  35. package/src/bun/BunRuntime.ts +33 -0
  36. package/src/bun/BunTailwindPlugin.test.ts +1 -1
  37. package/src/bun/_empty.html +1 -0
  38. package/src/bun/index.ts +2 -1
  39. package/src/testing.ts +12 -3
  40. package/src/Datastar.test.ts +0 -267
  41. package/src/Datastar.ts +0 -68
  42. package/src/bun/BunFullstackServer.ts +0 -45
  43. package/src/bun/BunFullstackServer_httpServer.ts +0 -541
  44. package/src/jsx-datastar.d.ts +0 -63
@@ -0,0 +1,218 @@
1
+ import * as Bun from "bun"
2
+ import * as t from "bun:test"
3
+ import * as Effect from "effect/Effect"
4
+ import * as Route from "../Route.ts"
5
+ import type * as Router from "../Router.ts"
6
+ import * as BunRoute from "./BunRoute.ts"
7
+
8
+ t.describe("BunRoute proxy with Bun.serve", () => {
9
+ t.test("BunRoute proxy returns same content as direct bundle access", async () => {
10
+ const bunRoute = BunRoute.loadBundle(() =>
11
+ import("../../static/TestPage.html")
12
+ )
13
+
14
+ const router: Router.RouterContext = {
15
+ routes: [
16
+ {
17
+ path: "/test",
18
+ load: () => Promise.resolve({ default: bunRoute }),
19
+ },
20
+ ],
21
+ }
22
+
23
+ const routes = await Effect.runPromise(BunRoute.routesFromRouter(router))
24
+
25
+ const internalPath = Object.keys(routes).find((k) =>
26
+ k.includes("~BunRoute-")
27
+ )
28
+ t.expect(internalPath).toBeDefined()
29
+
30
+ const proxyHandler = routes["/test"]
31
+ t.expect(typeof proxyHandler).toBe("function")
32
+
33
+ const internalBundle = routes[internalPath!]
34
+ t.expect(internalBundle).toHaveProperty("index")
35
+
36
+ const server = Bun.serve({
37
+ port: 0,
38
+ routes,
39
+ fetch: () => new Response("Not found", { status: 404 }),
40
+ })
41
+
42
+ try {
43
+ const directResponse = await fetch(
44
+ `http://localhost:${server.port}${internalPath}`,
45
+ )
46
+ const proxyResponse = await fetch(`http://localhost:${server.port}/test`)
47
+
48
+ t.expect(proxyResponse.status).toBe(directResponse.status)
49
+
50
+ const directText = await directResponse.text()
51
+ const proxyText = await proxyResponse.text()
52
+
53
+ t.expect(proxyText).toBe(directText)
54
+ t.expect(proxyText).toContain("Test Page Content")
55
+ } finally {
56
+ server.stop()
57
+ }
58
+ })
59
+
60
+ t.test("multiple BunRoutes each get unique internal paths", async () => {
61
+ const bunRoute1 = BunRoute.loadBundle(() =>
62
+ import("../../static/TestPage.html")
63
+ )
64
+ const bunRoute2 = BunRoute.loadBundle(() =>
65
+ import("../../static/AnotherPage.html")
66
+ )
67
+
68
+ const router: Router.RouterContext = {
69
+ routes: [
70
+ {
71
+ path: "/page1",
72
+ load: () => Promise.resolve({ default: bunRoute1 }),
73
+ },
74
+ {
75
+ path: "/page2",
76
+ load: () => Promise.resolve({ default: bunRoute2 }),
77
+ },
78
+ ],
79
+ }
80
+
81
+ const routes = await Effect.runPromise(BunRoute.routesFromRouter(router))
82
+
83
+ const internalPaths = Object.keys(routes).filter((k) =>
84
+ k.includes("~BunRoute-")
85
+ )
86
+ t.expect(internalPaths).toHaveLength(2)
87
+
88
+ const nonces = internalPaths.map((p) => {
89
+ const match = p.match(/~BunRoute-([a-z0-9]+)$/)
90
+ return match?.[1]
91
+ })
92
+ t.expect(nonces[0]).toBe(nonces[1])
93
+
94
+ const server = Bun.serve({
95
+ port: 0,
96
+ routes,
97
+ fetch: () => new Response("Not found", { status: 404 }),
98
+ })
99
+
100
+ try {
101
+ const response1 = await fetch(`http://localhost:${server.port}/page1`)
102
+ const response2 = await fetch(`http://localhost:${server.port}/page2`)
103
+
104
+ const text1 = await response1.text()
105
+ const text2 = await response2.text()
106
+
107
+ t.expect(text1).toContain("Test Page Content")
108
+ t.expect(text2).toContain("Another Page Content")
109
+ } finally {
110
+ server.stop()
111
+ }
112
+ })
113
+
114
+ t.test("proxy preserves request headers", async () => {
115
+ const bunRoute = BunRoute.loadBundle(() =>
116
+ import("../../static/TestPage.html")
117
+ )
118
+
119
+ const router: Router.RouterContext = {
120
+ routes: [
121
+ {
122
+ path: "/headers-test",
123
+ load: () => Promise.resolve({ default: bunRoute }),
124
+ },
125
+ ],
126
+ }
127
+
128
+ const routes = await Effect.runPromise(BunRoute.routesFromRouter(router))
129
+
130
+ const server = Bun.serve({
131
+ port: 0,
132
+ routes,
133
+ fetch: () => new Response("Not found", { status: 404 }),
134
+ })
135
+
136
+ try {
137
+ const response = await fetch(
138
+ `http://localhost:${server.port}/headers-test`,
139
+ {
140
+ headers: {
141
+ "Accept": "text/html",
142
+ "X-Custom-Header": "test-value",
143
+ },
144
+ },
145
+ )
146
+
147
+ t.expect(response.status).toBe(200)
148
+ t.expect(await response.text()).toContain("Test Page Content")
149
+ } finally {
150
+ server.stop()
151
+ }
152
+ })
153
+
154
+ t.test("mixed BunRoute and regular routes work together", async () => {
155
+ const bunRoute = BunRoute.loadBundle(() =>
156
+ import("../../static/TestPage.html")
157
+ )
158
+ const textRoute = Route.text(Effect.succeed("Hello from text route"))
159
+
160
+ const router: Router.RouterContext = {
161
+ routes: [
162
+ {
163
+ path: "/html",
164
+ load: () => Promise.resolve({ default: bunRoute }),
165
+ },
166
+ {
167
+ path: "/api",
168
+ load: () => Promise.resolve({ default: textRoute }),
169
+ },
170
+ ],
171
+ }
172
+
173
+ const routes = await Effect.runPromise(BunRoute.routesFromRouter(router))
174
+
175
+ const server = Bun.serve({
176
+ port: 0,
177
+ routes,
178
+ fetch: () => new Response("Not found", { status: 404 }),
179
+ })
180
+
181
+ try {
182
+ const htmlResponse = await fetch(`http://localhost:${server.port}/html`)
183
+ const apiResponse = await fetch(`http://localhost:${server.port}/api`)
184
+
185
+ t.expect(await htmlResponse.text()).toContain("Test Page Content")
186
+ t.expect(await apiResponse.text()).toBe("Hello from text route")
187
+ } finally {
188
+ server.stop()
189
+ }
190
+ })
191
+
192
+ t.test("nonce is different across separate routesFromRouter calls", async () => {
193
+ const bunRoute = BunRoute.loadBundle(() =>
194
+ import("../../static/TestPage.html")
195
+ )
196
+
197
+ const router: Router.RouterContext = {
198
+ routes: [
199
+ {
200
+ path: "/test",
201
+ load: () => Promise.resolve({ default: bunRoute }),
202
+ },
203
+ ],
204
+ }
205
+
206
+ const routes1 = await Effect.runPromise(BunRoute.routesFromRouter(router))
207
+ const routes2 = await Effect.runPromise(BunRoute.routesFromRouter(router))
208
+
209
+ const internalPath1 = Object.keys(routes1).find((k) =>
210
+ k.includes("~BunRoute-")
211
+ )
212
+ const internalPath2 = Object.keys(routes2).find((k) =>
213
+ k.includes("~BunRoute-")
214
+ )
215
+
216
+ t.expect(internalPath1).not.toBe(internalPath2)
217
+ })
218
+ })
@@ -0,0 +1,33 @@
1
+ import { makeRunMain } from "@effect/platform/Runtime"
2
+ import { constVoid } from "effect/Function"
3
+
4
+ export const runMain = makeRunMain(({
5
+ fiber,
6
+ teardown,
7
+ }) => {
8
+ const keepAlive = setInterval(constVoid, 2 ** 31 - 1)
9
+ let receivedSignal = false
10
+
11
+ fiber.addObserver((exit) => {
12
+ if (!receivedSignal) {
13
+ process.removeListener("SIGINT", onSigint)
14
+ process.removeListener("SIGTERM", onSigint)
15
+ }
16
+ clearInterval(keepAlive)
17
+ teardown(exit, (code) => {
18
+ if (receivedSignal || code !== 0) {
19
+ process.exit(code)
20
+ }
21
+ })
22
+ })
23
+
24
+ function onSigint() {
25
+ receivedSignal = true
26
+ process.removeListener("SIGINT", onSigint)
27
+ process.removeListener("SIGTERM", onSigint)
28
+ fiber.unsafeInterruptAsFork(fiber.id())
29
+ }
30
+
31
+ process.on("SIGINT", onSigint)
32
+ process.on("SIGTERM", onSigint)
33
+ })
@@ -17,7 +17,7 @@ function extractClassNamesBroad(source: string): Set<string> {
17
17
  )
18
18
  }
19
19
 
20
- t.describe("extractClassNames", () => {
20
+ t.describe(`${extractClassNames.name}`, () => {
21
21
  t.test("Basic HTML class attributes", () => {
22
22
  const source = `<div class="bg-red-500 text-white">Hello</div>`
23
23
  const result = extractClassNames(source)
@@ -0,0 +1 @@
1
+
package/src/bun/index.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export * as BunBundle from "./BunBundle.ts"
2
- export * as BunFullstackServer from "./BunFullstackServer.ts"
2
+ export * as BunHttpServer from "./BunHttpServer.ts"
3
3
  export * as BunImportTrackerPlugin from "./BunImportTrackerPlugin.ts"
4
+ export * as BunRoute from "./BunRoute.ts"
4
5
  export * as BunTailwindPlugin from "./BunTailwindPlugin.ts"
package/src/testing.ts CHANGED
@@ -39,12 +39,21 @@ export const effectFn = <RL>(layer?: Layer.Layer<RL, any>) =>
39
39
  * some tools, like effect-start, use it to generate temporary
40
40
  * files that are then loaded into a runtime.
41
41
  */
42
- const clearStackTraces = (err: any | Error) => {
42
+ const clearStackTraces = (err: unknown) => {
43
43
  const ExternalStackTraceLineRegexp = /\(.*\/node_modules\/[^\.]/
44
44
 
45
- const newErr = new Error(err.message)
46
- const stack: string = err.stack ?? ""
45
+ const message = err instanceof Error
46
+ ? err.message
47
+ : typeof err === "object" && err !== null && "message" in err
48
+ ? String(err.message)
49
+ : String(err)
50
+ const stack: string = err instanceof Error
51
+ ? err.stack ?? ""
52
+ : typeof err === "object" && err !== null && "stack" in err
53
+ ? String(err.stack)
54
+ : ""
47
55
 
56
+ const newErr = new Error(message)
48
57
  newErr.stack = Function.pipe(
49
58
  stack.split("\n"),
50
59
  Array.takeWhile(s => !ExternalStackTraceLineRegexp.test(s)),
@@ -1,267 +0,0 @@
1
- import * as t from "bun:test"
2
- import * as Datastar from "./Datastar.ts"
3
- import * as HyperHtml from "./HyperHtml.ts"
4
- import * as HyperNode from "./HyperNode.ts"
5
- import { jsx } from "./jsx-runtime.ts"
6
-
7
- t.it("data-signals object serialization", () => {
8
- const node = HyperNode.make("div", {
9
- "data-signals": { foo: 1, bar: { baz: "hello" } } as any,
10
- })
11
-
12
- const html = HyperHtml.renderToString(node, Datastar.HyperHooks)
13
-
14
- t
15
- .expect(html)
16
- .toBe(
17
- "<div data-signals=\"{&quot;foo&quot;:1,&quot;bar&quot;:{&quot;baz&quot;:&quot;hello&quot;}}\"></div>",
18
- )
19
- })
20
-
21
- t.it("data-signals string passthrough", () => {
22
- const node = HyperNode.make("div", {
23
- "data-signals": "$mySignal",
24
- })
25
-
26
- const html = HyperHtml.renderToString(node, Datastar.HyperHooks)
27
-
28
- t
29
- .expect(html)
30
- .toBe("<div data-signals=\"$mySignal\"></div>")
31
- })
32
-
33
- t.it("data-signals-* object serialization", () => {
34
- const node = HyperNode.make("div", {
35
- "data-signals-user": { name: "John", age: 30 } as any,
36
- })
37
-
38
- const html = HyperHtml.renderToString(node, Datastar.HyperHooks)
39
-
40
- t
41
- .expect(html)
42
- .toBe(
43
- "<div data-signals-user=\"{&quot;name&quot;:&quot;John&quot;,&quot;age&quot;:30}\"></div>",
44
- )
45
- })
46
-
47
- t.it("non-data attributes unchanged", () => {
48
- const node = HyperNode.make("div", {
49
- id: "test",
50
- class: "my-class",
51
- "data-text": "$count",
52
- "data-signals": { count: 0 } as any,
53
- })
54
-
55
- const html = HyperHtml.renderToString(node, Datastar.HyperHooks)
56
-
57
- t
58
- .expect(html)
59
- .toContain("id=\"test\"")
60
- t
61
- .expect(html)
62
- .toContain("class=\"my-class\"")
63
- t
64
- .expect(html)
65
- .toContain("data-text=\"$count\"")
66
- t
67
- .expect(html)
68
- .toContain("data-signals=\"{&quot;count&quot;:0}\"")
69
- })
70
-
71
- t.it("null and undefined values ignored", () => {
72
- const node = HyperNode.make("div", {
73
- "data-signals": null,
74
- "data-other": undefined,
75
- })
76
-
77
- const html = HyperHtml.renderToString(node, Datastar.HyperHooks)
78
-
79
- t
80
- .expect(html)
81
- .toBe("<div></div>")
82
- })
83
-
84
- t.it("complex nested objects serialization", () => {
85
- const complexObject = {
86
- user: { name: "John Doe", preferences: { theme: "dark" } },
87
- items: [1, 2, 3],
88
- }
89
-
90
- const node = HyperNode.make("div", {
91
- "data-signals": complexObject as any,
92
- })
93
-
94
- const html = HyperHtml.renderToString(node, Datastar.HyperHooks)
95
-
96
- t
97
- .expect(html)
98
- .toContain("data-signals=")
99
- t
100
- .expect(html)
101
- .toContain("John Doe")
102
- })
103
-
104
- t.it("non-signals data attributes serialized", () => {
105
- const node = HyperNode.make("div", {
106
- "data-class": { hidden: true, visible: false } as any,
107
- "data-style": { color: "red", display: "none" } as any,
108
- "data-show": true as any,
109
- "data-text": "$count",
110
- })
111
-
112
- const html = HyperHtml.renderToString(node, Datastar.HyperHooks)
113
-
114
- t
115
- .expect(html)
116
- .toContain(
117
- "data-class=\"{&quot;hidden&quot;:true,&quot;visible&quot;:false}\"",
118
- )
119
- t
120
- .expect(html)
121
- .toContain(
122
- "data-style=\"{&quot;color&quot;:&quot;red&quot;,&quot;display&quot;:&quot;none&quot;}\"",
123
- )
124
- t
125
- .expect(html)
126
- .toContain("data-show=\"true\"")
127
- t
128
- .expect(html)
129
- .toContain("data-text=\"$count\"")
130
- })
131
-
132
- t.it("data-attr object serialization", () => {
133
- const node = HyperNode.make("div", {
134
- "data-attr": { disabled: true, tabindex: 0 } as any,
135
- })
136
-
137
- const html = HyperHtml.renderToString(node, Datastar.HyperHooks)
138
-
139
- t
140
- .expect(html)
141
- .toBe(
142
- "<div data-attr=\"{&quot;disabled&quot;:true,&quot;tabindex&quot;:0}\"></div>",
143
- )
144
- })
145
-
146
- t.it("boolean attributes converted to strings", () => {
147
- const node = HyperNode.make("div", {
148
- "data-ignore": false as any,
149
- "data-ignore-morph": true as any,
150
- })
151
-
152
- const html = HyperHtml.renderToString(node, Datastar.HyperHooks)
153
-
154
- t
155
- .expect(html)
156
- .not
157
- .toContain("data-ignore=\"")
158
- t
159
- .expect(html)
160
- .toContain("data-ignore-morph")
161
- t
162
- .expect(html)
163
- .not
164
- .toContain("data-ignore-morph=")
165
- })
166
-
167
- t.it("data-ignore attributes only present when true", () => {
168
- const nodeTrue = HyperNode.make("div", {
169
- "data-ignore": true as any,
170
- })
171
-
172
- const nodeFalse = HyperNode.make("div", {
173
- "data-ignore": false as any,
174
- })
175
-
176
- const htmlTrue = HyperHtml.renderToString(nodeTrue, Datastar.HyperHooks)
177
- const htmlFalse = HyperHtml.renderToString(nodeFalse, Datastar.HyperHooks)
178
-
179
- t
180
- .expect(htmlTrue)
181
- .toContain("data-ignore")
182
- t
183
- .expect(htmlTrue)
184
- .not
185
- .toContain("data-ignore=")
186
- t
187
- .expect(htmlFalse)
188
- .not
189
- .toContain("data-ignore")
190
- })
191
-
192
- t.it("dynamic attributes with suffixes", () => {
193
- const node = HyperNode.make("div", {
194
- "data-class-active": "hidden" as any,
195
- "data-attr-tabindex": "5" as any,
196
- "data-style-opacity": "0.5" as any,
197
- })
198
-
199
- const html = HyperHtml.renderToString(node, Datastar.HyperHooks)
200
-
201
- t
202
- .expect(html)
203
- .toContain("data-class-active=\"hidden\"")
204
- t
205
- .expect(html)
206
- .toContain("data-attr-tabindex=\"5\"")
207
- t
208
- .expect(html)
209
- .toContain("data-style-opacity=\"0.5\"")
210
- })
211
-
212
- t.it("JSX with data-signals object", () => {
213
- const node = jsx("div", {
214
- "data-signals": { isOpen: false, count: 42 } as any,
215
- children: "content",
216
- })
217
-
218
- const html = HyperHtml.renderToString(node, Datastar.HyperHooks)
219
-
220
- t
221
- .expect(html)
222
- .toBe(
223
- "<div data-signals=\"{&quot;isOpen&quot;:false,&quot;count&quot;:42}\">content</div>",
224
- )
225
- t
226
- .expect(html)
227
- .not
228
- .toContain("[object Object]")
229
- })
230
-
231
- t.it("JSX component returning element with data-signals", () => {
232
- function TestComponent() {
233
- return jsx("div", {
234
- "data-signals": { isOpen: false } as any,
235
- children: jsx("span", { children: "nested content" }),
236
- })
237
- }
238
-
239
- const node = jsx(TestComponent, {})
240
-
241
- const html = HyperHtml.renderToString(node, Datastar.HyperHooks)
242
-
243
- t
244
- .expect(html)
245
- .toBe(
246
- "<div data-signals=\"{&quot;isOpen&quot;:false}\"><span>nested content</span></div>",
247
- )
248
- t
249
- .expect(html)
250
- .not
251
- .toContain("[object Object]")
252
- })
253
-
254
- t.it("debug hook execution", () => {
255
- const node = jsx("div", {
256
- "data-signals": { isOpen: false, count: 42 } as any,
257
- children: "content",
258
- })
259
-
260
- const html = HyperHtml.renderToString(node, Datastar.HyperHooks)
261
- console.log("Final HTML:", html)
262
-
263
- t
264
- .expect(html)
265
- .not
266
- .toContain("[object Object]")
267
- })
package/src/Datastar.ts DELETED
@@ -1,68 +0,0 @@
1
- import * as HyperNode from "./HyperNode.ts"
2
-
3
- export const HyperHooks = {
4
- onNode,
5
- } as const
6
-
7
- function onNode(node: HyperNode.HyperNode) {
8
- const {
9
- "data-signals": dataSignals,
10
- "data-class": dataClass,
11
- "data-attr": dataAttr,
12
- "data-style": dataStyle,
13
- "data-show": dataShow,
14
- "data-ignore": dataIgnore,
15
- "data-ignore-morph": dataIgnoreMorph,
16
- } = node.props as any
17
-
18
- if (typeof dataSignals === "object" && dataSignals !== null) {
19
- node.props["data-signals"] = JSON.stringify(dataSignals)
20
- }
21
-
22
- if (typeof dataClass === "function") {
23
- node.props["data-class"] = `(${dataClass.toString()})()`
24
- } else if (typeof dataClass === "object" && dataClass !== null) {
25
- node.props["data-class"] = JSON.stringify(dataClass)
26
- }
27
-
28
- if (typeof dataAttr === "object" && dataAttr !== null) {
29
- node.props["data-attr"] = JSON.stringify(dataAttr)
30
- }
31
-
32
- if (typeof dataStyle === "function") {
33
- node.props["data-style"] = `(${dataStyle.toString()})()`
34
- } else if (typeof dataStyle === "object" && dataStyle !== null) {
35
- node.props["data-style"] = JSON.stringify(dataStyle)
36
- }
37
-
38
- if (typeof dataShow === "boolean") {
39
- node.props["data-show"] = dataShow.toString()
40
- }
41
-
42
- if (dataIgnore !== true && dataIgnore !== undefined) {
43
- delete node.props["data-ignore"]
44
- }
45
-
46
- if (dataIgnoreMorph !== true && dataIgnoreMorph !== undefined) {
47
- delete node.props["data-ignore-morph"]
48
- }
49
-
50
- // Handle dynamic attributes with suffixes
51
- for (const [key, value] of Object.entries(node.props)) {
52
- if (
53
- key.startsWith("data-signals-")
54
- && typeof value === "object"
55
- && value !== null
56
- ) {
57
- node.props[key] = JSON.stringify(value)
58
- }
59
-
60
- if (
61
- key.startsWith("data-on-")
62
- && typeof value === "function"
63
- ) {
64
- // @ts-ignore
65
- node.props[key] = `(${value.toString()})()`
66
- }
67
- }
68
- }
@@ -1,45 +0,0 @@
1
- import { BunHttpServer } from "@effect/platform-bun"
2
- import * as Config from "effect/Config"
3
- import * as Effect from "effect/Effect"
4
- import * as Fiber from "effect/Fiber"
5
- import * as Layer from "effect/Layer"
6
- import * as Option from "effect/Option"
7
- import * as httpServer from "./BunFullstackServer_httpServer.ts"
8
-
9
- // As of Bun v1.2.13, these types are not publicy exported.
10
- type BunServeFuntionOptions = Parameters<
11
- typeof Bun.serve<any, {}>
12
- >[0]
13
-
14
- type DefaultOptions = Parameters<typeof BunHttpServer.make>[0]
15
-
16
- type Options =
17
- & DefaultOptions
18
- & Omit<BunServeFuntionOptions, "fetch" | "error">
19
-
20
- export const make = (opts: Options) => {
21
- return Effect.gen(function*() {
22
- const env = yield* Config
23
- .string("NODE_ENV")
24
- .pipe(Config.option)
25
-
26
- return httpServer.make({
27
- development: Option.getOrNull(env) === "development",
28
- ...opts,
29
- })
30
- })
31
- }
32
-
33
- export const layer = (opts: Options) => {
34
- return Layer.unwrapEffect(
35
- Effect.gen(function*() {
36
- const env = yield* Config.string("NODE_ENV").pipe(Config.option)
37
- const development = Option.getOrNull(env) !== "development"
38
-
39
- return httpServer.layer({
40
- development,
41
- ...opts,
42
- })
43
- }),
44
- )
45
- }