alchemy 0.87.0 → 0.90.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.
- package/bin/alchemy.js +485 -543
- package/lib/cloudflare/compatibility-date.d.ts +1 -1
- package/lib/cloudflare/compatibility-date.d.ts.map +1 -1
- package/lib/cloudflare/dns-records.d.ts +6 -0
- package/lib/cloudflare/dns-records.d.ts.map +1 -1
- package/lib/cloudflare/dns-records.js +14 -12
- package/lib/cloudflare/dns-records.js.map +1 -1
- package/lib/cloudflare/index.d.ts +1 -0
- package/lib/cloudflare/index.d.ts.map +1 -1
- package/lib/cloudflare/index.js +1 -0
- package/lib/cloudflare/index.js.map +1 -1
- package/lib/cloudflare/r2-bucket-notification.d.ts +312 -0
- package/lib/cloudflare/r2-bucket-notification.d.ts.map +1 -0
- package/lib/cloudflare/r2-bucket-notification.js +387 -0
- package/lib/cloudflare/r2-bucket-notification.js.map +1 -0
- package/lib/cloudflare/worker.d.ts.map +1 -1
- package/lib/cloudflare/worker.js +8 -1
- package/lib/cloudflare/worker.js.map +1 -1
- package/lib/cloudflare/workflow.d.ts +17 -0
- package/lib/cloudflare/workflow.d.ts.map +1 -1
- package/lib/cloudflare/workflow.js +1 -0
- package/lib/cloudflare/workflow.js.map +1 -1
- package/lib/cloudflare/wrangler.json.js +1 -0
- package/lib/cloudflare/wrangler.json.js.map +1 -1
- package/lib/docker/api.d.ts +40 -0
- package/lib/docker/api.d.ts.map +1 -1
- package/lib/docker/api.js +30 -0
- package/lib/docker/api.js.map +1 -1
- package/lib/docker/network.d.ts.map +1 -1
- package/lib/docker/network.js +24 -7
- package/lib/docker/network.js.map +1 -1
- package/lib/encrypt.d.ts.map +1 -1
- package/lib/encrypt.js +2 -2
- package/lib/encrypt.js.map +1 -1
- package/lib/planetscale/api.d.ts +1 -0
- package/lib/planetscale/api.d.ts.map +1 -1
- package/lib/planetscale/api.js +1 -1
- package/lib/planetscale/api.js.map +1 -1
- package/lib/planetscale/branch.d.ts +28 -0
- package/lib/planetscale/branch.d.ts.map +1 -1
- package/lib/planetscale/branch.js +12 -0
- package/lib/planetscale/branch.js.map +1 -1
- package/lib/planetscale/database-extensions.d.ts +515 -0
- package/lib/planetscale/database-extensions.d.ts.map +1 -0
- package/lib/planetscale/database-extensions.js +389 -0
- package/lib/planetscale/database-extensions.js.map +1 -0
- package/lib/planetscale/database.d.ts +40 -2
- package/lib/planetscale/database.d.ts.map +1 -1
- package/lib/planetscale/database.js +76 -1
- package/lib/planetscale/database.js.map +1 -1
- package/lib/scope.d.ts.map +1 -1
- package/lib/scope.js +1 -2
- package/lib/scope.js.map +1 -1
- package/lib/util/telemetry.d.ts +1 -6
- package/lib/util/telemetry.d.ts.map +1 -1
- package/lib/util/telemetry.js.map +1 -1
- package/package.json +6 -6
- package/src/cloudflare/dns-records.ts +25 -16
- package/src/cloudflare/index.ts +1 -0
- package/src/cloudflare/r2-bucket-notification.ts +809 -0
- package/src/cloudflare/worker.ts +8 -1
- package/src/cloudflare/workflow.ts +18 -0
- package/src/cloudflare/wrangler.json.ts +1 -0
- package/src/docker/api.ts +61 -0
- package/src/docker/network.ts +27 -7
- package/src/encrypt.ts +2 -0
- package/src/planetscale/api.ts +1 -1
- package/src/planetscale/branch.ts +45 -0
- package/src/planetscale/database-extensions.ts +947 -0
- package/src/planetscale/database.ts +141 -2
- package/src/scope.ts +1 -2
- package/src/util/telemetry.ts +1 -20
- package/workers/cloudflare-state-store.js +57 -57
- package/workers/tunnel-proxy.js +1 -1
- package/lib/state/instrumented-state-store.d.ts +0 -19
- package/lib/state/instrumented-state-store.d.ts.map +0 -1
- package/lib/state/instrumented-state-store.js +0 -68
- package/lib/state/instrumented-state-store.js.map +0 -1
- package/src/state/instrumented-state-store.ts +0 -108
package/bin/alchemy.js
CHANGED
|
@@ -36878,7 +36878,7 @@ async function deriveScryptKey(passphrase, salt) {
|
|
|
36878
36878
|
async function libsodiumDecrypt(encryptedValue, key) {
|
|
36879
36879
|
const sodium = (await import("libsodium-wrappers")).default;
|
|
36880
36880
|
await sodium.ready;
|
|
36881
|
-
const cryptoKey = sodium.crypto_generichash(sodium.crypto_secretbox_KEYBYTES, sodium.from_string(key));
|
|
36881
|
+
const cryptoKey = sodium.crypto_generichash(sodium.crypto_secretbox_KEYBYTES, sodium.from_string(key), null);
|
|
36882
36882
|
const combined = sodium.from_base64(encryptedValue, sodium.base64_variants.ORIGINAL);
|
|
36883
36883
|
const nonce = combined.slice(0, sodium.crypto_secretbox_NONCEBYTES);
|
|
36884
36884
|
const ciphertext = combined.slice(sodium.crypto_secretbox_NONCEBYTES);
|
|
@@ -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.
|
|
37056
|
+
var version = "0.90.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": "
|
|
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 = [
|
|
@@ -37290,7 +37246,7 @@ var dependencies = {
|
|
|
37290
37246
|
"glob": "^10.0.0",
|
|
37291
37247
|
"jszip": "^3.0.0",
|
|
37292
37248
|
"libsodium-wrappers": "^0.8.0",
|
|
37293
|
-
"miniflare": "
|
|
37249
|
+
"miniflare": "4.20260310.0",
|
|
37294
37250
|
"neverthrow": "^8.2.0",
|
|
37295
37251
|
"open": "^10.1.2",
|
|
37296
37252
|
"openapi-types": "^12.1.3",
|
|
@@ -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
|
-
"
|
|
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/
|
|
37419
|
-
|
|
37420
|
-
|
|
37421
|
-
|
|
37422
|
-
|
|
37423
|
-
|
|
37424
|
-
|
|
37425
|
-
|
|
37426
|
-
|
|
37427
|
-
|
|
37428
|
-
|
|
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/
|
|
37433
|
-
const
|
|
37434
|
-
|
|
37435
|
-
|
|
37436
|
-
|
|
37437
|
-
|
|
37438
|
-
|
|
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
|
-
|
|
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/
|
|
37447
|
-
|
|
37448
|
-
|
|
37449
|
-
|
|
37450
|
-
|
|
37451
|
-
|
|
37452
|
-
|
|
37453
|
-
|
|
37454
|
-
|
|
37455
|
-
|
|
37456
|
-
|
|
37457
|
-
|
|
37458
|
-
|
|
37459
|
-
|
|
37460
|
-
|
|
37461
|
-
|
|
37462
|
-
|
|
37463
|
-
|
|
37464
|
-
|
|
37465
|
-
|
|
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
|
-
|
|
37518
|
+
process.kill(pid$1, 0);
|
|
37519
|
+
return true;
|
|
37468
37520
|
} catch {
|
|
37469
|
-
return
|
|
37521
|
+
return false;
|
|
37470
37522
|
}
|
|
37471
37523
|
}
|
|
37472
|
-
|
|
37473
|
-
|
|
37474
|
-
|
|
37475
|
-
|
|
37476
|
-
|
|
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
|
-
|
|
37486
|
-
|
|
37487
|
-
|
|
37488
|
-
|
|
37489
|
-
|
|
37490
|
-
|
|
37491
|
-
|
|
37492
|
-
|
|
37493
|
-
|
|
37494
|
-
|
|
37495
|
-
|
|
37496
|
-
|
|
37497
|
-
|
|
37498
|
-
|
|
37499
|
-
|
|
37500
|
-
|
|
37501
|
-
|
|
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
|
-
|
|
37508
|
-
|
|
37509
|
-
|
|
37510
|
-
|
|
37511
|
-
|
|
37512
|
-
|
|
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
|
-
|
|
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
|
-
|
|
37519
|
-
|
|
37520
|
-
|
|
37521
|
-
|
|
37522
|
-
|
|
37523
|
-
|
|
37524
|
-
|
|
37525
|
-
|
|
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
|
-
|
|
37528
|
-
|
|
37529
|
-
|
|
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
|
-
|
|
37532
|
-
|
|
37533
|
-
|
|
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
|
-
|
|
37537
|
-
|
|
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
|
-
|
|
37541
|
-
|
|
37542
|
-
|
|
37543
|
-
|
|
37544
|
-
|
|
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 =
|
|
38132
|
+
this.state = this.stateStore(this);
|
|
38191
38133
|
this.dataMutex = new AsyncMutex();
|
|
38192
38134
|
}
|
|
38193
38135
|
async has(id, type$1) {
|