devsess 0.4.0 → 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 (51) hide show
  1. package/dist/async/index.js +2 -2
  2. package/dist/{chunk-SX5HBQHG.js → chunk-KG24ZAKO.js} +20 -80
  3. package/dist/chunk-KG24ZAKO.js.map +1 -0
  4. package/dist/{chunk-GX4J6JR3.js → chunk-MBPIOYW3.js} +157 -9
  5. package/dist/chunk-MBPIOYW3.js.map +1 -0
  6. package/dist/chunk-OODLZZCC.js +287 -0
  7. package/dist/chunk-OODLZZCC.js.map +1 -0
  8. package/dist/{chunk-CBRTTFXG.js → chunk-UOIMVHGX.js} +2 -2
  9. package/dist/current-session.d.ts +1 -0
  10. package/dist/current-session.d.ts.map +1 -1
  11. package/dist/dev/daemon-client.d.ts +21 -0
  12. package/dist/dev/daemon-client.d.ts.map +1 -0
  13. package/dist/dev/daemon-readiness.d.ts.map +1 -1
  14. package/dist/dev/daemon-services.d.ts +3 -0
  15. package/dist/dev/daemon-services.d.ts.map +1 -0
  16. package/dist/dev/sticky-port.d.ts +4 -1
  17. package/dist/dev/sticky-port.d.ts.map +1 -1
  18. package/dist/dev-sessions.d.ts +2 -0
  19. package/dist/dev-sessions.d.ts.map +1 -1
  20. package/dist/index.js +3 -3
  21. package/dist/lock.d.ts +3 -0
  22. package/dist/lock.d.ts.map +1 -0
  23. package/dist/services/container.d.ts.map +1 -1
  24. package/dist/services/core.d.ts +12 -0
  25. package/dist/services/core.d.ts.map +1 -1
  26. package/dist/services/index.d.ts +9 -4
  27. package/dist/services/index.d.ts.map +1 -1
  28. package/dist/services/index.js +119 -52
  29. package/dist/services/index.js.map +1 -1
  30. package/dist/services/local.d.ts +11 -0
  31. package/dist/services/local.d.ts.map +1 -0
  32. package/dist/services/shared/callsite.d.ts +4 -0
  33. package/dist/services/shared/callsite.d.ts.map +1 -0
  34. package/dist/services/shared/entry.d.ts +2 -0
  35. package/dist/services/shared/entry.d.ts.map +1 -0
  36. package/dist/services/shared/entry.js +137 -0
  37. package/dist/services/shared/entry.js.map +1 -0
  38. package/dist/services/shared/host.d.ts +5 -0
  39. package/dist/services/shared/host.d.ts.map +1 -0
  40. package/dist/services/shared/index.d.ts +11 -0
  41. package/dist/services/shared/index.d.ts.map +1 -0
  42. package/dist/services/shared/protocol.d.ts +39 -0
  43. package/dist/services/shared/protocol.d.ts.map +1 -0
  44. package/dist/services/shared/root.d.ts +5 -0
  45. package/dist/services/shared/root.d.ts.map +1 -0
  46. package/dist/socket-line.d.ts +3 -0
  47. package/dist/socket-line.d.ts.map +1 -0
  48. package/package.json +4 -2
  49. package/dist/chunk-GX4J6JR3.js.map +0 -1
  50. package/dist/chunk-SX5HBQHG.js.map +0 -1
  51. /package/dist/{chunk-CBRTTFXG.js.map → chunk-UOIMVHGX.js.map} +0 -0
@@ -4,13 +4,13 @@ import {
4
4
  resolveSiblingDir,
5
5
  runManagedSubprocess,
6
6
  runningSignalPath
7
- } from "../chunk-SX5HBQHG.js";
7
+ } from "../chunk-KG24ZAKO.js";
8
8
  import {
9
9
  DevSessions,
10
10
  SessionState,
11
11
  SessionStateError,
12
12
  getStickyPort
13
- } from "../chunk-GX4J6JR3.js";
13
+ } from "../chunk-MBPIOYW3.js";
14
14
 
15
15
  // src/async/index.ts
16
16
  import { Schema } from "effect";
@@ -1,6 +1,8 @@
1
1
  import {
2
- DevSessions
3
- } from "./chunk-GX4J6JR3.js";
2
+ DevSessions,
3
+ readDaemonIdentity,
4
+ sendDaemonRequest
5
+ } from "./chunk-MBPIOYW3.js";
4
6
 
5
7
  // src/dev/running-signal.ts
6
8
  import { watch as fsWatch, realpathSync } from "fs";
@@ -11,97 +13,35 @@ import { FileSystem } from "effect/FileSystem";
11
13
  import { Path } from "effect/Path";
12
14
 
13
15
  // src/dev/daemon-readiness.ts
14
- import { createConnection } from "net";
15
- import { StringDecoder } from "string_decoder";
16
16
  import { Effect, Exit, Schema } from "effect";
17
- var RESPONSE_TIMEOUT_MS = 5e3;
18
- var MAX_RESPONSE_BYTES = 1024 * 1024;
19
- var DaemonResponse = Schema.fromJsonString(
20
- Schema.Struct({
21
- version: Schema.Literal(1),
22
- requestId: Schema.NonEmptyString,
23
- ok: Schema.Boolean,
24
- result: Schema.optional(Schema.Unknown),
25
- error: Schema.optional(Schema.String)
26
- })
27
- );
28
- var sendReadiness = (identity, request) => Effect.tryPromise({
29
- try: () => new Promise((resolve2, reject) => {
30
- const socket = createConnection(identity.socketPath);
31
- const decoder = new StringDecoder("utf8");
32
- let input = "";
33
- let settled = false;
34
- const finish = (complete) => {
35
- if (settled) return;
36
- settled = true;
37
- clearTimeout(timer);
38
- socket.destroy();
39
- complete();
40
- };
41
- const timer = setTimeout(
42
- () => finish(
43
- () => reject(new Error("Daemon readiness request timed out"))
44
- ),
45
- RESPONSE_TIMEOUT_MS
46
- );
47
- socket.once(
48
- "connect",
49
- () => socket.write(`${JSON.stringify(request)}
50
- `)
51
- );
52
- socket.on("data", (chunk) => {
53
- input += decoder.write(chunk);
54
- if (Buffer.byteLength(input) > MAX_RESPONSE_BYTES) {
55
- finish(
56
- () => reject(new Error("Daemon readiness response is too large"))
57
- );
58
- return;
59
- }
60
- const boundary = input.indexOf("\n");
61
- if (boundary !== -1) finish(() => resolve2(input.slice(0, boundary)));
62
- });
63
- socket.once("error", (cause) => finish(() => reject(cause)));
64
- socket.once(
65
- "close",
66
- () => finish(
67
- () => reject(new Error("Daemon closed before acknowledging readiness"))
68
- )
69
- );
70
- }),
71
- catch: (cause) => new Error("Could not report readiness to daemon", { cause })
72
- }).pipe(
73
- Effect.flatMap(Schema.decodeUnknownEffect(DaemonResponse)),
74
- Effect.flatMap(
75
- (response) => response.requestId === request.requestId && response.ok ? Effect.void : Effect.fail(new Error("Daemon did not acknowledge readiness"))
76
- )
77
- );
78
- var publishDaemonReadiness = (value) => Effect.suspend(() => {
79
- const socketPath = process.env.DEVSESS_SOCKET;
80
- const runId = process.env.DEVSESS_RUN_ID;
81
- const service = process.env.DEVSESS_SERVICE;
82
- if (socketPath === void 0 || runId === void 0 || service === void 0)
83
- return Effect.void;
84
- const identity = { socketPath, runId, service };
85
- return Effect.acquireRelease(
17
+ var publishDaemonReadiness = (value) => Effect.gen(function* () {
18
+ const identity = yield* readDaemonIdentity;
19
+ if (identity === void 0) return;
20
+ const params = {
21
+ runId: identity.runId,
22
+ service: identity.service,
23
+ ...identity.instanceId === void 0 ? {} : { instanceId: identity.instanceId }
24
+ };
25
+ yield* Effect.acquireRelease(
86
26
  Schema.decodeUnknownEffect(Schema.Json)(value).pipe(
87
27
  Effect.flatMap(
88
- (json) => sendReadiness(identity, {
28
+ (json) => sendDaemonRequest(identity, {
89
29
  version: 1,
90
30
  requestId: crypto.randomUUID(),
91
31
  method: "publish",
92
- params: { runId, service, value: json }
32
+ params: { ...params, value: json }
93
33
  })
94
34
  ),
95
35
  Effect.exit
96
36
  ),
97
- (result) => Exit.isSuccess(result) ? sendReadiness(identity, {
37
+ (result) => Exit.isSuccess(result) ? sendDaemonRequest(identity, {
98
38
  version: 1,
99
39
  requestId: crypto.randomUUID(),
100
40
  method: "unpublish",
101
- params: { runId, service }
41
+ params
102
42
  }).pipe(Effect.catch(() => Effect.void)) : Effect.void
103
- ).pipe(Effect.asVoid);
104
- });
43
+ );
44
+ }).pipe(Effect.catch(() => Effect.void));
105
45
 
106
46
  // src/dev/running-signal.ts
107
47
  var RUNNING_SIGNAL_FILE = ".data/running.json";
@@ -223,4 +163,4 @@ export {
223
163
  awaitRunning,
224
164
  runManagedSubprocess
225
165
  };
226
- //# sourceMappingURL=chunk-SX5HBQHG.js.map
166
+ //# sourceMappingURL=chunk-KG24ZAKO.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/dev/running-signal.ts","../src/dev/daemon-readiness.ts","../src/dev/subprocess.ts"],"sourcesContent":["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 { Effect, Exit, Schema } from 'effect';\nimport { readDaemonIdentity, sendDaemonRequest } from './daemon-client';\n\nexport const publishDaemonReadiness = (value: unknown) =>\n\tEffect.gen(function* () {\n\t\tconst identity = yield* readDaemonIdentity;\n\t\tif (identity === undefined) return;\n\t\tconst params = {\n\t\t\trunId: identity.runId,\n\t\t\tservice: identity.service,\n\t\t\t...(identity.instanceId === undefined\n\t\t\t\t? {}\n\t\t\t\t: { instanceId: identity.instanceId }),\n\t\t};\n\t\tyield* Effect.acquireRelease(\n\t\t\tSchema.decodeUnknownEffect(Schema.Json)(value).pipe(\n\t\t\t\tEffect.flatMap((json) =>\n\t\t\t\t\tsendDaemonRequest(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: { ...params, 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? sendDaemonRequest(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,\n\t\t\t\t\t\t}).pipe(Effect.catch(() => Effect.void))\n\t\t\t\t\t: Effect.void,\n\t\t);\n\t}).pipe(Effect.catch(() => Effect.void));\n","import { Effect } from 'effect';\nimport { ChildProcess } from 'effect/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\tif (!(yield* proc.isRunning.pipe(Effect.orDie))) return;\n\t\t\t\t\tyield* Effect.logInfo(`[dev] stopping: ${label} (pid=${proc.pid})`);\n\t\t\t\t\tyield* proc.kill().pipe(\n\t\t\t\t\t\tEffect.catch((err) =>\n\t\t\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\t\t\tif (yield* proc.isRunning.pipe(Effect.orDie))\n\t\t\t\t\t\t\t\t\tyield* Effect.logError(\n\t\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\t);\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);\n\n\t\treturn yield* child.exitCode;\n\t});\n"],"mappings":"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","names":["Effect","require","Effect","Effect"]}
@@ -7,6 +7,7 @@ var DevSession;
7
7
  ((DevSession2) => {
8
8
  function make(input) {
9
9
  return {
10
+ rootDir: input.rootDir,
10
11
  name: input.name,
11
12
  lastModifiedAt: input.lastModifiedAt,
12
13
  path: (relativePath) => Effect.succeed(input.pathJoin(relativePath)),
@@ -60,7 +61,7 @@ var findProjectRoot = Effect.gen(function* () {
60
61
  });
61
62
  return yield* walkUp(startDir);
62
63
  });
63
- var buildDevSessionsCore = (sessionsDir) => Effect.gen(function* () {
64
+ var buildDevSessionsCore = (sessionsDir, rootDir) => Effect.gen(function* () {
64
65
  const fs = yield* FileSystem;
65
66
  const path = yield* Path;
66
67
  yield* Effect.logDebug(`DevSessions dir: ${sessionsDir}`);
@@ -75,7 +76,7 @@ var buildDevSessionsCore = (sessionsDir) => Effect.gen(function* () {
75
76
  DevSession.make({
76
77
  name: entry,
77
78
  lastModifiedAt: Option.getOrNull(stat.mtime),
78
- rootDir: sessionsDir,
79
+ rootDir,
79
80
  pathJoin: (relativePath) => path.join(sessionsDir, entry, relativePath)
80
81
  })
81
82
  );
@@ -93,7 +94,7 @@ var buildDevSessionsCore = (sessionsDir) => Effect.gen(function* () {
93
94
  return DevSession.make({
94
95
  name: slug,
95
96
  lastModifiedAt: null,
96
- rootDir: sessionsDir,
97
+ rootDir,
97
98
  pathJoin: (relativePath) => path.join(sessionsDir, slug, relativePath)
98
99
  });
99
100
  });
@@ -109,14 +110,25 @@ var buildDevSessionsCore = (sessionsDir) => Effect.gen(function* () {
109
110
  }
110
111
  return yield* createSession;
111
112
  });
112
- return { getSessions, createSession, getLatestOrCreate };
113
+ const getOrCreate = (name) => Effect.gen(function* () {
114
+ yield* fs.makeDirectory(path.join(sessionsDir, name), {
115
+ recursive: true
116
+ });
117
+ return DevSession.make({
118
+ name,
119
+ rootDir,
120
+ lastModifiedAt: null,
121
+ pathJoin: (relativePath) => path.join(sessionsDir, name, relativePath)
122
+ });
123
+ });
124
+ return { getSessions, createSession, getLatestOrCreate, getOrCreate };
113
125
  });
114
126
  var buildDevSessionsLayer = (rootDir) => Layer.effect(
115
127
  DevSessions,
116
128
  Effect.gen(function* () {
117
129
  const path = yield* Path;
118
130
  const sessionsDir = path.join(rootDir, ".data", "sessions");
119
- const core = yield* buildDevSessionsCore(sessionsDir);
131
+ const core = yield* buildDevSessionsCore(sessionsDir, rootDir);
120
132
  return {
121
133
  dir: rootDir,
122
134
  path: (relativePath) => path.join(rootDir, relativePath),
@@ -178,9 +190,31 @@ var SessionState;
178
190
  })(SessionState || (SessionState = {}));
179
191
 
180
192
  // src/dev/sticky-port.ts
181
- import { Effect as Effect3 } from "effect";
193
+ import { Effect as Effect4 } from "effect";
194
+ import { FileSystem as FileSystem3 } from "effect/FileSystem";
195
+ import { Path as Path3 } from "effect/Path";
182
196
  import * as S2 from "effect/Schema";
183
197
  import getPort from "get-port";
198
+
199
+ // src/lock.ts
200
+ import { Effect as Effect3, Schedule } from "effect";
201
+ import lockfile from "proper-lockfile";
202
+ var acquireFileLock = (file, lockPath = `${file}.lock`) => Effect3.tryPromise({
203
+ try: () => lockfile.lock(file, {
204
+ realpath: false,
205
+ lockfilePath: lockPath,
206
+ stale: 6e4,
207
+ retries: 0
208
+ }),
209
+ catch: (cause) => new Error(`Failed to lock ${file}`, { cause })
210
+ }).pipe(
211
+ Effect3.retry({
212
+ while: (error) => typeof error.cause === "object" && error.cause !== null && "code" in error.cause && error.cause.code === "ELOCKED",
213
+ schedule: Schedule.spaced("50 millis")
214
+ })
215
+ );
216
+
217
+ // src/dev/sticky-port.ts
184
218
  var DEFAULT_NAME = "default";
185
219
  var StickyPorts = SessionState.slot(
186
220
  S2.Struct({
@@ -188,13 +222,28 @@ var StickyPorts = SessionState.slot(
188
222
  port: S2.optional(S2.Number)
189
223
  })
190
224
  );
191
- var getStickyPort = (session, options) => Effect3.gen(function* () {
225
+ var getStickyPort = (session, options) => Effect4.gen(function* () {
192
226
  const name = options?.name ?? DEFAULT_NAME;
227
+ const fs = yield* FileSystem3;
228
+ const path = yield* Path3;
229
+ const statePath = yield* session.path("sess.json");
230
+ yield* fs.makeDirectory(path.dirname(statePath), { recursive: true });
231
+ yield* Effect4.acquireRelease(
232
+ acquireFileLock(statePath).pipe(
233
+ Effect4.mapError(
234
+ (cause) => new SessionStateError({
235
+ message: "Failed to lock sticky ports",
236
+ cause
237
+ })
238
+ )
239
+ ),
240
+ (release) => Effect4.promise(() => release())
241
+ );
193
242
  const file = yield* StickyPorts.read(session);
194
243
  const ports = file?.ports ?? (file?.port !== void 0 ? { [DEFAULT_NAME]: file.port } : {});
195
244
  const preferred = ports[name];
196
245
  const exclude = Object.entries(ports).filter((entry) => entry[0] !== name).map((entry) => entry[1]);
197
- const port = yield* Effect3.promise(
246
+ const port = yield* Effect4.promise(
198
247
  async () => getPort({
199
248
  port: preferred !== void 0 ? [preferred] : void 0,
200
249
  ...exclude.length > 0 ? { exclude } : {}
@@ -202,13 +251,112 @@ var getStickyPort = (session, options) => Effect3.gen(function* () {
202
251
  );
203
252
  yield* StickyPorts.write(session, { ports: { ...ports, [name]: port } });
204
253
  return port;
254
+ }).pipe(Effect4.scoped);
255
+
256
+ // src/socket-line.ts
257
+ import { StringDecoder } from "string_decoder";
258
+ var readSocketLine = (socket, maxBytes = 1048576) => new Promise((resolve, reject) => {
259
+ const decoder = new StringDecoder("utf8");
260
+ const state = { input: "", bytes: 0 };
261
+ const cleanup = () => {
262
+ socket.removeListener("data", data);
263
+ socket.removeListener("error", error);
264
+ socket.removeListener("close", closed);
265
+ };
266
+ const error = (cause) => {
267
+ cleanup();
268
+ reject(cause);
269
+ };
270
+ const closed = () => error(new Error("Socket closed before sending a ready line"));
271
+ const data = (chunk) => {
272
+ const boundary = chunk.indexOf(10);
273
+ const part = boundary < 0 ? chunk : chunk.subarray(0, boundary);
274
+ state.bytes += part.length;
275
+ if (state.bytes > maxBytes)
276
+ return error(new Error("Socket line exceeds byte limit"));
277
+ state.input += decoder.write(part);
278
+ if (boundary >= 0) {
279
+ cleanup();
280
+ resolve(state.input + decoder.end());
281
+ }
282
+ };
283
+ socket.on("data", data);
284
+ socket.once("error", error);
285
+ socket.once("close", closed);
286
+ });
287
+
288
+ // src/dev/daemon-client.ts
289
+ import { createConnection } from "net";
290
+ import { Config, ConfigProvider, Effect as Effect5, Option as Option2, Schema } from "effect";
291
+ var RESPONSE_TIMEOUT_MS = 5e3;
292
+ var MAX_RESPONSE_BYTES = 1024 * 1024;
293
+ var DaemonResponse = Schema.fromJsonString(
294
+ Schema.Struct({
295
+ version: Schema.Literal(1),
296
+ requestId: Schema.NonEmptyString,
297
+ ok: Schema.Boolean,
298
+ result: Schema.optional(Schema.Unknown),
299
+ error: Schema.optional(Schema.String)
300
+ })
301
+ );
302
+ var readDaemonIdentity = Effect5.gen(function* () {
303
+ const provider = ConfigProvider.fromEnv();
304
+ const read2 = (key) => Config.String(key).pipe(Config.option).parse(provider).pipe(Effect5.map(Option2.getOrUndefined));
305
+ const socketPath = yield* read2("DEVSESS_SOCKET");
306
+ const runId = yield* read2("DEVSESS_RUN_ID");
307
+ const service = yield* read2("DEVSESS_SERVICE");
308
+ const instanceId = yield* read2("DEVSESS_INSTANCE_ID");
309
+ if (socketPath === void 0 || runId === void 0 || service === void 0)
310
+ return void 0;
311
+ return {
312
+ socketPath,
313
+ runId,
314
+ service,
315
+ ...instanceId === void 0 ? {} : { instanceId }
316
+ };
205
317
  });
318
+ var sendDaemonRequest = (identity, request, timeoutMs = RESPONSE_TIMEOUT_MS) => Effect5.tryPromise({
319
+ try: () => new Promise((resolve, reject) => {
320
+ const socket = createConnection(identity.socketPath);
321
+ let settled = false;
322
+ const finish = (complete) => {
323
+ if (settled) return;
324
+ settled = true;
325
+ clearTimeout(timer);
326
+ socket.destroy();
327
+ complete();
328
+ };
329
+ const timer = setTimeout(
330
+ () => finish(() => reject(new Error("Daemon request timed out"))),
331
+ timeoutMs
332
+ );
333
+ socket.once(
334
+ "connect",
335
+ () => socket.write(`${JSON.stringify(request)}
336
+ `)
337
+ );
338
+ readSocketLine(socket, MAX_RESPONSE_BYTES).then(
339
+ (line) => finish(() => resolve(line)),
340
+ (cause) => finish(() => reject(cause))
341
+ );
342
+ }),
343
+ catch: (cause) => new Error("Could not send request to daemon", { cause })
344
+ }).pipe(
345
+ Effect5.flatMap(Schema.decodeUnknownEffect(DaemonResponse)),
346
+ Effect5.flatMap(
347
+ (response) => response.requestId === request.requestId && response.ok ? Effect5.void : Effect5.fail(new Error("Daemon did not acknowledge request"))
348
+ )
349
+ );
206
350
 
207
351
  export {
208
352
  DevSessions,
209
353
  ProjectRootNotFoundError,
354
+ readSocketLine,
355
+ readDaemonIdentity,
356
+ sendDaemonRequest,
210
357
  SessionStateError,
211
358
  SessionState,
359
+ acquireFileLock,
212
360
  getStickyPort
213
361
  };
214
- //# sourceMappingURL=chunk-GX4J6JR3.js.map
362
+ //# sourceMappingURL=chunk-MBPIOYW3.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/dev-sessions.ts","../src/dev/session-state.ts","../src/dev/sticky-port.ts","../src/lock.ts","../src/socket-line.ts","../src/dev/daemon-client.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\trootDir: input.rootDir,\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\treadonly getOrCreate: (\n\t\t\tname: string,\n\t\t) => 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, rootDir: 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,\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,\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\tconst getOrCreate = (name: string) =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tyield* fs.makeDirectory(path.join(sessionsDir, name), {\n\t\t\t\t\trecursive: true,\n\t\t\t\t});\n\t\t\t\treturn DevSession.make({\n\t\t\t\t\tname,\n\t\t\t\t\trootDir,\n\t\t\t\t\tlastModifiedAt: null,\n\t\t\t\t\tpathJoin: (relativePath) =>\n\t\t\t\t\t\tpath.join(sessionsDir, name, relativePath),\n\t\t\t\t});\n\t\t\t});\n\t\treturn { getSessions, createSession, getLatestOrCreate, getOrCreate };\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, rootDir);\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 { 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 { FileSystem } from 'effect/FileSystem';\nimport { Path } from 'effect/Path';\nimport * as S from 'effect/Schema';\nimport getPort from 'get-port';\nimport type { DevSession } from '../dev-sessions';\nimport { acquireFileLock } from '../lock';\nimport { SessionState, SessionStateError } 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\t\tconst fs = yield* FileSystem;\n\t\tconst path = yield* Path;\n\t\tconst statePath = yield* session.path('sess.json');\n\t\tyield* fs.makeDirectory(path.dirname(statePath), { recursive: true });\n\t\tyield* Effect.acquireRelease(\n\t\t\tacquireFileLock(statePath).pipe(\n\t\t\t\tEffect.mapError(\n\t\t\t\t\t(cause) =>\n\t\t\t\t\t\tnew SessionStateError({\n\t\t\t\t\t\t\tmessage: 'Failed to lock sticky ports',\n\t\t\t\t\t\t\tcause,\n\t\t\t\t\t\t}),\n\t\t\t\t),\n\t\t\t),\n\t\t\t(release) => Effect.promise(() => release()),\n\t\t);\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}).pipe(Effect.scoped);\n","import { Effect, Schedule } from 'effect';\nimport lockfile from 'proper-lockfile';\n\nexport const acquireFileLock = (file: string, lockPath = `${file}.lock`) =>\n\tEffect.tryPromise({\n\t\ttry: () =>\n\t\t\tlockfile.lock(file, {\n\t\t\t\trealpath: false,\n\t\t\t\tlockfilePath: lockPath,\n\t\t\t\tstale: 60_000,\n\t\t\t\tretries: 0,\n\t\t\t}),\n\t\tcatch: (cause) => new Error(`Failed to lock ${file}`, { cause }),\n\t}).pipe(\n\t\tEffect.retry({\n\t\t\twhile: (error) =>\n\t\t\t\ttypeof error.cause === 'object' &&\n\t\t\t\terror.cause !== null &&\n\t\t\t\t'code' in error.cause &&\n\t\t\t\terror.cause.code === 'ELOCKED',\n\t\t\tschedule: Schedule.spaced('50 millis'),\n\t\t}),\n\t);\n","import type { Socket } from 'node:net';\nimport { StringDecoder } from 'node:string_decoder';\n\nexport const readSocketLine = (\n\tsocket: Socket,\n\tmaxBytes = 1_048_576,\n): Promise<string> =>\n\tnew Promise((resolve, reject) => {\n\t\tconst decoder = new StringDecoder('utf8');\n\t\tconst state = { input: '', bytes: 0 };\n\t\tconst cleanup = () => {\n\t\t\tsocket.removeListener('data', data);\n\t\t\tsocket.removeListener('error', error);\n\t\t\tsocket.removeListener('close', closed);\n\t\t};\n\t\tconst error = (cause: Error) => {\n\t\t\tcleanup();\n\t\t\treject(cause);\n\t\t};\n\t\tconst closed = () =>\n\t\t\terror(new Error('Socket closed before sending a ready line'));\n\t\tconst data = (chunk: Buffer) => {\n\t\t\tconst boundary = chunk.indexOf(10);\n\t\t\tconst part = boundary < 0 ? chunk : chunk.subarray(0, boundary);\n\t\t\tstate.bytes += part.length;\n\t\t\tif (state.bytes > maxBytes)\n\t\t\t\treturn error(new Error('Socket line exceeds byte limit'));\n\t\t\tstate.input += decoder.write(part);\n\t\t\tif (boundary >= 0) {\n\t\t\t\tcleanup();\n\t\t\t\tresolve(state.input + decoder.end());\n\t\t\t}\n\t\t};\n\t\tsocket.on('data', data);\n\t\tsocket.once('error', error);\n\t\tsocket.once('close', closed);\n\t});\n","import { createConnection } from 'node:net';\nimport { Config, ConfigProvider, Effect, Option, Schema } from 'effect';\nimport { readSocketLine } from '../socket-line';\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\treadonly instanceId?: string;\n};\n\nexport const readDaemonIdentity = Effect.gen(function* () {\n\tconst provider = ConfigProvider.fromEnv();\n\tconst read = (key: string) =>\n\t\tConfig.String(key)\n\t\t\t.pipe(Config.option)\n\t\t\t.parse(provider)\n\t\t\t.pipe(Effect.map(Option.getOrUndefined));\n\tconst socketPath = yield* read('DEVSESS_SOCKET');\n\tconst runId = yield* read('DEVSESS_RUN_ID');\n\tconst service = yield* read('DEVSESS_SERVICE');\n\tconst instanceId = yield* read('DEVSESS_INSTANCE_ID');\n\tif (socketPath === undefined || runId === undefined || service === undefined)\n\t\treturn undefined;\n\treturn {\n\t\tsocketPath,\n\t\trunId,\n\t\tservice,\n\t\t...(instanceId === undefined ? {} : { instanceId }),\n\t};\n});\n\nexport const sendDaemonRequest = (\n\tidentity: ServiceIdentity,\n\trequest: {\n\t\treadonly version: 1;\n\t\treadonly requestId: string;\n\t\treadonly method: string;\n\t\treadonly params: Readonly<Record<string, unknown>>;\n\t},\n\ttimeoutMs = RESPONSE_TIMEOUT_MS,\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\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() => finish(() => reject(new Error('Daemon request timed out'))),\n\t\t\t\t\ttimeoutMs,\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\treadSocketLine(socket, MAX_RESPONSE_BYTES).then(\n\t\t\t\t\t(line) => finish(() => resolve(line)),\n\t\t\t\t\t(cause) => finish(() => reject(cause)),\n\t\t\t\t);\n\t\t\t}),\n\t\tcatch: (cause) => new Error('Could not send request 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 request')),\n\t\t),\n\t);\n"],"mappings":"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","names":["DevSession","Data","Effect","FileSystem","Path","SessionState","Effect","FileSystem","Path","S","Effect","Effect","FileSystem","Path","Effect","Option","Effect","read","Option"]}