devsess 0.2.0 → 0.3.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.
@@ -6,7 +6,9 @@ import { type DevSession } from './session';
6
6
  /** Promise-returning mirror of the Effect core's free functions (`getStickyPort`, `runManagedSubprocess`, `publishRunning`, `awaitRunning`), bundled into a single per-run context. */
7
7
  type AsyncRunContext = {
8
8
  session: () => Promise<DevSession>;
9
- getStickyPort: () => Promise<number>;
9
+ getStickyPort: (options?: {
10
+ name?: string;
11
+ }) => Promise<number>;
10
12
  runManagedSubprocess: (...args: Parameters<typeof runManagedSubprocess>) => Promise<Effect.Success<ReturnType<typeof runManagedSubprocess>>>;
11
13
  publishRunning: (data: unknown) => Promise<void>;
12
14
  awaitRunning: <T>(pkg: string) => Promise<T>;
@@ -1 +1 @@
1
- {"version":3,"file":"define-cli.d.ts","sourceRoot":"","sources":["../../src/async/define-cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAc,MAAM,QAAQ,CAAC;AAW5C,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAEzD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,EAAE,KAAK,aAAa,EAAa,MAAM,eAAe,CAAC;AAC9D,OAAO,EAAE,KAAK,UAAU,EAAkB,MAAM,WAAW,CAAC;AAE5D,uLAAuL;AACvL,KAAK,eAAe,GAAG;IACtB,OAAO,EAAE,MAAM,OAAO,CAAC,UAAU,CAAC,CAAC;IACnC,aAAa,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IACrC,oBAAoB,EAAE,CACrB,GAAG,IAAI,EAAE,UAAU,CAAC,OAAO,oBAAoB,CAAC,KAC5C,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAC,CAAC,CAAC;IACtE,cAAc,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACjD,YAAY,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;CAC7C,CAAC;AAEF,eAAO,MAAM,YAAY,WAAY;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,QAAQ,EAAE,WAAW,CAAC;IACtB,GAAG,EAAE,CACJ,GAAG,EAAE,eAAe,EACpB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KACzB,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;CAC1B,KAAG,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,IAAI,CA6C1B,CAAC"}
1
+ {"version":3,"file":"define-cli.d.ts","sourceRoot":"","sources":["../../src/async/define-cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAc,MAAM,QAAQ,CAAC;AAW5C,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAEzD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,EAAE,KAAK,aAAa,EAAa,MAAM,eAAe,CAAC;AAC9D,OAAO,EAAE,KAAK,UAAU,EAAkB,MAAM,WAAW,CAAC;AAE5D,uLAAuL;AACvL,KAAK,eAAe,GAAG;IACtB,OAAO,EAAE,MAAM,OAAO,CAAC,UAAU,CAAC,CAAC;IACnC,aAAa,EAAE,CAAC,OAAO,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAChE,oBAAoB,EAAE,CACrB,GAAG,IAAI,EAAE,UAAU,CAAC,OAAO,oBAAoB,CAAC,KAC5C,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAC,CAAC,CAAC;IACtE,cAAc,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACjD,YAAY,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;CAC7C,CAAC;AAEF,eAAO,MAAM,YAAY,WAAY;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,QAAQ,EAAE,WAAW,CAAC;IACtB,GAAG,EAAE,CACJ,GAAG,EAAE,eAAe,EACpB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KACzB,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;CAC1B,KAAG,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,IAAI,CA+C1B,CAAC"}
@@ -8,7 +8,7 @@ import {
8
8
  resolveSiblingDir,
9
9
  runManagedSubprocess,
10
10
  runningSignalPath
11
- } from "../chunk-AQGYVNYU.js";
11
+ } from "../chunk-EKG6NIBH.js";
12
12
 
13
13
  // src/async/index.ts
14
14
  import { Schema } from "effect";
@@ -76,8 +76,11 @@ var defineDevCli = (config) => runDevCli({
76
76
  const services = yield* Effect3.context();
77
77
  const ctx = {
78
78
  session: () => Effect3.runPromiseWith(services)(session).then(toAsyncSession),
79
- getStickyPort: () => Effect3.runPromiseWith(services)(
80
- Effect3.flatMap(session, getStickyPort)
79
+ getStickyPort: (options) => Effect3.runPromiseWith(services)(
80
+ Effect3.flatMap(
81
+ session,
82
+ (resolved) => getStickyPort(resolved, options)
83
+ )
81
84
  ),
82
85
  runManagedSubprocess: (...args) => Effect3.runPromiseWith(services)(runManagedSubprocess(...args)),
83
86
  publishRunning: (data) => Effect3.runPromiseWith(services)(
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/async/index.ts","../../src/async/define-cli.ts","../../src/async/run-dev-cli.ts","../../src/async/session.ts","../../src/async/dev-sessions.ts","../../src/async/run.ts","../../src/async/session-state.ts"],"sourcesContent":["// Re-exported so consumers can declare CLI flags/arguments without a direct\n// dependency on `effect/unstable/cli`, e.g. `cli.Flag.string('local')`.\n\nexport { Schema } from 'effect';\nexport * as cli from 'effect/unstable/cli';\n\nexport { SessionStateError } from '../dev/session-state';\nexport { defineDevCli } from './define-cli';\nexport { createDevSessions, type DevSessionManager } from './dev-sessions';\nexport type { DevPlatform, DevServices } from './platform';\nexport type { DevSession } from './session';\nexport { SessionState } from './session-state';\n","import { Effect, type Scope } from 'effect';\nimport type { FileSystem } from 'effect/FileSystem';\nimport type { Path } from 'effect/Path';\nimport type { ChildProcessSpawner } from 'effect/unstable/process/ChildProcessSpawner';\nimport {\n\tawaitRunningSignal,\n\tpublishRunningSignal,\n\tresolveSiblingDir,\n\trunningSignalPath,\n} from '../dev/running-signal';\nimport { getStickyPort } from '../dev/sticky-port';\nimport { runManagedSubprocess } from '../dev/subprocess';\nimport { DevSessions } from '../dev-sessions';\nimport type { DevPlatform } from './platform';\nimport { type CommandConfig, runDevCli } from './run-dev-cli';\nimport { type DevSession, toAsyncSession } from './session';\n\n/** Promise-returning mirror of the Effect core's free functions (`getStickyPort`, `runManagedSubprocess`, `publishRunning`, `awaitRunning`), bundled into a single per-run context. */\ntype AsyncRunContext = {\n\tsession: () => Promise<DevSession>;\n\tgetStickyPort: () => Promise<number>;\n\trunManagedSubprocess: (\n\t\t...args: Parameters<typeof runManagedSubprocess>\n\t) => Promise<Effect.Success<ReturnType<typeof runManagedSubprocess>>>;\n\tpublishRunning: (data: unknown) => Promise<void>;\n\tawaitRunning: <T>(pkg: string) => Promise<T>;\n};\n\nexport const defineDevCli = (config: {\n\tname: string;\n\tdir: string;\n\toptions?: CommandConfig;\n\tplatform: DevPlatform;\n\trun: (\n\t\tctx: AsyncRunContext,\n\t\topts: Record<string, unknown>,\n\t) => Promise<void> | void;\n}): ((argv: string[]) => void) =>\n\trunDevCli({\n\t\tname: config.name,\n\t\tdir: config.dir,\n\t\toptions: config.options,\n\t\tplatform: config.platform,\n\t\tmakeHandler: (opts) =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst sessions = yield* DevSessions;\n\t\t\t\tconst session = yield* Effect.cached(sessions.getLatestOrCreate);\n\n\t\t\t\t// Capture the process-lifetime context (the Scope opened by `runDevCli`'s\n\t\t\t\t// outer `Effect.scoped`, plus DevSessions | FileSystem | Path | ChildProcessSpawner).\n\t\t\t\t// Running each ctx helper with this captured context — instead of a fresh,\n\t\t\t\t// transient `Effect.runPromise` per call — attaches its scoped resources\n\t\t\t\t// (managed subprocess, running-signal file) to that process-lifetime scope,\n\t\t\t\t// so they only release when the CLI process ends, not right after the Promise settles.\n\t\t\t\tconst services = yield* Effect.context<\n\t\t\t\t\tDevSessions | FileSystem | Path | ChildProcessSpawner | Scope.Scope\n\t\t\t\t>();\n\n\t\t\t\tconst ctx: AsyncRunContext = {\n\t\t\t\t\tsession: () =>\n\t\t\t\t\t\tEffect.runPromiseWith(services)(session).then(toAsyncSession),\n\t\t\t\t\tgetStickyPort: () =>\n\t\t\t\t\t\tEffect.runPromiseWith(services)(\n\t\t\t\t\t\t\tEffect.flatMap(session, getStickyPort),\n\t\t\t\t\t\t),\n\t\t\t\t\trunManagedSubprocess: (...args) =>\n\t\t\t\t\t\tEffect.runPromiseWith(services)(runManagedSubprocess(...args)),\n\t\t\t\t\tpublishRunning: (data) =>\n\t\t\t\t\t\tEffect.runPromiseWith(services)(\n\t\t\t\t\t\t\tpublishRunningSignal(runningSignalPath(config.dir), data),\n\t\t\t\t\t\t),\n\t\t\t\t\tawaitRunning: <T>(pkg: string) =>\n\t\t\t\t\t\tEffect.runPromiseWith(services)(\n\t\t\t\t\t\t\tawaitRunningSignal<T>(\n\t\t\t\t\t\t\t\trunningSignalPath(resolveSiblingDir(pkg, config.dir)),\n\t\t\t\t\t\t\t\t{ parse: (raw) => JSON.parse(raw) as T },\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t),\n\t\t\t\t};\n\n\t\t\t\tyield* Effect.promise(() => Promise.resolve(config.run(ctx, opts)));\n\t\t\t}),\n\t});\n","import { Effect, Layer, type Scope } from 'effect';\nimport * as cli from 'effect/unstable/cli';\nimport { DevSessions } from '../dev-sessions';\nimport type { DevPlatform, DevServices } from './platform';\n\nexport type CommandConfig = typeof cli.Command.make extends (\n\tname: string,\n\tconfig: infer C,\n\t...rest: Array<unknown>\n) => unknown\n\t? C\n\t: never;\n\ntype CliHandler = (\n\topts: Record<string, unknown>,\n) => Effect.Effect<void, unknown, DevSessions | DevServices | Scope.Scope>;\n\n/**\n * CLI scaffolding for the plain-async `defineDevCli` facade (`src/async/define-cli.ts`):\n * builds the `cli.Command`, wires the per-project `DevSessions` + caller-supplied\n * platform services layer, and runs it via `platform.runMain`.\n */\nexport const runDevCli = (config: {\n\tname: string;\n\tdir: string;\n\toptions?: CommandConfig;\n\tplatform: DevPlatform;\n\tmakeHandler: CliHandler;\n}): ((argv: string[]) => void) => {\n\tconst command = cli.Command.make(config.name, config.options ?? {}, (opts) =>\n\t\tconfig.makeHandler(opts as Record<string, unknown>),\n\t);\n\n\treturn (argv) => {\n\t\tconst layer = DevSessions.layerAt(config.dir).pipe(\n\t\t\tLayer.provideMerge(config.platform.services),\n\t\t);\n\n\t\tconst program = cli.Command.runWith(command, {\n\t\t\tversion: '0.0.0',\n\t\t})(argv);\n\n\t\tconfig.platform.runMain(Effect.scoped(Effect.provide(program, layer)));\n\t};\n};\n","import { Effect } from 'effect';\nimport type { DevSession as EffectDevSession } from '../dev-sessions';\n\n/** Non-enumerable, module-private key used to stash the Effect session on its async wrapper. */\nconst EFFECT_SESSION = Symbol('devsess/async/DevSession#effectSession');\n\nexport type DevSession = {\n\treadonly name: string;\n\treadonly lastModifiedAt: Date | null;\n\tpath(relativePath: string): string;\n\ttoString(): string;\n};\n\n/**\n * Wraps an Effect `DevSession` as a plain-async `DevSession`. The original Effect\n * session is stashed under `EFFECT_SESSION` so other async ops (e.g. `SessionState`)\n * can reuse the Effect core via `unwrapSession`.\n */\nexport const toAsyncSession = (session: EffectDevSession): DevSession => {\n\tconst asyncSession: DevSession = {\n\t\tname: session.name,\n\t\tlastModifiedAt: session.lastModifiedAt,\n\t\t// Safe to run synchronously: the core `path` is `Effect.succeed(pathJoin(rel))`, pure.\n\t\tpath: (relativePath) => Effect.runSync(session.path(relativePath)),\n\t\ttoString: () => session.toString(),\n\t};\n\n\tObject.defineProperty(asyncSession, EFFECT_SESSION, {\n\t\tvalue: session,\n\t\tenumerable: false,\n\t});\n\n\treturn asyncSession;\n};\n\n/** Recovers the Effect `DevSession` stashed by `toAsyncSession`. */\nexport const unwrapSession = (session: DevSession): EffectDevSession => {\n\tconst effectSession = (\n\t\tsession as unknown as Record<\n\t\t\ttypeof EFFECT_SESSION,\n\t\t\tEffectDevSession | undefined\n\t\t>\n\t)[EFFECT_SESSION];\n\tif (effectSession === undefined) {\n\t\tthrow new Error(\n\t\t\t'devsess/async: expected a DevSession created by this package, but the internal Effect session handle is missing',\n\t\t);\n\t}\n\treturn effectSession;\n};\n","import { Effect, Layer } from 'effect';\nimport { DevSessions } from '../dev-sessions';\nimport type { DevServices } from './platform';\nimport { type DevSession, toAsyncSession } from './session';\n\nexport type DevSessionManager = {\n\treadonly dir: string;\n\tgetSessions(): Promise<Array<DevSession>>;\n\tcreateSession(): Promise<DevSession>;\n\tgetLatestOrCreate(): Promise<DevSession>;\n};\n\n/**\n * Creates a manager for slug-named dev session directories under\n * `<rootDir>/.data/sessions`, using the caller-supplied platform `services` layer\n * (e.g. `NodeServices.layer`). Same `rootDir` convention as `DevSessions.layerAt` — the\n * CLI facade and this one agree on where sessions live given the same root.\n *\n * `DevSessions.layerAt` only wires lazy service methods when built (no directory IO\n * happens at build time — see `src/dev-sessions.ts`), so it's cheap to re-provide the\n * same `layer` value on every call rather than resolving a shared runtime once.\n */\nexport const createDevSessions = (\n\trootDir: string,\n\tservices: Layer.Layer<DevServices>,\n): DevSessionManager => {\n\tconst layer = DevSessions.layerAt(rootDir).pipe(Layer.provide(services));\n\n\tconst run = <A, E>(effect: Effect.Effect<A, E, DevSessions>): Promise<A> =>\n\t\tEffect.runPromise(Effect.provide(effect, layer));\n\n\treturn {\n\t\t// Matches the service's own `dir`, which is `rootDir` verbatim — safe to read synchronously.\n\t\tdir: rootDir,\n\t\tgetSessions: () =>\n\t\t\trun(DevSessions.use((sessions) => sessions.getSessions)).then(\n\t\t\t\t(sessions) => sessions.map(toAsyncSession),\n\t\t\t),\n\t\tcreateSession: () =>\n\t\t\trun(DevSessions.use((sessions) => sessions.createSession)).then(\n\t\t\t\ttoAsyncSession,\n\t\t\t),\n\t\tgetLatestOrCreate: () =>\n\t\t\trun(DevSessions.use((sessions) => sessions.getLatestOrCreate)).then(\n\t\t\t\ttoAsyncSession,\n\t\t\t),\n\t};\n};\n","import { Effect, type Layer, type Scope } from 'effect';\nimport type { DevServices } from './platform';\n\n/** Runs an Effect that only needs the caller-supplied platform services (+ an optional Scope) as a Promise. */\nexport const run = <A, E>(\n\teffect: Effect.Effect<A, E, DevServices | Scope.Scope>,\n\tservices: Layer.Layer<DevServices>,\n): Promise<A> =>\n\tEffect.runPromise(Effect.scoped(Effect.provide(effect, services)));\n","import type { Layer } from 'effect';\nimport type * as S from 'effect/Schema';\nimport { SessionState as EffectSessionState } from '../dev/session-state';\nimport type { DevServices } from './platform';\nimport { run } from './run';\nimport { type DevSession, unwrapSession } from './session';\n\nexport namespace SessionState {\n\t// Decoding must run without extra context beyond the platform `services` layer, so\n\t// schemas cannot require decoding services of their own.\n\ttype Shape = S.Codec<unknown, unknown, never>;\n\n\texport function slot<T extends Shape>(\n\t\tschema: T,\n\t\tservices: Layer.Layer<DevServices>,\n\t) {\n\t\tconst effectSlot = EffectSessionState.slot(schema);\n\t\treturn {\n\t\t\tread: (session: DevSession): Promise<T['Type'] | null> =>\n\t\t\t\trun(effectSlot.read(unwrapSession(session)), services),\n\t\t\twrite: (session: DevSession, data: T['Type']): Promise<void> =>\n\t\t\t\trun(effectSlot.write(unwrapSession(session), data), services),\n\t\t};\n\t}\n}\n"],"mappings":"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","names":["cli","Effect","Effect","Effect","Effect","Layer","Layer","run","Effect","Effect","SessionState"]}
1
+ {"version":3,"sources":["../../src/async/index.ts","../../src/async/define-cli.ts","../../src/async/run-dev-cli.ts","../../src/async/session.ts","../../src/async/dev-sessions.ts","../../src/async/run.ts","../../src/async/session-state.ts"],"sourcesContent":["// Re-exported so consumers can declare CLI flags/arguments without a direct\n// dependency on `effect/unstable/cli`, e.g. `cli.Flag.string('local')`.\n\nexport { Schema } from 'effect';\nexport * as cli from 'effect/unstable/cli';\n\nexport { SessionStateError } from '../dev/session-state';\nexport { defineDevCli } from './define-cli';\nexport { createDevSessions, type DevSessionManager } from './dev-sessions';\nexport type { DevPlatform, DevServices } from './platform';\nexport type { DevSession } from './session';\nexport { SessionState } from './session-state';\n","import { Effect, type Scope } from 'effect';\nimport type { FileSystem } from 'effect/FileSystem';\nimport type { Path } from 'effect/Path';\nimport type { ChildProcessSpawner } from 'effect/unstable/process/ChildProcessSpawner';\nimport {\n\tawaitRunningSignal,\n\tpublishRunningSignal,\n\tresolveSiblingDir,\n\trunningSignalPath,\n} from '../dev/running-signal';\nimport { getStickyPort } from '../dev/sticky-port';\nimport { runManagedSubprocess } from '../dev/subprocess';\nimport { DevSessions } from '../dev-sessions';\nimport type { DevPlatform } from './platform';\nimport { type CommandConfig, runDevCli } from './run-dev-cli';\nimport { type DevSession, toAsyncSession } from './session';\n\n/** Promise-returning mirror of the Effect core's free functions (`getStickyPort`, `runManagedSubprocess`, `publishRunning`, `awaitRunning`), bundled into a single per-run context. */\ntype AsyncRunContext = {\n\tsession: () => Promise<DevSession>;\n\tgetStickyPort: (options?: { name?: string }) => Promise<number>;\n\trunManagedSubprocess: (\n\t\t...args: Parameters<typeof runManagedSubprocess>\n\t) => Promise<Effect.Success<ReturnType<typeof runManagedSubprocess>>>;\n\tpublishRunning: (data: unknown) => Promise<void>;\n\tawaitRunning: <T>(pkg: string) => Promise<T>;\n};\n\nexport const defineDevCli = (config: {\n\tname: string;\n\tdir: string;\n\toptions?: CommandConfig;\n\tplatform: DevPlatform;\n\trun: (\n\t\tctx: AsyncRunContext,\n\t\topts: Record<string, unknown>,\n\t) => Promise<void> | void;\n}): ((argv: string[]) => void) =>\n\trunDevCli({\n\t\tname: config.name,\n\t\tdir: config.dir,\n\t\toptions: config.options,\n\t\tplatform: config.platform,\n\t\tmakeHandler: (opts) =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst sessions = yield* DevSessions;\n\t\t\t\tconst session = yield* Effect.cached(sessions.getLatestOrCreate);\n\n\t\t\t\t// Capture the process-lifetime context (the Scope opened by `runDevCli`'s\n\t\t\t\t// outer `Effect.scoped`, plus DevSessions | FileSystem | Path | ChildProcessSpawner).\n\t\t\t\t// Running each ctx helper with this captured context — instead of a fresh,\n\t\t\t\t// transient `Effect.runPromise` per call — attaches its scoped resources\n\t\t\t\t// (managed subprocess, running-signal file) to that process-lifetime scope,\n\t\t\t\t// so they only release when the CLI process ends, not right after the Promise settles.\n\t\t\t\tconst services = yield* Effect.context<\n\t\t\t\t\tDevSessions | FileSystem | Path | ChildProcessSpawner | Scope.Scope\n\t\t\t\t>();\n\n\t\t\t\tconst ctx: AsyncRunContext = {\n\t\t\t\t\tsession: () =>\n\t\t\t\t\t\tEffect.runPromiseWith(services)(session).then(toAsyncSession),\n\t\t\t\t\tgetStickyPort: (options) =>\n\t\t\t\t\t\tEffect.runPromiseWith(services)(\n\t\t\t\t\t\t\tEffect.flatMap(session, (resolved) =>\n\t\t\t\t\t\t\t\tgetStickyPort(resolved, options),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t),\n\t\t\t\t\trunManagedSubprocess: (...args) =>\n\t\t\t\t\t\tEffect.runPromiseWith(services)(runManagedSubprocess(...args)),\n\t\t\t\t\tpublishRunning: (data) =>\n\t\t\t\t\t\tEffect.runPromiseWith(services)(\n\t\t\t\t\t\t\tpublishRunningSignal(runningSignalPath(config.dir), data),\n\t\t\t\t\t\t),\n\t\t\t\t\tawaitRunning: <T>(pkg: string) =>\n\t\t\t\t\t\tEffect.runPromiseWith(services)(\n\t\t\t\t\t\t\tawaitRunningSignal<T>(\n\t\t\t\t\t\t\t\trunningSignalPath(resolveSiblingDir(pkg, config.dir)),\n\t\t\t\t\t\t\t\t{ parse: (raw) => JSON.parse(raw) as T },\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t),\n\t\t\t\t};\n\n\t\t\t\tyield* Effect.promise(() => Promise.resolve(config.run(ctx, opts)));\n\t\t\t}),\n\t});\n","import { Effect, Layer, type Scope } from 'effect';\nimport * as cli from 'effect/unstable/cli';\nimport { DevSessions } from '../dev-sessions';\nimport type { DevPlatform, DevServices } from './platform';\n\nexport type CommandConfig = typeof cli.Command.make extends (\n\tname: string,\n\tconfig: infer C,\n\t...rest: Array<unknown>\n) => unknown\n\t? C\n\t: never;\n\ntype CliHandler = (\n\topts: Record<string, unknown>,\n) => Effect.Effect<void, unknown, DevSessions | DevServices | Scope.Scope>;\n\n/**\n * CLI scaffolding for the plain-async `defineDevCli` facade (`src/async/define-cli.ts`):\n * builds the `cli.Command`, wires the per-project `DevSessions` + caller-supplied\n * platform services layer, and runs it via `platform.runMain`.\n */\nexport const runDevCli = (config: {\n\tname: string;\n\tdir: string;\n\toptions?: CommandConfig;\n\tplatform: DevPlatform;\n\tmakeHandler: CliHandler;\n}): ((argv: string[]) => void) => {\n\tconst command = cli.Command.make(config.name, config.options ?? {}, (opts) =>\n\t\tconfig.makeHandler(opts as Record<string, unknown>),\n\t);\n\n\treturn (argv) => {\n\t\tconst layer = DevSessions.layerAt(config.dir).pipe(\n\t\t\tLayer.provideMerge(config.platform.services),\n\t\t);\n\n\t\tconst program = cli.Command.runWith(command, {\n\t\t\tversion: '0.0.0',\n\t\t})(argv);\n\n\t\tconfig.platform.runMain(Effect.scoped(Effect.provide(program, layer)));\n\t};\n};\n","import { Effect } from 'effect';\nimport type { DevSession as EffectDevSession } from '../dev-sessions';\n\n/** Non-enumerable, module-private key used to stash the Effect session on its async wrapper. */\nconst EFFECT_SESSION = Symbol('devsess/async/DevSession#effectSession');\n\nexport type DevSession = {\n\treadonly name: string;\n\treadonly lastModifiedAt: Date | null;\n\tpath(relativePath: string): string;\n\ttoString(): string;\n};\n\n/**\n * Wraps an Effect `DevSession` as a plain-async `DevSession`. The original Effect\n * session is stashed under `EFFECT_SESSION` so other async ops (e.g. `SessionState`)\n * can reuse the Effect core via `unwrapSession`.\n */\nexport const toAsyncSession = (session: EffectDevSession): DevSession => {\n\tconst asyncSession: DevSession = {\n\t\tname: session.name,\n\t\tlastModifiedAt: session.lastModifiedAt,\n\t\t// Safe to run synchronously: the core `path` is `Effect.succeed(pathJoin(rel))`, pure.\n\t\tpath: (relativePath) => Effect.runSync(session.path(relativePath)),\n\t\ttoString: () => session.toString(),\n\t};\n\n\tObject.defineProperty(asyncSession, EFFECT_SESSION, {\n\t\tvalue: session,\n\t\tenumerable: false,\n\t});\n\n\treturn asyncSession;\n};\n\n/** Recovers the Effect `DevSession` stashed by `toAsyncSession`. */\nexport const unwrapSession = (session: DevSession): EffectDevSession => {\n\tconst effectSession = (\n\t\tsession as unknown as Record<\n\t\t\ttypeof EFFECT_SESSION,\n\t\t\tEffectDevSession | undefined\n\t\t>\n\t)[EFFECT_SESSION];\n\tif (effectSession === undefined) {\n\t\tthrow new Error(\n\t\t\t'devsess/async: expected a DevSession created by this package, but the internal Effect session handle is missing',\n\t\t);\n\t}\n\treturn effectSession;\n};\n","import { Effect, Layer } from 'effect';\nimport { DevSessions } from '../dev-sessions';\nimport type { DevServices } from './platform';\nimport { type DevSession, toAsyncSession } from './session';\n\nexport type DevSessionManager = {\n\treadonly dir: string;\n\tgetSessions(): Promise<Array<DevSession>>;\n\tcreateSession(): Promise<DevSession>;\n\tgetLatestOrCreate(): Promise<DevSession>;\n};\n\n/**\n * Creates a manager for slug-named dev session directories under\n * `<rootDir>/.data/sessions`, using the caller-supplied platform `services` layer\n * (e.g. `NodeServices.layer`). Same `rootDir` convention as `DevSessions.layerAt` — the\n * CLI facade and this one agree on where sessions live given the same root.\n *\n * `DevSessions.layerAt` only wires lazy service methods when built (no directory IO\n * happens at build time — see `src/dev-sessions.ts`), so it's cheap to re-provide the\n * same `layer` value on every call rather than resolving a shared runtime once.\n */\nexport const createDevSessions = (\n\trootDir: string,\n\tservices: Layer.Layer<DevServices>,\n): DevSessionManager => {\n\tconst layer = DevSessions.layerAt(rootDir).pipe(Layer.provide(services));\n\n\tconst run = <A, E>(effect: Effect.Effect<A, E, DevSessions>): Promise<A> =>\n\t\tEffect.runPromise(Effect.provide(effect, layer));\n\n\treturn {\n\t\t// Matches the service's own `dir`, which is `rootDir` verbatim — safe to read synchronously.\n\t\tdir: rootDir,\n\t\tgetSessions: () =>\n\t\t\trun(DevSessions.use((sessions) => sessions.getSessions)).then(\n\t\t\t\t(sessions) => sessions.map(toAsyncSession),\n\t\t\t),\n\t\tcreateSession: () =>\n\t\t\trun(DevSessions.use((sessions) => sessions.createSession)).then(\n\t\t\t\ttoAsyncSession,\n\t\t\t),\n\t\tgetLatestOrCreate: () =>\n\t\t\trun(DevSessions.use((sessions) => sessions.getLatestOrCreate)).then(\n\t\t\t\ttoAsyncSession,\n\t\t\t),\n\t};\n};\n","import { Effect, type Layer, type Scope } from 'effect';\nimport type { DevServices } from './platform';\n\n/** Runs an Effect that only needs the caller-supplied platform services (+ an optional Scope) as a Promise. */\nexport const run = <A, E>(\n\teffect: Effect.Effect<A, E, DevServices | Scope.Scope>,\n\tservices: Layer.Layer<DevServices>,\n): Promise<A> =>\n\tEffect.runPromise(Effect.scoped(Effect.provide(effect, services)));\n","import type { Layer } from 'effect';\nimport type * as S from 'effect/Schema';\nimport { SessionState as EffectSessionState } from '../dev/session-state';\nimport type { DevServices } from './platform';\nimport { run } from './run';\nimport { type DevSession, unwrapSession } from './session';\n\nexport namespace SessionState {\n\t// Decoding must run without extra context beyond the platform `services` layer, so\n\t// schemas cannot require decoding services of their own.\n\ttype Shape = S.Codec<unknown, unknown, never>;\n\n\texport function slot<T extends Shape>(\n\t\tschema: T,\n\t\tservices: Layer.Layer<DevServices>,\n\t) {\n\t\tconst effectSlot = EffectSessionState.slot(schema);\n\t\treturn {\n\t\t\tread: (session: DevSession): Promise<T['Type'] | null> =>\n\t\t\t\trun(effectSlot.read(unwrapSession(session)), services),\n\t\t\twrite: (session: DevSession, data: T['Type']): Promise<void> =>\n\t\t\t\trun(effectSlot.write(unwrapSession(session), data), services),\n\t\t};\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;AAGA,SAAS,cAAc;AACvB,YAAYA,UAAS;;;ACJrB,SAAS,UAAAC,eAA0B;;;ACAnC,SAAS,QAAQ,aAAyB;AAC1C,YAAY,SAAS;AAqBd,IAAM,YAAY,CAAC,WAMQ;AACjC,QAAM,UAAc,YAAQ;AAAA,IAAK,OAAO;AAAA,IAAM,OAAO,WAAW,CAAC;AAAA,IAAG,CAAC,SACpE,OAAO,YAAY,IAA+B;AAAA,EACnD;AAEA,SAAO,CAAC,SAAS;AAChB,UAAM,QAAQ,YAAY,QAAQ,OAAO,GAAG,EAAE;AAAA,MAC7C,MAAM,aAAa,OAAO,SAAS,QAAQ;AAAA,IAC5C;AAEA,UAAM,UAAc,YAAQ,QAAQ,SAAS;AAAA,MAC5C,SAAS;AAAA,IACV,CAAC,EAAE,IAAI;AAEP,WAAO,SAAS,QAAQ,OAAO,OAAO,OAAO,QAAQ,SAAS,KAAK,CAAC,CAAC;AAAA,EACtE;AACD;;;AC5CA,SAAS,UAAAC,eAAc;AAIvB,IAAM,iBAAiB,uBAAO,wCAAwC;AAc/D,IAAM,iBAAiB,CAAC,YAA0C;AACxE,QAAM,eAA2B;AAAA,IAChC,MAAM,QAAQ;AAAA,IACd,gBAAgB,QAAQ;AAAA;AAAA,IAExB,MAAM,CAAC,iBAAiBA,QAAO,QAAQ,QAAQ,KAAK,YAAY,CAAC;AAAA,IACjE,UAAU,MAAM,QAAQ,SAAS;AAAA,EAClC;AAEA,SAAO,eAAe,cAAc,gBAAgB;AAAA,IACnD,OAAO;AAAA,IACP,YAAY;AAAA,EACb,CAAC;AAED,SAAO;AACR;AAGO,IAAM,gBAAgB,CAAC,YAA0C;AACvE,QAAM,gBACL,QAIC,cAAc;AAChB,MAAI,kBAAkB,QAAW;AAChC,UAAM,IAAI;AAAA,MACT;AAAA,IACD;AAAA,EACD;AACA,SAAO;AACR;;;AFrBO,IAAM,eAAe,CAAC,WAU5B,UAAU;AAAA,EACT,MAAM,OAAO;AAAA,EACb,KAAK,OAAO;AAAA,EACZ,SAAS,OAAO;AAAA,EAChB,UAAU,OAAO;AAAA,EACjB,aAAa,CAAC,SACbC,QAAO,IAAI,aAAa;AACvB,UAAM,WAAW,OAAO;AACxB,UAAM,UAAU,OAAOA,QAAO,OAAO,SAAS,iBAAiB;AAQ/D,UAAM,WAAW,OAAOA,QAAO,QAE7B;AAEF,UAAM,MAAuB;AAAA,MAC5B,SAAS,MACRA,QAAO,eAAe,QAAQ,EAAE,OAAO,EAAE,KAAK,cAAc;AAAA,MAC7D,eAAe,CAAC,YACfA,QAAO,eAAe,QAAQ;AAAA,QAC7BA,QAAO;AAAA,UAAQ;AAAA,UAAS,CAAC,aACxB,cAAc,UAAU,OAAO;AAAA,QAChC;AAAA,MACD;AAAA,MACD,sBAAsB,IAAI,SACzBA,QAAO,eAAe,QAAQ,EAAE,qBAAqB,GAAG,IAAI,CAAC;AAAA,MAC9D,gBAAgB,CAAC,SAChBA,QAAO,eAAe,QAAQ;AAAA,QAC7B,qBAAqB,kBAAkB,OAAO,GAAG,GAAG,IAAI;AAAA,MACzD;AAAA,MACD,cAAc,CAAI,QACjBA,QAAO,eAAe,QAAQ;AAAA,QAC7B;AAAA,UACC,kBAAkB,kBAAkB,KAAK,OAAO,GAAG,CAAC;AAAA,UACpD,EAAE,OAAO,CAAC,QAAQ,KAAK,MAAM,GAAG,EAAO;AAAA,QACxC;AAAA,MACD;AAAA,IACF;AAEA,WAAOA,QAAO,QAAQ,MAAM,QAAQ,QAAQ,OAAO,IAAI,KAAK,IAAI,CAAC,CAAC;AAAA,EACnE,CAAC;AACH,CAAC;;;AGpFF,SAAS,UAAAC,SAAQ,SAAAC,cAAa;AAsBvB,IAAM,oBAAoB,CAChC,SACA,aACuB;AACvB,QAAM,QAAQ,YAAY,QAAQ,OAAO,EAAE,KAAKC,OAAM,QAAQ,QAAQ,CAAC;AAEvE,QAAMC,OAAM,CAAO,WAClBC,QAAO,WAAWA,QAAO,QAAQ,QAAQ,KAAK,CAAC;AAEhD,SAAO;AAAA;AAAA,IAEN,KAAK;AAAA,IACL,aAAa,MACZD,KAAI,YAAY,IAAI,CAAC,aAAa,SAAS,WAAW,CAAC,EAAE;AAAA,MACxD,CAAC,aAAa,SAAS,IAAI,cAAc;AAAA,IAC1C;AAAA,IACD,eAAe,MACdA,KAAI,YAAY,IAAI,CAAC,aAAa,SAAS,aAAa,CAAC,EAAE;AAAA,MAC1D;AAAA,IACD;AAAA,IACD,mBAAmB,MAClBA,KAAI,YAAY,IAAI,CAAC,aAAa,SAAS,iBAAiB,CAAC,EAAE;AAAA,MAC9D;AAAA,IACD;AAAA,EACF;AACD;;;AC/CA,SAAS,UAAAE,eAAsC;AAIxC,IAAM,MAAM,CAClB,QACA,aAEAA,QAAO,WAAWA,QAAO,OAAOA,QAAO,QAAQ,QAAQ,QAAQ,CAAC,CAAC;;;ACD3D,IAAUC;AAAA,CAAV,CAAUA,kBAAV;AAKC,WAAS,KACf,QACA,UACC;AACD,UAAM,aAAa,aAAmB,KAAK,MAAM;AACjD,WAAO;AAAA,MACN,MAAM,CAAC,YACN,IAAI,WAAW,KAAK,cAAc,OAAO,CAAC,GAAG,QAAQ;AAAA,MACtD,OAAO,CAAC,SAAqB,SAC5B,IAAI,WAAW,MAAM,cAAc,OAAO,GAAG,IAAI,GAAG,QAAQ;AAAA,IAC9D;AAAA,EACD;AAXO,EAAAA,cAAS;AAAA,GALAA,kCAAA;","names":["cli","Effect","Effect","Effect","Effect","Layer","Layer","run","Effect","Effect","SessionState"]}
@@ -129,9 +129,104 @@ var buildDevSessionsLayer = (rootDir) => Layer.effect(
129
129
  import { watch as fsWatch, realpathSync } from "fs";
130
130
  import { createRequire } from "module";
131
131
  import { dirname, join, resolve } from "path";
132
- import { Deferred, Effect as Effect2 } from "effect";
132
+ import { Deferred, Effect as Effect3 } from "effect";
133
133
  import { FileSystem as FileSystem2 } from "effect/FileSystem";
134
134
  import { Path as Path2 } from "effect/Path";
135
+
136
+ // src/dev/daemon-readiness.ts
137
+ import { createConnection } from "net";
138
+ import { StringDecoder } from "string_decoder";
139
+ import { Effect as Effect2, Exit, Schema } from "effect";
140
+ var RESPONSE_TIMEOUT_MS = 200;
141
+ var MAX_RESPONSE_BYTES = 1024 * 1024;
142
+ var DaemonResponse = Schema.fromJsonString(
143
+ Schema.Struct({
144
+ version: Schema.Literal(1),
145
+ requestId: Schema.NonEmptyString,
146
+ ok: Schema.Boolean,
147
+ result: Schema.optional(Schema.Unknown),
148
+ error: Schema.optional(Schema.String)
149
+ })
150
+ );
151
+ var sendReadiness = (identity, request) => Effect2.tryPromise({
152
+ try: () => new Promise((resolve2, reject) => {
153
+ const socket = createConnection(identity.socketPath);
154
+ const decoder = new StringDecoder("utf8");
155
+ let input = "";
156
+ let settled = false;
157
+ const finish = (complete) => {
158
+ if (settled) return;
159
+ settled = true;
160
+ clearTimeout(timer);
161
+ socket.destroy();
162
+ complete();
163
+ };
164
+ const timer = setTimeout(
165
+ () => finish(
166
+ () => reject(new Error("Daemon readiness request timed out"))
167
+ ),
168
+ RESPONSE_TIMEOUT_MS
169
+ );
170
+ socket.once(
171
+ "connect",
172
+ () => socket.write(`${JSON.stringify(request)}
173
+ `)
174
+ );
175
+ socket.on("data", (chunk) => {
176
+ input += decoder.write(chunk);
177
+ if (Buffer.byteLength(input) > MAX_RESPONSE_BYTES) {
178
+ finish(
179
+ () => reject(new Error("Daemon readiness response is too large"))
180
+ );
181
+ return;
182
+ }
183
+ const boundary = input.indexOf("\n");
184
+ if (boundary !== -1) finish(() => resolve2(input.slice(0, boundary)));
185
+ });
186
+ socket.once("error", (cause) => finish(() => reject(cause)));
187
+ socket.once(
188
+ "close",
189
+ () => finish(
190
+ () => reject(new Error("Daemon closed before acknowledging readiness"))
191
+ )
192
+ );
193
+ }),
194
+ catch: (cause) => new Error("Could not report readiness to daemon", { cause })
195
+ }).pipe(
196
+ Effect2.flatMap(Schema.decodeUnknownEffect(DaemonResponse)),
197
+ Effect2.flatMap(
198
+ (response) => response.requestId === request.requestId && response.ok ? Effect2.void : Effect2.fail(new Error("Daemon did not acknowledge readiness"))
199
+ )
200
+ );
201
+ var publishDaemonReadiness = (value) => Effect2.suspend(() => {
202
+ const socketPath = process.env.DEVSESS_SOCKET;
203
+ const runId = process.env.DEVSESS_RUN_ID;
204
+ const service = process.env.DEVSESS_SERVICE;
205
+ if (socketPath === void 0 || runId === void 0 || service === void 0)
206
+ return Effect2.void;
207
+ const identity = { socketPath, runId, service };
208
+ return Effect2.acquireRelease(
209
+ Schema.decodeUnknownEffect(Schema.Json)(value).pipe(
210
+ Effect2.flatMap(
211
+ (json) => sendReadiness(identity, {
212
+ version: 1,
213
+ requestId: crypto.randomUUID(),
214
+ method: "publish",
215
+ params: { runId, service, value: json }
216
+ })
217
+ ),
218
+ Effect2.exit
219
+ ),
220
+ (result) => Exit.isSuccess(result) ? sendReadiness(identity, {
221
+ version: 1,
222
+ requestId: crypto.randomUUID(),
223
+ method: "unpublish",
224
+ params: { runId, service }
225
+ }).pipe(Effect2.catch(() => Effect2.void)) : Effect2.void
226
+ ).pipe(Effect2.asVoid);
227
+ });
228
+
229
+ // src/dev/running-signal.ts
135
230
  var RUNNING_SIGNAL_FILE = ".data/running.json";
136
231
  var runningSignalPath = (dir) => join(dir, RUNNING_SIGNAL_FILE);
137
232
  var resolveSiblingDir = (spec, fromDir) => {
@@ -142,8 +237,8 @@ var resolveSiblingDir = (spec, fromDir) => {
142
237
  const pkgJsonPath = require2.resolve(`${spec}/package.json`);
143
238
  return dirname(realpathSync(pkgJsonPath));
144
239
  };
145
- var publishRunningSignal = (filePath, value) => Effect2.acquireRelease(
146
- Effect2.gen(function* () {
240
+ var publishRunningSignal = (filePath, value) => Effect3.acquireRelease(
241
+ Effect3.gen(function* () {
147
242
  const fs = yield* FileSystem2;
148
243
  const path = yield* Path2;
149
244
  const tmp = `${filePath}.tmp`;
@@ -151,52 +246,53 @@ var publishRunningSignal = (filePath, value) => Effect2.acquireRelease(
151
246
  yield* fs.writeFileString(tmp, JSON.stringify(value));
152
247
  yield* fs.rename(tmp, filePath);
153
248
  }),
154
- () => Effect2.gen(function* () {
249
+ () => Effect3.gen(function* () {
155
250
  const fs = yield* FileSystem2;
156
- yield* fs.remove(filePath).pipe(Effect2.catch(() => Effect2.void));
251
+ yield* fs.remove(filePath).pipe(Effect3.catch(() => Effect3.void));
157
252
  })
158
253
  );
159
- var awaitRunningSignal = (filePath, opts) => Effect2.scoped(
160
- Effect2.gen(function* () {
254
+ var awaitRunningSignal = (filePath, opts) => Effect3.scoped(
255
+ Effect3.gen(function* () {
161
256
  const fs = yield* FileSystem2;
162
257
  const path = yield* Path2;
163
258
  const dir = path.dirname(filePath);
164
259
  const filename = path.basename(filePath);
165
260
  const deferred = yield* Deferred.make();
166
- const tryRead = Effect2.gen(function* () {
261
+ const tryRead = Effect3.gen(function* () {
167
262
  if (!(yield* fs.exists(filePath))) return false;
168
- const raw = yield* fs.readFileString(filePath).pipe(Effect2.catch(() => Effect2.succeed(null)));
263
+ const raw = yield* fs.readFileString(filePath).pipe(Effect3.catch(() => Effect3.succeed(null)));
169
264
  if (raw == null) return false;
170
- const parsed = yield* Effect2.try(() => opts.parse(raw)).pipe(
171
- Effect2.catch(() => Effect2.succeed(null))
265
+ const parsed = yield* Effect3.try(() => opts.parse(raw)).pipe(
266
+ Effect3.catch(() => Effect3.succeed(null))
172
267
  );
173
268
  if (parsed == null) return false;
174
269
  yield* Deferred.succeed(deferred, parsed);
175
270
  return true;
176
271
  });
177
272
  yield* fs.makeDirectory(dir, { recursive: true });
178
- yield* Effect2.acquireRelease(
179
- Effect2.sync(
273
+ yield* Effect3.acquireRelease(
274
+ Effect3.sync(
180
275
  () => (
181
276
  // Watch parent dir — the file may not exist yet, and watching a missing file errors.
182
277
  fsWatch(dir, { recursive: false }, (eventType, eventFilename) => {
183
278
  if (eventFilename !== filename) return;
184
279
  if (eventType !== "rename" && eventType !== "change") return;
185
- Effect2.runFork(tryRead);
280
+ Effect3.runFork(tryRead);
186
281
  })
187
282
  )
188
283
  ),
189
- (watcher) => Effect2.sync(() => watcher.close())
284
+ (watcher) => Effect3.sync(() => watcher.close())
190
285
  );
191
286
  yield* tryRead;
192
287
  return yield* Deferred.await(deferred);
193
288
  })
194
289
  );
195
- var publishRunning = (value) => Effect2.gen(function* () {
290
+ var publishRunning = (value) => Effect3.gen(function* () {
196
291
  const sessions = yield* DevSessions;
197
292
  yield* publishRunningSignal(runningSignalPath(sessions.dir), value);
293
+ yield* publishDaemonReadiness(value);
198
294
  });
199
- var awaitRunning = (pkg) => Effect2.gen(function* () {
295
+ var awaitRunning = (pkg) => Effect3.gen(function* () {
200
296
  const sessions = yield* DevSessions;
201
297
  return yield* awaitRunningSignal(
202
298
  runningSignalPath(resolveSiblingDir(pkg, sessions.dir)),
@@ -205,7 +301,7 @@ var awaitRunning = (pkg) => Effect2.gen(function* () {
205
301
  });
206
302
 
207
303
  // src/dev/session-state.ts
208
- import { Data as Data2, Effect as Effect3 } from "effect";
304
+ import { Data as Data2, Effect as Effect4 } from "effect";
209
305
  import { FileSystem as FileSystem3 } from "effect/FileSystem";
210
306
  import { Path as Path3 } from "effect/Path";
211
307
  import * as S from "effect/Schema";
@@ -215,7 +311,7 @@ function getPath(session) {
215
311
  return session.path("sess.json");
216
312
  }
217
313
  function read(filePath) {
218
- return Effect3.gen(function* () {
314
+ return Effect4.gen(function* () {
219
315
  const fs = yield* FileSystem3;
220
316
  if (!(yield* fs.exists(filePath))) {
221
317
  return null;
@@ -227,21 +323,21 @@ var SessionState;
227
323
  ((SessionState2) => {
228
324
  function slot(schema) {
229
325
  return {
230
- read: (session) => Effect3.gen(function* () {
326
+ read: (session) => Effect4.gen(function* () {
231
327
  const filePath = yield* getPath(session);
232
328
  const content = yield* read(filePath);
233
329
  if (content == null) return null;
234
330
  return yield* S.decodeEffect(S.fromJsonString(schema))(content).pipe(
235
- Effect3.catchTag("SchemaError", () => Effect3.succeed(null))
331
+ Effect4.catchTag("SchemaError", () => Effect4.succeed(null))
236
332
  );
237
333
  }),
238
- write: (session, data) => Effect3.gen(function* () {
334
+ write: (session, data) => Effect4.gen(function* () {
239
335
  const fs = yield* FileSystem3;
240
336
  const path = yield* Path3;
241
337
  const filePath = yield* getPath(session);
242
338
  yield* fs.makeDirectory(path.dirname(filePath), { recursive: true });
243
339
  const existing = yield* read(filePath);
244
- const parsed = yield* Effect3.try({
340
+ const parsed = yield* Effect4.try({
245
341
  try: () => JSON.parse(existing ?? "{}"),
246
342
  catch: (cause) => new SessionStateError({
247
343
  message: `Failed to parse ${filePath} as JSON`,
@@ -257,26 +353,36 @@ var SessionState;
257
353
  })(SessionState || (SessionState = {}));
258
354
 
259
355
  // src/dev/sticky-port.ts
260
- import { Effect as Effect4 } from "effect";
356
+ import { Effect as Effect5 } from "effect";
261
357
  import * as S2 from "effect/Schema";
262
358
  import getPort from "get-port";
263
- var StickyPort = SessionState.slot(S2.Struct({ port: S2.Number }));
264
- var getStickyPort = (session) => Effect4.gen(function* () {
265
- const file = yield* StickyPort.read(session);
266
- const preferred = file?.port;
267
- const port = yield* Effect4.promise(
359
+ var DEFAULT_NAME = "default";
360
+ var StickyPorts = SessionState.slot(
361
+ S2.Struct({
362
+ ports: S2.optional(S2.Record(S2.String, S2.Number)),
363
+ port: S2.optional(S2.Number)
364
+ })
365
+ );
366
+ var getStickyPort = (session, options) => Effect5.gen(function* () {
367
+ const name = options?.name ?? DEFAULT_NAME;
368
+ const file = yield* StickyPorts.read(session);
369
+ const ports = file?.ports ?? (file?.port !== void 0 ? { [DEFAULT_NAME]: file.port } : {});
370
+ const preferred = ports[name];
371
+ const exclude = Object.entries(ports).filter((entry) => entry[0] !== name).map((entry) => entry[1]);
372
+ const port = yield* Effect5.promise(
268
373
  async () => getPort({
269
- port: preferred !== void 0 ? [preferred] : void 0
374
+ port: preferred !== void 0 ? [preferred] : void 0,
375
+ ...exclude.length > 0 ? { exclude } : {}
270
376
  })
271
377
  );
272
- yield* StickyPort.write(session, { port });
378
+ yield* StickyPorts.write(session, { ports: { ...ports, [name]: port } });
273
379
  return port;
274
380
  });
275
381
 
276
382
  // src/dev/subprocess.ts
277
- import { Effect as Effect5 } from "effect";
383
+ import { Effect as Effect6 } from "effect";
278
384
  import { ChildProcess } from "effect/unstable/process";
279
- var runManagedSubprocess = (cmd, args, opts) => Effect5.gen(function* () {
385
+ var runManagedSubprocess = (cmd, args, opts) => Effect6.gen(function* () {
280
386
  const command = ChildProcess.make(cmd, args, {
281
387
  stdin: "inherit",
282
388
  stdout: "inherit",
@@ -284,17 +390,17 @@ var runManagedSubprocess = (cmd, args, opts) => Effect5.gen(function* () {
284
390
  env: { ...process.env, ...opts?.env ?? {} }
285
391
  });
286
392
  const label = [cmd, ...args].join(" ");
287
- const child = yield* Effect5.acquireRelease(
288
- Effect5.gen(function* () {
393
+ const child = yield* Effect6.acquireRelease(
394
+ Effect6.gen(function* () {
289
395
  const proc = yield* command;
290
- yield* Effect5.logInfo(`[dev] started: ${label} (pid=${proc.pid})`);
396
+ yield* Effect6.logInfo(`[dev] started: ${label} (pid=${proc.pid})`);
291
397
  return proc;
292
398
  }),
293
- (proc) => Effect5.gen(function* () {
294
- yield* Effect5.logInfo(`[dev] stopping: ${label} (pid=${proc.pid})`);
399
+ (proc) => Effect6.gen(function* () {
400
+ yield* Effect6.logInfo(`[dev] stopping: ${label} (pid=${proc.pid})`);
295
401
  yield* proc.kill().pipe(
296
- Effect5.catch(
297
- (err) => Effect5.logError(
402
+ Effect6.catch(
403
+ (err) => Effect6.logError(
298
404
  `[dev] failed to stop ${label} (pid=${proc.pid}): ${err}`
299
405
  )
300
406
  )
@@ -318,4 +424,4 @@ export {
318
424
  getStickyPort,
319
425
  runManagedSubprocess
320
426
  };
321
- //# sourceMappingURL=chunk-AQGYVNYU.js.map
427
+ //# sourceMappingURL=chunk-EKG6NIBH.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/dev-sessions.ts","../src/dev/running-signal.ts","../src/dev/daemon-readiness.ts","../src/dev/session-state.ts","../src/dev/sticky-port.ts","../src/dev/subprocess.ts"],"sourcesContent":["import { Array as A, Context, Data, Effect, Layer, Option } from 'effect';\nimport { FileSystem } from 'effect/FileSystem';\nimport { Path } from 'effect/Path';\nimport type { PlatformError } from 'effect/PlatformError';\nimport { generateSlug } from 'random-word-slugs';\n\nnamespace DevSession {\n\ttype DevSessionInput = {\n\t\tname: string;\n\t\tlastModifiedAt: Date | null;\n\t\trootDir: string;\n\t\tpathJoin: (relativePath: string) => string;\n\t};\n\n\texport function make(input: DevSessionInput) {\n\t\treturn {\n\t\t\tname: input.name,\n\t\t\tlastModifiedAt: input.lastModifiedAt,\n\t\t\tpath: (relativePath: string) =>\n\t\t\t\tEffect.succeed(input.pathJoin(relativePath)),\n\t\t\ttoString: () => input.name,\n\t\t};\n\t}\n}\n\nexport type DevSession = ReturnType<typeof DevSession.make>;\n\nexport class DevSessions extends Context.Service<\n\tDevSessions,\n\t{\n\t\treadonly dir: string;\n\t\treadonly path: (relativePath: string) => string;\n\t\treadonly getSessions: Effect.Effect<Array<DevSession>, PlatformError>;\n\t\treadonly createSession: Effect.Effect<DevSession, PlatformError>;\n\t\treadonly getLatestOrCreate: Effect.Effect<DevSession, PlatformError>;\n\t}\n>()('devsess/DevSessions') {\n\t/**\n\t * `DevSessions` explicitly rooted at `rootDir`: `dir`/`path` resolve against `rootDir`\n\t * itself (e.g. `sessions.path('drizzle')` reaches a project's real `drizzle/` folder),\n\t * while session directories are namespaced under `<rootDir>/.data/sessions`.\n\t *\n\t * Deliberately does *not* also provide `CurrentSession` — a `Layer`'s build effect\n\t * runs as soon as it's provided, before anything downstream (e.g. `cli.Command.run`'s\n\t * argv parsing) gets a say. Bundling session resolution in here would create a\n\t * session for `--help`/`--version`/a bad flag too, since those never reach a command\n\t * handler at all. Provide `CurrentSession.layer` around the handler itself instead.\n\t */\n\tstatic readonly layerAt = (rootDir: string) => buildDevSessionsLayer(rootDir);\n\n\t/**\n\t * `DevSessions.layerAt` rooted at the auto-detected project root — the nearest\n\t * ancestor of `process.cwd()` containing a `package.json`.\n\t */\n\tstatic readonly layer: Layer.Layer<\n\t\tDevSessions,\n\t\tPlatformError | ProjectRootNotFoundError,\n\t\tFileSystem | Path\n\t> = Layer.unwrap(\n\t\tEffect.suspend(() =>\n\t\t\tEffect.map(findProjectRoot, (rootDir) => DevSessions.layerAt(rootDir)),\n\t\t),\n\t);\n}\n\n/**\n * Raised by `DevSessions.layer` when no ancestor of `searchedFrom` contains a\n * `package.json`. Failing loudly beats silently falling back to `process.cwd()` — a\n * wrong root means dev sessions get written to a surprising place.\n */\nexport class ProjectRootNotFoundError extends Data.TaggedError(\n\t'ProjectRootNotFoundError',\n)<{\n\treadonly searchedFrom: string;\n}> {}\n\n/** Walks up from `process.cwd()` to the nearest ancestor containing a `package.json`. */\nconst findProjectRoot = Effect.gen(function* () {\n\tconst fs = yield* FileSystem;\n\tconst path = yield* Path;\n\tconst startDir = process.cwd();\n\n\tconst walkUp = (\n\t\tdir: string,\n\t): Effect.Effect<string, PlatformError | ProjectRootNotFoundError> =>\n\t\tEffect.gen(function* () {\n\t\t\tif (yield* fs.exists(path.join(dir, 'package.json'))) {\n\t\t\t\treturn dir;\n\t\t\t}\n\t\t\tconst parent = path.dirname(dir);\n\t\t\tif (parent === dir) {\n\t\t\t\treturn yield* Effect.fail(\n\t\t\t\t\tnew ProjectRootNotFoundError({ searchedFrom: startDir }),\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn yield* walkUp(parent);\n\t\t});\n\n\treturn yield* walkUp(startDir);\n});\n\n/** The listing/creation logic shared by every `DevSessions` layer, parameterized on the literal directory session subdirs live under. */\nconst buildDevSessionsCore = (sessionsDir: string) =>\n\tEffect.gen(function* () {\n\t\tconst fs = yield* FileSystem;\n\t\tconst path = yield* Path;\n\n\t\tyield* Effect.logDebug(`DevSessions dir: ${sessionsDir}`);\n\n\t\tconst getSessions = Effect.gen(function* () {\n\t\t\tconst entries = yield* fs\n\t\t\t\t.readDirectory(sessionsDir)\n\t\t\t\t.pipe(Effect.catch(() => Effect.succeed([] as Array<string>)));\n\n\t\t\tconst possibleSessions = yield* Effect.all(\n\t\t\t\tentries.map((entry) =>\n\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\tconst stat = yield* fs.stat(path.join(sessionsDir, entry));\n\t\t\t\t\t\tif (stat.type !== 'Directory') return Option.none();\n\t\t\t\t\t\treturn Option.some(\n\t\t\t\t\t\t\tDevSession.make({\n\t\t\t\t\t\t\t\tname: entry,\n\t\t\t\t\t\t\t\tlastModifiedAt: Option.getOrNull(stat.mtime),\n\t\t\t\t\t\t\t\trootDir: sessionsDir,\n\t\t\t\t\t\t\t\tpathJoin: (relativePath: string) =>\n\t\t\t\t\t\t\t\t\tpath.join(sessionsDir, entry, relativePath),\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t);\n\t\t\t\t\t}),\n\t\t\t\t),\n\t\t\t\t{ concurrency: 'unbounded' },\n\t\t\t);\n\n\t\t\treturn A.getSomes(possibleSessions);\n\t\t});\n\n\t\tconst createSession = Effect.gen(function* () {\n\t\t\tconst slug = generateSlug(1, { partsOfSpeech: ['noun'] });\n\t\t\tyield* fs.makeDirectory(path.join(sessionsDir, slug), {\n\t\t\t\trecursive: true,\n\t\t\t});\n\t\t\treturn DevSession.make({\n\t\t\t\tname: slug,\n\t\t\t\tlastModifiedAt: null,\n\t\t\t\trootDir: sessionsDir,\n\t\t\t\tpathJoin: (relativePath: string) =>\n\t\t\t\t\tpath.join(sessionsDir, slug, relativePath),\n\t\t\t});\n\t\t});\n\n\t\tconst getLatestOrCreate = Effect.gen(function* () {\n\t\t\tyield* fs.makeDirectory(sessionsDir, { recursive: true });\n\n\t\t\tconst sessions = yield* getSessions;\n\t\t\tif (sessions.length > 0) {\n\t\t\t\tsessions.sort(\n\t\t\t\t\t(a, b) =>\n\t\t\t\t\t\t(b.lastModifiedAt?.getTime() ?? 0) -\n\t\t\t\t\t\t(a.lastModifiedAt?.getTime() ?? 0),\n\t\t\t\t);\n\t\t\t\tconst latest = sessions[0];\n\t\t\t\treturn latest as DevSession;\n\t\t\t}\n\n\t\t\treturn yield* createSession;\n\t\t});\n\n\t\treturn { getSessions, createSession, getLatestOrCreate };\n\t});\n\n/**\n * The one `DevSessions` layer constructor: `dir`/`path` resolve against `rootDir`\n * itself, while session directories are namespaced under `<rootDir>/.data/sessions` —\n * the library owns that layout, so every caller (`DevSessions.layerAt`, the async\n * facade's `createDevSessions`) means the same thing by `rootDir`.\n */\nconst buildDevSessionsLayer = (rootDir: string) =>\n\tLayer.effect(\n\t\tDevSessions,\n\t\tEffect.gen(function* () {\n\t\t\tconst path = yield* Path;\n\t\t\tconst sessionsDir = path.join(rootDir, '.data', 'sessions');\n\t\t\tconst core = yield* buildDevSessionsCore(sessionsDir);\n\t\t\treturn {\n\t\t\t\tdir: rootDir,\n\t\t\t\tpath: (relativePath: string) => path.join(rootDir, relativePath),\n\t\t\t\t...core,\n\t\t\t};\n\t\t}),\n\t);\n","import { watch as fsWatch, realpathSync } from 'node:fs';\nimport { createRequire } from 'node:module';\nimport { dirname, join, resolve } from 'node:path';\nimport { Deferred, Effect } from 'effect';\nimport { FileSystem } from 'effect/FileSystem';\nimport { Path } from 'effect/Path';\nimport { DevSessions } from '../dev-sessions';\nimport { publishDaemonReadiness } from './daemon-readiness';\n\nconst RUNNING_SIGNAL_FILE = '.data/running.json';\n\n/** Path to the running-signal file for a dev-session (or sibling package) directory. */\nexport const runningSignalPath = (dir: string) =>\n\tjoin(dir, RUNNING_SIGNAL_FILE);\n\n/**\n * Resolves `spec` to a directory: relative/absolute paths are resolved against\n * `fromDir`, anything else is treated as a package name and resolved via its\n * `package.json`.\n */\nexport const resolveSiblingDir = (spec: string, fromDir: string) => {\n\tif (spec.startsWith('.') || spec.startsWith('/')) {\n\t\treturn resolve(fromDir, spec);\n\t}\n\tconst require = createRequire(join(fromDir, 'package.json'));\n\tconst pkgJsonPath = require.resolve(`${spec}/package.json`);\n\treturn dirname(realpathSync(pkgJsonPath));\n};\n\n/** Atomically writes `value` as JSON to `filePath` and removes it on release. */\nexport const publishRunningSignal = (filePath: string, value: unknown) =>\n\tEffect.acquireRelease(\n\t\tEffect.gen(function* () {\n\t\t\tconst fs = yield* FileSystem;\n\t\t\tconst path = yield* Path;\n\t\t\tconst tmp = `${filePath}.tmp`;\n\t\t\tyield* fs.makeDirectory(path.dirname(filePath), { recursive: true });\n\t\t\t// Write to a temp file then rename atomically so readers never see a partial file.\n\t\t\tyield* fs.writeFileString(tmp, JSON.stringify(value));\n\t\t\tyield* fs.rename(tmp, filePath);\n\t\t}),\n\t\t() =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst fs = yield* FileSystem;\n\t\t\t\tyield* fs.remove(filePath).pipe(Effect.catch(() => Effect.void));\n\t\t\t}),\n\t);\n\n/** Watches parent directory for the signal file and resolves once it can be parsed. */\nexport const awaitRunningSignal = <T>(\n\tfilePath: string,\n\topts: { parse: (raw: string) => T },\n) =>\n\tEffect.scoped(\n\t\tEffect.gen(function* () {\n\t\t\tconst fs = yield* FileSystem;\n\t\t\tconst path = yield* Path;\n\t\t\tconst dir = path.dirname(filePath);\n\t\t\tconst filename = path.basename(filePath);\n\n\t\t\tconst deferred = yield* Deferred.make<T>();\n\n\t\t\tconst tryRead = Effect.gen(function* () {\n\t\t\t\tif (!(yield* fs.exists(filePath))) return false;\n\t\t\t\tconst raw = yield* fs\n\t\t\t\t\t.readFileString(filePath)\n\t\t\t\t\t.pipe(Effect.catch(() => Effect.succeed(null)));\n\t\t\t\tif (raw == null) return false;\n\t\t\t\tconst parsed = yield* Effect.try(() => opts.parse(raw)).pipe(\n\t\t\t\t\tEffect.catch(() => Effect.succeed(null)),\n\t\t\t\t);\n\t\t\t\tif (parsed == null) return false;\n\t\t\t\tyield* Deferred.succeed(deferred, parsed);\n\t\t\t\treturn true;\n\t\t\t});\n\n\t\t\t// fsWatch throws synchronously if `dir` doesn't exist yet — e.g. a sibling\n\t\t\t// package's dev script has never run — so ensure it's there first.\n\t\t\tyield* fs.makeDirectory(dir, { recursive: true });\n\n\t\t\tyield* Effect.acquireRelease(\n\t\t\t\tEffect.sync(() =>\n\t\t\t\t\t// Watch parent dir — the file may not exist yet, and watching a missing file errors.\n\t\t\t\t\tfsWatch(dir, { recursive: false }, (eventType, eventFilename) => {\n\t\t\t\t\t\tif (eventFilename !== filename) return;\n\t\t\t\t\t\tif (eventType !== 'rename' && eventType !== 'change') return;\n\t\t\t\t\t\tEffect.runFork(tryRead);\n\t\t\t\t\t}),\n\t\t\t\t),\n\t\t\t\t(watcher) => Effect.sync(() => watcher.close()),\n\t\t\t);\n\n\t\t\tyield* tryRead;\n\t\t\treturn yield* Deferred.await(deferred);\n\t\t}),\n\t);\n\n/**\n * Publishes `value` at the project root's running-signal file, so a sibling package's\n * dev script can `awaitRunning` it. Removed when the enclosing scope closes.\n */\nexport const publishRunning = (value: unknown) =>\n\tEffect.gen(function* () {\n\t\tconst sessions = yield* DevSessions;\n\t\tyield* publishRunningSignal(runningSignalPath(sessions.dir), value);\n\t\tyield* publishDaemonReadiness(value);\n\t});\n\n/**\n * Awaits the running-signal file of another package in the workspace (`pkg`, resolved\n * relative to the project root the same way `resolveSiblingDir` resolves any spec).\n */\nexport const awaitRunning = <T>(pkg: string) =>\n\tEffect.gen(function* () {\n\t\tconst sessions = yield* DevSessions;\n\t\treturn yield* awaitRunningSignal<T>(\n\t\t\trunningSignalPath(resolveSiblingDir(pkg, sessions.dir)),\n\t\t\t{ parse: (raw) => JSON.parse(raw) as T },\n\t\t);\n\t});\n","import { createConnection } from 'node:net';\nimport { StringDecoder } from 'node:string_decoder';\nimport { Effect, Exit, Schema } from 'effect';\n\nconst RESPONSE_TIMEOUT_MS = 200;\nconst MAX_RESPONSE_BYTES = 1024 * 1024;\n\nconst DaemonResponse = Schema.fromJsonString(\n\tSchema.Struct({\n\t\tversion: Schema.Literal(1),\n\t\trequestId: Schema.NonEmptyString,\n\t\tok: Schema.Boolean,\n\t\tresult: Schema.optional(Schema.Unknown),\n\t\terror: Schema.optional(Schema.String),\n\t}),\n);\n\ntype ServiceIdentity = {\n\treadonly socketPath: string;\n\treadonly runId: string;\n\treadonly service: string;\n};\n\nconst sendReadiness = (\n\tidentity: ServiceIdentity,\n\trequest: {\n\t\treadonly version: 1;\n\t\treadonly requestId: string;\n\t\treadonly method: 'publish' | 'unpublish';\n\t\treadonly params: {\n\t\t\treadonly runId: string;\n\t\t\treadonly service: string;\n\t\t\treadonly value?: Schema.Json;\n\t\t};\n\t},\n) =>\n\tEffect.tryPromise({\n\t\ttry: () =>\n\t\t\tnew Promise<string>((resolve, reject) => {\n\t\t\t\tconst socket = createConnection(identity.socketPath);\n\t\t\t\tconst decoder = new StringDecoder('utf8');\n\t\t\t\tlet input = '';\n\t\t\t\tlet settled = false;\n\t\t\t\tconst finish = (complete: () => void) => {\n\t\t\t\t\tif (settled) return;\n\t\t\t\t\tsettled = true;\n\t\t\t\t\tclearTimeout(timer);\n\t\t\t\t\tsocket.destroy();\n\t\t\t\t\tcomplete();\n\t\t\t\t};\n\t\t\t\tconst timer = setTimeout(\n\t\t\t\t\t() =>\n\t\t\t\t\t\tfinish(() =>\n\t\t\t\t\t\t\treject(new Error('Daemon readiness request timed out')),\n\t\t\t\t\t\t),\n\t\t\t\t\tRESPONSE_TIMEOUT_MS,\n\t\t\t\t);\n\t\t\t\tsocket.once('connect', () =>\n\t\t\t\t\tsocket.write(`${JSON.stringify(request)}\\n`),\n\t\t\t\t);\n\t\t\t\tsocket.on('data', (chunk) => {\n\t\t\t\t\tinput += decoder.write(chunk);\n\t\t\t\t\tif (Buffer.byteLength(input) > MAX_RESPONSE_BYTES) {\n\t\t\t\t\t\tfinish(() =>\n\t\t\t\t\t\t\treject(new Error('Daemon readiness response is too large')),\n\t\t\t\t\t\t);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tconst boundary = input.indexOf('\\n');\n\t\t\t\t\tif (boundary !== -1) finish(() => resolve(input.slice(0, boundary)));\n\t\t\t\t});\n\t\t\t\tsocket.once('error', (cause) => finish(() => reject(cause)));\n\t\t\t\tsocket.once('close', () =>\n\t\t\t\t\tfinish(() =>\n\t\t\t\t\t\treject(new Error('Daemon closed before acknowledging readiness')),\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}),\n\t\tcatch: (cause) =>\n\t\t\tnew Error('Could not report readiness to daemon', { cause }),\n\t}).pipe(\n\t\tEffect.flatMap(Schema.decodeUnknownEffect(DaemonResponse)),\n\t\tEffect.flatMap((response) =>\n\t\t\tresponse.requestId === request.requestId && response.ok\n\t\t\t\t? Effect.void\n\t\t\t\t: Effect.fail(new Error('Daemon did not acknowledge readiness')),\n\t\t),\n\t);\n\nexport const publishDaemonReadiness = (value: unknown) =>\n\tEffect.suspend(() => {\n\t\tconst socketPath = process.env.DEVSESS_SOCKET;\n\t\tconst runId = process.env.DEVSESS_RUN_ID;\n\t\tconst service = process.env.DEVSESS_SERVICE;\n\t\tif (\n\t\t\tsocketPath === undefined ||\n\t\t\trunId === undefined ||\n\t\t\tservice === undefined\n\t\t)\n\t\t\treturn Effect.void;\n\t\tconst identity = { socketPath, runId, service };\n\t\treturn Effect.acquireRelease(\n\t\t\tSchema.decodeUnknownEffect(Schema.Json)(value).pipe(\n\t\t\t\tEffect.flatMap((json) =>\n\t\t\t\t\tsendReadiness(identity, {\n\t\t\t\t\t\tversion: 1,\n\t\t\t\t\t\trequestId: crypto.randomUUID(),\n\t\t\t\t\t\tmethod: 'publish',\n\t\t\t\t\t\tparams: { runId, service, value: json },\n\t\t\t\t\t}),\n\t\t\t\t),\n\t\t\t\tEffect.exit,\n\t\t\t),\n\t\t\t(result) =>\n\t\t\t\tExit.isSuccess(result)\n\t\t\t\t\t? sendReadiness(identity, {\n\t\t\t\t\t\t\tversion: 1,\n\t\t\t\t\t\t\trequestId: crypto.randomUUID(),\n\t\t\t\t\t\t\tmethod: 'unpublish',\n\t\t\t\t\t\t\tparams: { runId, service },\n\t\t\t\t\t\t}).pipe(Effect.catch(() => Effect.void))\n\t\t\t\t\t: Effect.void,\n\t\t).pipe(Effect.asVoid);\n\t});\n","import { Data, Effect } from 'effect';\nimport { FileSystem } from 'effect/FileSystem';\nimport { Path } from 'effect/Path';\nimport * as S from 'effect/Schema';\nimport type { DevSession } from '../dev-sessions';\n\nexport class SessionStateError extends Data.TaggedError('SessionStateError')<{\n\tmessage: string;\n\tcause?: unknown;\n}> {}\n\nfunction getPath(session: DevSession) {\n\treturn session.path('sess.json');\n}\n\nfunction read(filePath: string) {\n\treturn Effect.gen(function* () {\n\t\tconst fs = yield* FileSystem;\n\t\tif (!(yield* fs.exists(filePath))) {\n\t\t\treturn null;\n\t\t}\n\t\treturn yield* fs.readFileString(filePath);\n\t});\n}\n\nexport namespace SessionState {\n\ttype Shape = S.Top;\n\n\texport function slot<T extends Shape>(schema: T) {\n\t\treturn {\n\t\t\tread: (session: DevSession) =>\n\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\tconst filePath = yield* getPath(session);\n\t\t\t\t\tconst content = yield* read(filePath);\n\t\t\t\t\tif (content == null) return null;\n\t\t\t\t\treturn yield* S.decodeEffect(S.fromJsonString(schema))(content).pipe(\n\t\t\t\t\t\tEffect.catchTag('SchemaError', () => Effect.succeed(null)),\n\t\t\t\t\t);\n\t\t\t\t}),\n\t\t\twrite: (session: DevSession, data: T['Type']) =>\n\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\tconst fs = yield* FileSystem;\n\t\t\t\t\tconst path = yield* Path;\n\t\t\t\t\tconst filePath = yield* getPath(session);\n\t\t\t\t\tyield* fs.makeDirectory(path.dirname(filePath), { recursive: true });\n\t\t\t\t\tconst existing = yield* read(filePath);\n\t\t\t\t\tconst parsed = yield* Effect.try({\n\t\t\t\t\t\ttry: () => JSON.parse(existing ?? '{}') as Record<string, unknown>,\n\t\t\t\t\t\tcatch: (cause) =>\n\t\t\t\t\t\t\tnew SessionStateError({\n\t\t\t\t\t\t\t\tmessage: `Failed to parse ${filePath} as JSON`,\n\t\t\t\t\t\t\t\tcause,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t});\n\t\t\t\t\tconst merged = { ...parsed, ...(data as Record<string, unknown>) };\n\t\t\t\t\tyield* fs.writeFileString(filePath, JSON.stringify(merged));\n\t\t\t\t}),\n\t\t};\n\t}\n}\n","import { Effect } from 'effect';\nimport * as S from 'effect/Schema';\nimport getPort from 'get-port';\nimport type { DevSession } from '../dev-sessions';\nimport { SessionState } from './session-state';\n\nconst DEFAULT_NAME = 'default';\n\n/**\n * Reads both the current `{ ports: Record<string, number> }` shape and the legacy\n * top-level `{ port: number }` shape, so a session written before named ports existed\n * still yields its remembered port (as `default`). Writes only ever use `ports` —\n * the legacy `port` key is never touched again once the new shape is written.\n */\nconst StickyPorts = SessionState.slot(\n\tS.Struct({\n\t\tports: S.optional(S.Record(S.String, S.Number)),\n\t\tport: S.optional(S.Number),\n\t}),\n);\n\nexport const getStickyPort = (\n\tsession: DevSession,\n\toptions?: { name?: string },\n) =>\n\tEffect.gen(function* () {\n\t\tconst name = options?.name ?? DEFAULT_NAME;\n\n\t\tconst file = yield* StickyPorts.read(session);\n\t\tconst ports =\n\t\t\tfile?.ports ??\n\t\t\t(file?.port !== undefined ? { [DEFAULT_NAME]: file.port } : {});\n\n\t\tconst preferred = ports[name];\n\t\tconst exclude = Object.entries(ports)\n\t\t\t.filter((entry) => entry[0] !== name)\n\t\t\t.map((entry) => entry[1]);\n\n\t\tconst port = yield* Effect.promise(async () =>\n\t\t\tgetPort({\n\t\t\t\tport: preferred !== undefined ? [preferred] : undefined,\n\t\t\t\t...(exclude.length > 0 ? { exclude } : {}),\n\t\t\t}),\n\t\t);\n\n\t\tyield* StickyPorts.write(session, { ports: { ...ports, [name]: port } });\n\n\t\treturn port;\n\t});\n","import { Effect } from 'effect';\nimport { ChildProcess } from 'effect/unstable/process';\n\nexport const runManagedSubprocess = (\n\tcmd: string,\n\targs: string[],\n\topts?: { env?: Record<string, string> },\n) =>\n\tEffect.gen(function* () {\n\t\tconst command = ChildProcess.make(cmd, args, {\n\t\t\tstdin: 'inherit',\n\t\t\tstdout: 'inherit',\n\t\t\tstderr: 'inherit',\n\t\t\tenv: { ...process.env, ...(opts?.env ?? {}) },\n\t\t});\n\n\t\tconst label = [cmd, ...args].join(' ');\n\n\t\t// Registers against the ambient scope, not a scope of its own — the\n\t\t// child must outlive this call (it doesn't resolve until the child\n\t\t// exits) and get killed only when the caller's scope closes.\n\t\tconst child = yield* Effect.acquireRelease(\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst proc = yield* command;\n\t\t\t\tyield* Effect.logInfo(`[dev] started: ${label} (pid=${proc.pid})`);\n\t\t\t\treturn proc;\n\t\t\t}),\n\t\t\t(proc) =>\n\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\tyield* Effect.logInfo(`[dev] stopping: ${label} (pid=${proc.pid})`);\n\t\t\t\t\tyield* proc\n\t\t\t\t\t\t.kill()\n\t\t\t\t\t\t.pipe(\n\t\t\t\t\t\t\tEffect.catch((err) =>\n\t\t\t\t\t\t\t\tEffect.logError(\n\t\t\t\t\t\t\t\t\t`[dev] failed to stop ${label} (pid=${proc.pid}): ${err}`,\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t);\n\t\t\t\t}),\n\t\t);\n\n\t\treturn yield* child.exitCode;\n\t});\n"],"mappings":"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","names":["DevSession","Effect","FileSystem","Path","Effect","resolve","require","Effect","FileSystem","Path","Data","Effect","FileSystem","Path","SessionState","Effect","S","Effect","Effect"]}
@@ -0,0 +1,3 @@
1
+ import { Effect } from 'effect';
2
+ export declare const publishDaemonReadiness: (value: unknown) => Effect.Effect<void, never, import("effect/Scope").Scope>;
3
+ //# sourceMappingURL=daemon-readiness.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"daemon-readiness.d.ts","sourceRoot":"","sources":["../../src/dev/daemon-readiness.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAgB,MAAM,QAAQ,CAAC;AAuF9C,eAAO,MAAM,sBAAsB,UAAW,OAAO,6DAkClD,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"running-signal.d.ts","sourceRoot":"","sources":["../../src/dev/running-signal.ts"],"names":[],"mappings":"AAGA,OAAO,EAAY,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AACnC,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAI9C,wFAAwF;AACxF,eAAO,MAAM,iBAAiB,QAAS,MAAM,WACd,CAAC;AAEhC;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,SAAU,MAAM,WAAW,MAAM,WAO9D,CAAC;AAEF,iFAAiF;AACjF,eAAO,MAAM,oBAAoB,aAAc,MAAM,SAAS,OAAO,wHAgBnE,CAAC;AAEH,uFAAuF;AACvF,eAAO,MAAM,kBAAkB,GAAI,CAAC,YACzB,MAAM,QACV;IAAE,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,CAAC,CAAA;CAAE,sFA4ClC,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,cAAc,UAAW,OAAO,sIAI1C,CAAC;AAEJ;;;GAGG;AACH,eAAO,MAAM,YAAY,GAAI,CAAC,OAAO,MAAM,oGAOxC,CAAC"}
1
+ {"version":3,"file":"running-signal.d.ts","sourceRoot":"","sources":["../../src/dev/running-signal.ts"],"names":[],"mappings":"AAGA,OAAO,EAAY,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AACnC,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAK9C,wFAAwF;AACxF,eAAO,MAAM,iBAAiB,QAAS,MAAM,WACd,CAAC;AAEhC;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,SAAU,MAAM,WAAW,MAAM,WAO9D,CAAC;AAEF,iFAAiF;AACjF,eAAO,MAAM,oBAAoB,aAAc,MAAM,SAAS,OAAO,wHAgBnE,CAAC;AAEH,uFAAuF;AACvF,eAAO,MAAM,kBAAkB,GAAI,CAAC,YACzB,MAAM,QACV;IAAE,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,CAAC,CAAA;CAAE,sFA4ClC,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,cAAc,UAAW,OAAO,sIAK1C,CAAC;AAEJ;;;GAGG;AACH,eAAO,MAAM,YAAY,GAAI,CAAC,OAAO,MAAM,oGAOxC,CAAC"}
@@ -1,4 +1,6 @@
1
1
  import { Effect } from 'effect';
2
2
  import type { DevSession } from '../dev-sessions';
3
- export declare const getStickyPort: (session: DevSession) => Effect.Effect<number, import("effect/PlatformError").PlatformError | import("./session-state").SessionStateError, import("effect/FileSystem").FileSystem | import("effect/Path").Path>;
3
+ export declare const getStickyPort: (session: DevSession, options?: {
4
+ name?: string;
5
+ }) => Effect.Effect<number, import("effect/PlatformError").PlatformError | import("./session-state").SessionStateError, import("effect/FileSystem").FileSystem | import("effect/Path").Path>;
4
6
  //# sourceMappingURL=sticky-port.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"sticky-port.d.ts","sourceRoot":"","sources":["../../src/dev/sticky-port.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAGhC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAKlD,eAAO,MAAM,aAAa,YAAa,UAAU,2LAc9C,CAAC"}
1
+ {"version":3,"file":"sticky-port.d.ts","sourceRoot":"","sources":["../../src/dev/sticky-port.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAGhC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAkBlD,eAAO,MAAM,aAAa,YAChB,UAAU,YACT;IAAE,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,2LAyBzB,CAAC"}
package/dist/index.js CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  getStickyPort,
8
8
  publishRunning,
9
9
  runManagedSubprocess
10
- } from "./chunk-AQGYVNYU.js";
10
+ } from "./chunk-EKG6NIBH.js";
11
11
 
12
12
  // src/current-session.ts
13
13
  import { Context, Effect, Layer } from "effect";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "devsess",
3
- "version": "0.2.0",
3
+ "version": "0.3.1",
4
4
  "description": "Isolated, managed dev servers — each worktree gets its own session, with its own port and its own database",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://github.com/fdarian/devsess#readme",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/dev-sessions.ts","../src/dev/running-signal.ts","../src/dev/session-state.ts","../src/dev/sticky-port.ts","../src/dev/subprocess.ts"],"sourcesContent":["import { Array as A, Context, Data, Effect, Layer, Option } from 'effect';\nimport { FileSystem } from 'effect/FileSystem';\nimport { Path } from 'effect/Path';\nimport type { PlatformError } from 'effect/PlatformError';\nimport { generateSlug } from 'random-word-slugs';\n\nnamespace DevSession {\n\ttype DevSessionInput = {\n\t\tname: string;\n\t\tlastModifiedAt: Date | null;\n\t\trootDir: string;\n\t\tpathJoin: (relativePath: string) => string;\n\t};\n\n\texport function make(input: DevSessionInput) {\n\t\treturn {\n\t\t\tname: input.name,\n\t\t\tlastModifiedAt: input.lastModifiedAt,\n\t\t\tpath: (relativePath: string) =>\n\t\t\t\tEffect.succeed(input.pathJoin(relativePath)),\n\t\t\ttoString: () => input.name,\n\t\t};\n\t}\n}\n\nexport type DevSession = ReturnType<typeof DevSession.make>;\n\nexport class DevSessions extends Context.Service<\n\tDevSessions,\n\t{\n\t\treadonly dir: string;\n\t\treadonly path: (relativePath: string) => string;\n\t\treadonly getSessions: Effect.Effect<Array<DevSession>, PlatformError>;\n\t\treadonly createSession: Effect.Effect<DevSession, PlatformError>;\n\t\treadonly getLatestOrCreate: Effect.Effect<DevSession, PlatformError>;\n\t}\n>()('devsess/DevSessions') {\n\t/**\n\t * `DevSessions` explicitly rooted at `rootDir`: `dir`/`path` resolve against `rootDir`\n\t * itself (e.g. `sessions.path('drizzle')` reaches a project's real `drizzle/` folder),\n\t * while session directories are namespaced under `<rootDir>/.data/sessions`.\n\t *\n\t * Deliberately does *not* also provide `CurrentSession` — a `Layer`'s build effect\n\t * runs as soon as it's provided, before anything downstream (e.g. `cli.Command.run`'s\n\t * argv parsing) gets a say. Bundling session resolution in here would create a\n\t * session for `--help`/`--version`/a bad flag too, since those never reach a command\n\t * handler at all. Provide `CurrentSession.layer` around the handler itself instead.\n\t */\n\tstatic readonly layerAt = (rootDir: string) => buildDevSessionsLayer(rootDir);\n\n\t/**\n\t * `DevSessions.layerAt` rooted at the auto-detected project root — the nearest\n\t * ancestor of `process.cwd()` containing a `package.json`.\n\t */\n\tstatic readonly layer: Layer.Layer<\n\t\tDevSessions,\n\t\tPlatformError | ProjectRootNotFoundError,\n\t\tFileSystem | Path\n\t> = Layer.unwrap(\n\t\tEffect.suspend(() =>\n\t\t\tEffect.map(findProjectRoot, (rootDir) => DevSessions.layerAt(rootDir)),\n\t\t),\n\t);\n}\n\n/**\n * Raised by `DevSessions.layer` when no ancestor of `searchedFrom` contains a\n * `package.json`. Failing loudly beats silently falling back to `process.cwd()` — a\n * wrong root means dev sessions get written to a surprising place.\n */\nexport class ProjectRootNotFoundError extends Data.TaggedError(\n\t'ProjectRootNotFoundError',\n)<{\n\treadonly searchedFrom: string;\n}> {}\n\n/** Walks up from `process.cwd()` to the nearest ancestor containing a `package.json`. */\nconst findProjectRoot = Effect.gen(function* () {\n\tconst fs = yield* FileSystem;\n\tconst path = yield* Path;\n\tconst startDir = process.cwd();\n\n\tconst walkUp = (\n\t\tdir: string,\n\t): Effect.Effect<string, PlatformError | ProjectRootNotFoundError> =>\n\t\tEffect.gen(function* () {\n\t\t\tif (yield* fs.exists(path.join(dir, 'package.json'))) {\n\t\t\t\treturn dir;\n\t\t\t}\n\t\t\tconst parent = path.dirname(dir);\n\t\t\tif (parent === dir) {\n\t\t\t\treturn yield* Effect.fail(\n\t\t\t\t\tnew ProjectRootNotFoundError({ searchedFrom: startDir }),\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn yield* walkUp(parent);\n\t\t});\n\n\treturn yield* walkUp(startDir);\n});\n\n/** The listing/creation logic shared by every `DevSessions` layer, parameterized on the literal directory session subdirs live under. */\nconst buildDevSessionsCore = (sessionsDir: string) =>\n\tEffect.gen(function* () {\n\t\tconst fs = yield* FileSystem;\n\t\tconst path = yield* Path;\n\n\t\tyield* Effect.logDebug(`DevSessions dir: ${sessionsDir}`);\n\n\t\tconst getSessions = Effect.gen(function* () {\n\t\t\tconst entries = yield* fs\n\t\t\t\t.readDirectory(sessionsDir)\n\t\t\t\t.pipe(Effect.catch(() => Effect.succeed([] as Array<string>)));\n\n\t\t\tconst possibleSessions = yield* Effect.all(\n\t\t\t\tentries.map((entry) =>\n\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\tconst stat = yield* fs.stat(path.join(sessionsDir, entry));\n\t\t\t\t\t\tif (stat.type !== 'Directory') return Option.none();\n\t\t\t\t\t\treturn Option.some(\n\t\t\t\t\t\t\tDevSession.make({\n\t\t\t\t\t\t\t\tname: entry,\n\t\t\t\t\t\t\t\tlastModifiedAt: Option.getOrNull(stat.mtime),\n\t\t\t\t\t\t\t\trootDir: sessionsDir,\n\t\t\t\t\t\t\t\tpathJoin: (relativePath: string) =>\n\t\t\t\t\t\t\t\t\tpath.join(sessionsDir, entry, relativePath),\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t);\n\t\t\t\t\t}),\n\t\t\t\t),\n\t\t\t\t{ concurrency: 'unbounded' },\n\t\t\t);\n\n\t\t\treturn A.getSomes(possibleSessions);\n\t\t});\n\n\t\tconst createSession = Effect.gen(function* () {\n\t\t\tconst slug = generateSlug(1, { partsOfSpeech: ['noun'] });\n\t\t\tyield* fs.makeDirectory(path.join(sessionsDir, slug), {\n\t\t\t\trecursive: true,\n\t\t\t});\n\t\t\treturn DevSession.make({\n\t\t\t\tname: slug,\n\t\t\t\tlastModifiedAt: null,\n\t\t\t\trootDir: sessionsDir,\n\t\t\t\tpathJoin: (relativePath: string) =>\n\t\t\t\t\tpath.join(sessionsDir, slug, relativePath),\n\t\t\t});\n\t\t});\n\n\t\tconst getLatestOrCreate = Effect.gen(function* () {\n\t\t\tyield* fs.makeDirectory(sessionsDir, { recursive: true });\n\n\t\t\tconst sessions = yield* getSessions;\n\t\t\tif (sessions.length > 0) {\n\t\t\t\tsessions.sort(\n\t\t\t\t\t(a, b) =>\n\t\t\t\t\t\t(b.lastModifiedAt?.getTime() ?? 0) -\n\t\t\t\t\t\t(a.lastModifiedAt?.getTime() ?? 0),\n\t\t\t\t);\n\t\t\t\tconst latest = sessions[0];\n\t\t\t\treturn latest as DevSession;\n\t\t\t}\n\n\t\t\treturn yield* createSession;\n\t\t});\n\n\t\treturn { getSessions, createSession, getLatestOrCreate };\n\t});\n\n/**\n * The one `DevSessions` layer constructor: `dir`/`path` resolve against `rootDir`\n * itself, while session directories are namespaced under `<rootDir>/.data/sessions` —\n * the library owns that layout, so every caller (`DevSessions.layerAt`, the async\n * facade's `createDevSessions`) means the same thing by `rootDir`.\n */\nconst buildDevSessionsLayer = (rootDir: string) =>\n\tLayer.effect(\n\t\tDevSessions,\n\t\tEffect.gen(function* () {\n\t\t\tconst path = yield* Path;\n\t\t\tconst sessionsDir = path.join(rootDir, '.data', 'sessions');\n\t\t\tconst core = yield* buildDevSessionsCore(sessionsDir);\n\t\t\treturn {\n\t\t\t\tdir: rootDir,\n\t\t\t\tpath: (relativePath: string) => path.join(rootDir, relativePath),\n\t\t\t\t...core,\n\t\t\t};\n\t\t}),\n\t);\n","import { watch as fsWatch, realpathSync } from 'node:fs';\nimport { createRequire } from 'node:module';\nimport { dirname, join, resolve } from 'node:path';\nimport { Deferred, Effect } from 'effect';\nimport { FileSystem } from 'effect/FileSystem';\nimport { Path } from 'effect/Path';\nimport { DevSessions } from '../dev-sessions';\n\nconst RUNNING_SIGNAL_FILE = '.data/running.json';\n\n/** Path to the running-signal file for a dev-session (or sibling package) directory. */\nexport const runningSignalPath = (dir: string) =>\n\tjoin(dir, RUNNING_SIGNAL_FILE);\n\n/**\n * Resolves `spec` to a directory: relative/absolute paths are resolved against\n * `fromDir`, anything else is treated as a package name and resolved via its\n * `package.json`.\n */\nexport const resolveSiblingDir = (spec: string, fromDir: string) => {\n\tif (spec.startsWith('.') || spec.startsWith('/')) {\n\t\treturn resolve(fromDir, spec);\n\t}\n\tconst require = createRequire(join(fromDir, 'package.json'));\n\tconst pkgJsonPath = require.resolve(`${spec}/package.json`);\n\treturn dirname(realpathSync(pkgJsonPath));\n};\n\n/** Atomically writes `value` as JSON to `filePath` and removes it on release. */\nexport const publishRunningSignal = (filePath: string, value: unknown) =>\n\tEffect.acquireRelease(\n\t\tEffect.gen(function* () {\n\t\t\tconst fs = yield* FileSystem;\n\t\t\tconst path = yield* Path;\n\t\t\tconst tmp = `${filePath}.tmp`;\n\t\t\tyield* fs.makeDirectory(path.dirname(filePath), { recursive: true });\n\t\t\t// Write to a temp file then rename atomically so readers never see a partial file.\n\t\t\tyield* fs.writeFileString(tmp, JSON.stringify(value));\n\t\t\tyield* fs.rename(tmp, filePath);\n\t\t}),\n\t\t() =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst fs = yield* FileSystem;\n\t\t\t\tyield* fs.remove(filePath).pipe(Effect.catch(() => Effect.void));\n\t\t\t}),\n\t);\n\n/** Watches parent directory for the signal file and resolves once it can be parsed. */\nexport const awaitRunningSignal = <T>(\n\tfilePath: string,\n\topts: { parse: (raw: string) => T },\n) =>\n\tEffect.scoped(\n\t\tEffect.gen(function* () {\n\t\t\tconst fs = yield* FileSystem;\n\t\t\tconst path = yield* Path;\n\t\t\tconst dir = path.dirname(filePath);\n\t\t\tconst filename = path.basename(filePath);\n\n\t\t\tconst deferred = yield* Deferred.make<T>();\n\n\t\t\tconst tryRead = Effect.gen(function* () {\n\t\t\t\tif (!(yield* fs.exists(filePath))) return false;\n\t\t\t\tconst raw = yield* fs\n\t\t\t\t\t.readFileString(filePath)\n\t\t\t\t\t.pipe(Effect.catch(() => Effect.succeed(null)));\n\t\t\t\tif (raw == null) return false;\n\t\t\t\tconst parsed = yield* Effect.try(() => opts.parse(raw)).pipe(\n\t\t\t\t\tEffect.catch(() => Effect.succeed(null)),\n\t\t\t\t);\n\t\t\t\tif (parsed == null) return false;\n\t\t\t\tyield* Deferred.succeed(deferred, parsed);\n\t\t\t\treturn true;\n\t\t\t});\n\n\t\t\t// fsWatch throws synchronously if `dir` doesn't exist yet — e.g. a sibling\n\t\t\t// package's dev script has never run — so ensure it's there first.\n\t\t\tyield* fs.makeDirectory(dir, { recursive: true });\n\n\t\t\tyield* Effect.acquireRelease(\n\t\t\t\tEffect.sync(() =>\n\t\t\t\t\t// Watch parent dir — the file may not exist yet, and watching a missing file errors.\n\t\t\t\t\tfsWatch(dir, { recursive: false }, (eventType, eventFilename) => {\n\t\t\t\t\t\tif (eventFilename !== filename) return;\n\t\t\t\t\t\tif (eventType !== 'rename' && eventType !== 'change') return;\n\t\t\t\t\t\tEffect.runFork(tryRead);\n\t\t\t\t\t}),\n\t\t\t\t),\n\t\t\t\t(watcher) => Effect.sync(() => watcher.close()),\n\t\t\t);\n\n\t\t\tyield* tryRead;\n\t\t\treturn yield* Deferred.await(deferred);\n\t\t}),\n\t);\n\n/**\n * Publishes `value` at the project root's running-signal file, so a sibling package's\n * dev script can `awaitRunning` it. Removed when the enclosing scope closes.\n */\nexport const publishRunning = (value: unknown) =>\n\tEffect.gen(function* () {\n\t\tconst sessions = yield* DevSessions;\n\t\tyield* publishRunningSignal(runningSignalPath(sessions.dir), value);\n\t});\n\n/**\n * Awaits the running-signal file of another package in the workspace (`pkg`, resolved\n * relative to the project root the same way `resolveSiblingDir` resolves any spec).\n */\nexport const awaitRunning = <T>(pkg: string) =>\n\tEffect.gen(function* () {\n\t\tconst sessions = yield* DevSessions;\n\t\treturn yield* awaitRunningSignal<T>(\n\t\t\trunningSignalPath(resolveSiblingDir(pkg, sessions.dir)),\n\t\t\t{ parse: (raw) => JSON.parse(raw) as T },\n\t\t);\n\t});\n","import { Data, Effect } from 'effect';\nimport { FileSystem } from 'effect/FileSystem';\nimport { Path } from 'effect/Path';\nimport * as S from 'effect/Schema';\nimport type { DevSession } from '../dev-sessions';\n\nexport class SessionStateError extends Data.TaggedError('SessionStateError')<{\n\tmessage: string;\n\tcause?: unknown;\n}> {}\n\nfunction getPath(session: DevSession) {\n\treturn session.path('sess.json');\n}\n\nfunction read(filePath: string) {\n\treturn Effect.gen(function* () {\n\t\tconst fs = yield* FileSystem;\n\t\tif (!(yield* fs.exists(filePath))) {\n\t\t\treturn null;\n\t\t}\n\t\treturn yield* fs.readFileString(filePath);\n\t});\n}\n\nexport namespace SessionState {\n\ttype Shape = S.Top;\n\n\texport function slot<T extends Shape>(schema: T) {\n\t\treturn {\n\t\t\tread: (session: DevSession) =>\n\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\tconst filePath = yield* getPath(session);\n\t\t\t\t\tconst content = yield* read(filePath);\n\t\t\t\t\tif (content == null) return null;\n\t\t\t\t\treturn yield* S.decodeEffect(S.fromJsonString(schema))(content).pipe(\n\t\t\t\t\t\tEffect.catchTag('SchemaError', () => Effect.succeed(null)),\n\t\t\t\t\t);\n\t\t\t\t}),\n\t\t\twrite: (session: DevSession, data: T['Type']) =>\n\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\tconst fs = yield* FileSystem;\n\t\t\t\t\tconst path = yield* Path;\n\t\t\t\t\tconst filePath = yield* getPath(session);\n\t\t\t\t\tyield* fs.makeDirectory(path.dirname(filePath), { recursive: true });\n\t\t\t\t\tconst existing = yield* read(filePath);\n\t\t\t\t\tconst parsed = yield* Effect.try({\n\t\t\t\t\t\ttry: () => JSON.parse(existing ?? '{}') as Record<string, unknown>,\n\t\t\t\t\t\tcatch: (cause) =>\n\t\t\t\t\t\t\tnew SessionStateError({\n\t\t\t\t\t\t\t\tmessage: `Failed to parse ${filePath} as JSON`,\n\t\t\t\t\t\t\t\tcause,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t});\n\t\t\t\t\tconst merged = { ...parsed, ...(data as Record<string, unknown>) };\n\t\t\t\t\tyield* fs.writeFileString(filePath, JSON.stringify(merged));\n\t\t\t\t}),\n\t\t};\n\t}\n}\n","import { Effect } from 'effect';\nimport * as S from 'effect/Schema';\nimport getPort from 'get-port';\nimport type { DevSession } from '../dev-sessions';\nimport { SessionState } from './session-state';\n\nconst StickyPort = SessionState.slot(S.Struct({ port: S.Number }));\n\nexport const getStickyPort = (session: DevSession) =>\n\tEffect.gen(function* () {\n\t\tconst file = yield* StickyPort.read(session);\n\t\tconst preferred = file?.port;\n\n\t\tconst port = yield* Effect.promise(async () =>\n\t\t\tgetPort({\n\t\t\t\tport: preferred !== undefined ? [preferred] : undefined,\n\t\t\t}),\n\t\t);\n\n\t\tyield* StickyPort.write(session, { port });\n\n\t\treturn port;\n\t});\n","import { Effect } from 'effect';\nimport { ChildProcess } from 'effect/unstable/process';\n\nexport const runManagedSubprocess = (\n\tcmd: string,\n\targs: string[],\n\topts?: { env?: Record<string, string> },\n) =>\n\tEffect.gen(function* () {\n\t\tconst command = ChildProcess.make(cmd, args, {\n\t\t\tstdin: 'inherit',\n\t\t\tstdout: 'inherit',\n\t\t\tstderr: 'inherit',\n\t\t\tenv: { ...process.env, ...(opts?.env ?? {}) },\n\t\t});\n\n\t\tconst label = [cmd, ...args].join(' ');\n\n\t\t// Registers against the ambient scope, not a scope of its own — the\n\t\t// child must outlive this call (it doesn't resolve until the child\n\t\t// exits) and get killed only when the caller's scope closes.\n\t\tconst child = yield* Effect.acquireRelease(\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst proc = yield* command;\n\t\t\t\tyield* Effect.logInfo(`[dev] started: ${label} (pid=${proc.pid})`);\n\t\t\t\treturn proc;\n\t\t\t}),\n\t\t\t(proc) =>\n\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\tyield* Effect.logInfo(`[dev] stopping: ${label} (pid=${proc.pid})`);\n\t\t\t\t\tyield* proc\n\t\t\t\t\t\t.kill()\n\t\t\t\t\t\t.pipe(\n\t\t\t\t\t\t\tEffect.catch((err) =>\n\t\t\t\t\t\t\t\tEffect.logError(\n\t\t\t\t\t\t\t\t\t`[dev] failed to stop ${label} (pid=${proc.pid}): ${err}`,\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t);\n\t\t\t\t}),\n\t\t);\n\n\t\treturn yield* child.exitCode;\n\t});\n"],"mappings":"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","names":["DevSession","Effect","FileSystem","Path","require","Effect","FileSystem","Path","Data","Effect","FileSystem","Path","SessionState","Effect","S","Effect","Effect"]}