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
@@ -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),
@@ -125,185 +137,10 @@ var buildDevSessionsLayer = (rootDir) => Layer.effect(
125
137
  })
126
138
  );
127
139
 
128
- // src/dev/running-signal.ts
129
- import { watch as fsWatch, realpathSync } from "fs";
130
- import { createRequire } from "module";
131
- import { dirname, join, resolve } from "path";
132
- import { Deferred, Effect as Effect3 } from "effect";
140
+ // src/dev/session-state.ts
141
+ import { Data as Data2, Effect as Effect2 } from "effect";
133
142
  import { FileSystem as FileSystem2 } from "effect/FileSystem";
134
143
  import { Path as Path2 } from "effect/Path";
135
-
136
- // src/dev/daemon-readiness.ts
137
- import { createConnection } from "net";
138
- import { StringDecoder } from "string_decoder";
139
- import { Effect as Effect2, Exit, Schema } from "effect";
140
- var RESPONSE_TIMEOUT_MS = 5e3;
141
- var MAX_RESPONSE_BYTES = 1024 * 1024;
142
- var DaemonResponse = Schema.fromJsonString(
143
- Schema.Struct({
144
- version: Schema.Literal(1),
145
- requestId: Schema.NonEmptyString,
146
- ok: Schema.Boolean,
147
- result: Schema.optional(Schema.Unknown),
148
- error: Schema.optional(Schema.String)
149
- })
150
- );
151
- var sendReadiness = (identity, request) => Effect2.tryPromise({
152
- try: () => new Promise((resolve2, reject) => {
153
- const socket = createConnection(identity.socketPath);
154
- const decoder = new StringDecoder("utf8");
155
- let input = "";
156
- let settled = false;
157
- const finish = (complete) => {
158
- if (settled) return;
159
- settled = true;
160
- clearTimeout(timer);
161
- socket.destroy();
162
- complete();
163
- };
164
- const timer = setTimeout(
165
- () => finish(
166
- () => reject(new Error("Daemon readiness request timed out"))
167
- ),
168
- RESPONSE_TIMEOUT_MS
169
- );
170
- socket.once(
171
- "connect",
172
- () => socket.write(`${JSON.stringify(request)}
173
- `)
174
- );
175
- socket.on("data", (chunk) => {
176
- input += decoder.write(chunk);
177
- if (Buffer.byteLength(input) > MAX_RESPONSE_BYTES) {
178
- finish(
179
- () => reject(new Error("Daemon readiness response is too large"))
180
- );
181
- return;
182
- }
183
- const boundary = input.indexOf("\n");
184
- if (boundary !== -1) finish(() => resolve2(input.slice(0, boundary)));
185
- });
186
- socket.once("error", (cause) => finish(() => reject(cause)));
187
- socket.once(
188
- "close",
189
- () => finish(
190
- () => reject(new Error("Daemon closed before acknowledging readiness"))
191
- )
192
- );
193
- }),
194
- catch: (cause) => new Error("Could not report readiness to daemon", { cause })
195
- }).pipe(
196
- Effect2.flatMap(Schema.decodeUnknownEffect(DaemonResponse)),
197
- Effect2.flatMap(
198
- (response) => response.requestId === request.requestId && response.ok ? Effect2.void : Effect2.fail(new Error("Daemon did not acknowledge readiness"))
199
- )
200
- );
201
- var publishDaemonReadiness = (value) => Effect2.suspend(() => {
202
- const socketPath = process.env.DEVSESS_SOCKET;
203
- const runId = process.env.DEVSESS_RUN_ID;
204
- const service = process.env.DEVSESS_SERVICE;
205
- if (socketPath === void 0 || runId === void 0 || service === void 0)
206
- return Effect2.void;
207
- const identity = { socketPath, runId, service };
208
- return Effect2.acquireRelease(
209
- Schema.decodeUnknownEffect(Schema.Json)(value).pipe(
210
- Effect2.flatMap(
211
- (json) => sendReadiness(identity, {
212
- version: 1,
213
- requestId: crypto.randomUUID(),
214
- method: "publish",
215
- params: { runId, service, value: json }
216
- })
217
- ),
218
- Effect2.exit
219
- ),
220
- (result) => Exit.isSuccess(result) ? sendReadiness(identity, {
221
- version: 1,
222
- requestId: crypto.randomUUID(),
223
- method: "unpublish",
224
- params: { runId, service }
225
- }).pipe(Effect2.catch(() => Effect2.void)) : Effect2.void
226
- ).pipe(Effect2.asVoid);
227
- });
228
-
229
- // src/dev/running-signal.ts
230
- var RUNNING_SIGNAL_FILE = ".data/running.json";
231
- var runningSignalPath = (dir) => join(dir, RUNNING_SIGNAL_FILE);
232
- var resolveSiblingDir = (spec, fromDir) => {
233
- if (spec.startsWith(".") || spec.startsWith("/")) {
234
- return resolve(fromDir, spec);
235
- }
236
- const require2 = createRequire(join(fromDir, "package.json"));
237
- const pkgJsonPath = require2.resolve(`${spec}/package.json`);
238
- return dirname(realpathSync(pkgJsonPath));
239
- };
240
- var publishRunningSignal = (filePath, value) => Effect3.acquireRelease(
241
- Effect3.gen(function* () {
242
- const fs = yield* FileSystem2;
243
- const path = yield* Path2;
244
- const tmp = `${filePath}.tmp`;
245
- yield* fs.makeDirectory(path.dirname(filePath), { recursive: true });
246
- yield* fs.writeFileString(tmp, JSON.stringify(value));
247
- yield* fs.rename(tmp, filePath);
248
- }),
249
- () => Effect3.gen(function* () {
250
- const fs = yield* FileSystem2;
251
- yield* fs.remove(filePath).pipe(Effect3.catch(() => Effect3.void));
252
- })
253
- );
254
- var awaitRunningSignal = (filePath, opts) => Effect3.scoped(
255
- Effect3.gen(function* () {
256
- const fs = yield* FileSystem2;
257
- const path = yield* Path2;
258
- const dir = path.dirname(filePath);
259
- const filename = path.basename(filePath);
260
- const deferred = yield* Deferred.make();
261
- const tryRead = Effect3.gen(function* () {
262
- if (!(yield* fs.exists(filePath))) return false;
263
- const raw = yield* fs.readFileString(filePath).pipe(Effect3.catch(() => Effect3.succeed(null)));
264
- if (raw == null) return false;
265
- const parsed = yield* Effect3.try(() => opts.parse(raw)).pipe(
266
- Effect3.catch(() => Effect3.succeed(null))
267
- );
268
- if (parsed == null) return false;
269
- yield* Deferred.succeed(deferred, parsed);
270
- return true;
271
- });
272
- yield* fs.makeDirectory(dir, { recursive: true });
273
- yield* Effect3.acquireRelease(
274
- Effect3.sync(
275
- () => (
276
- // Watch parent dir — the file may not exist yet, and watching a missing file errors.
277
- fsWatch(dir, { recursive: false }, (eventType, eventFilename) => {
278
- if (eventFilename !== filename) return;
279
- if (eventType !== "rename" && eventType !== "change") return;
280
- Effect3.runFork(tryRead);
281
- })
282
- )
283
- ),
284
- (watcher) => Effect3.sync(() => watcher.close())
285
- );
286
- yield* tryRead;
287
- return yield* Deferred.await(deferred);
288
- })
289
- );
290
- var publishRunning = (value) => Effect3.gen(function* () {
291
- const sessions = yield* DevSessions;
292
- yield* publishRunningSignal(runningSignalPath(sessions.dir), value);
293
- yield* publishDaemonReadiness(value);
294
- });
295
- var awaitRunning = (pkg) => Effect3.gen(function* () {
296
- const sessions = yield* DevSessions;
297
- return yield* awaitRunningSignal(
298
- runningSignalPath(resolveSiblingDir(pkg, sessions.dir)),
299
- { parse: (raw) => JSON.parse(raw) }
300
- );
301
- });
302
-
303
- // src/dev/session-state.ts
304
- import { Data as Data2, Effect as Effect4 } from "effect";
305
- import { FileSystem as FileSystem3 } from "effect/FileSystem";
306
- import { Path as Path3 } from "effect/Path";
307
144
  import * as S from "effect/Schema";
308
145
  var SessionStateError = class extends Data2.TaggedError("SessionStateError") {
309
146
  };
@@ -311,8 +148,8 @@ function getPath(session) {
311
148
  return session.path("sess.json");
312
149
  }
313
150
  function read(filePath) {
314
- return Effect4.gen(function* () {
315
- const fs = yield* FileSystem3;
151
+ return Effect2.gen(function* () {
152
+ const fs = yield* FileSystem2;
316
153
  if (!(yield* fs.exists(filePath))) {
317
154
  return null;
318
155
  }
@@ -323,21 +160,21 @@ var SessionState;
323
160
  ((SessionState2) => {
324
161
  function slot(schema) {
325
162
  return {
326
- read: (session) => Effect4.gen(function* () {
163
+ read: (session) => Effect2.gen(function* () {
327
164
  const filePath = yield* getPath(session);
328
165
  const content = yield* read(filePath);
329
166
  if (content == null) return null;
330
167
  return yield* S.decodeEffect(S.fromJsonString(schema))(content).pipe(
331
- Effect4.catchTag("SchemaError", () => Effect4.succeed(null))
168
+ Effect2.catchTag("SchemaError", () => Effect2.succeed(null))
332
169
  );
333
170
  }),
334
- write: (session, data) => Effect4.gen(function* () {
335
- const fs = yield* FileSystem3;
336
- const path = yield* Path3;
171
+ write: (session, data) => Effect2.gen(function* () {
172
+ const fs = yield* FileSystem2;
173
+ const path = yield* Path2;
337
174
  const filePath = yield* getPath(session);
338
175
  yield* fs.makeDirectory(path.dirname(filePath), { recursive: true });
339
176
  const existing = yield* read(filePath);
340
- const parsed = yield* Effect4.try({
177
+ const parsed = yield* Effect2.try({
341
178
  try: () => JSON.parse(existing ?? "{}"),
342
179
  catch: (cause) => new SessionStateError({
343
180
  message: `Failed to parse ${filePath} as JSON`,
@@ -353,9 +190,31 @@ var SessionState;
353
190
  })(SessionState || (SessionState = {}));
354
191
 
355
192
  // src/dev/sticky-port.ts
356
- import { Effect as Effect5 } 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";
357
196
  import * as S2 from "effect/Schema";
358
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
359
218
  var DEFAULT_NAME = "default";
360
219
  var StickyPorts = SessionState.slot(
361
220
  S2.Struct({
@@ -363,13 +222,28 @@ var StickyPorts = SessionState.slot(
363
222
  port: S2.optional(S2.Number)
364
223
  })
365
224
  );
366
- var getStickyPort = (session, options) => Effect5.gen(function* () {
225
+ var getStickyPort = (session, options) => Effect4.gen(function* () {
367
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
+ );
368
242
  const file = yield* StickyPorts.read(session);
369
243
  const ports = file?.ports ?? (file?.port !== void 0 ? { [DEFAULT_NAME]: file.port } : {});
370
244
  const preferred = ports[name];
371
245
  const exclude = Object.entries(ports).filter((entry) => entry[0] !== name).map((entry) => entry[1]);
372
- const port = yield* Effect5.promise(
246
+ const port = yield* Effect4.promise(
373
247
  async () => getPort({
374
248
  port: preferred !== void 0 ? [preferred] : void 0,
375
249
  ...exclude.length > 0 ? { exclude } : {}
@@ -377,53 +251,112 @@ var getStickyPort = (session, options) => Effect5.gen(function* () {
377
251
  );
378
252
  yield* StickyPorts.write(session, { ports: { ...ports, [name]: port } });
379
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);
380
286
  });
381
287
 
382
- // src/dev/subprocess.ts
383
- import { Effect as Effect6 } from "effect";
384
- import { ChildProcess } from "effect/unstable/process";
385
- var runManagedSubprocess = (cmd, args, opts) => Effect6.gen(function* () {
386
- const command = ChildProcess.make(cmd, args, {
387
- // Keep children in the caller's group so its supervisor can signal them together.
388
- detached: false,
389
- stdin: "inherit",
390
- stdout: "inherit",
391
- stderr: "inherit",
392
- env: { ...process.env, ...opts?.env ?? {} }
393
- });
394
- const label = [cmd, ...args].join(" ");
395
- const child = yield* Effect6.acquireRelease(
396
- Effect6.gen(function* () {
397
- const proc = yield* command;
398
- yield* Effect6.logInfo(`[dev] started: ${label} (pid=${proc.pid})`);
399
- return proc;
400
- }),
401
- (proc) => Effect6.gen(function* () {
402
- yield* Effect6.logInfo(`[dev] stopping: ${label} (pid=${proc.pid})`);
403
- yield* proc.kill().pipe(
404
- Effect6.catch(
405
- (err) => Effect6.logError(
406
- `[dev] failed to stop ${label} (pid=${proc.pid}): ${err}`
407
- )
408
- )
409
- );
410
- })
411
- );
412
- return yield* child.exitCode;
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
+ };
413
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
+ );
414
350
 
415
351
  export {
416
352
  DevSessions,
417
353
  ProjectRootNotFoundError,
418
- runningSignalPath,
419
- resolveSiblingDir,
420
- publishRunningSignal,
421
- awaitRunningSignal,
422
- publishRunning,
423
- awaitRunning,
354
+ readSocketLine,
355
+ readDaemonIdentity,
356
+ sendDaemonRequest,
424
357
  SessionStateError,
425
358
  SessionState,
426
- getStickyPort,
427
- runManagedSubprocess
359
+ acquireFileLock,
360
+ getStickyPort
428
361
  };
429
- //# sourceMappingURL=chunk-ULIVG5ID.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"]}