alchemy 0.86.0 → 0.89.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 (75) hide show
  1. package/bin/alchemy.js +483 -541
  2. package/lib/cloudflare/compatibility-date.d.ts +1 -1
  3. package/lib/cloudflare/compatibility-date.d.ts.map +1 -1
  4. package/lib/cloudflare/dns-records.d.ts +5 -1
  5. package/lib/cloudflare/dns-records.d.ts.map +1 -1
  6. package/lib/cloudflare/dns-records.js +54 -31
  7. package/lib/cloudflare/dns-records.js.map +1 -1
  8. package/lib/cloudflare/index.d.ts +1 -0
  9. package/lib/cloudflare/index.d.ts.map +1 -1
  10. package/lib/cloudflare/index.js +1 -0
  11. package/lib/cloudflare/index.js.map +1 -1
  12. package/lib/cloudflare/r2-bucket-notification.d.ts +312 -0
  13. package/lib/cloudflare/r2-bucket-notification.d.ts.map +1 -0
  14. package/lib/cloudflare/r2-bucket-notification.js +387 -0
  15. package/lib/cloudflare/r2-bucket-notification.js.map +1 -0
  16. package/lib/cloudflare/workflow.d.ts +17 -0
  17. package/lib/cloudflare/workflow.d.ts.map +1 -1
  18. package/lib/cloudflare/workflow.js +1 -0
  19. package/lib/cloudflare/workflow.js.map +1 -1
  20. package/lib/cloudflare/wrangler.json.js +1 -0
  21. package/lib/cloudflare/wrangler.json.js.map +1 -1
  22. package/lib/docker/api.d.ts +51 -0
  23. package/lib/docker/api.d.ts.map +1 -1
  24. package/lib/docker/api.js +30 -0
  25. package/lib/docker/api.js.map +1 -1
  26. package/lib/docker/container.d.ts +7 -1
  27. package/lib/docker/container.d.ts.map +1 -1
  28. package/lib/docker/container.js +51 -3
  29. package/lib/docker/container.js.map +1 -1
  30. package/lib/docker/network.d.ts.map +1 -1
  31. package/lib/docker/network.js +24 -7
  32. package/lib/docker/network.js.map +1 -1
  33. package/lib/planetscale/api.d.ts +1 -0
  34. package/lib/planetscale/api.d.ts.map +1 -1
  35. package/lib/planetscale/api.js +1 -1
  36. package/lib/planetscale/api.js.map +1 -1
  37. package/lib/planetscale/branch.d.ts +28 -0
  38. package/lib/planetscale/branch.d.ts.map +1 -1
  39. package/lib/planetscale/branch.js +12 -0
  40. package/lib/planetscale/branch.js.map +1 -1
  41. package/lib/planetscale/database-extensions.d.ts +515 -0
  42. package/lib/planetscale/database-extensions.d.ts.map +1 -0
  43. package/lib/planetscale/database-extensions.js +389 -0
  44. package/lib/planetscale/database-extensions.js.map +1 -0
  45. package/lib/planetscale/database.d.ts +40 -2
  46. package/lib/planetscale/database.d.ts.map +1 -1
  47. package/lib/planetscale/database.js +76 -1
  48. package/lib/planetscale/database.js.map +1 -1
  49. package/lib/scope.d.ts.map +1 -1
  50. package/lib/scope.js +1 -2
  51. package/lib/scope.js.map +1 -1
  52. package/lib/util/telemetry.d.ts +1 -6
  53. package/lib/util/telemetry.d.ts.map +1 -1
  54. package/lib/util/telemetry.js.map +1 -1
  55. package/package.json +4 -4
  56. package/src/cloudflare/dns-records.ts +71 -51
  57. package/src/cloudflare/index.ts +1 -0
  58. package/src/cloudflare/r2-bucket-notification.ts +809 -0
  59. package/src/cloudflare/workflow.ts +18 -0
  60. package/src/cloudflare/wrangler.json.ts +1 -0
  61. package/src/docker/api.ts +73 -0
  62. package/src/docker/container.ts +78 -4
  63. package/src/docker/network.ts +27 -7
  64. package/src/planetscale/api.ts +1 -1
  65. package/src/planetscale/branch.ts +45 -0
  66. package/src/planetscale/database-extensions.ts +947 -0
  67. package/src/planetscale/database.ts +141 -2
  68. package/src/scope.ts +1 -2
  69. package/src/util/telemetry.ts +1 -20
  70. package/workers/tunnel-proxy.js +1 -1
  71. package/lib/state/instrumented-state-store.d.ts +0 -19
  72. package/lib/state/instrumented-state-store.d.ts.map +0 -1
  73. package/lib/state/instrumented-state-store.js +0 -68
  74. package/lib/state/instrumented-state-store.js.map +0 -1
  75. package/src/state/instrumented-state-store.ts +0 -108
package/bin/alchemy.js CHANGED
@@ -37050,54 +37050,10 @@ var FileSystemStateStore = class {
37050
37050
  }
37051
37051
  };
37052
37052
 
37053
- //#endregion
37054
- //#region ../node_modules/.bun/env-paths@3.0.0/node_modules/env-paths/index.js
37055
- const homedir = os.homedir();
37056
- const tmpdir = os.tmpdir();
37057
- const { env: env$1 } = y;
37058
- const macos = (name$1) => {
37059
- const library = path.join(homedir, "Library");
37060
- return {
37061
- data: path.join(library, "Application Support", name$1),
37062
- config: path.join(library, "Preferences", name$1),
37063
- cache: path.join(library, "Caches", name$1),
37064
- log: path.join(library, "Logs", name$1),
37065
- temp: path.join(tmpdir, name$1)
37066
- };
37067
- };
37068
- const windows = (name$1) => {
37069
- const appData = env$1.APPDATA || path.join(homedir, "AppData", "Roaming");
37070
- const localAppData = env$1.LOCALAPPDATA || path.join(homedir, "AppData", "Local");
37071
- return {
37072
- data: path.join(localAppData, name$1, "Data"),
37073
- config: path.join(appData, name$1, "Config"),
37074
- cache: path.join(localAppData, name$1, "Cache"),
37075
- log: path.join(localAppData, name$1, "Log"),
37076
- temp: path.join(tmpdir, name$1)
37077
- };
37078
- };
37079
- const linux = (name$1) => {
37080
- const username = path.basename(homedir);
37081
- return {
37082
- data: path.join(env$1.XDG_DATA_HOME || path.join(homedir, ".local", "share"), name$1),
37083
- config: path.join(env$1.XDG_CONFIG_HOME || path.join(homedir, ".config"), name$1),
37084
- cache: path.join(env$1.XDG_CACHE_HOME || path.join(homedir, ".cache"), name$1),
37085
- log: path.join(env$1.XDG_STATE_HOME || path.join(homedir, ".local", "state"), name$1),
37086
- temp: path.join(tmpdir, username, name$1)
37087
- };
37088
- };
37089
- function envPaths(name$1, { suffix = "nodejs" } = {}) {
37090
- if (typeof name$1 !== "string") throw new TypeError(`Expected a string, got ${typeof name$1}`);
37091
- if (suffix) name$1 += `-${suffix}`;
37092
- if (y.platform === "darwin") return macos(name$1);
37093
- if (y.platform === "win32") return windows(name$1);
37094
- return linux(name$1);
37095
- }
37096
-
37097
37053
  //#endregion
37098
37054
  //#region package.json
37099
37055
  var name = "alchemy";
37100
- var version = "0.86.0";
37056
+ var version = "0.89.0";
37101
37057
  var license = "Apache-2.0";
37102
37058
  var author = "Sam Goodwin <sam@alchemy.run>";
37103
37059
  var homepage = "https://alchemy.run";
@@ -37113,7 +37069,7 @@ var scripts = {
37113
37069
  "build": "rm -rf ./*.tsbuildinfo && rm -rf ./lib && tsc -b && tsdown",
37114
37070
  "dev": "tsdown --watch",
37115
37071
  "drizzle:generate": "bun run scripts/drizzle-generate.ts",
37116
- "postbuild": "cpx 'src/**/*/types.d.ts' lib && cpx 'src/**/*.txt' lib",
37072
+ "postbuild": "(cpy '**/*.d.ts' ../lib --cwd=src || true) && (cpy '**/*.txt' ../lib --cwd=src || true)",
37117
37073
  "publish:npm": "cp ../README.md . && bun run build && tsdown && bun publish && rm README.md"
37118
37074
  };
37119
37075
  var files = [
@@ -37374,7 +37330,7 @@ var devDependencies = {
37374
37330
  "braintrust": "^0.0.201",
37375
37331
  "capnweb": "^0.4.0",
37376
37332
  "cojson-core-wasm": "^0.18.16",
37377
- "cpx": "^1.5.0",
37333
+ "cpy-cli": "^7.0.0",
37378
37334
  "debug": "^4.4.1",
37379
37335
  "dofs": "^0.1.0",
37380
37336
  "drizzle-kit": "^0.31.8",
@@ -37415,134 +37371,508 @@ var package_default = {
37415
37371
  };
37416
37372
 
37417
37373
  //#endregion
37418
- //#region src/util/cli-args.ts
37419
- const cliArgs = process.argv.slice(2);
37420
- function parseOption(option, defaultValue) {
37421
- const i$2 = cliArgs.indexOf(option);
37422
- return i$2 !== -1 && i$2 + 1 < cliArgs.length ? cliArgs[i$2 + 1] : defaultValue;
37423
- }
37424
-
37425
- //#endregion
37426
- //#region src/util/is-transient-error.ts
37427
- function isTransientNetworkError(err) {
37428
- return err?.code === "UND_ERR_SOCKET" || err?.code === "ECONNRESET" || err?.code === "UND_ERR_CONNECT_TIMEOUT" || err?.code === "EPIPE" || err?.name === "FetchError";
37374
+ //#region src/util/dedent.ts
37375
+ /**
37376
+ * Tagged template literal for removing indentation from a block of text.
37377
+ *
37378
+ * If the first line is empty, it will be ignored.
37379
+ */
37380
+ function dedent(strings, ...values) {
37381
+ let lines = String.raw({ raw: strings }, ...values).split("\n");
37382
+ while (lines.length > 0 && lines[0].trim() === "") lines = lines.slice(1);
37383
+ while (lines.length > 0 && lines[lines.length - 1].trim() === "") lines = lines.slice(0, lines.length - 1);
37384
+ if (lines.length === 0) return "";
37385
+ let minIndentLength = Number.POSITIVE_INFINITY;
37386
+ for (const line of lines) if (line.trim() !== "") {
37387
+ const indent = line.match(/^[ \t]*/)?.[0];
37388
+ if (indent != null && indent.length < minIndentLength) minIndentLength = indent.length;
37389
+ }
37390
+ if (minIndentLength === Number.POSITIVE_INFINITY) return lines.join("\n");
37391
+ lines = lines.map((line) => {
37392
+ if (line.trim() === "") return line;
37393
+ return line.startsWith(" ".repeat(minIndentLength)) || line.startsWith(" ".repeat(minIndentLength)) ? line.substring(minIndentLength) : line;
37394
+ });
37395
+ return lines.join("\n");
37429
37396
  }
37430
37397
 
37431
37398
  //#endregion
37432
- //#region src/util/logger.ts
37433
- const logger = new Proxy({}, { get: (_$2, prop) => {
37434
- const logger$1 = Scope.getScope()?.logger ?? {
37435
- log: console.log,
37436
- error: console.error,
37437
- warn: console.warn,
37438
- warnOnce: console.warn,
37399
+ //#region src/util/cli.ts
37400
+ const colors = {
37401
+ reset: "\x1B[0m",
37402
+ cyanBright: "\x1B[96m",
37403
+ yellowBright: "\x1B[93m",
37404
+ magenta: "\x1B[35m",
37405
+ greenBright: "\x1B[92m",
37406
+ redBright: "\x1B[91m",
37407
+ gray: "\x1B[90m"
37408
+ };
37409
+ const shouldDisableColors = () => {
37410
+ return Boolean(process.env.NO_COLOR);
37411
+ };
37412
+ const colorize = (text, color) => {
37413
+ if (shouldDisableColors()) return text;
37414
+ return `${colors[color]}${text}${colors.reset}`;
37415
+ };
37416
+ let loggers = {};
37417
+ const createLoggerInstance = (alchemyInfo, customLogger) => {
37418
+ return loggers[alchemyInfo.appName] ??= customLogger ?? createFallbackLogger(alchemyInfo);
37419
+ };
37420
+ const createDummyLogger = () => {
37421
+ return {
37422
+ log: () => {},
37423
+ error: () => {},
37424
+ warn: () => {},
37425
+ warnOnce: () => {},
37439
37426
  task: () => {},
37440
37427
  exit: () => {}
37441
37428
  };
37442
- return logger$1[prop].bind(logger$1);
37443
- } });
37429
+ };
37430
+ const createFallbackLogger = (alchemyInfo) => {
37431
+ if (alchemyInfo.phase !== "read") console.log(dedent`
37432
+ ${colorize("Alchemy", "cyanBright")} (v${package_default.version})
37433
+ App: ${alchemyInfo.appName}
37434
+ Phase: ${alchemyInfo.phase}
37435
+ Stage: ${alchemyInfo.stage}
37436
+
37437
+ `);
37438
+ return {
37439
+ log: console.log,
37440
+ error: (...args) => console.error(colorize("ERROR", "redBright"), ...args),
37441
+ warn: (...args) => console.warn(colorize("WARN", "yellowBright"), ...args),
37442
+ warnOnce: (message) => {
37443
+ globalThis._ALCHEMY_WARNINGS ??= /* @__PURE__ */ new Set();
37444
+ if (globalThis._ALCHEMY_WARNINGS.has(message)) return;
37445
+ globalThis._ALCHEMY_WARNINGS.add(message);
37446
+ console.warn(colorize("WARN", "yellowBright"), message);
37447
+ },
37448
+ task: (_id, data) => {
37449
+ const prefix = data.prefix ? `[${data.prefix}]` : "";
37450
+ const paddedPrefix = prefix.padEnd(12);
37451
+ const prefixWithColor = data.prefixColor && prefix ? colorize(paddedPrefix, data.prefixColor) : paddedPrefix;
37452
+ const resource = data.resource ? colorize(data.resource, "gray") : "";
37453
+ const message = data.message;
37454
+ if (prefixWithColor && resource) console.log(`${prefixWithColor}${resource} ${message}`);
37455
+ else if (prefixWithColor) console.log(`${prefixWithColor}${message}`);
37456
+ else console.log(`${message}`);
37457
+ },
37458
+ exit: () => {}
37459
+ };
37460
+ };
37461
+ const formatFQN = (fqn) => fqn.split("/").slice(2).join(" > ");
37444
37462
 
37445
37463
  //#endregion
37446
- //#region src/util/telemetry.ts
37447
- const ALCHEMY_DIR = path$1.join(os.homedir(), ".alchemy");
37448
- const ID_PATH = path$1.join(ALCHEMY_DIR, "id");
37449
- const ID_PATH_LEGACY = path$1.join(envPaths("alchemy", { suffix: "" }).config, "id");
37450
- const DISABLED_PATH = path$1.join(ALCHEMY_DIR, "telemetry-disabled");
37451
- const TELEMETRY_DISABLED = !!process.env.ALCHEMY_TELEMETRY_DISABLED || !!process.env.DO_NOT_TRACK;
37452
- const TELEMETRY_API_URL = process.env.ALCHEMY_TELEMETRY_API_URL ?? "https://telemetry.alchemy.run";
37453
- const SUPPRESS_TELEMETRY_ERRORS = !!process.env.ALCHEMY_TELEMETRY_SUPPRESS_ERRORS;
37454
- const getGlobalTelemetryDisabled = memoize(async () => {
37455
- return await fs$1.readFile(DISABLED_PATH, "utf-8").then((data) => data.trim() === "true").catch(() => false);
37456
- });
37457
- async function setGlobalTelemetryDisabled() {
37458
- await fs$1.mkdir(ALCHEMY_DIR, { recursive: true });
37459
- await fs$1.writeFile(DISABLED_PATH, "true");
37460
- }
37461
- async function setGlobalTelemetryEnabled() {
37462
- await fs$1.rm(DISABLED_PATH, { force: true });
37463
- }
37464
- async function getOrCreateUserId() {
37465
- async function readUserId(path$32) {
37464
+ //#region src/util/mutex.ts
37465
+ /**
37466
+ * Simple async mutex that ensures only one async operation executes at a time
37467
+ */
37468
+ var AsyncMutex = class {
37469
+ locked = false;
37470
+ queue = [];
37471
+ async lock(operation) {
37472
+ return new Promise((resolve$1, reject) => {
37473
+ const execute = async () => {
37474
+ this.locked = true;
37475
+ try {
37476
+ resolve$1(await operation());
37477
+ } catch (error$84) {
37478
+ reject(error$84);
37479
+ } finally {
37480
+ this.locked = false;
37481
+ const next = this.queue.shift();
37482
+ if (next) next();
37483
+ }
37484
+ };
37485
+ if (this.locked) this.queue.push(execute);
37486
+ else execute();
37487
+ });
37488
+ }
37489
+ };
37490
+
37491
+ //#endregion
37492
+ //#region src/util/idempotent-spawn.ts
37493
+ /**
37494
+ * Idempotently ensure a long-lived child is running with stdout/stderr -> files,
37495
+ * and mirror those logs to THIS process's console with persisted offsets.
37496
+ *
37497
+ * Use: await ensureLoggedChildAndMirror({ cmd: "vite dev" })
37498
+ */
37499
+ async function idempotentSpawn({ cmd, cwd: cwd$1, env: env$2, stateFile = "state.json", overlapBytes = 0, resume = false, log = "log.txt", processName, extract, isSameProcess, quiet = false }) {
37500
+ if (!processName && !isSameProcess) processName = cmd.split(" ")[0];
37501
+ const outPath = log;
37502
+ await Promise.all([fs$1.mkdir(path$1.dirname(stateFile), { recursive: true }), fs$1.mkdir(path$1.dirname(log), { recursive: true })]);
37503
+ const { promise: extracted, resolve: resolveExtracted } = Promise.withResolvers();
37504
+ if (!extract) resolveExtracted(void 0);
37505
+ const pid = await ensureChildRunning();
37506
+ const write = quiet ? () => false : (buf) => process.stdout.write(buf);
37507
+ await followFilePersisted(outPath, {
37508
+ stateKey: `${path$1.resolve(outPath)}::stdout`,
37509
+ write
37510
+ });
37511
+ return {
37512
+ extracted,
37513
+ stop: async () => pid ? await kill(pid) : void 0
37514
+ };
37515
+ function isPidAlive(pid$1) {
37516
+ if (!pid$1 || Number.isNaN(pid$1)) return false;
37466
37517
  try {
37467
- return (await fs$1.readFile(path$32, "utf-8")).trim();
37518
+ process.kill(pid$1, 0);
37519
+ return true;
37468
37520
  } catch {
37469
- return null;
37521
+ return false;
37470
37522
  }
37471
37523
  }
37472
- const id = await readUserId(ID_PATH);
37473
- if (id) return id;
37474
- const legacyId = await readUserId(ID_PATH_LEGACY);
37475
- try {
37476
- const id$1 = legacyId ?? crypto.randomUUID();
37477
- await fs$1.mkdir(ALCHEMY_DIR, { recursive: true });
37478
- await fs$1.writeFile(ID_PATH, id$1);
37479
- if (!legacyId) console.warn(["Attention: To help improve Alchemy, we collect anonymous usage, performance, and error data.", "You can opt out by setting the ALCHEMY_TELEMETRY_DISABLED or DO_NOT_TRACK environment variable to a truthy value."].join("\n"));
37480
- return id$1;
37481
- } catch {
37482
- return null;
37524
+ async function writeJsonAtomic(file$2, data) {
37525
+ const dir = path$1.dirname(file$2);
37526
+ const tmp = path$1.join(dir, `.${path$1.basename(file$2)}.${process.pid}.${Date.now()}.tmp`);
37527
+ await fs$1.writeFile(tmp, JSON.stringify(data, null, 2), { mode: 384 });
37528
+ await fs$1.rename(tmp, file$2);
37483
37529
  }
37484
- }
37485
- async function getRootCommitHash() {
37486
- return new Promise((resolve$1) => {
37487
- exec(process.platform === "win32" ? `git rev-list --max-parents=0 HEAD | ForEach-Object { if (-not (git cat-file -p $_ | Select-String "^parent ")) { $_ } }` : `git rev-list --max-parents=0 HEAD | xargs -r -I{} sh -c 'git cat-file -p {} | grep -q "^parent " || echo {}'`, (err, stdout$1) => {
37488
- if (err) {
37489
- resolve$1(null);
37490
- return;
37491
- }
37492
- resolve$1(stdout$1.trim());
37493
- });
37494
- });
37495
- }
37496
- async function getGitOriginUrlHash() {
37497
- return new Promise((resolve$1) => {
37498
- exec("git config --get remote.origin.url", (err, stdout$1) => {
37499
- if (err) {
37500
- resolve$1(null);
37501
- return;
37502
- }
37503
- resolve$1(hashString(stdout$1.trim()));
37530
+ async function readJson$3(file$2) {
37531
+ try {
37532
+ return JSON.parse(await fs$1.readFile(file$2, "utf8"));
37533
+ } catch {
37534
+ return;
37535
+ }
37536
+ }
37537
+ async function spawnLoggedChild() {
37538
+ const out = await fs$1.open(outPath, "a");
37539
+ const child = spawn(cmd, {
37540
+ shell: true,
37541
+ cwd: cwd$1,
37542
+ stdio: [
37543
+ "inherit",
37544
+ out.fd,
37545
+ out.fd
37546
+ ],
37547
+ env: env$2,
37548
+ detached: false
37504
37549
  });
37505
- });
37506
- }
37507
- async function getBranchName() {
37508
- return new Promise((resolve$1) => {
37509
- exec("git rev-parse --abbrev-ref HEAD", (err, stdout$1) => {
37510
- if (err) {
37511
- resolve$1(null);
37512
- return;
37550
+ await out.close();
37551
+ const stateAll = await readJson$3(stateFile) ?? {};
37552
+ stateAll.pid = child.pid;
37553
+ await writeJsonAtomic(stateFile, stateAll);
37554
+ return child;
37555
+ }
37556
+ async function ensureChildRunning() {
37557
+ const stateAll = await readJson$3(stateFile);
37558
+ if (stateAll) {
37559
+ const pid$1 = Number.parseInt(stateAll.pid, 10);
37560
+ if (isPidAlive(pid$1)) return pid$1;
37561
+ if (await isSameProcess?.(pid$1)) return pid$1;
37562
+ if (processName) {
37563
+ const processes = await find("pid", pid$1);
37564
+ if (processes.length > 1) console.warn(`Found multiple processes with PID ${pid$1}, using the first one`);
37565
+ if (processes.length > 0) {
37566
+ if (processes[0].name.startsWith(processName)) return pid$1;
37567
+ }
37513
37568
  }
37514
- resolve$1(stdout$1.trim());
37569
+ }
37570
+ await Promise.all([fs$1.rm(stateFile).catch(() => {}), fs$1.rm(log).catch(() => {})]);
37571
+ return (await spawnLoggedChild()).pid;
37572
+ }
37573
+ async function followFilePersisted(logPath, { write: write$1, chunkSize = 64 * 1024, tickMs = 100 }) {
37574
+ logPath = path$1.resolve(logPath);
37575
+ let state = await readJson$3(stateFile) ?? {};
37576
+ let fh = await fs$1.open(logPath, "r");
37577
+ let st = await fh.stat();
37578
+ let ino = Number(st.ino ?? 0);
37579
+ let dev$1 = Number(st.dev ?? 0);
37580
+ let offset = resume && state.ino === ino && state.dev === dev$1 && typeof state.offset === "number" ? Math.max(0, Math.min(st.size, state.offset - overlapBytes)) : st.size;
37581
+ let closed = false;
37582
+ const drainMutex = new AsyncMutex();
37583
+ let remainder;
37584
+ if (offset > 0) {
37585
+ const fullBuffer = Buffer.allocUnsafe(offset);
37586
+ await fh.read({
37587
+ position: 0,
37588
+ buffer: fullBuffer,
37589
+ length: offset
37590
+ });
37591
+ parse$10(fullBuffer.toString("utf8"));
37592
+ }
37593
+ await drain();
37594
+ const watcher = fs.watch(logPath, async () => {
37595
+ if (closed) return;
37596
+ try {
37597
+ const cur = await fh.stat();
37598
+ const curIno = Number(cur.ino ?? 0);
37599
+ const curDev = Number(cur.dev ?? 0);
37600
+ if (curIno !== ino || curDev !== dev$1) {
37601
+ await fh.close().catch(() => {});
37602
+ fh = await fs$1.open(logPath, "r");
37603
+ const cur2 = await fh.stat();
37604
+ ino = Number(cur2.ino ?? 0);
37605
+ dev$1 = Number(cur2.dev ?? 0);
37606
+ offset = 0;
37607
+ }
37608
+ } catch {}
37609
+ await drain();
37515
37610
  });
37516
- });
37611
+ const tick = setInterval(() => {
37612
+ drain().catch(() => {});
37613
+ }, tickMs);
37614
+ return async function stop() {
37615
+ closed = true;
37616
+ watcher.close();
37617
+ clearInterval(tick);
37618
+ await fh.close().catch(() => {});
37619
+ await persist();
37620
+ };
37621
+ async function persist() {
37622
+ try {
37623
+ const cur = await fh.stat();
37624
+ state = {
37625
+ ...state,
37626
+ offset,
37627
+ ino: Number(cur.ino ?? ino),
37628
+ dev: Number(cur.dev ?? dev$1),
37629
+ size: cur.size,
37630
+ mtimeMs: cur.mtimeMs
37631
+ };
37632
+ await writeJsonAtomic(stateFile, state);
37633
+ } catch {}
37634
+ }
37635
+ function parse$10(content) {
37636
+ const lines = content.split("\n");
37637
+ if (extract) {
37638
+ if (lines.length > 0) remainder = lines[lines.length - 1];
37639
+ for (const line of lines.slice(0, -1)) {
37640
+ const extracted$1 = extract(line);
37641
+ if (extracted$1) {
37642
+ resolveExtracted(extracted$1);
37643
+ break;
37644
+ }
37645
+ }
37646
+ }
37647
+ }
37648
+ function drain() {
37649
+ return drainMutex.lock(async () => {
37650
+ while (true) {
37651
+ const cur = await fh.stat().catch(() => null);
37652
+ if (!cur) break;
37653
+ if (cur.size < offset) offset = 0;
37654
+ const toRead = Math.min(chunkSize, cur.size - offset);
37655
+ if (toRead <= 0) break;
37656
+ const { bytesRead, buffer } = await fh.read({
37657
+ position: offset,
37658
+ buffer: Buffer.allocUnsafe(toRead),
37659
+ length: toRead
37660
+ });
37661
+ if (!bytesRead) break;
37662
+ const chunk = buffer.subarray(0, bytesRead);
37663
+ write$1(chunk);
37664
+ parse$10(`${remainder || ""}${chunk.toString("utf8")}`);
37665
+ offset += bytesRead;
37666
+ }
37667
+ await persist();
37668
+ });
37669
+ }
37670
+ }
37671
+ async function kill(pid$1) {
37672
+ try {
37673
+ process.kill(pid$1, "SIGTERM");
37674
+ } catch {
37675
+ return;
37676
+ }
37677
+ if ((await find("pid", pid$1)).some((p$1) => p$1.name !== "<defunct>")) try {
37678
+ process.kill(pid$1, "SIGKILL");
37679
+ } catch {}
37680
+ }
37517
37681
  }
37518
- function getRuntime$1() {
37519
- if (globalThis.Bun) return {
37520
- name: "bun",
37521
- version: Bun.version
37522
- };
37523
- if (globalThis.Deno) return {
37524
- name: "deno",
37525
- version: Deno.version?.deno ?? null
37682
+ /**
37683
+ * The find-process package is a bit of a mess and has different behavior across runtimes (bun, tsx, node, etc.)
37684
+ *
37685
+ * This wrapper function defensively searches for the find function in the imported module.
37686
+ *
37687
+ * It confirmed to support:
37688
+ * 1. node
37689
+ * 2. bun
37690
+ * 3. tsx
37691
+ *
37692
+ * TODO(sam): check deno?
37693
+ */
37694
+ const find = async (...args) => {
37695
+ const findProcess = await import("find-process");
37696
+ return (findProcess.default?.default ?? findProcess.default ?? findProcess.find ?? findProcess)(...args);
37697
+ };
37698
+
37699
+ //#endregion
37700
+ //#region src/util/logger.ts
37701
+ const logger = new Proxy({}, { get: (_$2, prop) => {
37702
+ const logger$1 = Scope.getScope()?.logger ?? {
37703
+ log: console.log,
37704
+ error: console.error,
37705
+ warn: console.warn,
37706
+ warnOnce: console.warn,
37707
+ task: () => {},
37708
+ exit: () => {}
37526
37709
  };
37527
- if (globalThis.EdgeRuntime) return {
37528
- name: "workerd",
37529
- version: null
37710
+ return logger$1[prop].bind(logger$1);
37711
+ } });
37712
+
37713
+ //#endregion
37714
+ //#region src/util/root-dir.ts
37715
+ const ALCHEMY_ROOT = process.env.ALCHEMY_ROOT ?? process.cwd();
37716
+
37717
+ //#endregion
37718
+ //#region ../node_modules/.bun/env-paths@3.0.0/node_modules/env-paths/index.js
37719
+ const homedir = os.homedir();
37720
+ const tmpdir = os.tmpdir();
37721
+ const { env: env$1 } = y;
37722
+ const macos = (name$1) => {
37723
+ const library = path.join(homedir, "Library");
37724
+ return {
37725
+ data: path.join(library, "Application Support", name$1),
37726
+ config: path.join(library, "Preferences", name$1),
37727
+ cache: path.join(library, "Caches", name$1),
37728
+ log: path.join(library, "Logs", name$1),
37729
+ temp: path.join(tmpdir, name$1)
37530
37730
  };
37531
- if (globalThis.process?.versions?.node) return {
37532
- name: "node",
37533
- version: process.versions.node
37731
+ };
37732
+ const windows = (name$1) => {
37733
+ const appData = env$1.APPDATA || path.join(homedir, "AppData", "Roaming");
37734
+ const localAppData = env$1.LOCALAPPDATA || path.join(homedir, "AppData", "Local");
37735
+ return {
37736
+ data: path.join(localAppData, name$1, "Data"),
37737
+ config: path.join(appData, name$1, "Config"),
37738
+ cache: path.join(localAppData, name$1, "Cache"),
37739
+ log: path.join(localAppData, name$1, "Log"),
37740
+ temp: path.join(tmpdir, name$1)
37534
37741
  };
37742
+ };
37743
+ const linux = (name$1) => {
37744
+ const username = path.basename(homedir);
37535
37745
  return {
37536
- name: null,
37537
- version: null
37746
+ data: path.join(env$1.XDG_DATA_HOME || path.join(homedir, ".local", "share"), name$1),
37747
+ config: path.join(env$1.XDG_CONFIG_HOME || path.join(homedir, ".config"), name$1),
37748
+ cache: path.join(env$1.XDG_CACHE_HOME || path.join(homedir, ".cache"), name$1),
37749
+ log: path.join(env$1.XDG_STATE_HOME || path.join(homedir, ".local", "state"), name$1),
37750
+ temp: path.join(tmpdir, username, name$1)
37538
37751
  };
37752
+ };
37753
+ function envPaths(name$1, { suffix = "nodejs" } = {}) {
37754
+ if (typeof name$1 !== "string") throw new TypeError(`Expected a string, got ${typeof name$1}`);
37755
+ if (suffix) name$1 += `-${suffix}`;
37756
+ if (y.platform === "darwin") return macos(name$1);
37757
+ if (y.platform === "win32") return windows(name$1);
37758
+ return linux(name$1);
37539
37759
  }
37540
- const PROVIDERS$1 = [
37541
- {
37542
- env: "GITHUB_ACTIONS",
37543
- provider: "GitHub Actions",
37544
- isCI: true
37545
- },
37760
+
37761
+ //#endregion
37762
+ //#region src/util/cli-args.ts
37763
+ const cliArgs = process.argv.slice(2);
37764
+ function parseOption(option, defaultValue) {
37765
+ const i$2 = cliArgs.indexOf(option);
37766
+ return i$2 !== -1 && i$2 + 1 < cliArgs.length ? cliArgs[i$2 + 1] : defaultValue;
37767
+ }
37768
+
37769
+ //#endregion
37770
+ //#region src/util/is-transient-error.ts
37771
+ function isTransientNetworkError(err) {
37772
+ return err?.code === "UND_ERR_SOCKET" || err?.code === "ECONNRESET" || err?.code === "UND_ERR_CONNECT_TIMEOUT" || err?.code === "EPIPE" || err?.name === "FetchError";
37773
+ }
37774
+
37775
+ //#endregion
37776
+ //#region src/util/telemetry.ts
37777
+ const ALCHEMY_DIR = path$1.join(os.homedir(), ".alchemy");
37778
+ const ID_PATH = path$1.join(ALCHEMY_DIR, "id");
37779
+ const ID_PATH_LEGACY = path$1.join(envPaths("alchemy", { suffix: "" }).config, "id");
37780
+ const DISABLED_PATH = path$1.join(ALCHEMY_DIR, "telemetry-disabled");
37781
+ const TELEMETRY_DISABLED = !!process.env.ALCHEMY_TELEMETRY_DISABLED || !!process.env.DO_NOT_TRACK;
37782
+ const TELEMETRY_API_URL = process.env.ALCHEMY_TELEMETRY_API_URL ?? "https://telemetry.alchemy.run";
37783
+ const SUPPRESS_TELEMETRY_ERRORS = !!process.env.ALCHEMY_TELEMETRY_SUPPRESS_ERRORS;
37784
+ const getGlobalTelemetryDisabled = memoize(async () => {
37785
+ return await fs$1.readFile(DISABLED_PATH, "utf-8").then((data) => data.trim() === "true").catch(() => false);
37786
+ });
37787
+ async function setGlobalTelemetryDisabled() {
37788
+ await fs$1.mkdir(ALCHEMY_DIR, { recursive: true });
37789
+ await fs$1.writeFile(DISABLED_PATH, "true");
37790
+ }
37791
+ async function setGlobalTelemetryEnabled() {
37792
+ await fs$1.rm(DISABLED_PATH, { force: true });
37793
+ }
37794
+ async function getOrCreateUserId() {
37795
+ async function readUserId(path$32) {
37796
+ try {
37797
+ return (await fs$1.readFile(path$32, "utf-8")).trim();
37798
+ } catch {
37799
+ return null;
37800
+ }
37801
+ }
37802
+ const id = await readUserId(ID_PATH);
37803
+ if (id) return id;
37804
+ const legacyId = await readUserId(ID_PATH_LEGACY);
37805
+ try {
37806
+ const id$1 = legacyId ?? crypto.randomUUID();
37807
+ await fs$1.mkdir(ALCHEMY_DIR, { recursive: true });
37808
+ await fs$1.writeFile(ID_PATH, id$1);
37809
+ if (!legacyId) console.warn(["Attention: To help improve Alchemy, we collect anonymous usage, performance, and error data.", "You can opt out by setting the ALCHEMY_TELEMETRY_DISABLED or DO_NOT_TRACK environment variable to a truthy value."].join("\n"));
37810
+ return id$1;
37811
+ } catch {
37812
+ return null;
37813
+ }
37814
+ }
37815
+ async function getRootCommitHash() {
37816
+ return new Promise((resolve$1) => {
37817
+ exec(process.platform === "win32" ? `git rev-list --max-parents=0 HEAD | ForEach-Object { if (-not (git cat-file -p $_ | Select-String "^parent ")) { $_ } }` : `git rev-list --max-parents=0 HEAD | xargs -r -I{} sh -c 'git cat-file -p {} | grep -q "^parent " || echo {}'`, (err, stdout$1) => {
37818
+ if (err) {
37819
+ resolve$1(null);
37820
+ return;
37821
+ }
37822
+ resolve$1(stdout$1.trim());
37823
+ });
37824
+ });
37825
+ }
37826
+ async function getGitOriginUrlHash() {
37827
+ return new Promise((resolve$1) => {
37828
+ exec("git config --get remote.origin.url", (err, stdout$1) => {
37829
+ if (err) {
37830
+ resolve$1(null);
37831
+ return;
37832
+ }
37833
+ resolve$1(hashString(stdout$1.trim()));
37834
+ });
37835
+ });
37836
+ }
37837
+ async function getBranchName() {
37838
+ return new Promise((resolve$1) => {
37839
+ exec("git rev-parse --abbrev-ref HEAD", (err, stdout$1) => {
37840
+ if (err) {
37841
+ resolve$1(null);
37842
+ return;
37843
+ }
37844
+ resolve$1(stdout$1.trim());
37845
+ });
37846
+ });
37847
+ }
37848
+ function getRuntime$1() {
37849
+ if (globalThis.Bun) return {
37850
+ name: "bun",
37851
+ version: Bun.version
37852
+ };
37853
+ if (globalThis.Deno) return {
37854
+ name: "deno",
37855
+ version: Deno.version?.deno ?? null
37856
+ };
37857
+ if (globalThis.EdgeRuntime) return {
37858
+ name: "workerd",
37859
+ version: null
37860
+ };
37861
+ if (globalThis.process?.versions?.node) return {
37862
+ name: "node",
37863
+ version: process.versions.node
37864
+ };
37865
+ return {
37866
+ name: null,
37867
+ version: null
37868
+ };
37869
+ }
37870
+ const PROVIDERS$1 = [
37871
+ {
37872
+ env: "GITHUB_ACTIONS",
37873
+ provider: "GitHub Actions",
37874
+ isCI: true
37875
+ },
37546
37876
  {
37547
37877
  env: "GITLAB_CI",
37548
37878
  provider: "GitLab CI",
@@ -37692,394 +38022,6 @@ async function fetchNoResponse(url$2, options = {}) {
37692
38022
  });
37693
38023
  }
37694
38024
 
37695
- //#endregion
37696
- //#region src/state/instrumented-state-store.ts
37697
- var InstrumentedStateStore = class {
37698
- /** @internal */
37699
- __phantom;
37700
- stateStore;
37701
- stateStoreClass;
37702
- constructor(stateStore) {
37703
- this.stateStore = stateStore;
37704
- this.stateStoreClass = stateStore.constructor.name;
37705
- }
37706
- async callWithTelemetry(event, fn) {
37707
- const start = performance.now();
37708
- let error$84;
37709
- try {
37710
- return await fn();
37711
- } catch (err) {
37712
- error$84 = err;
37713
- throw err;
37714
- } finally {
37715
- await createAndSendEvent({
37716
- event,
37717
- stateStore: this.stateStoreClass,
37718
- duration: performance.now() - start
37719
- }, error$84 instanceof Error ? error$84 : error$84 ? new Error(String(error$84)) : void 0);
37720
- }
37721
- }
37722
- async init() {
37723
- if (this.stateStore.init == null) return;
37724
- await this.callWithTelemetry("statestore.init", this.stateStore.init.bind(this.stateStore));
37725
- }
37726
- async deinit() {
37727
- if (this.stateStore.deinit == null) return;
37728
- await this.callWithTelemetry("statestore.deinit", this.stateStore.deinit.bind(this.stateStore));
37729
- }
37730
- async list() {
37731
- return await this.callWithTelemetry("statestore.list", this.stateStore.list.bind(this.stateStore));
37732
- }
37733
- async count() {
37734
- return await this.callWithTelemetry("statestore.count", this.stateStore.count.bind(this.stateStore));
37735
- }
37736
- async get(key) {
37737
- return await this.callWithTelemetry("statestore.get", this.stateStore.get.bind(this.stateStore, key));
37738
- }
37739
- async getBatch(ids) {
37740
- return await this.callWithTelemetry("statestore.getBatch", this.stateStore.getBatch.bind(this.stateStore, ids));
37741
- }
37742
- async all() {
37743
- return await this.callWithTelemetry("statestore.all", this.stateStore.all.bind(this.stateStore));
37744
- }
37745
- async set(key, value) {
37746
- await this.callWithTelemetry("statestore.set", this.stateStore.set.bind(this.stateStore, key, value));
37747
- }
37748
- async delete(key) {
37749
- await this.callWithTelemetry("statestore.delete", this.stateStore.delete.bind(this.stateStore, key));
37750
- }
37751
- };
37752
-
37753
- //#endregion
37754
- //#region src/util/dedent.ts
37755
- /**
37756
- * Tagged template literal for removing indentation from a block of text.
37757
- *
37758
- * If the first line is empty, it will be ignored.
37759
- */
37760
- function dedent(strings, ...values) {
37761
- let lines = String.raw({ raw: strings }, ...values).split("\n");
37762
- while (lines.length > 0 && lines[0].trim() === "") lines = lines.slice(1);
37763
- while (lines.length > 0 && lines[lines.length - 1].trim() === "") lines = lines.slice(0, lines.length - 1);
37764
- if (lines.length === 0) return "";
37765
- let minIndentLength = Number.POSITIVE_INFINITY;
37766
- for (const line of lines) if (line.trim() !== "") {
37767
- const indent = line.match(/^[ \t]*/)?.[0];
37768
- if (indent != null && indent.length < minIndentLength) minIndentLength = indent.length;
37769
- }
37770
- if (minIndentLength === Number.POSITIVE_INFINITY) return lines.join("\n");
37771
- lines = lines.map((line) => {
37772
- if (line.trim() === "") return line;
37773
- return line.startsWith(" ".repeat(minIndentLength)) || line.startsWith(" ".repeat(minIndentLength)) ? line.substring(minIndentLength) : line;
37774
- });
37775
- return lines.join("\n");
37776
- }
37777
-
37778
- //#endregion
37779
- //#region src/util/cli.ts
37780
- const colors = {
37781
- reset: "\x1B[0m",
37782
- cyanBright: "\x1B[96m",
37783
- yellowBright: "\x1B[93m",
37784
- magenta: "\x1B[35m",
37785
- greenBright: "\x1B[92m",
37786
- redBright: "\x1B[91m",
37787
- gray: "\x1B[90m"
37788
- };
37789
- const shouldDisableColors = () => {
37790
- return Boolean(process.env.NO_COLOR);
37791
- };
37792
- const colorize = (text, color) => {
37793
- if (shouldDisableColors()) return text;
37794
- return `${colors[color]}${text}${colors.reset}`;
37795
- };
37796
- let loggers = {};
37797
- const createLoggerInstance = (alchemyInfo, customLogger) => {
37798
- return loggers[alchemyInfo.appName] ??= customLogger ?? createFallbackLogger(alchemyInfo);
37799
- };
37800
- const createDummyLogger = () => {
37801
- return {
37802
- log: () => {},
37803
- error: () => {},
37804
- warn: () => {},
37805
- warnOnce: () => {},
37806
- task: () => {},
37807
- exit: () => {}
37808
- };
37809
- };
37810
- const createFallbackLogger = (alchemyInfo) => {
37811
- if (alchemyInfo.phase !== "read") console.log(dedent`
37812
- ${colorize("Alchemy", "cyanBright")} (v${package_default.version})
37813
- App: ${alchemyInfo.appName}
37814
- Phase: ${alchemyInfo.phase}
37815
- Stage: ${alchemyInfo.stage}
37816
-
37817
- `);
37818
- return {
37819
- log: console.log,
37820
- error: (...args) => console.error(colorize("ERROR", "redBright"), ...args),
37821
- warn: (...args) => console.warn(colorize("WARN", "yellowBright"), ...args),
37822
- warnOnce: (message) => {
37823
- globalThis._ALCHEMY_WARNINGS ??= /* @__PURE__ */ new Set();
37824
- if (globalThis._ALCHEMY_WARNINGS.has(message)) return;
37825
- globalThis._ALCHEMY_WARNINGS.add(message);
37826
- console.warn(colorize("WARN", "yellowBright"), message);
37827
- },
37828
- task: (_id, data) => {
37829
- const prefix = data.prefix ? `[${data.prefix}]` : "";
37830
- const paddedPrefix = prefix.padEnd(12);
37831
- const prefixWithColor = data.prefixColor && prefix ? colorize(paddedPrefix, data.prefixColor) : paddedPrefix;
37832
- const resource = data.resource ? colorize(data.resource, "gray") : "";
37833
- const message = data.message;
37834
- if (prefixWithColor && resource) console.log(`${prefixWithColor}${resource} ${message}`);
37835
- else if (prefixWithColor) console.log(`${prefixWithColor}${message}`);
37836
- else console.log(`${message}`);
37837
- },
37838
- exit: () => {}
37839
- };
37840
- };
37841
- const formatFQN = (fqn) => fqn.split("/").slice(2).join(" > ");
37842
-
37843
- //#endregion
37844
- //#region src/util/mutex.ts
37845
- /**
37846
- * Simple async mutex that ensures only one async operation executes at a time
37847
- */
37848
- var AsyncMutex = class {
37849
- locked = false;
37850
- queue = [];
37851
- async lock(operation) {
37852
- return new Promise((resolve$1, reject) => {
37853
- const execute = async () => {
37854
- this.locked = true;
37855
- try {
37856
- resolve$1(await operation());
37857
- } catch (error$84) {
37858
- reject(error$84);
37859
- } finally {
37860
- this.locked = false;
37861
- const next = this.queue.shift();
37862
- if (next) next();
37863
- }
37864
- };
37865
- if (this.locked) this.queue.push(execute);
37866
- else execute();
37867
- });
37868
- }
37869
- };
37870
-
37871
- //#endregion
37872
- //#region src/util/idempotent-spawn.ts
37873
- /**
37874
- * Idempotently ensure a long-lived child is running with stdout/stderr -> files,
37875
- * and mirror those logs to THIS process's console with persisted offsets.
37876
- *
37877
- * Use: await ensureLoggedChildAndMirror({ cmd: "vite dev" })
37878
- */
37879
- async function idempotentSpawn({ cmd, cwd: cwd$1, env: env$2, stateFile = "state.json", overlapBytes = 0, resume = false, log = "log.txt", processName, extract, isSameProcess, quiet = false }) {
37880
- if (!processName && !isSameProcess) processName = cmd.split(" ")[0];
37881
- const outPath = log;
37882
- await Promise.all([fs$1.mkdir(path$1.dirname(stateFile), { recursive: true }), fs$1.mkdir(path$1.dirname(log), { recursive: true })]);
37883
- const { promise: extracted, resolve: resolveExtracted } = Promise.withResolvers();
37884
- if (!extract) resolveExtracted(void 0);
37885
- const pid = await ensureChildRunning();
37886
- const write = quiet ? () => false : (buf) => process.stdout.write(buf);
37887
- await followFilePersisted(outPath, {
37888
- stateKey: `${path$1.resolve(outPath)}::stdout`,
37889
- write
37890
- });
37891
- return {
37892
- extracted,
37893
- stop: async () => pid ? await kill(pid) : void 0
37894
- };
37895
- function isPidAlive(pid$1) {
37896
- if (!pid$1 || Number.isNaN(pid$1)) return false;
37897
- try {
37898
- process.kill(pid$1, 0);
37899
- return true;
37900
- } catch {
37901
- return false;
37902
- }
37903
- }
37904
- async function writeJsonAtomic(file$2, data) {
37905
- const dir = path$1.dirname(file$2);
37906
- const tmp = path$1.join(dir, `.${path$1.basename(file$2)}.${process.pid}.${Date.now()}.tmp`);
37907
- await fs$1.writeFile(tmp, JSON.stringify(data, null, 2), { mode: 384 });
37908
- await fs$1.rename(tmp, file$2);
37909
- }
37910
- async function readJson$3(file$2) {
37911
- try {
37912
- return JSON.parse(await fs$1.readFile(file$2, "utf8"));
37913
- } catch {
37914
- return;
37915
- }
37916
- }
37917
- async function spawnLoggedChild() {
37918
- const out = await fs$1.open(outPath, "a");
37919
- const child = spawn(cmd, {
37920
- shell: true,
37921
- cwd: cwd$1,
37922
- stdio: [
37923
- "inherit",
37924
- out.fd,
37925
- out.fd
37926
- ],
37927
- env: env$2,
37928
- detached: false
37929
- });
37930
- await out.close();
37931
- const stateAll = await readJson$3(stateFile) ?? {};
37932
- stateAll.pid = child.pid;
37933
- await writeJsonAtomic(stateFile, stateAll);
37934
- return child;
37935
- }
37936
- async function ensureChildRunning() {
37937
- const stateAll = await readJson$3(stateFile);
37938
- if (stateAll) {
37939
- const pid$1 = Number.parseInt(stateAll.pid, 10);
37940
- if (isPidAlive(pid$1)) return pid$1;
37941
- if (await isSameProcess?.(pid$1)) return pid$1;
37942
- if (processName) {
37943
- const processes = await find("pid", pid$1);
37944
- if (processes.length > 1) console.warn(`Found multiple processes with PID ${pid$1}, using the first one`);
37945
- if (processes.length > 0) {
37946
- if (processes[0].name.startsWith(processName)) return pid$1;
37947
- }
37948
- }
37949
- }
37950
- await Promise.all([fs$1.rm(stateFile).catch(() => {}), fs$1.rm(log).catch(() => {})]);
37951
- return (await spawnLoggedChild()).pid;
37952
- }
37953
- async function followFilePersisted(logPath, { write: write$1, chunkSize = 64 * 1024, tickMs = 100 }) {
37954
- logPath = path$1.resolve(logPath);
37955
- let state = await readJson$3(stateFile) ?? {};
37956
- let fh = await fs$1.open(logPath, "r");
37957
- let st = await fh.stat();
37958
- let ino = Number(st.ino ?? 0);
37959
- let dev$1 = Number(st.dev ?? 0);
37960
- let offset = resume && state.ino === ino && state.dev === dev$1 && typeof state.offset === "number" ? Math.max(0, Math.min(st.size, state.offset - overlapBytes)) : st.size;
37961
- let closed = false;
37962
- const drainMutex = new AsyncMutex();
37963
- let remainder;
37964
- if (offset > 0) {
37965
- const fullBuffer = Buffer.allocUnsafe(offset);
37966
- await fh.read({
37967
- position: 0,
37968
- buffer: fullBuffer,
37969
- length: offset
37970
- });
37971
- parse$10(fullBuffer.toString("utf8"));
37972
- }
37973
- await drain();
37974
- const watcher = fs.watch(logPath, async () => {
37975
- if (closed) return;
37976
- try {
37977
- const cur = await fh.stat();
37978
- const curIno = Number(cur.ino ?? 0);
37979
- const curDev = Number(cur.dev ?? 0);
37980
- if (curIno !== ino || curDev !== dev$1) {
37981
- await fh.close().catch(() => {});
37982
- fh = await fs$1.open(logPath, "r");
37983
- const cur2 = await fh.stat();
37984
- ino = Number(cur2.ino ?? 0);
37985
- dev$1 = Number(cur2.dev ?? 0);
37986
- offset = 0;
37987
- }
37988
- } catch {}
37989
- await drain();
37990
- });
37991
- const tick = setInterval(() => {
37992
- drain().catch(() => {});
37993
- }, tickMs);
37994
- return async function stop() {
37995
- closed = true;
37996
- watcher.close();
37997
- clearInterval(tick);
37998
- await fh.close().catch(() => {});
37999
- await persist();
38000
- };
38001
- async function persist() {
38002
- try {
38003
- const cur = await fh.stat();
38004
- state = {
38005
- ...state,
38006
- offset,
38007
- ino: Number(cur.ino ?? ino),
38008
- dev: Number(cur.dev ?? dev$1),
38009
- size: cur.size,
38010
- mtimeMs: cur.mtimeMs
38011
- };
38012
- await writeJsonAtomic(stateFile, state);
38013
- } catch {}
38014
- }
38015
- function parse$10(content) {
38016
- const lines = content.split("\n");
38017
- if (extract) {
38018
- if (lines.length > 0) remainder = lines[lines.length - 1];
38019
- for (const line of lines.slice(0, -1)) {
38020
- const extracted$1 = extract(line);
38021
- if (extracted$1) {
38022
- resolveExtracted(extracted$1);
38023
- break;
38024
- }
38025
- }
38026
- }
38027
- }
38028
- function drain() {
38029
- return drainMutex.lock(async () => {
38030
- while (true) {
38031
- const cur = await fh.stat().catch(() => null);
38032
- if (!cur) break;
38033
- if (cur.size < offset) offset = 0;
38034
- const toRead = Math.min(chunkSize, cur.size - offset);
38035
- if (toRead <= 0) break;
38036
- const { bytesRead, buffer } = await fh.read({
38037
- position: offset,
38038
- buffer: Buffer.allocUnsafe(toRead),
38039
- length: toRead
38040
- });
38041
- if (!bytesRead) break;
38042
- const chunk = buffer.subarray(0, bytesRead);
38043
- write$1(chunk);
38044
- parse$10(`${remainder || ""}${chunk.toString("utf8")}`);
38045
- offset += bytesRead;
38046
- }
38047
- await persist();
38048
- });
38049
- }
38050
- }
38051
- async function kill(pid$1) {
38052
- try {
38053
- process.kill(pid$1, "SIGTERM");
38054
- } catch {
38055
- return;
38056
- }
38057
- if ((await find("pid", pid$1)).some((p$1) => p$1.name !== "<defunct>")) try {
38058
- process.kill(pid$1, "SIGKILL");
38059
- } catch {}
38060
- }
38061
- }
38062
- /**
38063
- * The find-process package is a bit of a mess and has different behavior across runtimes (bun, tsx, node, etc.)
38064
- *
38065
- * This wrapper function defensively searches for the find function in the imported module.
38066
- *
38067
- * It confirmed to support:
38068
- * 1. node
38069
- * 2. bun
38070
- * 3. tsx
38071
- *
38072
- * TODO(sam): check deno?
38073
- */
38074
- const find = async (...args) => {
38075
- const findProcess = await import("find-process");
38076
- return (findProcess.default?.default ?? findProcess.default ?? findProcess.find ?? findProcess)(...args);
38077
- };
38078
-
38079
- //#endregion
38080
- //#region src/util/root-dir.ts
38081
- const ALCHEMY_ROOT = process.env.ALCHEMY_ROOT ?? process.cwd();
38082
-
38083
38025
  //#endregion
38084
38026
  //#region src/util/validate-resource-id.ts
38085
38027
  function validateResourceID(id, qualifier = "Resource") {
@@ -38187,7 +38129,7 @@ var Scope = class Scope {
38187
38129
  this.destroyStrategy = destroyStrategy ?? this.parent?.destroyStrategy ?? "sequential";
38188
38130
  if (this.local) this.logger.warnOnce("Development mode is in beta. Please report any issues to https://github.com/alchemy-run/alchemy/issues.");
38189
38131
  this.stateStore = stateStore ?? this.parent?.stateStore ?? ((scope) => new FileSystemStateStore(scope));
38190
- this.state = new InstrumentedStateStore(this.stateStore(this));
38132
+ this.state = this.stateStore(this);
38191
38133
  this.dataMutex = new AsyncMutex();
38192
38134
  }
38193
38135
  async has(id, type$1) {