effect-start 0.23.0 → 0.23.1

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "effect-start",
3
- "version": "0.23.0",
3
+ "version": "0.23.1",
4
4
  "license": "MIT",
5
5
  "files": [
6
6
  "src/",
package/src/Start.ts CHANGED
@@ -1,14 +1,15 @@
1
1
  import type * as FileSystem from "./FileSystem.ts"
2
2
  import * as Context from "effect/Context"
3
+ import * as Deferred from "effect/Deferred"
3
4
  import * as Effect from "effect/Effect"
4
5
  import * as Function from "effect/Function"
5
6
  import * as Layer from "effect/Layer"
6
- import * as Option from "effect/Option"
7
7
  import type * as ChildProcess from "./ChildProcess.ts"
8
8
  import * as BunRuntime from "./bun/BunRuntime.ts"
9
9
  import * as BunServer from "./bun/BunServer.ts"
10
10
  import * as NodeFileSystem from "./node/NodeFileSystem.ts"
11
11
  import * as BunChildProcessSpawner from "./bun/BunChildProcessSpawner.ts"
12
+ import * as StartApp from "./StartApp.ts"
12
13
 
13
14
  export function layer<
14
15
  Layers extends [Layer.Layer<never, any, any>, ...Array<Layer.Layer<never, any, any>>],
@@ -66,12 +67,25 @@ export function pack<const Layers extends readonly [Layer.Layer.Any, ...Array<La
66
67
  export function serve<
67
68
  ROut,
68
69
  E,
69
- RIn extends BunServer.BunServer | FileSystem.FileSystem | ChildProcess.ChildProcessSpawner,
70
+ RIn extends
71
+ | BunServer.BunServer
72
+ | FileSystem.FileSystem
73
+ | ChildProcess.ChildProcessSpawner
74
+ | StartApp.StartApp,
70
75
  >(
71
76
  load: () => Promise<{
72
77
  default: Layer.Layer<ROut, E, RIn>
73
78
  }>,
74
79
  ) {
80
+ const startAppLayer = Layer.effect(
81
+ StartApp.StartApp,
82
+ Deferred.make<BunServer.BunServer>().pipe(
83
+ Effect.map((server) => ({
84
+ server,
85
+ })),
86
+ ),
87
+ )
88
+
75
89
  const appLayer = Function.pipe(
76
90
  Effect.tryPromise(load),
77
91
  Effect.map((v) => v.default),
@@ -79,24 +93,15 @@ export function serve<
79
93
  Layer.unwrapEffect,
80
94
  )
81
95
 
82
- const serverLayer = Layer.scoped(
83
- BunServer.BunServer,
84
- Effect.gen(function* () {
85
- const existing = yield* Effect.serviceOption(BunServer.BunServer)
86
- if (Option.isSome(existing)) return existing.value
87
- return yield* BunServer.make({})
88
- }),
89
- )
90
-
91
96
  const appLayerResolved = Function.pipe(
92
97
  appLayer,
93
- Layer.provide(serverLayer),
94
98
  Layer.provide(NodeFileSystem.layer),
95
99
  Layer.provide(BunChildProcessSpawner.layer),
100
+ Layer.provideMerge(startAppLayer),
96
101
  )
97
102
 
98
103
  const composed = Function.pipe(
99
- serverLayer,
104
+ BunServer.layerStart(),
100
105
  BunServer.withLogAddress,
101
106
  Layer.provide(appLayerResolved),
102
107
  ) as Layer.Layer<BunServer.BunServer, never, never>
@@ -0,0 +1,11 @@
1
+ import * as Context from "effect/Context"
2
+ import * as Deferred from "effect/Deferred"
3
+ import type * as BunServer from "./bun/BunServer.ts"
4
+
5
+ export namespace StartApp {
6
+ export interface Service {
7
+ readonly server: Deferred.Deferred<BunServer.BunServer>
8
+ }
9
+ }
10
+
11
+ export class StartApp extends Context.Tag("effect-start/StartApp")<StartApp, StartApp.Service>() {}
@@ -15,6 +15,7 @@ import * as PathPattern from "../PathPattern.ts"
15
15
  import * as PlataformRuntime from "../PlatformRuntime.ts"
16
16
  import * as Route from "../Route.ts"
17
17
  import * as RouteHttp from "../RouteHttp.ts"
18
+ import * as StartApp from "../StartApp.ts"
18
19
  import type * as RouteMount from "../RouteMount.ts"
19
20
  import * as RouteTree from "../RouteTree.ts"
20
21
  import * as BunRoute from "./BunRoute.ts"
@@ -52,12 +53,11 @@ export type BunServer = {
52
53
 
53
54
  export const BunServer = Context.GenericTag<BunServer>("effect-start/BunServer")
54
55
 
55
- export const make = (options: BunServeOptions): Effect.Effect<BunServer, never, Scope.Scope> =>
56
+ export const make = (
57
+ options: BunServeOptions,
58
+ tree?: RouteTree.RouteTree,
59
+ ): Effect.Effect<BunServer, never, Scope.Scope> =>
56
60
  Effect.gen(function* () {
57
- const routes = yield* Effect.serviceOption(Route.Routes).pipe(
58
- Effect.andThen(Option.getOrUndefined),
59
- )
60
-
61
61
  const port = yield* Config.number("PORT").pipe(
62
62
  Effect.catchTag("ConfigError", () => {
63
63
  return PlataformRuntime.isAgentHarness()
@@ -98,7 +98,7 @@ export const make = (options: BunServeOptions): Effect.Effect<BunServer, never,
98
98
  Effect.andThen(Runtime.provideService(BunServer, service)),
99
99
  )
100
100
 
101
- let currentRoutes: BunRoute.BunRoutes = routes ? yield* walkBunRoutes(runtime, routes) : {}
101
+ let currentRoutes: BunRoute.BunRoutes = tree ? yield* walkBunRoutes(runtime, tree) : {}
102
102
 
103
103
  const websocket: Bun.WebSocketHandler<WebSocketContext> = {
104
104
  open(ws) {
@@ -170,6 +170,47 @@ export const make = (options: BunServeOptions): Effect.Effect<BunServer, never,
170
170
  export const layer = (options?: BunServeOptions): Layer.Layer<BunServer> =>
171
171
  Layer.scoped(BunServer, make(options ?? {}))
172
172
 
173
+ export const layerRoutes = (
174
+ options?: BunServeOptions,
175
+ ): Layer.Layer<BunServer, never, Route.Routes> =>
176
+ Layer.scoped(
177
+ BunServer,
178
+ Effect.gen(function* () {
179
+ const routes = yield* Route.Routes
180
+ return yield* make(options ?? {}, routes)
181
+ }),
182
+ )
183
+
184
+ /**
185
+ * Resolves the Bun server in one place for Start.serve so routes are available:
186
+ * 1) Reuse a user-provided BunServer when one already exists in context.
187
+ * 2) Otherwise create the server from Route.Routes when routes are available.
188
+ * 3) Otherwise create a fallback server with the default 404 handler.
189
+ */
190
+ export const layerStart = (
191
+ options?: BunServeOptions,
192
+ ): Layer.Layer<BunServer, never, StartApp.StartApp> =>
193
+ Layer.scoped(
194
+ BunServer,
195
+ Effect.gen(function* () {
196
+ const app = yield* StartApp.StartApp
197
+ const existing = yield* Effect.serviceOption(BunServer)
198
+ if (Option.isSome(existing)) {
199
+ yield* Deferred.succeed(app.server, existing.value)
200
+ return existing.value
201
+ }
202
+ const routes = yield* Effect.serviceOption(Route.Routes)
203
+ if (Option.isSome(routes)) {
204
+ const server = yield* make(options ?? {}, routes.value)
205
+ yield* Deferred.succeed(app.server, server)
206
+ return server
207
+ }
208
+ const server = yield* make(options ?? {})
209
+ yield* Deferred.succeed(app.server, server)
210
+ return server
211
+ }),
212
+ )
213
+
173
214
  export const withLogAddress = <A, E, R>(layer: Layer.Layer<A, E, R>) =>
174
215
  Layer.effectDiscard(
175
216
  BunServer.pipe(
@@ -1,6 +1,7 @@
1
- import * as BunServer from "../../bun/BunServer.ts"
2
1
  import * as PlatformError from "../../PlatformError.ts"
2
+ import * as StartApp from "../../StartApp.ts"
3
3
  import * as System from "../../System.ts"
4
+ import * as Deferred from "effect/Deferred"
4
5
  import * as Effect from "effect/Effect"
5
6
  import * as Layer from "effect/Layer"
6
7
  import * as LogLevel from "effect/LogLevel"
@@ -86,12 +87,13 @@ export const start = (opts: {
86
87
  export const layer = (opts?: { public?: boolean }) =>
87
88
  Layer.scopedDiscard(
88
89
  Effect.gen(function* () {
89
- const { server } = yield* BunServer.BunServer
90
- const port = server.port!
91
- const command = "tailscale"
92
-
93
90
  yield* Effect.forkScoped(
94
91
  Effect.gen(function* () {
92
+ const app = yield* StartApp.StartApp
93
+ const { server } = yield* Deferred.await(app.server)
94
+ const port = server.port!
95
+ const command = "tailscale"
96
+
95
97
  yield* System.which(command)
96
98
  const status = yield* getStatus(command)
97
99
  const dnsName = status.Self?.DNSName?.replace(/\.$/, "")