effect-start 0.9.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 (67) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +109 -0
  3. package/package.json +57 -0
  4. package/src/Bundle.ts +167 -0
  5. package/src/BundleFiles.ts +174 -0
  6. package/src/BundleHttp.test.ts +160 -0
  7. package/src/BundleHttp.ts +259 -0
  8. package/src/Commander.test.ts +1378 -0
  9. package/src/Commander.ts +672 -0
  10. package/src/Datastar.test.ts +267 -0
  11. package/src/Datastar.ts +68 -0
  12. package/src/Effect_HttpRouter.test.ts +570 -0
  13. package/src/EncryptedCookies.test.ts +427 -0
  14. package/src/EncryptedCookies.ts +451 -0
  15. package/src/FileHttpRouter.test.ts +207 -0
  16. package/src/FileHttpRouter.ts +122 -0
  17. package/src/FileRouter.ts +405 -0
  18. package/src/FileRouterCodegen.test.ts +598 -0
  19. package/src/FileRouterCodegen.ts +251 -0
  20. package/src/FileRouter_files.test.ts +64 -0
  21. package/src/FileRouter_path.test.ts +132 -0
  22. package/src/FileRouter_tree.test.ts +126 -0
  23. package/src/FileSystemExtra.ts +102 -0
  24. package/src/HttpAppExtra.ts +127 -0
  25. package/src/Hyper.ts +194 -0
  26. package/src/HyperHtml.test.ts +90 -0
  27. package/src/HyperHtml.ts +139 -0
  28. package/src/HyperNode.ts +37 -0
  29. package/src/JsModule.test.ts +14 -0
  30. package/src/JsModule.ts +116 -0
  31. package/src/PublicDirectory.test.ts +280 -0
  32. package/src/PublicDirectory.ts +108 -0
  33. package/src/Route.test.ts +873 -0
  34. package/src/Route.ts +992 -0
  35. package/src/Router.ts +80 -0
  36. package/src/SseHttpResponse.ts +55 -0
  37. package/src/Start.ts +133 -0
  38. package/src/StartApp.ts +43 -0
  39. package/src/StartHttp.ts +42 -0
  40. package/src/StreamExtra.ts +146 -0
  41. package/src/TestHttpClient.test.ts +54 -0
  42. package/src/TestHttpClient.ts +100 -0
  43. package/src/bun/BunBundle.test.ts +277 -0
  44. package/src/bun/BunBundle.ts +309 -0
  45. package/src/bun/BunBundle_imports.test.ts +50 -0
  46. package/src/bun/BunFullstackServer.ts +45 -0
  47. package/src/bun/BunFullstackServer_httpServer.ts +541 -0
  48. package/src/bun/BunImportTrackerPlugin.test.ts +77 -0
  49. package/src/bun/BunImportTrackerPlugin.ts +97 -0
  50. package/src/bun/BunTailwindPlugin.test.ts +335 -0
  51. package/src/bun/BunTailwindPlugin.ts +322 -0
  52. package/src/bun/BunVirtualFilesPlugin.ts +59 -0
  53. package/src/bun/index.ts +4 -0
  54. package/src/client/Overlay.ts +34 -0
  55. package/src/client/ScrollState.ts +120 -0
  56. package/src/client/index.ts +101 -0
  57. package/src/index.ts +24 -0
  58. package/src/jsx-datastar.d.ts +63 -0
  59. package/src/jsx-runtime.ts +23 -0
  60. package/src/jsx.d.ts +4402 -0
  61. package/src/testing.ts +55 -0
  62. package/src/x/cloudflare/CloudflareTunnel.ts +110 -0
  63. package/src/x/cloudflare/index.ts +1 -0
  64. package/src/x/datastar/Datastar.test.ts +267 -0
  65. package/src/x/datastar/Datastar.ts +68 -0
  66. package/src/x/datastar/index.ts +4 -0
  67. package/src/x/datastar/jsx-datastar.d.ts +63 -0
@@ -0,0 +1,277 @@
1
+ import * as BunContext from "@effect/platform-bun/BunContext"
2
+ import * as BunHttpServer from "@effect/platform-bun/BunHttpServer"
3
+ import * as HttpRouter from "@effect/platform/HttpRouter"
4
+ import * as HttpServer from "@effect/platform/HttpServer"
5
+ import * as t from "bun:test"
6
+ import * as Effect from "effect/Effect"
7
+ import * as Layer from "effect/Layer"
8
+ import * as NFS from "node:fs/promises"
9
+ import * as NOS from "node:os"
10
+ import * as NPath from "node:path"
11
+ import * as Bundle from "../Bundle.ts"
12
+ import * as BundleHttp from "../BundleHttp.ts"
13
+ import * as TestHttpClient from "../TestHttpClient.ts"
14
+ import * as BunBundle from "./BunBundle.ts"
15
+
16
+ t.describe("BunBundle manifest structure", () => {
17
+ t.it("should generate manifest with inputs and outputs arrays", async () => {
18
+ const tmpDir = await NFS.mkdtemp(
19
+ NPath.join(NOS.tmpdir(), "effect-start-test-"),
20
+ )
21
+
22
+ try {
23
+ const htmlContent = `<!DOCTYPE html>
24
+ <html>
25
+ <head><title>Test</title></head>
26
+ <body>
27
+ <div id="app"></div>
28
+ <script src="./index.ts" type="module"></script>
29
+ </body>
30
+ </html>`
31
+
32
+ const jsContent = `console.log("Hello from test bundle");
33
+ export const greeting = "Hello World";`
34
+
35
+ const htmlPath = NPath.join(tmpDir, "index.html")
36
+ const jsPath = NPath.join(tmpDir, "index.ts")
37
+
38
+ await NFS.writeFile(htmlPath, htmlContent)
39
+ await NFS.writeFile(jsPath, jsContent)
40
+
41
+ const bundle = await Effect.runPromise(
42
+ BunBundle.buildClient({
43
+ entrypoints: [htmlPath],
44
+ }),
45
+ )
46
+
47
+ t
48
+ .expect(bundle.entrypoints)
49
+ .toBeObject()
50
+ t
51
+ .expect(bundle.artifacts)
52
+ .toBeArray()
53
+
54
+ t
55
+ .expect(Object.keys(bundle.entrypoints).length)
56
+ .toBe(1)
57
+ t
58
+ .expect(bundle.artifacts.length)
59
+ .toBe(3)
60
+
61
+ const entrypointKeys = Object.keys(bundle.entrypoints)
62
+ const firstEntrypoint = entrypointKeys[0]
63
+
64
+ t
65
+ .expect(firstEntrypoint)
66
+ .toBeString()
67
+ t
68
+ .expect(bundle.entrypoints[firstEntrypoint])
69
+ .toBeString()
70
+
71
+ const firstArtifact = bundle.artifacts[0]
72
+
73
+ t
74
+ .expect(firstArtifact)
75
+ .toHaveProperty("path")
76
+ t
77
+ .expect(firstArtifact)
78
+ .toHaveProperty("type")
79
+ t
80
+ .expect(firstArtifact)
81
+ .toHaveProperty("size")
82
+
83
+ t
84
+ .expect(firstArtifact.size)
85
+ .toBeGreaterThan(0)
86
+ } finally {
87
+ await NFS.rm(tmpDir, {
88
+ recursive: true,
89
+ force: true,
90
+ })
91
+ }
92
+ })
93
+
94
+ t.it("should serve manifest via HTTP with correct structure", async () => {
95
+ const tmpDir = await NFS.mkdtemp(
96
+ NPath.join(NOS.tmpdir(), "effect-start-test-"),
97
+ )
98
+
99
+ try {
100
+ const htmlContent = `<!DOCTYPE html>
101
+ <html>
102
+ <head><title>Test App</title></head>
103
+ <body><h1>Test</h1></body>
104
+ </html>`
105
+
106
+ const htmlPath = NPath.join(tmpDir, "app.html")
107
+
108
+ await NFS.writeFile(htmlPath, htmlContent)
109
+
110
+ const testLayer = Layer.effect(
111
+ Bundle.ClientBundle,
112
+ BunBundle.buildClient({
113
+ entrypoints: [htmlPath],
114
+ }),
115
+ )
116
+
117
+ const result = await Effect.runPromise(
118
+ Effect
119
+ .scoped(
120
+ Effect.gen(function*() {
121
+ const App = HttpRouter.empty.pipe(
122
+ HttpRouter.mountApp(
123
+ "/_bundle",
124
+ BundleHttp.httpApp(),
125
+ ),
126
+ )
127
+
128
+ const Client = TestHttpClient.make(App)
129
+
130
+ const response = yield* Client.get("/_bundle/manifest.json")
131
+
132
+ const manifestText = yield* response.text
133
+
134
+ return JSON.parse(manifestText)
135
+ }),
136
+ )
137
+ .pipe(
138
+ Effect.provide(testLayer),
139
+ ),
140
+ )
141
+
142
+ t
143
+ .expect(result)
144
+ .toHaveProperty("entrypoints")
145
+ t
146
+ .expect(result)
147
+ .toHaveProperty("artifacts")
148
+
149
+ t
150
+ .expect(result.entrypoints)
151
+ .toBeObject()
152
+ t
153
+ .expect(result.artifacts)
154
+ .toBeArray()
155
+
156
+ t
157
+ .expect(Object.keys(result.entrypoints).length)
158
+ .toBe(1)
159
+ t
160
+ .expect(result.artifacts.length)
161
+ .toBe(3)
162
+
163
+ const entrypointKeys = Object.keys(result.entrypoints)
164
+ const firstKey = entrypointKeys[0]
165
+
166
+ t
167
+ .expect(firstKey)
168
+ .toBeString()
169
+ t
170
+ .expect(result.entrypoints[firstKey])
171
+ .toBeString()
172
+
173
+ const artifact = result.artifacts[0]
174
+
175
+ t
176
+ .expect(artifact)
177
+ .toHaveProperty("path")
178
+ t
179
+ .expect(artifact)
180
+ .toHaveProperty("type")
181
+ t
182
+ .expect(artifact)
183
+ .toHaveProperty("size")
184
+ } finally {
185
+ await NFS.rm(tmpDir, {
186
+ recursive: true,
187
+ force: true,
188
+ })
189
+ }
190
+ })
191
+
192
+ t.it("should resolve entrypoints to artifacts correctly", async () => {
193
+ const tmpDir = await NFS.mkdtemp(
194
+ NPath.join(NOS.tmpdir(), "effect-start-test-"),
195
+ )
196
+
197
+ try {
198
+ const htmlContent = `<!DOCTYPE html>
199
+ <html><body>Test</body></html>`
200
+
201
+ const htmlPath = NPath.join(tmpDir, "test.html")
202
+
203
+ await NFS.writeFile(htmlPath, htmlContent)
204
+
205
+ const bundle = await Effect.runPromise(
206
+ BunBundle.buildClient({
207
+ entrypoints: [htmlPath],
208
+ }),
209
+ )
210
+
211
+ const entrypointKeys = Object.keys(bundle.entrypoints)
212
+ const firstEntrypoint = entrypointKeys[0]
213
+ const expectedOutput = bundle.entrypoints[firstEntrypoint]
214
+ const resolvedOutput = bundle.resolve(firstEntrypoint)
215
+
216
+ t
217
+ .expect(resolvedOutput)
218
+ .toBe(expectedOutput)
219
+
220
+ const artifact = bundle.getArtifact(resolvedOutput!)
221
+
222
+ t
223
+ .expect(artifact)
224
+ .not
225
+ .toBeNull()
226
+
227
+ t
228
+ .expect(artifact)
229
+ .toBeTruthy()
230
+ } finally {
231
+ await NFS.rm(tmpDir, {
232
+ recursive: true,
233
+ force: true,
234
+ })
235
+ }
236
+ })
237
+
238
+ t.it("should include all artifact metadata", async () => {
239
+ const tmpDir = await NFS.mkdtemp(
240
+ NPath.join(NOS.tmpdir(), "effect-start-test-"),
241
+ )
242
+
243
+ try {
244
+ const jsContent = `export const value = 42;`
245
+ const jsPath = NPath.join(tmpDir, "module.ts")
246
+
247
+ await NFS.writeFile(jsPath, jsContent)
248
+
249
+ const bundle = await Effect.runPromise(
250
+ BunBundle.buildClient({
251
+ entrypoints: [jsPath],
252
+ }),
253
+ )
254
+
255
+ const artifact = bundle.artifacts[0]
256
+
257
+ t
258
+ .expect(artifact.path)
259
+ .toBeString()
260
+ t
261
+ .expect(artifact.type)
262
+ .toBeString()
263
+ t
264
+ .expect(artifact.size)
265
+ .toBeNumber()
266
+
267
+ t
268
+ .expect(artifact.type)
269
+ .toContain("javascript")
270
+ } finally {
271
+ await NFS.rm(tmpDir, {
272
+ recursive: true,
273
+ force: true,
274
+ })
275
+ }
276
+ })
277
+ })
@@ -0,0 +1,309 @@
1
+ import type {
2
+ BuildConfig,
3
+ BuildOutput,
4
+ } from "bun"
5
+ import {
6
+ Array,
7
+ Context,
8
+ Effect,
9
+ Iterable,
10
+ Layer,
11
+ pipe,
12
+ PubSub,
13
+ Record,
14
+ Stream,
15
+ SynchronizedRef,
16
+ } from "effect"
17
+ import * as NPath from "node:path"
18
+ import type {
19
+ BundleContext,
20
+ BundleManifest,
21
+ } from "../Bundle.ts"
22
+ import * as Bundle from "../Bundle.ts"
23
+ import * as FileSystemExtra from "../FileSystemExtra.ts"
24
+ import { BunImportTrackerPlugin } from "./index.ts"
25
+
26
+ export type BuildOptions = Omit<
27
+ BuildConfig,
28
+ "outdir"
29
+ >
30
+
31
+ export const buildClient = (
32
+ config: BuildOptions | string,
33
+ ) => {
34
+ if (typeof config === "string") {
35
+ config = {
36
+ entrypoints: [config],
37
+ }
38
+ }
39
+
40
+ const baseConfig: Partial<BuildOptions> = {
41
+ sourcemap: "linked",
42
+ naming: {
43
+ entry: "[name]-[hash].[ext]",
44
+ chunk: "[name]-[hash].[ext]",
45
+ asset: "[name]-[hash].[ext]",
46
+ },
47
+ packages: "bundle",
48
+ publicPath: "/_bundle/",
49
+ } as const
50
+ const resolvedConfig = {
51
+ ...baseConfig,
52
+ target: "browser" as const,
53
+ ...config,
54
+ }
55
+
56
+ return build(resolvedConfig)
57
+ }
58
+
59
+ export const buildServer = (
60
+ config: BuildOptions | string,
61
+ ) => {
62
+ if (typeof config === "string") {
63
+ config = {
64
+ entrypoints: [config],
65
+ }
66
+ }
67
+
68
+ const baseConfig: Partial<BuildOptions> = {
69
+ sourcemap: "linked",
70
+ naming: {
71
+ entry: "[dir]/[name]-[hash].[ext]",
72
+ chunk: "[name]-[hash].[ext]",
73
+ asset: "[name]-[hash].[ext]",
74
+ },
75
+ packages: "bundle",
76
+ } as const
77
+ const resolvedConfig = {
78
+ ...baseConfig,
79
+ target: "bun" as const,
80
+ ...config,
81
+ }
82
+
83
+ return build(resolvedConfig)
84
+ }
85
+
86
+ /**
87
+ * Given a config, build a bundle and returns every time when effect is executed.
88
+ */
89
+ export function build(
90
+ config: BuildOptions,
91
+ ): Effect.Effect<BundleContext, Bundle.BundleError> {
92
+ return Effect.gen(function*() {
93
+ const output = yield* buildBun(config)
94
+ const manifest = generateManifestfromBunBundle(
95
+ config,
96
+ output,
97
+ )
98
+ const artifactsMap = Record.fromIterableBy(
99
+ output.outputs,
100
+ (v) => v.path.replace(/^\.\//, ""),
101
+ )
102
+
103
+ const resolve = (path: string) => {
104
+ return manifest.entrypoints[path] ?? null
105
+ }
106
+
107
+ const getArtifact = (path: string): Blob | null => {
108
+ return artifactsMap[resolve(path)]
109
+ ?? artifactsMap[path]
110
+ ?? null
111
+ }
112
+
113
+ return {
114
+ ...manifest,
115
+ resolve,
116
+ getArtifact,
117
+ }
118
+ })
119
+ }
120
+
121
+ export function layer<T>(
122
+ tag: Context.Tag<T, BundleContext>,
123
+ config: BuildOptions,
124
+ ) {
125
+ return Layer.effect(tag, build(config))
126
+ }
127
+
128
+ export function layerDev<T>(
129
+ tag: Context.Tag<T, BundleContext>,
130
+ config: BuildOptions,
131
+ ) {
132
+ return Layer.scoped(
133
+ tag,
134
+ Effect.gen(function*() {
135
+ const loadRefKey = "_loadRef"
136
+ const sharedBundle = yield* build(config)
137
+
138
+ const loadRef = yield* SynchronizedRef.make(null)
139
+ sharedBundle[loadRefKey] = loadRef
140
+ sharedBundle.events = yield* PubSub.unbounded<Bundle.BundleEvent>()
141
+
142
+ yield* Effect.fork(
143
+ pipe(
144
+ FileSystemExtra.watchSource({
145
+ filter: FileSystemExtra.filterSourceFiles,
146
+ }),
147
+ Stream.map(v =>
148
+ ({
149
+ _tag: "Change",
150
+ path: v.path,
151
+ }) as Bundle.BundleEvent
152
+ ),
153
+ Stream.onError(err =>
154
+ Effect.logError("Error while watching files", err)
155
+ ),
156
+ Stream.runForEach((v) =>
157
+ pipe(
158
+ Effect.gen(function*() {
159
+ yield* Effect.logDebug("Updating bundle: " + tag.key)
160
+
161
+ const newBundle = yield* build(config)
162
+
163
+ Object.assign(sharedBundle, newBundle)
164
+
165
+ // Clean old loaded bundle
166
+ yield* SynchronizedRef.update(loadRef, () => null)
167
+
168
+ // publish event after the built
169
+ if (sharedBundle.events) {
170
+ yield* PubSub.publish(sharedBundle.events, v)
171
+ }
172
+ }),
173
+ Effect.catchAll(err =>
174
+ Effect.gen(function*() {
175
+ yield* Effect.logError(
176
+ "Error while updating bundle",
177
+ err,
178
+ )
179
+ if (sharedBundle.events) {
180
+ yield* PubSub.publish(sharedBundle.events, {
181
+ _tag: "BuildError",
182
+ error: String(err),
183
+ })
184
+ }
185
+ })
186
+ ),
187
+ )
188
+ ),
189
+ ),
190
+ )
191
+
192
+ return sharedBundle
193
+ }),
194
+ )
195
+ }
196
+
197
+ /**
198
+ * Finds common path prefix across provided paths.
199
+ */
200
+ function getBaseDir(paths: string[]) {
201
+ if (paths.length === 0) return ""
202
+ if (paths.length === 1) return NPath.dirname(paths[0])
203
+
204
+ const segmentsList = paths.map((path) =>
205
+ NPath.dirname(path).split("/").filter(Boolean)
206
+ )
207
+
208
+ return segmentsList[0]
209
+ .filter((segment, i) => segmentsList.every((segs) => segs[i] === segment))
210
+ .reduce((path, seg) => `${path}/${seg}`, "") ?? ""
211
+ }
212
+
213
+ /**
214
+ * Maps entrypoints to their respective build artifacts.
215
+ * Entrypoint key is trimmed to remove common path prefix.
216
+ */
217
+ function joinBuildEntrypoints(
218
+ options: BuildOptions,
219
+ output: BuildOutput,
220
+ ) {
221
+ const commonPathPrefix = getBaseDir(options.entrypoints) + "/"
222
+
223
+ return pipe(
224
+ Iterable.zip(
225
+ options.entrypoints,
226
+ pipe(
227
+ output.outputs,
228
+ // Filter out source maps to properly map artifacts to entrypoints.
229
+ Iterable.filter((v) =>
230
+ v.kind !== "sourcemap"
231
+ && !(v.loader === "html" && v.path.endsWith(".js"))
232
+ ),
233
+ ),
234
+ ),
235
+ Iterable.map(([entrypoint, artifact]) => {
236
+ return {
237
+ shortPath: entrypoint.replace(commonPathPrefix, ""),
238
+ fullPath: entrypoint,
239
+ artifact,
240
+ } as const
241
+ }),
242
+ )
243
+ }
244
+
245
+ /**
246
+ * Generate manifest from a build.
247
+ * Useful for SSR and providing source->artifact path mapping.
248
+ */
249
+ function generateManifestfromBunBundle(
250
+ options: BuildOptions,
251
+ output: BuildOutput,
252
+ imports?: BunImportTrackerPlugin.ImportMap,
253
+ ): BundleManifest {
254
+ const entrypointArtifacts = joinBuildEntrypoints(options, output)
255
+
256
+ return {
257
+ entrypoints: pipe(
258
+ entrypointArtifacts,
259
+ Iterable.map((v) =>
260
+ [
261
+ v.shortPath,
262
+ v.artifact.path.replace(/^\.\//, ""),
263
+ ] as const
264
+ ),
265
+ Record.fromEntries,
266
+ ),
267
+
268
+ artifacts: pipe(
269
+ output.outputs,
270
+ Iterable.map((v) => {
271
+ // strip './' prefix
272
+ const shortPath = v.path.replace(/^\.\//, "")
273
+
274
+ return {
275
+ path: shortPath,
276
+ type: v.type,
277
+ size: v.size,
278
+ hash: v.hash ?? undefined,
279
+ imports: imports?.get(v.path),
280
+ }
281
+ }),
282
+ Array.fromIterable,
283
+ ),
284
+ }
285
+ }
286
+
287
+ function buildBun(
288
+ config: BuildOptions,
289
+ ): Effect.Effect<BuildOutput, Bundle.BundleError, never> {
290
+ return Object.assign(
291
+ Effect.gen(function*() {
292
+ const buildOutput: BuildOutput = yield* Effect.tryPromise({
293
+ try: () => Bun.build(config),
294
+ catch: (err: AggregateError | unknown) => {
295
+ const cause = err instanceof AggregateError
296
+ ? err.errors?.[0] ?? err
297
+ : err
298
+
299
+ return new Bundle.BundleError({
300
+ message: "Failed to Bun.build: " + cause,
301
+ cause: cause,
302
+ })
303
+ },
304
+ })
305
+
306
+ return buildOutput
307
+ }),
308
+ )
309
+ }
@@ -0,0 +1,50 @@
1
+ import * as t from "bun:test"
2
+ import { effectFn } from "../testing.ts"
3
+ import * as BunBundle from "./BunBundle.ts"
4
+ import * as BunImportTrackerPlugin from "./BunImportTrackerPlugin.ts"
5
+
6
+ const effect = effectFn()
7
+
8
+ t.it("imports", () =>
9
+ effect(function*() {
10
+ const importTracker = BunImportTrackerPlugin.make()
11
+ yield* BunBundle.build({
12
+ target: "bun",
13
+ plugins: [
14
+ importTracker,
15
+ ],
16
+ entrypoints: [
17
+ Bun.fileURLToPath(import.meta.resolve("./BunBundle_imports.test.ts")),
18
+ ],
19
+ })
20
+
21
+ const [
22
+ e0,
23
+ ] = importTracker.state.entries()
24
+
25
+ t
26
+ .expect(
27
+ e0,
28
+ )
29
+ .toEqual([
30
+ "src/bun/BunBundle_imports.test.ts",
31
+ [
32
+ {
33
+ kind: "import-statement",
34
+ path: "bun:test",
35
+ },
36
+ {
37
+ kind: "import-statement",
38
+ path: "src/testing.ts",
39
+ },
40
+ {
41
+ kind: "import-statement",
42
+ path: "src/bun/BunBundle.ts",
43
+ },
44
+ {
45
+ kind: "import-statement",
46
+ path: "src/bun/BunImportTrackerPlugin.ts",
47
+ },
48
+ ],
49
+ ])
50
+ }))
@@ -0,0 +1,45 @@
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
+ }