devsess 0.3.4 → 0.5.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 (69) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +3 -3
  3. package/dist/async/index.d.ts +1 -1
  4. package/dist/async/index.d.ts.map +1 -1
  5. package/dist/async/index.js +9 -7
  6. package/dist/async/index.js.map +1 -1
  7. package/dist/async/platform.d.ts +2 -2
  8. package/dist/async/platform.d.ts.map +1 -1
  9. package/dist/async/run-dev-cli.d.ts +1 -1
  10. package/dist/async/run-dev-cli.d.ts.map +1 -1
  11. package/dist/chunk-KG24ZAKO.js +166 -0
  12. package/dist/chunk-KG24ZAKO.js.map +1 -0
  13. package/dist/{chunk-ULIVG5ID.js → chunk-MBPIOYW3.js} +166 -233
  14. package/dist/chunk-MBPIOYW3.js.map +1 -0
  15. package/dist/chunk-OODLZZCC.js +287 -0
  16. package/dist/chunk-OODLZZCC.js.map +1 -0
  17. package/dist/chunk-UOIMVHGX.js +29 -0
  18. package/dist/chunk-UOIMVHGX.js.map +1 -0
  19. package/dist/current-session.d.ts +1 -0
  20. package/dist/current-session.d.ts.map +1 -1
  21. package/dist/dev/daemon-client.d.ts +21 -0
  22. package/dist/dev/daemon-client.d.ts.map +1 -0
  23. package/dist/dev/daemon-readiness.d.ts.map +1 -1
  24. package/dist/dev/daemon-services.d.ts +3 -0
  25. package/dist/dev/daemon-services.d.ts.map +1 -0
  26. package/dist/dev/sticky-port.d.ts +4 -1
  27. package/dist/dev/sticky-port.d.ts.map +1 -1
  28. package/dist/dev/subprocess.d.ts +1 -1
  29. package/dist/dev/subprocess.d.ts.map +1 -1
  30. package/dist/dev-sessions.d.ts +2 -0
  31. package/dist/dev-sessions.d.ts.map +1 -1
  32. package/dist/index.js +10 -26
  33. package/dist/index.js.map +1 -1
  34. package/dist/lock.d.ts +3 -0
  35. package/dist/lock.d.ts.map +1 -0
  36. package/dist/pglite/index.d.ts +2 -2
  37. package/dist/pglite/index.d.ts.map +1 -1
  38. package/dist/pglite/index.js +20 -30
  39. package/dist/pglite/index.js.map +1 -1
  40. package/dist/services/container.d.ts +17 -0
  41. package/dist/services/container.d.ts.map +1 -0
  42. package/dist/services/core.d.ts +30 -0
  43. package/dist/services/core.d.ts.map +1 -0
  44. package/dist/services/docker-args.d.ts +31 -0
  45. package/dist/services/docker-args.d.ts.map +1 -0
  46. package/dist/services/index.d.ts +50 -0
  47. package/dist/services/index.d.ts.map +1 -0
  48. package/dist/services/index.js +379 -0
  49. package/dist/services/index.js.map +1 -0
  50. package/dist/services/local.d.ts +11 -0
  51. package/dist/services/local.d.ts.map +1 -0
  52. package/dist/services/shared/callsite.d.ts +4 -0
  53. package/dist/services/shared/callsite.d.ts.map +1 -0
  54. package/dist/services/shared/entry.d.ts +2 -0
  55. package/dist/services/shared/entry.d.ts.map +1 -0
  56. package/dist/services/shared/entry.js +137 -0
  57. package/dist/services/shared/entry.js.map +1 -0
  58. package/dist/services/shared/host.d.ts +5 -0
  59. package/dist/services/shared/host.d.ts.map +1 -0
  60. package/dist/services/shared/index.d.ts +11 -0
  61. package/dist/services/shared/index.d.ts.map +1 -0
  62. package/dist/services/shared/protocol.d.ts +39 -0
  63. package/dist/services/shared/protocol.d.ts.map +1 -0
  64. package/dist/services/shared/root.d.ts +5 -0
  65. package/dist/services/shared/root.d.ts.map +1 -0
  66. package/dist/socket-line.d.ts +3 -0
  67. package/dist/socket-line.d.ts.map +1 -0
  68. package/package.json +94 -88
  69. package/dist/chunk-ULIVG5ID.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/services/index.ts","../../src/services/container.ts","../../src/services/docker-args.ts","../../src/services/shared/index.ts","../../src/services/shared/callsite.ts","../../src/services/shared/root.ts"],"sourcesContent":["import { Context, Effect, Layer } from 'effect';\nimport type { FileSystem } from 'effect/FileSystem';\nimport type { Path } from 'effect/Path';\nimport type { Scope } from 'effect/Scope';\nimport { CurrentSession } from '../current-session';\nimport type { DevSession } from '../dev-sessions';\nimport { type ContainerSpec, startContainer } from './container';\nimport {\n\trunningService,\n\ttype ServiceContext,\n\tServiceError,\n\tserviceBrand,\n} from './core';\nimport { runLocal } from './local';\nimport { runShared, type SharedOptions } from './shared/index';\n\nexport type { Healthcheck } from './docker-args';\nexport { type ServiceContext, ServiceError };\n\nexport type RunningService<Port extends string, A> = (A extends object\n\t? Omit<A, 'ports'>\n\t: unknown) & { readonly ports: Readonly<Record<Port, number>> };\n\ntype ServiceIdentifier<Name extends string> = {\n\treadonly devsessService: Name;\n};\n\nexport type ServiceDefinition<\n\tName extends string,\n\tPort extends string,\n\tA,\n\tE,\n\tR,\n> = {\n\treadonly [serviceBrand]: true;\n\treadonly name: Name;\n\treadonly ports: ReadonlyArray<Port>;\n\treadonly shared?: SharedOptions<A>;\n\treadonly definitionStack?: string;\n\t/** Resolves once the service is ready; its scope closing stops it. */\n\treadonly start: (ctx: ServiceContext<Port>) => Effect.Effect<A, E, R>;\n\t/** Yield inside another service's `start` (or any effect) to depend on this one. */\n\treadonly key: Context.Key<ServiceIdentifier<Name>, RunningService<Port, A>>;\n\t/** Starts the service once per layer build and shares it with every consumer. */\n\treadonly layer: Layer.Layer<\n\t\tServiceIdentifier<Name>,\n\t\tE | ServiceError,\n\t\tExclude<R, Scope> | CurrentSession | FileSystem | Path\n\t>;\n};\n\nconst run = <Name extends string, Port extends string, A, E, R>(\n\tsession: DevSession,\n\tdef: Pick<\n\t\tServiceDefinition<Name, Port, A, E, R>,\n\t\t'name' | 'ports' | 'start' | 'shared' | 'definitionStack'\n\t>,\n) =>\n\t(def.shared !== undefined\n\t\t? runShared<Port, A>(session, def.name, def.shared, def.definitionStack)\n\t\t: runLocal(session, def)\n\t).pipe(Effect.map(runningService<Port, A>));\n\nconst make = <\n\tconst Name extends string,\n\tA,\n\tE,\n\tR,\n\tconst Port extends string = never,\n>(def: {\n\tname: Name;\n\tports?: ReadonlyArray<Port>;\n\tshared?: SharedOptions<A>;\n\tstart: (ctx: ServiceContext<Port>) => Effect.Effect<A, E, R>;\n}): ServiceDefinition<Name, Port, A, E, R> => {\n\tconst base = {\n\t\t[serviceBrand]: true as const,\n\t\tname: def.name,\n\t\tports: def.ports ?? [],\n\t\tstart: def.start,\n\t\tshared: def.shared,\n\t\tdefinitionStack: def.shared === undefined ? undefined : new Error().stack,\n\t};\n\tconst key = Context.Service<ServiceIdentifier<Name>, RunningService<Port, A>>(\n\t\t`devsess/services/${def.name}`,\n\t);\n\tconst layer = Layer.effect(\n\t\tkey,\n\t\tEffect.gen(function* () {\n\t\t\tconst session = yield* CurrentSession;\n\t\t\treturn yield* run(session, base);\n\t\t}),\n\t);\n\treturn { ...base, key, layer };\n};\n\nexport const Service = {\n\t/** Defines a service whose `start` handler you write yourself. */\n\tmake,\n\t/** Defines a service backed by a Docker container, ready once its healthcheck passes. */\n\tcontainer: <const Name extends string, const Port extends string = never>(\n\t\tdef: { name: Name } & ContainerSpec<Port>,\n\t) =>\n\t\tmake({\n\t\t\tname: def.name,\n\t\t\tports: Object.keys(def.ports ?? {}) as Port[],\n\t\t\tstart: startContainer(def.name, def),\n\t\t}),\n\t/** Starts a service in `session`, outside a layer. */\n\trun,\n};\n","import { Effect, Stream } from 'effect';\nimport { FileSystem } from 'effect/FileSystem';\nimport { Path } from 'effect/Path';\nimport { ChildProcess } from 'effect/process';\nimport { DevSessions } from '../dev-sessions';\nimport {\n\ttype ServiceContext,\n\tServiceError,\n\tvalidName,\n\twithServiceError,\n} from './core';\nimport { buildRunArgs, type Healthcheck, selectOrphans } from './docker-args';\n\nexport type ContainerSpec<Port extends string> = {\n\timage: string;\n\tports?: Record<Port, number>;\n\tvolumes?: Record<string, string>;\n\tenv?: Record<string, string>;\n\thealthcheck?: Healthcheck;\n};\n\nconst docker = (\n\targs: string[],\n\toptions?: { progress?: boolean; allowFailure?: boolean },\n) =>\n\tEffect.gen(function* () {\n\t\tconst command = ChildProcess.make('docker', args, {\n\t\t\tstdout: options?.progress ? 'inherit' : 'pipe',\n\t\t\tstderr: 'inherit',\n\t\t});\n\t\tconst result = yield* Effect.scoped(\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst child = yield* command;\n\t\t\t\tconst output = options?.progress\n\t\t\t\t\t? undefined\n\t\t\t\t\t: (yield* Stream.mkString(Stream.decodeText(child.stdout))).trim();\n\t\t\t\tconst code = yield* child.exitCode;\n\t\t\t\treturn { output, code };\n\t\t\t}),\n\t\t).pipe(\n\t\t\tEffect.mapError(\n\t\t\t\t(cause) =>\n\t\t\t\t\tnew ServiceError({\n\t\t\t\t\t\tmessage: `Docker command failed: docker ${args.join(' ')}`,\n\t\t\t\t\t\tcause,\n\t\t\t\t\t}),\n\t\t\t),\n\t\t);\n\t\tif (result.code !== 0 && !options?.allowFailure) {\n\t\t\treturn yield* new ServiceError({\n\t\t\t\tmessage: `Docker command failed (exit ${result.code}): docker ${args.join(' ')}`,\n\t\t\t});\n\t\t}\n\t\treturn result;\n\t});\n\nconst listContainers = (root: string) =>\n\tEffect.gen(function* () {\n\t\tconst listed = yield* docker([\n\t\t\t'ps',\n\t\t\t'-a',\n\t\t\t'--filter',\n\t\t\t`label=devsess.root=${root}`,\n\t\t\t'--format',\n\t\t\t'{{.ID}}\\t{{.Label \"devsess.session\"}}',\n\t\t]);\n\t\tif (listed.output === undefined)\n\t\t\treturn yield* new ServiceError({\n\t\t\t\tmessage: 'Docker container listing returned no output',\n\t\t\t});\n\t\tconst containers: Array<{ id: string; session: string }> = [];\n\t\tfor (const line of listed.output.split('\\n').filter(Boolean)) {\n\t\t\tconst fields = line.split('\\t');\n\t\t\tconst id = fields[0];\n\t\t\tconst session = fields[1];\n\t\t\tif (!id || !session)\n\t\t\t\treturn yield* new ServiceError({\n\t\t\t\t\tmessage: `Invalid devsess container metadata: ${line}`,\n\t\t\t\t});\n\t\t\tcontainers.push({ id, session });\n\t\t}\n\t\treturn containers;\n\t});\n\nconst waitHealthy = (name: string, container: string) =>\n\tEffect.gen(function* () {\n\t\twhile (true) {\n\t\t\tconst state = yield* docker([\n\t\t\t\t'inspect',\n\t\t\t\t'--format',\n\t\t\t\t'{{.State.Running}} {{.State.Health.Status}}',\n\t\t\t\tcontainer,\n\t\t\t]);\n\t\t\tif (state.output === undefined)\n\t\t\t\treturn yield* new ServiceError({\n\t\t\t\t\tmessage: `Docker returned no status for ${container}`,\n\t\t\t\t});\n\t\t\tif (state.output === 'true healthy') return;\n\t\t\tif (state.output.startsWith('false '))\n\t\t\t\treturn yield* new ServiceError({\n\t\t\t\t\tmessage: `Service ${name} exited before healthy; services run as your user; this image may require root`,\n\t\t\t\t});\n\t\t\tif (state.output === 'true unhealthy')\n\t\t\t\treturn yield* new ServiceError({\n\t\t\t\t\tmessage: `Service ${name} is unhealthy`,\n\t\t\t\t});\n\t\t\tyield* Effect.sleep('500 millis');\n\t\t}\n\t});\n\nexport const startContainer =\n\t<Port extends string>(name: string, spec: ContainerSpec<Port>) =>\n\t(ctx: ServiceContext<Port>) =>\n\t\tEffect.gen(function* () {\n\t\t\tconst fs = yield* FileSystem;\n\t\t\tconst path = yield* Path;\n\t\t\tconst sessions = yield* DevSessions;\n\n\t\t\tconst known = new Set(\n\t\t\t\t(yield* sessions.getSessions).map((existing) => existing.name),\n\t\t\t);\n\t\t\tconst containers = yield* listContainers(sessions.dir);\n\t\t\tfor (const id of selectOrphans(containers, known))\n\t\t\t\tyield* docker(['rm', '-f', id]);\n\n\t\t\tconst container = `devsess-${ctx.session.name}-${name}`;\n\t\t\tconst portBindings = Object.entries<number>(spec.ports ?? {}).map(\n\t\t\t\t(entry) => ({\n\t\t\t\t\thost: ctx.ports[entry[0] as Port],\n\t\t\t\t\tcontainer: entry[1],\n\t\t\t\t}),\n\t\t\t);\n\t\t\tconst volumes: Array<{ source: string; target: string }> = [];\n\t\t\tfor (const entry of Object.entries(spec.volumes ?? {})) {\n\t\t\t\tif (!validName(entry[0]))\n\t\t\t\t\treturn yield* new ServiceError({\n\t\t\t\t\t\tmessage: `Invalid volume name: ${entry[0]}`,\n\t\t\t\t\t});\n\t\t\t\tvolumes.push({\n\t\t\t\t\tsource: path.join(ctx.dataDir, entry[0]),\n\t\t\t\t\ttarget: entry[1],\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst start = Effect.gen(function* () {\n\t\t\t\tconst found = yield* docker(\n\t\t\t\t\t[\n\t\t\t\t\t\t'container',\n\t\t\t\t\t\t'inspect',\n\t\t\t\t\t\t'--format',\n\t\t\t\t\t\t'{{.State.Running}}\\t{{index .Config.Labels \"devsess.root\"}}\\t{{index .Config.Labels \"devsess.session\"}}',\n\t\t\t\t\t\tcontainer,\n\t\t\t\t\t],\n\t\t\t\t\t{ allowFailure: true },\n\t\t\t\t);\n\t\t\t\tconst identity = found.output?.split('\\t');\n\t\t\t\tif (\n\t\t\t\t\tfound.code === 0 &&\n\t\t\t\t\t(identity?.[1] !== sessions.dir || identity[2] !== ctx.session.name)\n\t\t\t\t)\n\t\t\t\t\treturn yield* new ServiceError({\n\t\t\t\t\t\tmessage: `Container name ${container} belongs to another session or project`,\n\t\t\t\t\t});\n\t\t\t\tif (found.code === 0 && identity?.[0] === 'true') return;\n\t\t\t\tif (found.code === 0) yield* docker(['rm', '-f', container]);\n\t\t\t\tif (!process.getuid || !process.getgid)\n\t\t\t\t\treturn yield* new ServiceError({\n\t\t\t\t\t\tmessage: 'Docker services require a POSIX user and group ID',\n\t\t\t\t\t});\n\t\t\t\tfor (const volume of volumes)\n\t\t\t\t\tyield* fs.makeDirectory(volume.source, { recursive: true });\n\t\t\t\tconst image = yield* docker(\n\t\t\t\t\t['image', 'inspect', '--format', '{{.Id}}', spec.image],\n\t\t\t\t\t{ allowFailure: true },\n\t\t\t\t);\n\t\t\t\tif (image.code !== 0) {\n\t\t\t\t\tyield* Effect.logInfo(`[dev] pulling ${spec.image}`);\n\t\t\t\t\tyield* docker(['pull', spec.image], { progress: true });\n\t\t\t\t}\n\t\t\t\tyield* docker(\n\t\t\t\t\tbuildRunArgs({\n\t\t\t\t\t\timage: spec.image,\n\t\t\t\t\t\tcontainer,\n\t\t\t\t\t\troot: sessions.dir,\n\t\t\t\t\t\tsession: ctx.session.name,\n\t\t\t\t\t\tuid: process.getuid(),\n\t\t\t\t\t\tgid: process.getgid(),\n\t\t\t\t\t\tports: portBindings,\n\t\t\t\t\t\tvolumes,\n\t\t\t\t\t\tenv: spec.env ?? {},\n\t\t\t\t\t\thealthcheck: spec.healthcheck,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t});\n\t\t\tyield* Effect.acquireRelease(start, () =>\n\t\t\t\tdocker(['rm', '-f', container]).pipe(\n\t\t\t\t\tEffect.catch((error) =>\n\t\t\t\t\t\tEffect.logError(`[dev] failed to remove ${container}: ${error}`),\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t\t);\n\t\t\tif (spec.healthcheck) yield* waitHealthy(name, container);\n\t\t\treturn { container };\n\t\t}).pipe(withServiceError(`Failed to start container for service ${name}`));\n","import { Duration } from 'effect';\n\nexport type Healthcheck = {\n\ttest: readonly string[];\n\tinterval?: Duration.Input;\n\ttimeout?: Duration.Input;\n\tretries?: number;\n\tstartPeriod?: Duration.Input;\n};\n\nconst dockerDuration = (input: Duration.Input) =>\n\t`${Duration.toMillis(input)}ms`;\n\nconst shellQuote = (part: string) => `'${part.replaceAll(\"'\", \"'\\\\''\")}'`;\n\n// `docker run --health-cmd` always goes through a shell, unlike compose's exec-form `CMD`.\nconst healthCmd = (test: readonly string[]) => {\n\tif (test[0] === 'CMD-SHELL') return test.slice(1).join(' ');\n\tif (test[0] === 'CMD') return test.slice(1).map(shellQuote).join(' ');\n\treturn test.join(' ');\n};\n\nexport const buildRunArgs = (input: {\n\timage: string;\n\tcontainer: string;\n\troot: string;\n\tsession: string;\n\tuid: number;\n\tgid: number;\n\tports: ReadonlyArray<{ host: number; container: number }>;\n\tvolumes: ReadonlyArray<{ source: string; target: string }>;\n\tenv: Record<string, string>;\n\thealthcheck: Healthcheck | undefined;\n}): string[] => {\n\tconst args = [\n\t\t'run',\n\t\t'-d',\n\t\t'--name',\n\t\tinput.container,\n\t\t'--label',\n\t\t`devsess.session=${input.session}`,\n\t\t'--label',\n\t\t`devsess.root=${input.root}`,\n\t\t'--user',\n\t\t`${input.uid}:${input.gid}`,\n\t];\n\tfor (const port of input.ports)\n\t\targs.push('-p', `127.0.0.1:${port.host}:${port.container}`);\n\tfor (const volume of input.volumes)\n\t\targs.push('-v', `${volume.source}:${volume.target}`);\n\tfor (const entry of Object.entries(input.env))\n\t\targs.push('-e', `${entry[0]}=${entry[1]}`);\n\tconst healthcheck = input.healthcheck;\n\tif (healthcheck) {\n\t\targs.push('--health-cmd', healthCmd(healthcheck.test));\n\t\tif (healthcheck.interval !== undefined)\n\t\t\targs.push('--health-interval', dockerDuration(healthcheck.interval));\n\t\tif (healthcheck.timeout !== undefined)\n\t\t\targs.push('--health-timeout', dockerDuration(healthcheck.timeout));\n\t\tif (healthcheck.retries !== undefined)\n\t\t\targs.push('--health-retries', String(healthcheck.retries));\n\t\tif (healthcheck.startPeriod !== undefined)\n\t\t\targs.push(\n\t\t\t\t'--health-start-period',\n\t\t\t\tdockerDuration(healthcheck.startPeriod),\n\t\t\t);\n\t}\n\targs.push(input.image);\n\treturn args;\n};\n\nexport const selectOrphans = (\n\tcontainers: ReadonlyArray<{ id: string; session: string }>,\n\texistingSessions: ReadonlySet<string>,\n) =>\n\tcontainers\n\t\t.filter((container) => !existingSessions.has(container.session))\n\t\t.map((container) => container.id);\n","import { Effect, Schema } from 'effect';\nimport { reportDaemonService } from '../../dev/daemon-services';\nimport { type DevSession, DevSessions } from '../../dev-sessions';\nimport { ServiceError, validName, withServiceError } from '../core';\nimport { definitionModule } from './callsite';\nimport {\n\tacquireHost,\n\tfailWhenHostExits,\n\thostPaths,\n\tlaunchHost,\n\treadyMessage,\n} from './protocol';\nimport { findSharedRoot } from './root';\n\nexport type SharedOptions<A> = { readonly output: Schema.Codec<A, unknown> };\nconst locateHost = (\n\tsession: DevSession,\n\tname: string,\n\tstack: string | undefined,\n) =>\n\tEffect.gen(function* () {\n\t\tconst module = yield* definitionModule(stack);\n\t\tconst root = yield* findSharedRoot(session.rootDir);\n\t\tconst sharedSession = yield* DevSessions.use((store) =>\n\t\t\tstore.getOrCreate('shared-services'),\n\t\t).pipe(Effect.provide(DevSessions.layerAt(root)));\n\t\tconst paths = hostPaths(yield* sharedSession.path(''), name);\n\t\treturn { paths, args: { module, name, root, session: sharedSession.name } };\n\t});\n\nexport const runShared = <Port extends string, A>(\n\tsession: DevSession,\n\tname: string,\n\toptions: SharedOptions<A>,\n\tstack: string | undefined,\n) =>\n\tEffect.gen(function* () {\n\t\tif (!validName(name) || !validName(session.name))\n\t\t\treturn yield* new ServiceError({\n\t\t\t\tmessage: 'Invalid shared service or session name',\n\t\t\t});\n\t\tconst host = yield* locateHost(session, name, stack);\n\t\tconst lease = yield* Effect.acquireRelease(\n\t\t\tacquireHost(host.paths, () => launchHost(host.paths, host.args)),\n\t\t\t(value) => Effect.sync(() => value.socket.destroy()),\n\t\t);\n\t\tyield* Effect.forkScoped(\n\t\t\tfailWhenHostExits(lease, name).pipe(\n\t\t\t\tEffect.catch((error) => Effect.logError(error)),\n\t\t\t),\n\t\t);\n\t\tconst ready = yield* Schema.decodeUnknownEffect(\n\t\t\treadyMessage(options.output),\n\t\t)(lease.line).pipe(withServiceError(`Invalid shared output from ${name}`));\n\t\tyield* reportDaemonService(name, ready.ports);\n\t\treturn {\n\t\t\tvalue: ready.output,\n\t\t\tports: ready.ports as Readonly<Record<Port, number>>,\n\t\t};\n\t}).pipe(withServiceError(`Failed to run shared service ${name}`));\n","import { resolve } from 'node:path';\nimport { fileURLToPath, pathToFileURL } from 'node:url';\nimport { Effect } from 'effect';\nimport { ServiceError } from '../core';\n\nconst packageDir = fileURLToPath(import.meta.url).match(\n\t/^(.*)\\/(?:src|dist)\\//,\n)?.[1];\n\nexport const definitionModule = (stack: string | undefined) =>\n\tEffect.gen(function* () {\n\t\tif (packageDir === undefined)\n\t\t\treturn yield* new ServiceError({\n\t\t\t\tmessage: 'Unable to identify devsess internal stack frames',\n\t\t\t});\n\t\tif (stack !== undefined) {\n\t\t\tfor (const frame of stack.split('\\n').slice(1)) {\n\t\t\t\tconst match = frame.match(\n\t\t\t\t\t/(?:\\(|\\s|@)((?:file:\\/\\/|\\/)[^\\n]*?):\\d+:\\d+\\)?$/,\n\t\t\t\t);\n\t\t\t\tconst location = match?.[1];\n\t\t\t\tif (location === undefined) continue;\n\t\t\t\tconst file = yield* Effect.try({\n\t\t\t\t\ttry: () =>\n\t\t\t\t\t\tlocation.startsWith('file://')\n\t\t\t\t\t\t\t? fileURLToPath(location)\n\t\t\t\t\t\t\t: resolve(location),\n\t\t\t\t\tcatch: (cause) =>\n\t\t\t\t\t\tnew ServiceError({\n\t\t\t\t\t\t\tmessage: 'Invalid service callsite path',\n\t\t\t\t\t\t\tcause,\n\t\t\t\t\t\t}),\n\t\t\t\t});\n\t\t\t\tif (\n\t\t\t\t\tfile.startsWith(`${packageDir}/src/`) ||\n\t\t\t\t\tfile.startsWith(`${packageDir}/dist/`)\n\t\t\t\t)\n\t\t\t\t\tcontinue;\n\t\t\t\treturn yield* Effect.try({\n\t\t\t\t\ttry: () => pathToFileURL(file).href,\n\t\t\t\t\tcatch: (cause) =>\n\t\t\t\t\t\tnew ServiceError({ message: 'Invalid service module URL', cause }),\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\treturn yield* new ServiceError({\n\t\t\tmessage:\n\t\t\t\t'Unable to locate the file that calls Service.make for a shared service',\n\t\t});\n\t});\n","import { Effect } from 'effect';\nimport { FileSystem } from 'effect/FileSystem';\nimport { Path } from 'effect/Path';\n\nexport const findSharedRoot = (projectDir: string) =>\n\tEffect.gen(function* () {\n\t\tconst fs = yield* FileSystem;\n\t\tconst path = yield* Path;\n\t\tconst start = path.resolve(projectDir);\n\t\tconst walk = (\n\t\t\tdir: string,\n\t\t\tcandidate: string,\n\t\t): Effect.Effect<string, import('effect/PlatformError').PlatformError> =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst topmost = yield* fs.exists(path.join(dir, 'package.json'));\n\t\t\t\tconst next = topmost ? dir : candidate;\n\t\t\t\tif (yield* fs.exists(path.join(dir, '.git'))) return next;\n\t\t\t\tconst parent = path.dirname(dir);\n\t\t\t\tif (parent === dir) return start;\n\t\t\t\treturn yield* walk(parent, next);\n\t\t\t});\n\t\treturn yield* walk(start, start);\n\t});\n"],"mappings":"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","names":["Effect","Effect","Effect","Effect","Effect","FileSystem","Path","Effect","Effect"]}
@@ -0,0 +1,11 @@
1
+ import { Effect } from 'effect';
2
+ import { FileSystem } from 'effect/FileSystem';
3
+ import { Path } from 'effect/Path';
4
+ import type { DevSession } from '../dev-sessions';
5
+ import { ServiceError } from './core';
6
+ import type { ServiceDefinition } from './index';
7
+ export declare const runLocal: <Name extends string, Port extends string, A, E, R>(session: DevSession, def: Pick<ServiceDefinition<Name, Port, A, E, R>, 'name' | 'ports' | 'start'>) => Effect.Effect<{
8
+ value: A;
9
+ ports: Record<Port, number>;
10
+ }, E | ServiceError, R | FileSystem | Path | import("effect/Scope").Scope>;
11
+ //# sourceMappingURL=local.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local.d.ts","sourceRoot":"","sources":["../../src/services/local.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAGnC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,YAAY,EAA+B,MAAM,QAAQ,CAAC;AACnE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAyBjD,eAAO,MAAM,QAAQ,GAAI,IAAI,SAAS,MAAM,EAAE,IAAI,SAAS,MAAM,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,WAChE,UAAU,OACd,IAAI,CAAC,iBAAiB,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC;;;0EAW1B,CAAC"}
@@ -0,0 +1,4 @@
1
+ import { Effect } from 'effect';
2
+ import { ServiceError } from '../core';
3
+ export declare const definitionModule: (stack: string | undefined) => Effect.Effect<string, ServiceError, never>;
4
+ //# sourceMappingURL=callsite.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"callsite.d.ts","sourceRoot":"","sources":["../../../src/services/shared/callsite.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAMvC,eAAO,MAAM,gBAAgB,UAAW,MAAM,GAAG,SAAS,+CAwCvD,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=entry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entry.d.ts","sourceRoot":"","sources":["../../../src/services/shared/entry.ts"],"names":[],"mappings":""}
@@ -0,0 +1,137 @@
1
+ import {
2
+ ServiceError,
3
+ hostArguments,
4
+ hostPaths,
5
+ io,
6
+ isServiceDefinition,
7
+ readyMessage,
8
+ runLocal,
9
+ sharedGraceMs,
10
+ socketPath,
11
+ startupLock
12
+ } from "../../chunk-OODLZZCC.js";
13
+ import {
14
+ DevSessions
15
+ } from "../../chunk-MBPIOYW3.js";
16
+
17
+ // src/services/shared/entry.ts
18
+ import { NodeServices } from "@effect/platform-node";
19
+ import { Effect as Effect2, Exit, Schema, Scope } from "effect";
20
+
21
+ // src/services/shared/host.ts
22
+ import { rm } from "fs/promises";
23
+ import { createServer } from "net";
24
+ import { Effect } from "effect";
25
+ var serveHost = (paths, line, stop, graceMs = sharedGraceMs) => Effect.gen(function* () {
26
+ const address = yield* io(
27
+ "Failed to resolve shared host socket",
28
+ () => socketPath(paths)
29
+ );
30
+ return yield* Effect.callback((resume) => {
31
+ const state = {
32
+ consumers: 0,
33
+ stopping: false,
34
+ timer: void 0
35
+ };
36
+ const shutdown = Effect.gen(function* () {
37
+ yield* Effect.acquireRelease(
38
+ startupLock(paths),
39
+ (release) => io("Failed to release shared host lock", release).pipe(Effect.orDie)
40
+ );
41
+ if (state.consumers !== 0) return;
42
+ state.stopping = true;
43
+ server.close();
44
+ yield* io(
45
+ "Failed to remove shared host socket",
46
+ () => rm(paths.socket, { force: true })
47
+ ).pipe(Effect.ensuring(stop));
48
+ }).pipe(
49
+ Effect.scoped,
50
+ Effect.matchCauseEffect({
51
+ onFailure: (cause) => Effect.sync(() => resume(Effect.failCause(cause))),
52
+ onSuccess: () => Effect.sync(() => {
53
+ if (state.stopping) resume(Effect.void);
54
+ })
55
+ })
56
+ );
57
+ const schedule = () => {
58
+ if (state.timer !== void 0) clearTimeout(state.timer);
59
+ state.timer = setTimeout(() => Effect.runFork(shutdown), graceMs);
60
+ };
61
+ const server = createServer((socket) => {
62
+ if (state.stopping) {
63
+ socket.destroy();
64
+ return;
65
+ }
66
+ state.consumers++;
67
+ if (state.timer !== void 0) clearTimeout(state.timer);
68
+ socket.on("error", () => socket.destroy());
69
+ socket.once("close", () => {
70
+ state.consumers--;
71
+ if (state.consumers === 0) schedule();
72
+ });
73
+ socket.write(`${line}
74
+ `);
75
+ });
76
+ server.once("error", (cause) => resume(Effect.die(cause)));
77
+ server.listen(address, schedule);
78
+ return Effect.suspend(
79
+ () => state.stopping ? Effect.void : Effect.sync(() => {
80
+ if (state.timer !== void 0) clearTimeout(state.timer);
81
+ server.close();
82
+ }).pipe(
83
+ Effect.ensuring(
84
+ io(
85
+ "Failed to remove shared host socket",
86
+ () => rm(paths.socket, { force: true })
87
+ ).pipe(Effect.orDie)
88
+ )
89
+ )
90
+ );
91
+ });
92
+ });
93
+
94
+ // src/services/shared/entry.ts
95
+ var program = Effect2.gen(function* () {
96
+ const args = yield* Schema.decodeUnknownEffect(hostArguments)(
97
+ process.argv[2]
98
+ );
99
+ const module = yield* io(
100
+ "Failed to import shared service definition",
101
+ () => import(args.module)
102
+ );
103
+ const candidates = Object.values(module).filter(isServiceDefinition).filter((value) => value.name === args.name);
104
+ const def = candidates[0];
105
+ if (candidates.length !== 1 || def === void 0 || def.shared === void 0)
106
+ return yield* new ServiceError({
107
+ message: `export the shared service \`${args.name}\` from the file that calls Service.make (${args.module}); exactly one matching export is required`
108
+ });
109
+ const scope = yield* Scope.make();
110
+ const output = def.shared.output;
111
+ const session = yield* DevSessions.use(
112
+ (sessions) => sessions.getOrCreate(args.session)
113
+ ).pipe(Effect2.provide(DevSessions.layerAt(args.root)));
114
+ const paths = hostPaths(yield* session.path(""), def.name);
115
+ const close = Scope.close(scope, Exit.void);
116
+ yield* Effect2.gen(function* () {
117
+ const result = yield* runLocal(session, def).pipe(
118
+ Effect2.provideService(Scope.Scope, scope)
119
+ );
120
+ const line = yield* Schema.encodeEffect(readyMessage(output))({
121
+ output: result.value,
122
+ ports: result.ports
123
+ });
124
+ yield* serveHost(paths, line, close);
125
+ }).pipe(Effect2.ensuring(close));
126
+ }).pipe(Effect2.provide(NodeServices.layer));
127
+ var lifetime = Effect2.acquireUseRelease(
128
+ Effect2.sync(() => setInterval(() => {
129
+ }, 1e3)),
130
+ () => program,
131
+ (timer) => Effect2.sync(() => clearInterval(timer))
132
+ );
133
+ Effect2.runPromise(lifetime).catch((cause) => {
134
+ Effect2.runSync(Effect2.logError(cause));
135
+ process.exitCode = 1;
136
+ });
137
+ //# sourceMappingURL=entry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/services/shared/entry.ts","../../../src/services/shared/host.ts"],"sourcesContent":["import { NodeServices } from '@effect/platform-node';\nimport { Effect, Exit, Schema, Scope } from 'effect';\nimport { DevSessions } from '../../dev-sessions';\nimport { isServiceDefinition, ServiceError } from '../core';\nimport { runLocal } from '../local';\nimport { serveHost } from './host';\nimport { hostArguments, hostPaths, io, readyMessage } from './protocol';\n\nconst program = Effect.gen(function* () {\n\tconst args = yield* Schema.decodeUnknownEffect(hostArguments)(\n\t\tprocess.argv[2],\n\t);\n\tconst module = yield* io(\n\t\t'Failed to import shared service definition',\n\t\t() => import(args.module) as Promise<Record<string, unknown>>,\n\t);\n\tconst candidates = Object.values(module)\n\t\t.filter(isServiceDefinition)\n\t\t.filter((value) => value.name === args.name);\n\tconst def = candidates[0];\n\tif (candidates.length !== 1 || def === undefined || def.shared === undefined)\n\t\treturn yield* new ServiceError({\n\t\t\tmessage: `export the shared service \\`${args.name}\\` from the file that calls Service.make (${args.module}); exactly one matching export is required`,\n\t\t});\n\tconst scope = yield* Scope.make();\n\tconst output = def.shared.output;\n\tconst session = yield* DevSessions.use((sessions) =>\n\t\tsessions.getOrCreate(args.session),\n\t).pipe(Effect.provide(DevSessions.layerAt(args.root)));\n\tconst paths = hostPaths(yield* session.path(''), def.name);\n\tconst close = Scope.close(scope, Exit.void);\n\tyield* Effect.gen(function* () {\n\t\tconst result = yield* runLocal(session, def).pipe(\n\t\t\tEffect.provideService(Scope.Scope, scope),\n\t\t);\n\t\tconst line = yield* Schema.encodeEffect(readyMessage(output))({\n\t\t\toutput: result.value,\n\t\t\tports: result.ports,\n\t\t});\n\t\tyield* serveHost(paths, line, close);\n\t}).pipe(Effect.ensuring(close));\n}).pipe(Effect.provide(NodeServices.layer));\n\n/** A pending Promise and unref'd handler resources cannot keep the process alive during scope close. */\nconst lifetime = Effect.acquireUseRelease(\n\tEffect.sync(() => setInterval(() => {}, 1_000)),\n\t() => program,\n\t(timer) => Effect.sync(() => clearInterval(timer)),\n);\n\nEffect.runPromise(lifetime).catch((cause) => {\n\tEffect.runSync(Effect.logError(cause));\n\tprocess.exitCode = 1;\n});\n","import { rm } from 'node:fs/promises';\nimport { createServer } from 'node:net';\nimport { Effect } from 'effect';\nimport type { ServiceError } from '../core';\nimport {\n\ttype HostPaths,\n\tio,\n\tsharedGraceMs,\n\tsocketPath,\n\tstartupLock,\n} from './protocol';\n\nexport const serveHost = (\n\tpaths: HostPaths,\n\tline: string,\n\tstop: Effect.Effect<void>,\n\tgraceMs = sharedGraceMs,\n) =>\n\tEffect.gen(function* () {\n\t\tconst address = yield* io('Failed to resolve shared host socket', () =>\n\t\t\tsocketPath(paths),\n\t\t);\n\t\treturn yield* Effect.callback<void, ServiceError>((resume) => {\n\t\t\tconst state = {\n\t\t\t\tconsumers: 0,\n\t\t\t\tstopping: false,\n\t\t\t\ttimer: undefined as ReturnType<typeof setTimeout> | undefined,\n\t\t\t};\n\t\t\tconst shutdown = Effect.gen(function* () {\n\t\t\t\tyield* Effect.acquireRelease(startupLock(paths), (release) =>\n\t\t\t\t\tio('Failed to release shared host lock', release).pipe(Effect.orDie),\n\t\t\t\t);\n\t\t\t\tif (state.consumers !== 0) return;\n\t\t\t\tstate.stopping = true;\n\t\t\t\tserver.close();\n\t\t\t\tyield* io('Failed to remove shared host socket', () =>\n\t\t\t\t\trm(paths.socket, { force: true }),\n\t\t\t\t).pipe(Effect.ensuring(stop));\n\t\t\t}).pipe(\n\t\t\t\tEffect.scoped,\n\t\t\t\tEffect.matchCauseEffect({\n\t\t\t\t\tonFailure: (cause) =>\n\t\t\t\t\t\tEffect.sync(() => resume(Effect.failCause(cause))),\n\t\t\t\t\tonSuccess: () =>\n\t\t\t\t\t\tEffect.sync(() => {\n\t\t\t\t\t\t\tif (state.stopping) resume(Effect.void);\n\t\t\t\t\t\t}),\n\t\t\t\t}),\n\t\t\t);\n\t\t\tconst schedule = () => {\n\t\t\t\tif (state.timer !== undefined) clearTimeout(state.timer);\n\t\t\t\tstate.timer = setTimeout(() => Effect.runFork(shutdown), graceMs);\n\t\t\t};\n\t\t\tconst server = createServer((socket) => {\n\t\t\t\tif (state.stopping) {\n\t\t\t\t\tsocket.destroy();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tstate.consumers++;\n\t\t\t\tif (state.timer !== undefined) clearTimeout(state.timer);\n\t\t\t\tsocket.on('error', () => socket.destroy());\n\t\t\t\tsocket.once('close', () => {\n\t\t\t\t\tstate.consumers--;\n\t\t\t\t\tif (state.consumers === 0) schedule();\n\t\t\t\t});\n\t\t\t\tsocket.write(`${line}\\n`);\n\t\t\t});\n\t\t\tserver.once('error', (cause) => resume(Effect.die(cause)));\n\t\t\tserver.listen(address, schedule);\n\t\t\treturn Effect.suspend(() =>\n\t\t\t\tstate.stopping\n\t\t\t\t\t? Effect.void\n\t\t\t\t\t: Effect.sync(() => {\n\t\t\t\t\t\t\tif (state.timer !== undefined) clearTimeout(state.timer);\n\t\t\t\t\t\t\tserver.close();\n\t\t\t\t\t\t}).pipe(\n\t\t\t\t\t\t\tEffect.ensuring(\n\t\t\t\t\t\t\t\tio('Failed to remove shared host socket', () =>\n\t\t\t\t\t\t\t\t\trm(paths.socket, { force: true }),\n\t\t\t\t\t\t\t\t).pipe(Effect.orDie),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t),\n\t\t\t);\n\t\t});\n\t});\n"],"mappings":"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","names":["Effect","Effect"]}
@@ -0,0 +1,5 @@
1
+ import { Effect } from 'effect';
2
+ import type { ServiceError } from '../core';
3
+ import { type HostPaths } from './protocol';
4
+ export declare const serveHost: (paths: HostPaths, line: string, stop: Effect.Effect<void>, graceMs?: number) => Effect.Effect<void, ServiceError, never>;
5
+ //# sourceMappingURL=host.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"host.d.ts","sourceRoot":"","sources":["../../../src/services/shared/host.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,EACN,KAAK,SAAS,EAKd,MAAM,YAAY,CAAC;AAEpB,eAAO,MAAM,SAAS,UACd,SAAS,QACV,MAAM,QACN,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,+DAqEvB,CAAC"}
@@ -0,0 +1,11 @@
1
+ import { Effect, Schema } from 'effect';
2
+ import { type DevSession } from '../../dev-sessions';
3
+ import { ServiceError } from '../core';
4
+ export type SharedOptions<A> = {
5
+ readonly output: Schema.Codec<A, unknown>;
6
+ };
7
+ export declare const runShared: <Port extends string, A>(session: DevSession, name: string, options: SharedOptions<A>, stack: string | undefined) => Effect.Effect<{
8
+ value: A;
9
+ ports: Readonly<Record<Port, number>>;
10
+ }, ServiceError, import("effect/FileSystem").FileSystem | import("effect/Path").Path | import("effect/Scope").Scope>;
11
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/services/shared/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAExC,OAAO,EAAE,KAAK,UAAU,EAAe,MAAM,oBAAoB,CAAC;AAClE,OAAO,EAAE,YAAY,EAA+B,MAAM,SAAS,CAAC;AAWpE,MAAM,MAAM,aAAa,CAAC,CAAC,IAAI;IAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,OAAO,CAAC,CAAA;CAAE,CAAC;AAgB7E,eAAO,MAAM,SAAS,GAAI,IAAI,SAAS,MAAM,EAAE,CAAC,WACtC,UAAU,QACb,MAAM,WACH,aAAa,CAAC,CAAC,CAAC,SAClB,MAAM,GAAG,SAAS;;WAuBD,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;oHAEW,CAAC"}
@@ -0,0 +1,39 @@
1
+ import { type ChildProcess } from 'node:child_process';
2
+ import { type Socket } from 'node:net';
3
+ import { Effect, Schema } from 'effect';
4
+ import { ServiceError } from '../core';
5
+ export declare const sharedGraceMs = 5000;
6
+ export declare const hostPaths: (sessionDir: string, name: string) => {
7
+ dir: string;
8
+ socket: string;
9
+ lock: string;
10
+ log: string;
11
+ };
12
+ export type HostPaths = ReturnType<typeof hostPaths>;
13
+ export declare const hostArguments: Schema.fromJsonString<Schema.Struct<{
14
+ readonly module: Schema.String;
15
+ readonly name: Schema.String;
16
+ readonly root: Schema.String;
17
+ readonly session: Schema.String;
18
+ }>>;
19
+ type HostArguments = typeof hostArguments.Type;
20
+ export declare const readyMessage: <A>(output: Schema.Codec<A, unknown>) => Schema.fromJsonString<Schema.Struct<{
21
+ readonly output: Schema.Codec<A, unknown, never, never>;
22
+ readonly ports: Schema.$Record<Schema.String, Schema.Number>;
23
+ }>>;
24
+ export declare const io: <A>(message: string, operation: () => Promise<A>) => Effect.Effect<A, ServiceError, never>;
25
+ export declare const isErrno: (cause: unknown, code: string) => boolean;
26
+ export declare const startupLock: (paths: HostPaths) => Effect.Effect<() => Promise<void>, ServiceError, never>;
27
+ export type Lease = {
28
+ socket: Socket;
29
+ line: string;
30
+ };
31
+ export declare const failWhenHostExits: (lease: Lease, name: string) => Effect.Effect<never, ServiceError, never>;
32
+ /** Unix sockets have tiny pathname limits; a short alias keeps the socket in the host directory. */
33
+ export declare const socketPath: (paths: HostPaths) => Promise<string>;
34
+ export declare const connectHost: (paths: HostPaths, child?: ChildProcess) => Promise<Lease>;
35
+ type Launch = () => Promise<ChildProcess>;
36
+ export declare const acquireHost: (paths: HostPaths, launch: Launch) => Effect.Effect<Lease, ServiceError, never>;
37
+ export declare const launchHost: (paths: HostPaths, args: HostArguments) => Promise<ChildProcess>;
38
+ export {};
39
+ //# sourceMappingURL=protocol.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../../../src/services/shared/protocol.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,YAAY,EAAS,MAAM,oBAAoB,CAAC;AAG9D,OAAO,EAAW,KAAK,MAAM,EAAE,MAAM,UAAU,CAAC;AAIhD,OAAO,EAAE,MAAM,EAAY,MAAM,EAAE,MAAM,QAAQ,CAAC;AAGlD,OAAO,EAAE,YAAY,EAAoB,MAAM,SAAS,CAAC;AAEzD,eAAO,MAAM,aAAa,OAAQ,CAAC;AACnC,eAAO,MAAM,SAAS,eAAgB,MAAM,QAAQ,MAAM;;;;;CAQzD,CAAC;AACF,MAAM,MAAM,SAAS,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC;AACrD,eAAO,MAAM,aAAa;;;;;GAOzB,CAAC;AACF,KAAK,aAAa,GAAG,OAAO,aAAa,CAAC,IAAI,CAAC;AAC/C,eAAO,MAAM,YAAY,GAAI,CAAC,UAAU,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,OAAO,CAAC;;;GAM9D,CAAC;AAEH,eAAO,MAAM,EAAE,GAAI,CAAC,WAAW,MAAM,aAAa,MAAM,OAAO,CAAC,CAAC,CAAC,0CAI/D,CAAC;AACJ,eAAO,MAAM,OAAO,UAAW,OAAO,QAAQ,MAAM,YAIhC,CAAC;AACrB,eAAO,MAAM,WAAW,UAAW,SAAS,4DAG1C,CAAC;AACH,MAAM,MAAM,KAAK,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAErD,eAAO,MAAM,iBAAiB,UAAW,KAAK,QAAQ,MAAM,8CAUzD,CAAC;AAEJ,oGAAoG;AACpG,eAAO,MAAM,UAAU,UAAiB,SAAS,oBAYhD,CAAC;AAEF,eAAO,MAAM,WAAW,UAChB,SAAS,UACR,YAAY,KAClB,OAAO,CAAC,KAAK,CAoBf,CAAC;AAkBF,KAAK,MAAM,GAAG,MAAM,OAAO,CAAC,YAAY,CAAC,CAAC;AAwC1C,eAAO,MAAM,WAAW,UAAW,SAAS,UAAU,MAAM,8CAYzD,CAAC;AACJ,eAAO,MAAM,UAAU,UAAiB,SAAS,QAAQ,aAAa,0BA+BrE,CAAC"}
@@ -0,0 +1,5 @@
1
+ import { Effect } from 'effect';
2
+ import { FileSystem } from 'effect/FileSystem';
3
+ import { Path } from 'effect/Path';
4
+ export declare const findSharedRoot: (projectDir: string) => Effect.Effect<string, import("effect/PlatformError").PlatformError, FileSystem | Path>;
5
+ //# sourceMappingURL=root.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"root.d.ts","sourceRoot":"","sources":["../../../src/services/shared/root.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAEnC,eAAO,MAAM,cAAc,eAAgB,MAAM,2FAkB9C,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { Socket } from 'node:net';
2
+ export declare const readSocketLine: (socket: Socket, maxBytes?: number) => Promise<string>;
3
+ //# sourceMappingURL=socket-line.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"socket-line.d.ts","sourceRoot":"","sources":["../src/socket-line.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAGvC,eAAO,MAAM,cAAc,WAClB,MAAM,wBAEZ,OAAO,CAAC,MAAM,CA8Bd,CAAC"}
package/package.json CHANGED
@@ -1,89 +1,95 @@
1
1
  {
2
- "name": "devsess",
3
- "version": "0.3.4",
4
- "description": "Isolated, managed dev servers — each worktree gets its own session, with its own port and its own database",
5
- "license": "Apache-2.0",
6
- "homepage": "https://github.com/fdarian/devsess#readme",
7
- "repository": {
8
- "type": "git",
9
- "url": "git+https://github.com/fdarian/devsess.git",
10
- "directory": "packages/devsess"
11
- },
12
- "bugs": {
13
- "url": "https://github.com/fdarian/devsess/issues"
14
- },
15
- "type": "module",
16
- "keywords": [
17
- "effect",
18
- "dev",
19
- "cli",
20
- "dev-server",
21
- "pglite",
22
- "drizzle",
23
- "sqlite",
24
- "postgres"
25
- ],
26
- "sideEffects": false,
27
- "files": [
28
- "dist",
29
- "README.md"
30
- ],
31
- "main": "./dist/index.js",
32
- "module": "./dist/index.js",
33
- "types": "./dist/index.d.ts",
34
- "exports": {
35
- ".": {
36
- "types": "./dist/index.d.ts",
37
- "import": "./dist/index.js"
38
- },
39
- "./pglite": {
40
- "types": "./dist/pglite/index.d.ts",
41
- "import": "./dist/pglite/index.js"
42
- },
43
- "./async": {
44
- "types": "./dist/async/index.d.ts",
45
- "import": "./dist/async/index.js"
46
- },
47
- "./package.json": "./package.json"
48
- },
49
- "publishConfig": {
50
- "access": "public",
51
- "provenance": true
52
- },
53
- "scripts": {
54
- "build": "tsup && tsc -p tsconfig.build.json",
55
- "dev": "tsup --watch",
56
- "check:type": "tsc --noEmit",
57
- "check:lint": "biome check",
58
- "test": "vitest run",
59
- "prepack": "bun run build"
60
- },
61
- "dependencies": {
62
- "get-port": "^7.2.0",
63
- "random-word-slugs": "^0.1.7"
64
- },
65
- "peerDependencies": {
66
- "@electric-sql/pglite": ">=0.2.0",
67
- "drizzle-orm": ">=0.30.0",
68
- "effect": "^4.0.0-beta.101"
69
- },
70
- "peerDependenciesMeta": {
71
- "@electric-sql/pglite": {
72
- "optional": true
73
- },
74
- "drizzle-orm": {
75
- "optional": true
76
- }
77
- },
78
- "devDependencies": {
79
- "@devsess/config": "workspace:*",
80
- "@effect/platform-node": "^4.0.0-beta.101",
81
- "@effect/vitest": "^4.0.0-beta.101",
82
- "@electric-sql/pglite": "^0.5.3",
83
- "@types/node": "^22.0.0",
84
- "drizzle-orm": "^0.45.2",
85
- "effect": "^4.0.0-beta.101",
86
- "tsup": "^8.5.1",
87
- "vitest": "^4.1.10"
88
- }
89
- }
2
+ "name": "devsess",
3
+ "version": "0.5.0",
4
+ "description": "Isolated, managed dev servers — each worktree gets its own session, with its own port and its own database",
5
+ "license": "Apache-2.0",
6
+ "homepage": "https://github.com/fdarian/devsess#readme",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/fdarian/devsess.git",
10
+ "directory": "packages/devsess"
11
+ },
12
+ "bugs": {
13
+ "url": "https://github.com/fdarian/devsess/issues"
14
+ },
15
+ "type": "module",
16
+ "keywords": [
17
+ "effect",
18
+ "dev",
19
+ "cli",
20
+ "dev-server",
21
+ "pglite",
22
+ "drizzle",
23
+ "sqlite",
24
+ "postgres"
25
+ ],
26
+ "sideEffects": false,
27
+ "files": [
28
+ "dist",
29
+ "README.md"
30
+ ],
31
+ "main": "./dist/index.js",
32
+ "module": "./dist/index.js",
33
+ "types": "./dist/index.d.ts",
34
+ "exports": {
35
+ ".": {
36
+ "types": "./dist/index.d.ts",
37
+ "import": "./dist/index.js"
38
+ },
39
+ "./pglite": {
40
+ "types": "./dist/pglite/index.d.ts",
41
+ "import": "./dist/pglite/index.js"
42
+ },
43
+ "./services": {
44
+ "types": "./dist/services/index.d.ts",
45
+ "import": "./dist/services/index.js"
46
+ },
47
+ "./async": {
48
+ "types": "./dist/async/index.d.ts",
49
+ "import": "./dist/async/index.js"
50
+ },
51
+ "./package.json": "./package.json"
52
+ },
53
+ "publishConfig": {
54
+ "access": "public",
55
+ "provenance": true
56
+ },
57
+ "dependencies": {
58
+ "@effect/platform-node": "^4.0.0",
59
+ "get-port": "^7.2.0",
60
+ "proper-lockfile": "^4.1.2",
61
+ "random-word-slugs": "^0.1.7"
62
+ },
63
+ "peerDependencies": {
64
+ "@electric-sql/pglite": ">=0.2.0",
65
+ "drizzle-orm": "^1.0.0-rc.5-5935859",
66
+ "effect": "^4.0.0"
67
+ },
68
+ "peerDependenciesMeta": {
69
+ "@electric-sql/pglite": {
70
+ "optional": true
71
+ },
72
+ "drizzle-orm": {
73
+ "optional": true
74
+ }
75
+ },
76
+ "devDependencies": {
77
+ "@devsess/config": "0.0.0",
78
+ "@effect/vitest": "^4.0.0",
79
+ "@electric-sql/pglite": "^0.5.3",
80
+ "@types/node": "^22.0.0",
81
+ "@types/proper-lockfile": "^4.1.4",
82
+ "drizzle-orm": "1.0.0-rc.5-5935859",
83
+ "effect": "^4.0.0",
84
+ "tsup": "^8.5.1",
85
+ "typescript": "^7.0.2",
86
+ "vitest": "^5.0.3"
87
+ },
88
+ "scripts": {
89
+ "build": "tsup && tsc -p tsconfig.build.json",
90
+ "dev": "tsup --watch",
91
+ "check:type": "tsc --noEmit",
92
+ "check:lint": "biome check",
93
+ "test": "vitest run"
94
+ }
95
+ }
@@ -1 +0,0 @@
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\n// The daemon serves requests one at a time, so a readiness report can queue\n// behind slow ones (e.g. `devsess start` polling the run list while it waits).\nconst RESPONSE_TIMEOUT_MS = 5_000;\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\t// Keep children in the caller's group so its supervisor can signal them together.\n\t\t\tdetached: false,\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"]}