@prisma/composer-prisma-cloud 0.2.0 → 0.3.0-dev.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/dist/buckets-main.mjs +795 -0
  2. package/dist/buckets-main.mjs.map +1 -0
  3. package/dist/compute-main.mjs +559 -0
  4. package/dist/compute-main.mjs.map +1 -0
  5. package/dist/control.d.mts +34 -5
  6. package/dist/control.d.mts.map +1 -0
  7. package/dist/control.mjs +98 -1280
  8. package/dist/control.mjs.map +1 -1
  9. package/dist/cron/index.d.mts +54 -4
  10. package/dist/cron/index.d.mts.map +1 -0
  11. package/dist/cron/index.mjs +145 -42
  12. package/dist/cron/index.mjs.map +1 -1
  13. package/dist/cron/scheduler-entrypoint.mjs +637 -529
  14. package/dist/cron/scheduler-entrypoint.mjs.map +1 -1
  15. package/dist/cron/scheduler-service.mjs +145 -42
  16. package/dist/cron/scheduler-service.mjs.map +1 -1
  17. package/dist/email/email-entrypoint.mjs +19738 -0
  18. package/dist/email/email-entrypoint.mjs.map +1 -0
  19. package/dist/email/email-service.mjs +565 -0
  20. package/dist/email/email-service.mjs.map +1 -0
  21. package/dist/email/index.d.mts +282 -0
  22. package/dist/email/index.d.mts.map +1 -0
  23. package/dist/email/index.mjs +652 -0
  24. package/dist/email/index.mjs.map +1 -0
  25. package/dist/email/testing.d.mts +22 -0
  26. package/dist/email/testing.d.mts.map +1 -0
  27. package/dist/email/testing.mjs +1321 -0
  28. package/dist/email/testing.mjs.map +1 -0
  29. package/dist/get-port-BYnpbW5H.mjs +111 -0
  30. package/dist/get-port-BYnpbW5H.mjs.map +1 -0
  31. package/dist/index.d.mts +37 -6
  32. package/dist/index.d.mts.map +1 -0
  33. package/dist/index.mjs +70 -45
  34. package/dist/index.mjs.map +1 -1
  35. package/dist/local-target.d.mts +12 -0
  36. package/dist/local-target.d.mts.map +1 -0
  37. package/dist/local-target.mjs +6185 -0
  38. package/dist/local-target.mjs.map +1 -0
  39. package/dist/param-BTzY0ne5-CW6ojZ3E.mjs +66 -0
  40. package/dist/param-BTzY0ne5-CW6ojZ3E.mjs.map +1 -0
  41. package/dist/pg-connection-CadPZuEK-CF3FsSQu.mjs +79 -0
  42. package/dist/pg-connection-CadPZuEK-CF3FsSQu.mjs.map +1 -0
  43. package/dist/postgres-main.mjs +602 -0
  44. package/dist/postgres-main.mjs.map +1 -0
  45. package/dist/preflight-names-BPL-qsZF-BfoRgbgb.mjs +1252 -0
  46. package/dist/preflight-names-BPL-qsZF-BfoRgbgb.mjs.map +1 -0
  47. package/dist/{prisma-next-DlU01kXJ-2sP5mQb6.mjs → prisma-next-BvBwGCIt-DssgnctU.mjs} +21 -80
  48. package/dist/prisma-next-BvBwGCIt-DssgnctU.mjs.map +1 -0
  49. package/dist/prisma-next.d.mts +14 -5
  50. package/dist/prisma-next.d.mts.map +1 -0
  51. package/dist/prisma-next.mjs +1 -1
  52. package/dist/provisioned-edges-C1wSO3cx-YC5C19f9.mjs +151 -0
  53. package/dist/provisioned-edges-C1wSO3cx-YC5C19f9.mjs.map +1 -0
  54. package/dist/secret-rz_ZqNeq-_xd97Jd9.mjs +47 -0
  55. package/dist/secret-rz_ZqNeq-_xd97Jd9.mjs.map +1 -0
  56. package/dist/segments-NpKN-46R-C_LQ2urs.mjs +1454 -0
  57. package/dist/segments-NpKN-46R-C_LQ2urs.mjs.map +1 -0
  58. package/dist/{serializer-CX4VYdf_-KKGoAxfx.mjs → serializer-CJiOhF2d-DJUjQj1i.mjs} +34 -44
  59. package/dist/serializer-CJiOhF2d-DJUjQj1i.mjs.map +1 -0
  60. package/dist/serializer-Cx5slrV4-xJfH6EWS.d.mts.map +1 -0
  61. package/dist/storage/index.d.mts +52 -5
  62. package/dist/storage/index.d.mts.map +1 -0
  63. package/dist/storage/index.mjs +158 -47
  64. package/dist/storage/index.mjs.map +1 -1
  65. package/dist/storage/storage-entrypoint.mjs +370 -271
  66. package/dist/storage/storage-entrypoint.mjs.map +1 -1
  67. package/dist/storage/storage-service.mjs +158 -47
  68. package/dist/storage/storage-service.mjs.map +1 -1
  69. package/dist/storage/testing.d.mts +7 -6
  70. package/dist/storage/testing.d.mts.map +1 -0
  71. package/dist/storage/testing.mjs +4 -21
  72. package/dist/storage/testing.mjs.map +1 -1
  73. package/dist/streams/index.d.mts +56 -84
  74. package/dist/streams/index.d.mts.map +1 -0
  75. package/dist/streams/index.mjs +158 -47
  76. package/dist/streams/index.mjs.map +1 -1
  77. package/dist/streams/streams-entrypoint.mjs +366 -250
  78. package/dist/streams/streams-entrypoint.mjs.map +1 -1
  79. package/dist/streams/streams-service.mjs +158 -47
  80. package/dist/streams/streams-service.mjs.map +1 -1
  81. package/dist/streams/testing.d.mts.map +1 -0
  82. package/dist/testing.d.mts.map +1 -0
  83. package/dist/testing.mjs +1 -1
  84. package/dist/testing.mjs.map +1 -1
  85. package/package.json +34 -20
  86. package/src/exports/control.ts +1 -0
  87. package/src/exports/cron.ts +1 -0
  88. package/src/exports/email-testing.ts +1 -0
  89. package/src/exports/email.ts +1 -0
  90. package/src/exports/index.ts +1 -0
  91. package/src/exports/local-target.ts +1 -0
  92. package/src/exports/prisma-next.ts +1 -0
  93. package/src/exports/storage-testing.ts +1 -0
  94. package/src/exports/storage.ts +1 -0
  95. package/src/exports/streams-testing.ts +1 -0
  96. package/src/exports/streams.ts +1 -0
  97. package/src/exports/testing.ts +1 -0
  98. package/dist/prisma-next-DlU01kXJ-2sP5mQb6.mjs.map +0 -1
  99. package/dist/provisioned-edges-DIQAR4q4-Bn9op-JG.mjs +0 -151
  100. package/dist/provisioned-edges-DIQAR4q4-Bn9op-JG.mjs.map +0 -1
  101. package/dist/serializer-CX4VYdf_-KKGoAxfx.mjs.map +0 -1
@@ -0,0 +1,559 @@
1
+ import { a as readOwnVersion, i as readJsonFile, r as isValidSegment, t as StateFile } from "./segments-NpKN-46R-C_LQ2urs.mjs";
2
+ import { n as portNumbers, t as getPorts } from "./get-port-BYnpbW5H.mjs";
3
+ import * as fs from "node:fs";
4
+ import { spawn } from "node:child_process";
5
+ import * as path from "node:path";
6
+ import * as http from "node:http";
7
+ //#region ../../1-prisma-cloud/0-lowering/dev-emulators/dist/compute-main.mjs
8
+ /**
9
+ * The Compute emulator daemon (local-dev spec § 2 `compute-main.ts`): a small
10
+ * local counterpart of the platform's compute service. Owns service child
11
+ * processes (spawned `bun bootstrap.js` from a real packaged artifact),
12
+ * supervises them (crash backoff, held after repeated fast crashes), and
13
+ * serves a loopback JSON admin API plus per-service log streaming.
14
+ *
15
+ * Runs as its own OS process, started by `daemon.ts`'s `ensureDaemon` via
16
+ * `process.execPath <this file> --port <n> --state-dir <dir>`. Everything it
17
+ * needs travels on argv and each request's own body — it reads no
18
+ * environment variable of its own.
19
+ */
20
+ const MIN_SERVICE_PORT = 3e3;
21
+ const MAX_SERVICE_PORT = 65535;
22
+ const TERMINATE_GRACE_MS = 5e3;
23
+ const STABLE_UPTIME_MS = 3e4;
24
+ const BASE_BACKOFF_MS = 1e3;
25
+ const MAX_BACKOFF_MS = 3e4;
26
+ const MAX_CONSECUTIVE_FAST_EXITS = 5;
27
+ const LOG_POLL_INTERVAL_MS = 100;
28
+ const APPS_STATE_MODE = 384;
29
+ const BUN_NOT_FOUND_MESSAGE = "local dev runs services under bun — the Prisma Compute runtime — and `bun` was not found on PATH. Install it: https://bun.sh.";
30
+ function isServiceStatus(value) {
31
+ return value === "running" || value === "backoff" || value === "held" || value === "stopped";
32
+ }
33
+ function isStringRecord(value) {
34
+ return typeof value === "object" && value !== null && Object.values(value).every((v) => typeof v === "string");
35
+ }
36
+ function isServiceRecord(value) {
37
+ if (typeof value !== "object" || value === null) return false;
38
+ if (!("id" in value) || typeof value.id !== "string") return false;
39
+ if (!("address" in value) || typeof value.address !== "string") return false;
40
+ if (!("port" in value) || typeof value.port !== "number") return false;
41
+ if (!("status" in value) || !isServiceStatus(value.status)) return false;
42
+ if (!("logPath" in value) || typeof value.logPath !== "string") return false;
43
+ if ("pid" in value && value.pid !== void 0 && typeof value.pid !== "number") return false;
44
+ if ("lastExitCode" in value && value.lastExitCode !== void 0 && typeof value.lastExitCode !== "number") return false;
45
+ if ("artifactHash" in value && value.artifactHash !== void 0 && typeof value.artifactHash !== "string") return false;
46
+ if ("artifactDir" in value && value.artifactDir !== void 0 && typeof value.artifactDir !== "string") return false;
47
+ if ("env" in value && value.env !== void 0 && !isStringRecord(value.env)) return false;
48
+ return true;
49
+ }
50
+ function isAppRecord(value) {
51
+ return typeof value === "object" && value !== null && "services" in value && typeof value.services === "object" && value.services !== null && Object.values(value.services).every(isServiceRecord);
52
+ }
53
+ function isAppsState(value) {
54
+ return typeof value === "object" && value !== null && Object.values(value).every(isAppRecord);
55
+ }
56
+ function isDeploymentBody(value) {
57
+ return typeof value === "object" && value !== null && "address" in value && typeof value.address === "string" && "artifactDir" in value && typeof value.artifactDir === "string" && "artifactHash" in value && typeof value.artifactHash === "string" && "env" in value && isStringRecord(value.env) && "port" in value && typeof value.port === "number";
58
+ }
59
+ function sleep(ms) {
60
+ return new Promise((resolve) => setTimeout(resolve, ms));
61
+ }
62
+ /** Append-only per-service log file — child stdout/stderr plus `[emulator]` supervision lines. No rotation in v1. */
63
+ var ServiceLog = class {
64
+ filePath;
65
+ fd;
66
+ constructor(filePath) {
67
+ this.filePath = filePath;
68
+ }
69
+ ensureOpen() {
70
+ if (this.fd === void 0) {
71
+ fs.mkdirSync(path.dirname(this.filePath), { recursive: true });
72
+ this.fd = fs.openSync(this.filePath, "a");
73
+ }
74
+ return this.fd;
75
+ }
76
+ writeRaw(chunk) {
77
+ fs.writeSync(this.ensureOpen(), chunk);
78
+ }
79
+ writeLine(line) {
80
+ fs.writeSync(this.ensureOpen(), `${line}\n`);
81
+ }
82
+ /** Closes the fd if one was ever opened. Idempotent. */
83
+ close() {
84
+ if (this.fd === void 0) return;
85
+ fs.closeSync(this.fd);
86
+ this.fd = void 0;
87
+ }
88
+ };
89
+ function parseArgs(argv) {
90
+ let port;
91
+ let stateDir;
92
+ for (let i = 0; i < argv.length; i++) if (argv[i] === "--port") port = Number(argv[i + 1]);
93
+ else if (argv[i] === "--state-dir") stateDir = argv[i + 1];
94
+ if (port === void 0 || Number.isNaN(port)) throw new Error("compute-main: --port <n> is required");
95
+ if (stateDir === void 0) throw new Error("compute-main: --state-dir <dir> is required");
96
+ return {
97
+ port,
98
+ stateDir
99
+ };
100
+ }
101
+ /** Resolves `bun` from THIS request's env PATH — never the daemon's own. POSIX-only (Windows is out of scope). */
102
+ function resolveBunOnPath(env) {
103
+ const pathVar = env["PATH"];
104
+ if (!pathVar) return void 0;
105
+ for (const dir of pathVar.split(path.delimiter)) {
106
+ if (dir === "") continue;
107
+ const candidate = path.join(dir, "bun");
108
+ try {
109
+ fs.accessSync(candidate, fs.constants.X_OK);
110
+ return candidate;
111
+ } catch {}
112
+ }
113
+ }
114
+ function main() {
115
+ const { port, stateDir } = parseArgs(process.argv.slice(2));
116
+ const ownVersion = readOwnVersion();
117
+ const appsJsonPath = path.join(stateDir, "apps.json");
118
+ const stateFile = new StateFile(appsJsonPath, APPS_STATE_MODE);
119
+ let state = {};
120
+ const runtimes = /* @__PURE__ */ new Map();
121
+ function runtimeKey(app, id) {
122
+ return `${app}/${id}`;
123
+ }
124
+ function getRuntime(app, id) {
125
+ const key = runtimeKey(app, id);
126
+ let rt = runtimes.get(key);
127
+ if (!rt) {
128
+ rt = {
129
+ child: void 0,
130
+ consecutiveFastExits: 0,
131
+ startedAt: void 0,
132
+ stableTimer: void 0,
133
+ backoffTimer: void 0,
134
+ expectedExit: false,
135
+ log: void 0
136
+ };
137
+ runtimes.set(key, rt);
138
+ }
139
+ return rt;
140
+ }
141
+ function serviceLogPath(app, id) {
142
+ return path.join(stateDir, "logs", app, `${id}.log`);
143
+ }
144
+ function getLog(app, id, logPath) {
145
+ const rt = getRuntime(app, id);
146
+ if (!rt.log) rt.log = new ServiceLog(logPath);
147
+ return rt.log;
148
+ }
149
+ function schedulePersist() {
150
+ stateFile.write(state);
151
+ }
152
+ function clearStableTimer(rt) {
153
+ if (rt.stableTimer) clearTimeout(rt.stableTimer);
154
+ rt.stableTimer = void 0;
155
+ }
156
+ function clearBackoffTimer(rt) {
157
+ if (rt.backoffTimer) clearTimeout(rt.backoffTimer);
158
+ rt.backoffTimer = void 0;
159
+ }
160
+ function usedServicePorts() {
161
+ const ports = /* @__PURE__ */ new Set();
162
+ for (const app of Object.values(state)) for (const svc of Object.values(app.services)) ports.add(svc.port);
163
+ return ports;
164
+ }
165
+ /**
166
+ * The smallest genuinely free port at or above `MIN_SERVICE_PORT`,
167
+ * excluding this daemon's own persisted allocations — persistence and
168
+ * the range policy stay ours; whether a candidate is actually bindable
169
+ * is `get-port`'s (spec § 2's dependency razor).
170
+ */
171
+ async function smallestUnusedServicePort() {
172
+ return getPorts({
173
+ port: portNumbers(MIN_SERVICE_PORT, MAX_SERVICE_PORT),
174
+ exclude: usedServicePorts()
175
+ });
176
+ }
177
+ async function getOrCreateService(app, id) {
178
+ let appRec = state[app];
179
+ if (!appRec) {
180
+ appRec = { services: {} };
181
+ state[app] = appRec;
182
+ }
183
+ let svc = appRec.services[id];
184
+ if (svc) return svc;
185
+ svc = {
186
+ id,
187
+ address: id,
188
+ port: await smallestUnusedServicePort(),
189
+ status: "stopped",
190
+ logPath: serviceLogPath(app, id)
191
+ };
192
+ appRec.services[id] = svc;
193
+ schedulePersist();
194
+ return svc;
195
+ }
196
+ function waitForExit(child) {
197
+ return new Promise((resolve) => {
198
+ child.once("exit", () => resolve());
199
+ });
200
+ }
201
+ /** SIGTERM, wait up to `graceMs`, SIGKILL. Marks the exit as expected so supervision doesn't fire. */
202
+ async function killChild(rt, graceMs) {
203
+ const child = rt.child;
204
+ if (!child || child.pid === void 0) return;
205
+ rt.expectedExit = true;
206
+ const exited = waitForExit(child);
207
+ child.kill("SIGTERM");
208
+ const timedOut = Symbol("timeout");
209
+ if (await Promise.race([exited.then(() => void 0), sleep(graceMs).then(() => timedOut)]) === timedOut) {
210
+ child.kill("SIGKILL");
211
+ await exited;
212
+ }
213
+ }
214
+ function handleChildExit(app, id, svc, rt, code) {
215
+ clearStableTimer(rt);
216
+ rt.child = void 0;
217
+ delete svc.pid;
218
+ if (code !== null) svc.lastExitCode = code;
219
+ if (rt.expectedExit) {
220
+ rt.expectedExit = false;
221
+ schedulePersist();
222
+ return;
223
+ }
224
+ const uptime = rt.startedAt !== void 0 ? Date.now() - rt.startedAt : 0;
225
+ if (uptime >= STABLE_UPTIME_MS) rt.consecutiveFastExits = 0;
226
+ const attemptNumber = rt.consecutiveFastExits;
227
+ if (uptime < STABLE_UPTIME_MS) rt.consecutiveFastExits += 1;
228
+ if (rt.consecutiveFastExits >= MAX_CONSECUTIVE_FAST_EXITS) {
229
+ svc.status = "held";
230
+ schedulePersist();
231
+ getLog(app, id, svc.logPath).writeLine(`[emulator] held after ${String(MAX_CONSECUTIVE_FAST_EXITS)} consecutive fast exits — send a new deployment to resume`);
232
+ return;
233
+ }
234
+ const delayMs = Math.min(MAX_BACKOFF_MS, BASE_BACKOFF_MS * 2 ** attemptNumber);
235
+ svc.status = "backoff";
236
+ schedulePersist();
237
+ getLog(app, id, svc.logPath).writeLine(`[emulator] exited (code ${code === null ? "null" : String(code)}) — restarting in ${String(delayMs / 1e3)}s`);
238
+ rt.backoffTimer = setTimeout(() => {
239
+ rt.backoffTimer = void 0;
240
+ spawnService(app, id, svc, rt);
241
+ }, delayMs);
242
+ }
243
+ async function spawnService(app, id, svc, rt) {
244
+ const env = svc.env ?? {};
245
+ const artifactDir = svc.artifactDir ?? ".";
246
+ const bunPath = resolveBunOnPath(env);
247
+ if (!bunPath) {
248
+ svc.status = "stopped";
249
+ delete svc.pid;
250
+ schedulePersist();
251
+ return {
252
+ ok: false,
253
+ message: BUN_NOT_FOUND_MESSAGE
254
+ };
255
+ }
256
+ const log = getLog(app, id, svc.logPath);
257
+ const child = spawn(bunPath, ["bootstrap.js"], {
258
+ cwd: artifactDir,
259
+ env,
260
+ stdio: [
261
+ "ignore",
262
+ "pipe",
263
+ "pipe"
264
+ ]
265
+ });
266
+ rt.child = child;
267
+ rt.expectedExit = false;
268
+ rt.startedAt = Date.now();
269
+ clearStableTimer(rt);
270
+ rt.stableTimer = setTimeout(() => {
271
+ rt.consecutiveFastExits = 0;
272
+ }, STABLE_UPTIME_MS);
273
+ child.stdout?.on("data", (chunk) => log.writeRaw(chunk));
274
+ child.stderr?.on("data", (chunk) => log.writeRaw(chunk));
275
+ child.on("exit", (code) => handleChildExit(app, id, svc, rt, code));
276
+ svc.status = "running";
277
+ if (child.pid !== void 0) svc.pid = child.pid;
278
+ schedulePersist();
279
+ return { ok: true };
280
+ }
281
+ async function stopService(svc, rt) {
282
+ clearBackoffTimer(rt);
283
+ clearStableTimer(rt);
284
+ await killChild(rt, TERMINATE_GRACE_MS);
285
+ svc.status = "stopped";
286
+ delete svc.pid;
287
+ schedulePersist();
288
+ }
289
+ /**
290
+ * The session-resume signal: Alchemy's no-op reconcile never fires a
291
+ * deployment PUT, so nothing else restarts a service a previous session
292
+ * stopped. Reuses the exact deployment-PUT start rules — same spawn
293
+ * path, same supervision reset, and an explicit resume clears `held`
294
+ * the same way a deployment PUT does — but from the service's already
295
+ * STORED deployment spec, since no new one arrives with a start.
296
+ */
297
+ async function startService(app, id, svc) {
298
+ if (svc.artifactHash === void 0) return;
299
+ if (svc.status === "running") return;
300
+ const rt = getRuntime(app, id);
301
+ clearBackoffTimer(rt);
302
+ clearStableTimer(rt);
303
+ rt.consecutiveFastExits = 0;
304
+ await spawnService(app, id, svc, rt);
305
+ }
306
+ function json(res, status, body) {
307
+ const text = JSON.stringify(body);
308
+ res.writeHead(status, { "content-type": "application/json" });
309
+ res.end(text);
310
+ }
311
+ function text(res, status, body) {
312
+ res.writeHead(status, { "content-type": "text/plain; charset=utf-8" });
313
+ res.end(body);
314
+ }
315
+ function badSegment(res, segment) {
316
+ text(res, 400, `invalid path segment "${segment}": must match /^[a-z0-9][a-z0-9-]*$/ and be at most 63 characters`);
317
+ }
318
+ function readBody(req) {
319
+ return new Promise((resolve, reject) => {
320
+ const chunks = [];
321
+ req.on("data", (chunk) => chunks.push(chunk));
322
+ req.on("end", () => resolve(Buffer.concat(chunks)));
323
+ req.on("error", reject);
324
+ });
325
+ }
326
+ function serviceView(svc) {
327
+ return {
328
+ id: svc.id,
329
+ address: svc.address,
330
+ port: svc.port,
331
+ url: `http://localhost:${String(svc.port)}`,
332
+ status: svc.status,
333
+ ...svc.pid !== void 0 ? { pid: svc.pid } : {},
334
+ ...svc.lastExitCode !== void 0 ? { lastExitCode: svc.lastExitCode } : {},
335
+ ...svc.artifactHash !== void 0 ? { artifactHash: svc.artifactHash } : {},
336
+ logPath: svc.logPath
337
+ };
338
+ }
339
+ function envEquals(a, b) {
340
+ if (!a) return false;
341
+ const aKeys = Object.keys(a);
342
+ const bKeys = Object.keys(b);
343
+ if (aKeys.length !== bKeys.length) return false;
344
+ return aKeys.every((k) => a[k] === b[k]);
345
+ }
346
+ async function handleDeployment(req, res, app, id) {
347
+ const raw = await readBody(req);
348
+ let parsed;
349
+ try {
350
+ parsed = JSON.parse(raw.toString("utf8"));
351
+ } catch {
352
+ return text(res, 400, "malformed JSON body");
353
+ }
354
+ if (!isDeploymentBody(parsed)) return text(res, 400, "malformed deployment body");
355
+ const svc = await getOrCreateService(app, id);
356
+ const rt = getRuntime(app, id);
357
+ const changed = svc.artifactHash !== parsed.artifactHash || !envEquals(svc.env, parsed.env);
358
+ if (svc.status === "running") {
359
+ if (!changed) {
360
+ res.writeHead(204);
361
+ res.end();
362
+ return;
363
+ }
364
+ await killChild(rt, TERMINATE_GRACE_MS);
365
+ } else clearBackoffTimer(rt);
366
+ svc.address = parsed.address;
367
+ svc.port = parsed.port;
368
+ clearStableTimer(rt);
369
+ rt.consecutiveFastExits = 0;
370
+ svc.artifactHash = parsed.artifactHash;
371
+ svc.env = parsed.env;
372
+ svc.artifactDir = parsed.artifactDir;
373
+ schedulePersist();
374
+ const result = await spawnService(app, id, svc, rt);
375
+ if (!result.ok) return text(res, 500, result.message ?? "failed to start the service");
376
+ res.writeHead(204);
377
+ res.end();
378
+ }
379
+ /** Byte offset a follow should start at to show the last `tailLines` complete lines; `<= 0` (or an empty file) starts at the current end, no backlog. */
380
+ function startOffsetForTail(logPath, tailLines) {
381
+ let size = 0;
382
+ try {
383
+ size = fs.statSync(logPath).size;
384
+ } catch {
385
+ return 0;
386
+ }
387
+ if (tailLines <= 0 || size === 0) return size;
388
+ const fd = fs.openSync(logPath, "r");
389
+ try {
390
+ const buf = Buffer.alloc(size);
391
+ fs.readSync(fd, buf, 0, size, 0);
392
+ let seen = 0;
393
+ for (let i = size - 1; i >= 0; i -= 1) {
394
+ if (buf[i] !== 10) continue;
395
+ if (i === size - 1) continue;
396
+ seen += 1;
397
+ if (seen === tailLines) return i + 1;
398
+ }
399
+ return 0;
400
+ } finally {
401
+ fs.closeSync(fd);
402
+ }
403
+ }
404
+ function handleFollowLogs(res, logPath, tailLines) {
405
+ fs.mkdirSync(path.dirname(logPath), { recursive: true });
406
+ if (!fs.existsSync(logPath)) fs.closeSync(fs.openSync(logPath, "a"));
407
+ res.writeHead(200, { "content-type": "text/plain; charset=utf-8" });
408
+ let offset = startOffsetForTail(logPath, tailLines);
409
+ let stopped = false;
410
+ let timer;
411
+ const tick = () => {
412
+ if (stopped) return;
413
+ let stat;
414
+ try {
415
+ stat = fs.statSync(logPath);
416
+ } catch {
417
+ stat = void 0;
418
+ }
419
+ if (stat && stat.size > offset) {
420
+ const fd = fs.openSync(logPath, "r");
421
+ const buf = Buffer.alloc(stat.size - offset);
422
+ fs.readSync(fd, buf, 0, buf.length, offset);
423
+ fs.closeSync(fd);
424
+ offset = stat.size;
425
+ res.write(buf);
426
+ }
427
+ if (!stopped) timer = setTimeout(tick, LOG_POLL_INTERVAL_MS);
428
+ };
429
+ tick();
430
+ const stop = () => {
431
+ stopped = true;
432
+ if (timer) clearTimeout(timer);
433
+ };
434
+ res.on("close", stop);
435
+ }
436
+ async function handleRequest(req, res) {
437
+ const url = new URL(req.url ?? "/", `http://127.0.0.1:${String(port)}`);
438
+ const method = req.method ?? "GET";
439
+ const segments = url.pathname.split("/").filter((s) => s.length > 0).map((s) => decodeURIComponent(s));
440
+ if (method === "GET" && segments.length === 1 && segments[0] === "health") return json(res, 200, { version: ownVersion });
441
+ if (segments[0] === "apps" && segments.length >= 2) {
442
+ const app = segments[1];
443
+ if (app === void 0 || !isValidSegment(app)) return badSegment(res, app ?? "");
444
+ if (method === "PUT" && segments.length === 4 && segments[2] === "services") {
445
+ const id = segments[3];
446
+ if (id === void 0 || !isValidSegment(id)) return badSegment(res, id ?? "");
447
+ const svc = await getOrCreateService(app, id);
448
+ return json(res, 200, {
449
+ port: svc.port,
450
+ url: `http://localhost:${String(svc.port)}`
451
+ });
452
+ }
453
+ if (method === "PUT" && segments.length === 5 && segments[2] === "services" && segments[4] === "deployment") {
454
+ const id = segments[3];
455
+ if (id === void 0 || !isValidSegment(id)) return badSegment(res, id ?? "");
456
+ return handleDeployment(req, res, app, id);
457
+ }
458
+ if (method === "GET" && segments.length === 3 && segments[2] === "services") {
459
+ const appRec = state[app];
460
+ return json(res, 200, appRec ? Object.values(appRec.services).map(serviceView) : []);
461
+ }
462
+ if (method === "GET" && segments.length === 5 && segments[2] === "services" && segments[4] === "logs") {
463
+ const id = segments[3];
464
+ if (id === void 0 || !isValidSegment(id)) return badSegment(res, id ?? "");
465
+ const svc = state[app]?.services[id];
466
+ if (!svc) return text(res, 404, `no such service "${id}" in app "${app}"`);
467
+ if (url.searchParams.get("follow") === "1") {
468
+ const tailParam = url.searchParams.get("tail");
469
+ const tailLines = tailParam === null ? 0 : Math.max(0, Number.parseInt(tailParam, 10) || 0);
470
+ handleFollowLogs(res, svc.logPath, tailLines);
471
+ return;
472
+ }
473
+ const content = fs.existsSync(svc.logPath) ? fs.readFileSync(svc.logPath) : Buffer.alloc(0);
474
+ res.writeHead(200, { "content-type": "text/plain; charset=utf-8" });
475
+ res.end(content);
476
+ return;
477
+ }
478
+ if (method === "POST" && segments.length === 3 && segments[2] === "stop") {
479
+ const appRec = state[app];
480
+ if (appRec) await Promise.all(Object.entries(appRec.services).map(([id, svc]) => stopService(svc, getRuntime(app, id))));
481
+ res.writeHead(204);
482
+ res.end();
483
+ return;
484
+ }
485
+ if (method === "POST" && segments.length === 3 && segments[2] === "start") {
486
+ const appRec = state[app];
487
+ if (appRec) await Promise.all(Object.entries(appRec.services).map(([id, svc]) => startService(app, id, svc)));
488
+ res.writeHead(204);
489
+ res.end();
490
+ return;
491
+ }
492
+ if (method === "DELETE" && segments.length === 2) {
493
+ const appRec = state[app];
494
+ if (appRec) {
495
+ await Promise.all(Object.entries(appRec.services).map(([id, svc]) => stopService(svc, getRuntime(app, id))));
496
+ for (const id of Object.keys(appRec.services)) {
497
+ const key = runtimeKey(app, id);
498
+ runtimes.get(key)?.log?.close();
499
+ runtimes.delete(key);
500
+ }
501
+ delete state[app];
502
+ schedulePersist();
503
+ }
504
+ await fs.promises.rm(path.join(stateDir, "logs", app), {
505
+ recursive: true,
506
+ force: true
507
+ });
508
+ res.writeHead(204);
509
+ res.end();
510
+ return;
511
+ }
512
+ }
513
+ res.writeHead(404);
514
+ res.end();
515
+ }
516
+ const server = http.createServer((req, res) => {
517
+ handleRequest(req, res).catch((err) => {
518
+ if (!res.headersSent) res.writeHead(500, { "content-type": "text/plain" });
519
+ res.end(err instanceof Error ? err.message : String(err));
520
+ });
521
+ });
522
+ let shuttingDown = false;
523
+ async function shutdown() {
524
+ if (shuttingDown) return;
525
+ shuttingDown = true;
526
+ const kills = [];
527
+ for (const rt of runtimes.values()) {
528
+ clearBackoffTimer(rt);
529
+ clearStableTimer(rt);
530
+ if (rt.child) kills.push(killChild(rt, TERMINATE_GRACE_MS));
531
+ }
532
+ await Promise.all(kills);
533
+ await stateFile.flush();
534
+ server.close();
535
+ process.exit(0);
536
+ }
537
+ process.on("SIGTERM", () => void shutdown());
538
+ process.on("SIGINT", () => void shutdown());
539
+ readJsonFile(appsJsonPath, isAppsState).then((loaded) => {
540
+ if (loaded) {
541
+ for (const appRec of Object.values(loaded)) for (const svc of Object.values(appRec.services)) if (svc.status === "running" || svc.status === "backoff") {
542
+ svc.status = "stopped";
543
+ delete svc.pid;
544
+ }
545
+ state = loaded;
546
+ }
547
+ server.listen(port, "127.0.0.1", () => {
548
+ console.log(`[dev-emulators] compute-main listening on 127.0.0.1:${String(port)}`);
549
+ });
550
+ }).catch((err) => {
551
+ console.error("compute-main: failed to load state", err);
552
+ process.exit(1);
553
+ });
554
+ }
555
+ main();
556
+ //#endregion
557
+ export {};
558
+
559
+ //# sourceMappingURL=compute-main.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compute-main.mjs","names":["getPort"],"sources":["../../../1-prisma-cloud/0-lowering/dev-emulators/dist/compute-main.mjs"],"sourcesContent":["import { c as readOwnVersion, f as StateFile, p as readJsonFile, t as isValidSegment } from \"./segments-NpKN-46R.mjs\";\nimport * as fs from \"node:fs\";\nimport { spawn } from \"node:child_process\";\nimport * as path from \"node:path\";\nimport getPort, { portNumbers } from \"get-port\";\nimport * as http from \"node:http\";\n//#region src/compute-main.ts\n/**\n* The Compute emulator daemon (local-dev spec § 2 `compute-main.ts`): a small\n* local counterpart of the platform's compute service. Owns service child\n* processes (spawned `bun bootstrap.js` from a real packaged artifact),\n* supervises them (crash backoff, held after repeated fast crashes), and\n* serves a loopback JSON admin API plus per-service log streaming.\n*\n* Runs as its own OS process, started by `daemon.ts`'s `ensureDaemon` via\n* `process.execPath <this file> --port <n> --state-dir <dir>`. Everything it\n* needs travels on argv and each request's own body — it reads no\n* environment variable of its own.\n*/\nconst MIN_SERVICE_PORT = 3e3;\nconst MAX_SERVICE_PORT = 65535;\nconst TERMINATE_GRACE_MS = 5e3;\nconst STABLE_UPTIME_MS = 3e4;\nconst BASE_BACKOFF_MS = 1e3;\nconst MAX_BACKOFF_MS = 3e4;\nconst MAX_CONSECUTIVE_FAST_EXITS = 5;\nconst LOG_POLL_INTERVAL_MS = 100;\nconst APPS_STATE_MODE = 384;\nconst BUN_NOT_FOUND_MESSAGE = \"local dev runs services under bun — the Prisma Compute runtime — and `bun` was not found on PATH. Install it: https://bun.sh.\";\nfunction isServiceStatus(value) {\n\treturn value === \"running\" || value === \"backoff\" || value === \"held\" || value === \"stopped\";\n}\nfunction isStringRecord(value) {\n\treturn typeof value === \"object\" && value !== null && Object.values(value).every((v) => typeof v === \"string\");\n}\nfunction isServiceRecord(value) {\n\tif (typeof value !== \"object\" || value === null) return false;\n\tif (!(\"id\" in value) || typeof value.id !== \"string\") return false;\n\tif (!(\"address\" in value) || typeof value.address !== \"string\") return false;\n\tif (!(\"port\" in value) || typeof value.port !== \"number\") return false;\n\tif (!(\"status\" in value) || !isServiceStatus(value.status)) return false;\n\tif (!(\"logPath\" in value) || typeof value.logPath !== \"string\") return false;\n\tif (\"pid\" in value && value.pid !== void 0 && typeof value.pid !== \"number\") return false;\n\tif (\"lastExitCode\" in value && value.lastExitCode !== void 0 && typeof value.lastExitCode !== \"number\") return false;\n\tif (\"artifactHash\" in value && value.artifactHash !== void 0 && typeof value.artifactHash !== \"string\") return false;\n\tif (\"artifactDir\" in value && value.artifactDir !== void 0 && typeof value.artifactDir !== \"string\") return false;\n\tif (\"env\" in value && value.env !== void 0 && !isStringRecord(value.env)) return false;\n\treturn true;\n}\nfunction isAppRecord(value) {\n\treturn typeof value === \"object\" && value !== null && \"services\" in value && typeof value.services === \"object\" && value.services !== null && Object.values(value.services).every(isServiceRecord);\n}\nfunction isAppsState(value) {\n\treturn typeof value === \"object\" && value !== null && Object.values(value).every(isAppRecord);\n}\nfunction isDeploymentBody(value) {\n\treturn typeof value === \"object\" && value !== null && \"address\" in value && typeof value.address === \"string\" && \"artifactDir\" in value && typeof value.artifactDir === \"string\" && \"artifactHash\" in value && typeof value.artifactHash === \"string\" && \"env\" in value && isStringRecord(value.env) && \"port\" in value && typeof value.port === \"number\";\n}\nfunction sleep(ms) {\n\treturn new Promise((resolve) => setTimeout(resolve, ms));\n}\n/** Append-only per-service log file — child stdout/stderr plus `[emulator]` supervision lines. No rotation in v1. */\nvar ServiceLog = class {\n\tfilePath;\n\tfd;\n\tconstructor(filePath) {\n\t\tthis.filePath = filePath;\n\t}\n\tensureOpen() {\n\t\tif (this.fd === void 0) {\n\t\t\tfs.mkdirSync(path.dirname(this.filePath), { recursive: true });\n\t\t\tthis.fd = fs.openSync(this.filePath, \"a\");\n\t\t}\n\t\treturn this.fd;\n\t}\n\twriteRaw(chunk) {\n\t\tfs.writeSync(this.ensureOpen(), chunk);\n\t}\n\twriteLine(line) {\n\t\tfs.writeSync(this.ensureOpen(), `${line}\\n`);\n\t}\n\t/** Closes the fd if one was ever opened. Idempotent. */\n\tclose() {\n\t\tif (this.fd === void 0) return;\n\t\tfs.closeSync(this.fd);\n\t\tthis.fd = void 0;\n\t}\n};\nfunction parseArgs(argv) {\n\tlet port;\n\tlet stateDir;\n\tfor (let i = 0; i < argv.length; i++) if (argv[i] === \"--port\") port = Number(argv[i + 1]);\n\telse if (argv[i] === \"--state-dir\") stateDir = argv[i + 1];\n\tif (port === void 0 || Number.isNaN(port)) throw new Error(\"compute-main: --port <n> is required\");\n\tif (stateDir === void 0) throw new Error(\"compute-main: --state-dir <dir> is required\");\n\treturn {\n\t\tport,\n\t\tstateDir\n\t};\n}\n/** Resolves `bun` from THIS request's env PATH — never the daemon's own. POSIX-only (Windows is out of scope). */\nfunction resolveBunOnPath(env) {\n\tconst pathVar = env[\"PATH\"];\n\tif (!pathVar) return void 0;\n\tfor (const dir of pathVar.split(path.delimiter)) {\n\t\tif (dir === \"\") continue;\n\t\tconst candidate = path.join(dir, \"bun\");\n\t\ttry {\n\t\t\tfs.accessSync(candidate, fs.constants.X_OK);\n\t\t\treturn candidate;\n\t\t} catch {}\n\t}\n}\nfunction main() {\n\tconst { port, stateDir } = parseArgs(process.argv.slice(2));\n\tconst ownVersion = readOwnVersion();\n\tconst appsJsonPath = path.join(stateDir, \"apps.json\");\n\tconst stateFile = new StateFile(appsJsonPath, APPS_STATE_MODE);\n\tlet state = {};\n\tconst runtimes = /* @__PURE__ */ new Map();\n\tfunction runtimeKey(app, id) {\n\t\treturn `${app}/${id}`;\n\t}\n\tfunction getRuntime(app, id) {\n\t\tconst key = runtimeKey(app, id);\n\t\tlet rt = runtimes.get(key);\n\t\tif (!rt) {\n\t\t\trt = {\n\t\t\t\tchild: void 0,\n\t\t\t\tconsecutiveFastExits: 0,\n\t\t\t\tstartedAt: void 0,\n\t\t\t\tstableTimer: void 0,\n\t\t\t\tbackoffTimer: void 0,\n\t\t\t\texpectedExit: false,\n\t\t\t\tlog: void 0\n\t\t\t};\n\t\t\truntimes.set(key, rt);\n\t\t}\n\t\treturn rt;\n\t}\n\tfunction serviceLogPath(app, id) {\n\t\treturn path.join(stateDir, \"logs\", app, `${id}.log`);\n\t}\n\tfunction getLog(app, id, logPath) {\n\t\tconst rt = getRuntime(app, id);\n\t\tif (!rt.log) rt.log = new ServiceLog(logPath);\n\t\treturn rt.log;\n\t}\n\tfunction schedulePersist() {\n\t\tstateFile.write(state);\n\t}\n\tfunction clearStableTimer(rt) {\n\t\tif (rt.stableTimer) clearTimeout(rt.stableTimer);\n\t\trt.stableTimer = void 0;\n\t}\n\tfunction clearBackoffTimer(rt) {\n\t\tif (rt.backoffTimer) clearTimeout(rt.backoffTimer);\n\t\trt.backoffTimer = void 0;\n\t}\n\tfunction usedServicePorts() {\n\t\tconst ports = /* @__PURE__ */ new Set();\n\t\tfor (const app of Object.values(state)) for (const svc of Object.values(app.services)) ports.add(svc.port);\n\t\treturn ports;\n\t}\n\t/**\n\t* The smallest genuinely free port at or above `MIN_SERVICE_PORT`,\n\t* excluding this daemon's own persisted allocations — persistence and\n\t* the range policy stay ours; whether a candidate is actually bindable\n\t* is `get-port`'s (spec § 2's dependency razor).\n\t*/\n\tasync function smallestUnusedServicePort() {\n\t\treturn getPort({\n\t\t\tport: portNumbers(MIN_SERVICE_PORT, MAX_SERVICE_PORT),\n\t\t\texclude: usedServicePorts()\n\t\t});\n\t}\n\tasync function getOrCreateService(app, id) {\n\t\tlet appRec = state[app];\n\t\tif (!appRec) {\n\t\t\tappRec = { services: {} };\n\t\t\tstate[app] = appRec;\n\t\t}\n\t\tlet svc = appRec.services[id];\n\t\tif (svc) return svc;\n\t\tsvc = {\n\t\t\tid,\n\t\t\taddress: id,\n\t\t\tport: await smallestUnusedServicePort(),\n\t\t\tstatus: \"stopped\",\n\t\t\tlogPath: serviceLogPath(app, id)\n\t\t};\n\t\tappRec.services[id] = svc;\n\t\tschedulePersist();\n\t\treturn svc;\n\t}\n\tfunction waitForExit(child) {\n\t\treturn new Promise((resolve) => {\n\t\t\tchild.once(\"exit\", () => resolve());\n\t\t});\n\t}\n\t/** SIGTERM, wait up to `graceMs`, SIGKILL. Marks the exit as expected so supervision doesn't fire. */\n\tasync function killChild(rt, graceMs) {\n\t\tconst child = rt.child;\n\t\tif (!child || child.pid === void 0) return;\n\t\trt.expectedExit = true;\n\t\tconst exited = waitForExit(child);\n\t\tchild.kill(\"SIGTERM\");\n\t\tconst timedOut = Symbol(\"timeout\");\n\t\tif (await Promise.race([exited.then(() => void 0), sleep(graceMs).then(() => timedOut)]) === timedOut) {\n\t\t\tchild.kill(\"SIGKILL\");\n\t\t\tawait exited;\n\t\t}\n\t}\n\tfunction handleChildExit(app, id, svc, rt, code) {\n\t\tclearStableTimer(rt);\n\t\trt.child = void 0;\n\t\tdelete svc.pid;\n\t\tif (code !== null) svc.lastExitCode = code;\n\t\tif (rt.expectedExit) {\n\t\t\trt.expectedExit = false;\n\t\t\tschedulePersist();\n\t\t\treturn;\n\t\t}\n\t\tconst uptime = rt.startedAt !== void 0 ? Date.now() - rt.startedAt : 0;\n\t\tif (uptime >= STABLE_UPTIME_MS) rt.consecutiveFastExits = 0;\n\t\tconst attemptNumber = rt.consecutiveFastExits;\n\t\tif (uptime < STABLE_UPTIME_MS) rt.consecutiveFastExits += 1;\n\t\tif (rt.consecutiveFastExits >= MAX_CONSECUTIVE_FAST_EXITS) {\n\t\t\tsvc.status = \"held\";\n\t\t\tschedulePersist();\n\t\t\tgetLog(app, id, svc.logPath).writeLine(`[emulator] held after ${String(MAX_CONSECUTIVE_FAST_EXITS)} consecutive fast exits — send a new deployment to resume`);\n\t\t\treturn;\n\t\t}\n\t\tconst delayMs = Math.min(MAX_BACKOFF_MS, BASE_BACKOFF_MS * 2 ** attemptNumber);\n\t\tsvc.status = \"backoff\";\n\t\tschedulePersist();\n\t\tgetLog(app, id, svc.logPath).writeLine(`[emulator] exited (code ${code === null ? \"null\" : String(code)}) — restarting in ${String(delayMs / 1e3)}s`);\n\t\trt.backoffTimer = setTimeout(() => {\n\t\t\trt.backoffTimer = void 0;\n\t\t\tspawnService(app, id, svc, rt);\n\t\t}, delayMs);\n\t}\n\tasync function spawnService(app, id, svc, rt) {\n\t\tconst env = svc.env ?? {};\n\t\tconst artifactDir = svc.artifactDir ?? \".\";\n\t\tconst bunPath = resolveBunOnPath(env);\n\t\tif (!bunPath) {\n\t\t\tsvc.status = \"stopped\";\n\t\t\tdelete svc.pid;\n\t\t\tschedulePersist();\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\tmessage: BUN_NOT_FOUND_MESSAGE\n\t\t\t};\n\t\t}\n\t\tconst log = getLog(app, id, svc.logPath);\n\t\tconst child = spawn(bunPath, [\"bootstrap.js\"], {\n\t\t\tcwd: artifactDir,\n\t\t\tenv,\n\t\t\tstdio: [\n\t\t\t\t\"ignore\",\n\t\t\t\t\"pipe\",\n\t\t\t\t\"pipe\"\n\t\t\t]\n\t\t});\n\t\trt.child = child;\n\t\trt.expectedExit = false;\n\t\trt.startedAt = Date.now();\n\t\tclearStableTimer(rt);\n\t\trt.stableTimer = setTimeout(() => {\n\t\t\trt.consecutiveFastExits = 0;\n\t\t}, STABLE_UPTIME_MS);\n\t\tchild.stdout?.on(\"data\", (chunk) => log.writeRaw(chunk));\n\t\tchild.stderr?.on(\"data\", (chunk) => log.writeRaw(chunk));\n\t\tchild.on(\"exit\", (code) => handleChildExit(app, id, svc, rt, code));\n\t\tsvc.status = \"running\";\n\t\tif (child.pid !== void 0) svc.pid = child.pid;\n\t\tschedulePersist();\n\t\treturn { ok: true };\n\t}\n\tasync function stopService(svc, rt) {\n\t\tclearBackoffTimer(rt);\n\t\tclearStableTimer(rt);\n\t\tawait killChild(rt, TERMINATE_GRACE_MS);\n\t\tsvc.status = \"stopped\";\n\t\tdelete svc.pid;\n\t\tschedulePersist();\n\t}\n\t/**\n\t* The session-resume signal: Alchemy's no-op reconcile never fires a\n\t* deployment PUT, so nothing else restarts a service a previous session\n\t* stopped. Reuses the exact deployment-PUT start rules — same spawn\n\t* path, same supervision reset, and an explicit resume clears `held`\n\t* the same way a deployment PUT does — but from the service's already\n\t* STORED deployment spec, since no new one arrives with a start.\n\t*/\n\tasync function startService(app, id, svc) {\n\t\tif (svc.artifactHash === void 0) return;\n\t\tif (svc.status === \"running\") return;\n\t\tconst rt = getRuntime(app, id);\n\t\tclearBackoffTimer(rt);\n\t\tclearStableTimer(rt);\n\t\trt.consecutiveFastExits = 0;\n\t\tawait spawnService(app, id, svc, rt);\n\t}\n\tfunction json(res, status, body) {\n\t\tconst text = JSON.stringify(body);\n\t\tres.writeHead(status, { \"content-type\": \"application/json\" });\n\t\tres.end(text);\n\t}\n\tfunction text(res, status, body) {\n\t\tres.writeHead(status, { \"content-type\": \"text/plain; charset=utf-8\" });\n\t\tres.end(body);\n\t}\n\tfunction badSegment(res, segment) {\n\t\ttext(res, 400, `invalid path segment \"${segment}\": must match /^[a-z0-9][a-z0-9-]*$/ and be at most 63 characters`);\n\t}\n\tfunction readBody(req) {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tconst chunks = [];\n\t\t\treq.on(\"data\", (chunk) => chunks.push(chunk));\n\t\t\treq.on(\"end\", () => resolve(Buffer.concat(chunks)));\n\t\t\treq.on(\"error\", reject);\n\t\t});\n\t}\n\tfunction serviceView(svc) {\n\t\treturn {\n\t\t\tid: svc.id,\n\t\t\taddress: svc.address,\n\t\t\tport: svc.port,\n\t\t\turl: `http://localhost:${String(svc.port)}`,\n\t\t\tstatus: svc.status,\n\t\t\t...svc.pid !== void 0 ? { pid: svc.pid } : {},\n\t\t\t...svc.lastExitCode !== void 0 ? { lastExitCode: svc.lastExitCode } : {},\n\t\t\t...svc.artifactHash !== void 0 ? { artifactHash: svc.artifactHash } : {},\n\t\t\tlogPath: svc.logPath\n\t\t};\n\t}\n\tfunction envEquals(a, b) {\n\t\tif (!a) return false;\n\t\tconst aKeys = Object.keys(a);\n\t\tconst bKeys = Object.keys(b);\n\t\tif (aKeys.length !== bKeys.length) return false;\n\t\treturn aKeys.every((k) => a[k] === b[k]);\n\t}\n\tasync function handleDeployment(req, res, app, id) {\n\t\tconst raw = await readBody(req);\n\t\tlet parsed;\n\t\ttry {\n\t\t\tparsed = JSON.parse(raw.toString(\"utf8\"));\n\t\t} catch {\n\t\t\treturn text(res, 400, \"malformed JSON body\");\n\t\t}\n\t\tif (!isDeploymentBody(parsed)) return text(res, 400, \"malformed deployment body\");\n\t\tconst svc = await getOrCreateService(app, id);\n\t\tconst rt = getRuntime(app, id);\n\t\tconst changed = svc.artifactHash !== parsed.artifactHash || !envEquals(svc.env, parsed.env);\n\t\tif (svc.status === \"running\") {\n\t\t\tif (!changed) {\n\t\t\t\tres.writeHead(204);\n\t\t\t\tres.end();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tawait killChild(rt, TERMINATE_GRACE_MS);\n\t\t} else clearBackoffTimer(rt);\n\t\tsvc.address = parsed.address;\n\t\tsvc.port = parsed.port;\n\t\tclearStableTimer(rt);\n\t\trt.consecutiveFastExits = 0;\n\t\tsvc.artifactHash = parsed.artifactHash;\n\t\tsvc.env = parsed.env;\n\t\tsvc.artifactDir = parsed.artifactDir;\n\t\tschedulePersist();\n\t\tconst result = await spawnService(app, id, svc, rt);\n\t\tif (!result.ok) return text(res, 500, result.message ?? \"failed to start the service\");\n\t\tres.writeHead(204);\n\t\tres.end();\n\t}\n\t/** Byte offset a follow should start at to show the last `tailLines` complete lines; `<= 0` (or an empty file) starts at the current end, no backlog. */\n\tfunction startOffsetForTail(logPath, tailLines) {\n\t\tlet size = 0;\n\t\ttry {\n\t\t\tsize = fs.statSync(logPath).size;\n\t\t} catch {\n\t\t\treturn 0;\n\t\t}\n\t\tif (tailLines <= 0 || size === 0) return size;\n\t\tconst fd = fs.openSync(logPath, \"r\");\n\t\ttry {\n\t\t\tconst buf = Buffer.alloc(size);\n\t\t\tfs.readSync(fd, buf, 0, size, 0);\n\t\t\tlet seen = 0;\n\t\t\tfor (let i = size - 1; i >= 0; i -= 1) {\n\t\t\t\tif (buf[i] !== 10) continue;\n\t\t\t\tif (i === size - 1) continue;\n\t\t\t\tseen += 1;\n\t\t\t\tif (seen === tailLines) return i + 1;\n\t\t\t}\n\t\t\treturn 0;\n\t\t} finally {\n\t\t\tfs.closeSync(fd);\n\t\t}\n\t}\n\tfunction handleFollowLogs(res, logPath, tailLines) {\n\t\tfs.mkdirSync(path.dirname(logPath), { recursive: true });\n\t\tif (!fs.existsSync(logPath)) fs.closeSync(fs.openSync(logPath, \"a\"));\n\t\tres.writeHead(200, { \"content-type\": \"text/plain; charset=utf-8\" });\n\t\tlet offset = startOffsetForTail(logPath, tailLines);\n\t\tlet stopped = false;\n\t\tlet timer;\n\t\tconst tick = () => {\n\t\t\tif (stopped) return;\n\t\t\tlet stat;\n\t\t\ttry {\n\t\t\t\tstat = fs.statSync(logPath);\n\t\t\t} catch {\n\t\t\t\tstat = void 0;\n\t\t\t}\n\t\t\tif (stat && stat.size > offset) {\n\t\t\t\tconst fd = fs.openSync(logPath, \"r\");\n\t\t\t\tconst buf = Buffer.alloc(stat.size - offset);\n\t\t\t\tfs.readSync(fd, buf, 0, buf.length, offset);\n\t\t\t\tfs.closeSync(fd);\n\t\t\t\toffset = stat.size;\n\t\t\t\tres.write(buf);\n\t\t\t}\n\t\t\tif (!stopped) timer = setTimeout(tick, LOG_POLL_INTERVAL_MS);\n\t\t};\n\t\ttick();\n\t\tconst stop = () => {\n\t\t\tstopped = true;\n\t\t\tif (timer) clearTimeout(timer);\n\t\t};\n\t\tres.on(\"close\", stop);\n\t}\n\tasync function handleRequest(req, res) {\n\t\tconst url = new URL(req.url ?? \"/\", `http://127.0.0.1:${String(port)}`);\n\t\tconst method = req.method ?? \"GET\";\n\t\tconst segments = url.pathname.split(\"/\").filter((s) => s.length > 0).map((s) => decodeURIComponent(s));\n\t\tif (method === \"GET\" && segments.length === 1 && segments[0] === \"health\") return json(res, 200, { version: ownVersion });\n\t\tif (segments[0] === \"apps\" && segments.length >= 2) {\n\t\t\tconst app = segments[1];\n\t\t\tif (app === void 0 || !isValidSegment(app)) return badSegment(res, app ?? \"\");\n\t\t\tif (method === \"PUT\" && segments.length === 4 && segments[2] === \"services\") {\n\t\t\t\tconst id = segments[3];\n\t\t\t\tif (id === void 0 || !isValidSegment(id)) return badSegment(res, id ?? \"\");\n\t\t\t\tconst svc = await getOrCreateService(app, id);\n\t\t\t\treturn json(res, 200, {\n\t\t\t\t\tport: svc.port,\n\t\t\t\t\turl: `http://localhost:${String(svc.port)}`\n\t\t\t\t});\n\t\t\t}\n\t\t\tif (method === \"PUT\" && segments.length === 5 && segments[2] === \"services\" && segments[4] === \"deployment\") {\n\t\t\t\tconst id = segments[3];\n\t\t\t\tif (id === void 0 || !isValidSegment(id)) return badSegment(res, id ?? \"\");\n\t\t\t\treturn handleDeployment(req, res, app, id);\n\t\t\t}\n\t\t\tif (method === \"GET\" && segments.length === 3 && segments[2] === \"services\") {\n\t\t\t\tconst appRec = state[app];\n\t\t\t\treturn json(res, 200, appRec ? Object.values(appRec.services).map(serviceView) : []);\n\t\t\t}\n\t\t\tif (method === \"GET\" && segments.length === 5 && segments[2] === \"services\" && segments[4] === \"logs\") {\n\t\t\t\tconst id = segments[3];\n\t\t\t\tif (id === void 0 || !isValidSegment(id)) return badSegment(res, id ?? \"\");\n\t\t\t\tconst svc = state[app]?.services[id];\n\t\t\t\tif (!svc) return text(res, 404, `no such service \"${id}\" in app \"${app}\"`);\n\t\t\t\tif (url.searchParams.get(\"follow\") === \"1\") {\n\t\t\t\t\tconst tailParam = url.searchParams.get(\"tail\");\n\t\t\t\t\tconst tailLines = tailParam === null ? 0 : Math.max(0, Number.parseInt(tailParam, 10) || 0);\n\t\t\t\t\thandleFollowLogs(res, svc.logPath, tailLines);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst content = fs.existsSync(svc.logPath) ? fs.readFileSync(svc.logPath) : Buffer.alloc(0);\n\t\t\t\tres.writeHead(200, { \"content-type\": \"text/plain; charset=utf-8\" });\n\t\t\t\tres.end(content);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (method === \"POST\" && segments.length === 3 && segments[2] === \"stop\") {\n\t\t\t\tconst appRec = state[app];\n\t\t\t\tif (appRec) await Promise.all(Object.entries(appRec.services).map(([id, svc]) => stopService(svc, getRuntime(app, id))));\n\t\t\t\tres.writeHead(204);\n\t\t\t\tres.end();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (method === \"POST\" && segments.length === 3 && segments[2] === \"start\") {\n\t\t\t\tconst appRec = state[app];\n\t\t\t\tif (appRec) await Promise.all(Object.entries(appRec.services).map(([id, svc]) => startService(app, id, svc)));\n\t\t\t\tres.writeHead(204);\n\t\t\t\tres.end();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (method === \"DELETE\" && segments.length === 2) {\n\t\t\t\tconst appRec = state[app];\n\t\t\t\tif (appRec) {\n\t\t\t\t\tawait Promise.all(Object.entries(appRec.services).map(([id, svc]) => stopService(svc, getRuntime(app, id))));\n\t\t\t\t\tfor (const id of Object.keys(appRec.services)) {\n\t\t\t\t\t\tconst key = runtimeKey(app, id);\n\t\t\t\t\t\truntimes.get(key)?.log?.close();\n\t\t\t\t\t\truntimes.delete(key);\n\t\t\t\t\t}\n\t\t\t\t\tdelete state[app];\n\t\t\t\t\tschedulePersist();\n\t\t\t\t}\n\t\t\t\tawait fs.promises.rm(path.join(stateDir, \"logs\", app), {\n\t\t\t\t\trecursive: true,\n\t\t\t\t\tforce: true\n\t\t\t\t});\n\t\t\t\tres.writeHead(204);\n\t\t\t\tres.end();\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tres.writeHead(404);\n\t\tres.end();\n\t}\n\tconst server = http.createServer((req, res) => {\n\t\thandleRequest(req, res).catch((err) => {\n\t\t\tif (!res.headersSent) res.writeHead(500, { \"content-type\": \"text/plain\" });\n\t\t\tres.end(err instanceof Error ? err.message : String(err));\n\t\t});\n\t});\n\tlet shuttingDown = false;\n\tasync function shutdown() {\n\t\tif (shuttingDown) return;\n\t\tshuttingDown = true;\n\t\tconst kills = [];\n\t\tfor (const rt of runtimes.values()) {\n\t\t\tclearBackoffTimer(rt);\n\t\t\tclearStableTimer(rt);\n\t\t\tif (rt.child) kills.push(killChild(rt, TERMINATE_GRACE_MS));\n\t\t}\n\t\tawait Promise.all(kills);\n\t\tawait stateFile.flush();\n\t\tserver.close();\n\t\tprocess.exit(0);\n\t}\n\tprocess.on(\"SIGTERM\", () => void shutdown());\n\tprocess.on(\"SIGINT\", () => void shutdown());\n\treadJsonFile(appsJsonPath, isAppsState).then((loaded) => {\n\t\tif (loaded) {\n\t\t\tfor (const appRec of Object.values(loaded)) for (const svc of Object.values(appRec.services)) if (svc.status === \"running\" || svc.status === \"backoff\") {\n\t\t\t\tsvc.status = \"stopped\";\n\t\t\t\tdelete svc.pid;\n\t\t\t}\n\t\t\tstate = loaded;\n\t\t}\n\t\tserver.listen(port, \"127.0.0.1\", () => {\n\t\t\tconsole.log(`[dev-emulators] compute-main listening on 127.0.0.1:${String(port)}`);\n\t\t});\n\t}).catch((err) => {\n\t\tconsole.error(\"compute-main: failed to load state\", err);\n\t\tprocess.exit(1);\n\t});\n}\nmain();\n//#endregion\nexport {};\n\n//# sourceMappingURL=compute-main.mjs.map"],"mappings":"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"}