@lark-apaas/fullstack-cli 1.1.68-alpha.20261002212123 → 1.1.68-alpha.20261003004004
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/dist/index.js +52 -887
- package/package.json +1 -1
- package/templates/scripts/build.sh +17 -609
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
|
-
import
|
|
3
|
-
import
|
|
2
|
+
import fs31 from "fs";
|
|
3
|
+
import path26 from "path";
|
|
4
4
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
5
5
|
import { config as dotenvConfig } from "dotenv";
|
|
6
6
|
|
|
@@ -4882,7 +4882,11 @@ async function hydrateCapability(capability) {
|
|
|
4882
4882
|
let pluginInstance = null;
|
|
4883
4883
|
if (hasDynamicSchema) {
|
|
4884
4884
|
const plugin = await loadPlugin(capability.pluginKey);
|
|
4885
|
-
|
|
4885
|
+
try {
|
|
4886
|
+
pluginInstance = plugin.create(capability.formValue || {});
|
|
4887
|
+
} catch {
|
|
4888
|
+
throw new Error("plugin_initialization_failed");
|
|
4889
|
+
}
|
|
4886
4890
|
}
|
|
4887
4891
|
const actions = [];
|
|
4888
4892
|
for (let index = 0; index < manifest.actions.length; index++) {
|
|
@@ -4894,9 +4898,14 @@ async function hydrateCapability(capability) {
|
|
|
4894
4898
|
if (!pluginInstance) {
|
|
4895
4899
|
throw new Error(`Plugin instance not available for dynamic schema`);
|
|
4896
4900
|
}
|
|
4897
|
-
|
|
4901
|
+
let jsonSchema;
|
|
4902
|
+
try {
|
|
4903
|
+
jsonSchema = pluginInstance.getInputJsonSchema(manifestAction.key);
|
|
4904
|
+
} catch {
|
|
4905
|
+
throw new Error("plugin_input_schema_resolution_failed");
|
|
4906
|
+
}
|
|
4898
4907
|
if (!jsonSchema) {
|
|
4899
|
-
throw new Error(
|
|
4908
|
+
throw new Error("plugin_input_schema_resolution_failed");
|
|
4900
4909
|
}
|
|
4901
4910
|
inputSchema = jsonSchema;
|
|
4902
4911
|
} else {
|
|
@@ -4907,9 +4916,14 @@ async function hydrateCapability(capability) {
|
|
|
4907
4916
|
if (!pluginInstance) {
|
|
4908
4917
|
throw new Error(`Plugin instance not available for dynamic schema`);
|
|
4909
4918
|
}
|
|
4910
|
-
|
|
4919
|
+
let jsonSchema;
|
|
4920
|
+
try {
|
|
4921
|
+
jsonSchema = pluginInstance.getOutputJsonSchema(manifestAction.key, capability.formValue || {});
|
|
4922
|
+
} catch {
|
|
4923
|
+
throw new Error("plugin_output_schema_resolution_failed");
|
|
4924
|
+
}
|
|
4911
4925
|
if (!jsonSchema) {
|
|
4912
|
-
throw new Error(
|
|
4926
|
+
throw new Error("plugin_output_schema_resolution_failed");
|
|
4913
4927
|
}
|
|
4914
4928
|
outputSchema = jsonSchema;
|
|
4915
4929
|
} else {
|
|
@@ -4940,6 +4954,21 @@ async function hydrateCapability(capability) {
|
|
|
4940
4954
|
};
|
|
4941
4955
|
}
|
|
4942
4956
|
}
|
|
4957
|
+
function getHydrationFailureReason(result) {
|
|
4958
|
+
if ("_hydrateError" in result) {
|
|
4959
|
+
return result._hydrateError || "unknown hydration error";
|
|
4960
|
+
}
|
|
4961
|
+
if (!("actions" in result)) {
|
|
4962
|
+
return "hydration result is missing actions";
|
|
4963
|
+
}
|
|
4964
|
+
if (!Array.isArray(result.actions)) {
|
|
4965
|
+
return "hydration result actions is not an array";
|
|
4966
|
+
}
|
|
4967
|
+
if (result.actions.length === 0) {
|
|
4968
|
+
return "hydration result has no actions";
|
|
4969
|
+
}
|
|
4970
|
+
return void 0;
|
|
4971
|
+
}
|
|
4943
4972
|
function formatJsonOutput(data, compact = false) {
|
|
4944
4973
|
return compact ? JSON.stringify(data) : JSON.stringify(data, null, 2);
|
|
4945
4974
|
}
|
|
@@ -4974,6 +5003,12 @@ async function listSingle(id, summary) {
|
|
|
4974
5003
|
printJson(capability);
|
|
4975
5004
|
} else {
|
|
4976
5005
|
const hydrated = await hydrateCapability(capability);
|
|
5006
|
+
const failureReason = getHydrationFailureReason(hydrated);
|
|
5007
|
+
if (failureReason) {
|
|
5008
|
+
throw new Error(
|
|
5009
|
+
`capability_hydration_failed: pluginKey=${capability.pluginKey} capabilityId=${capability.id} reason=${failureReason}`
|
|
5010
|
+
);
|
|
5011
|
+
}
|
|
4977
5012
|
printJson(hydrated);
|
|
4978
5013
|
}
|
|
4979
5014
|
}
|
|
@@ -5201,7 +5236,7 @@ var PROMPT_PATTERNS = [
|
|
|
5201
5236
|
{ pattern: /proceed\?/i, answer: "y\n" }
|
|
5202
5237
|
];
|
|
5203
5238
|
async function executeShadcnAdd(registryItemPath) {
|
|
5204
|
-
return new Promise((
|
|
5239
|
+
return new Promise((resolve2) => {
|
|
5205
5240
|
let output = "";
|
|
5206
5241
|
const args = ["--yes", "shadcn@3.8.2", "add", registryItemPath];
|
|
5207
5242
|
const ptyProcess = pty.spawn("npx", args, {
|
|
@@ -5227,7 +5262,7 @@ async function executeShadcnAdd(registryItemPath) {
|
|
|
5227
5262
|
});
|
|
5228
5263
|
const timeoutId = setTimeout(() => {
|
|
5229
5264
|
ptyProcess.kill();
|
|
5230
|
-
|
|
5265
|
+
resolve2({
|
|
5231
5266
|
success: false,
|
|
5232
5267
|
files: [],
|
|
5233
5268
|
error: "\u6267\u884C\u8D85\u65F6"
|
|
@@ -5238,7 +5273,7 @@ async function executeShadcnAdd(registryItemPath) {
|
|
|
5238
5273
|
const success = exitCode === 0;
|
|
5239
5274
|
const filePaths = parseOutput(output);
|
|
5240
5275
|
const files = filePaths.map(toFileInfo);
|
|
5241
|
-
|
|
5276
|
+
resolve2({
|
|
5242
5277
|
success,
|
|
5243
5278
|
files,
|
|
5244
5279
|
error: success ? void 0 : output || `Process exited with code ${exitCode}`
|
|
@@ -5249,12 +5284,12 @@ async function executeShadcnAdd(registryItemPath) {
|
|
|
5249
5284
|
|
|
5250
5285
|
// src/commands/component/add.handler.ts
|
|
5251
5286
|
function runActionPluginInit() {
|
|
5252
|
-
return new Promise((
|
|
5287
|
+
return new Promise((resolve2) => {
|
|
5253
5288
|
execFile("fullstack-cli", ["action-plugin", "init"], { cwd: process.cwd(), stdio: "ignore" }, (error) => {
|
|
5254
5289
|
if (error) {
|
|
5255
5290
|
debug("action-plugin init \u5931\u8D25: %s", error.message);
|
|
5256
5291
|
}
|
|
5257
|
-
|
|
5292
|
+
resolve2();
|
|
5258
5293
|
});
|
|
5259
5294
|
});
|
|
5260
5295
|
}
|
|
@@ -8429,875 +8464,6 @@ var buildCommandGroup = {
|
|
|
8429
8464
|
commands: [getTokenCommand, uploadStaticCommand, preUploadStaticCommand]
|
|
8430
8465
|
};
|
|
8431
8466
|
|
|
8432
|
-
// src/output-lease.ts
|
|
8433
|
-
import * as crypto from "crypto";
|
|
8434
|
-
import * as fs31 from "fs";
|
|
8435
|
-
import * as os3 from "os";
|
|
8436
|
-
import * as path26 from "path";
|
|
8437
|
-
import { execFileSync as execFileSync2 } from "child_process";
|
|
8438
|
-
var OUTPUT_LEASE_PROTOCOL_VERSION = 1;
|
|
8439
|
-
var OUTPUT_LEASE_ENV = "MIAODA_PREVIEW_OUTPUT_LEASE_V1";
|
|
8440
|
-
var OUTPUT_LEASE_CONFLICT = "MIAODA_PREVIEW_OUTPUT_CONFLICT";
|
|
8441
|
-
var OUTPUT_LEASE_STATE_UNKNOWN = "MIAODA_PREVIEW_STATE_UNKNOWN";
|
|
8442
|
-
var DEFAULT_HEARTBEAT_INTERVAL_MS = 2e3;
|
|
8443
|
-
var DEFAULT_STALE_AFTER_MS = 15e3;
|
|
8444
|
-
var TRANSITION_RETRY_COUNT = 200;
|
|
8445
|
-
var TRANSITION_RETRY_MS = 5;
|
|
8446
|
-
var OWNER_FILE = "owner.json";
|
|
8447
|
-
var TOKEN_PATTERN = /^[A-Za-z0-9_-]{43}$/;
|
|
8448
|
-
var KEY_PATTERN = /^[a-f0-9]{64}$/;
|
|
8449
|
-
var TEMP_OWNER_PATTERN = /^\.owner-[0-9]+-[a-f0-9]{16}\.tmp$/;
|
|
8450
|
-
var OutputLeaseProtocolError = class extends Error {
|
|
8451
|
-
constructor(code, detail) {
|
|
8452
|
-
super(`${code}: ${detail}`);
|
|
8453
|
-
this.name = "OutputLeaseProtocolError";
|
|
8454
|
-
this.code = code;
|
|
8455
|
-
}
|
|
8456
|
-
};
|
|
8457
|
-
function unknown(detail, cause) {
|
|
8458
|
-
const suffix = cause instanceof Error && cause.message ? ` (${cause.message})` : "";
|
|
8459
|
-
return new OutputLeaseProtocolError(
|
|
8460
|
-
OUTPUT_LEASE_STATE_UNKNOWN,
|
|
8461
|
-
`${detail}${suffix}`
|
|
8462
|
-
);
|
|
8463
|
-
}
|
|
8464
|
-
function conflict(target, owner) {
|
|
8465
|
-
return new OutputLeaseProtocolError(
|
|
8466
|
-
OUTPUT_LEASE_CONFLICT,
|
|
8467
|
-
`${target.canonicalOutDir} is held by an active ${owner.kind} owner`
|
|
8468
|
-
);
|
|
8469
|
-
}
|
|
8470
|
-
function errnoCode(error) {
|
|
8471
|
-
return error instanceof Error && "code" in error ? error.code : void 0;
|
|
8472
|
-
}
|
|
8473
|
-
function isMissing(error) {
|
|
8474
|
-
const code = errnoCode(error);
|
|
8475
|
-
return code === "ENOENT" || code === "ESRCH";
|
|
8476
|
-
}
|
|
8477
|
-
function sleepSync(milliseconds) {
|
|
8478
|
-
const signal = new Int32Array(new SharedArrayBuffer(4));
|
|
8479
|
-
Atomics.wait(signal, 0, 0, milliseconds);
|
|
8480
|
-
}
|
|
8481
|
-
function canonicalizeWithoutCreating(input) {
|
|
8482
|
-
let cursor = path26.resolve(input);
|
|
8483
|
-
const missing = [];
|
|
8484
|
-
for (; ; ) {
|
|
8485
|
-
try {
|
|
8486
|
-
fs31.lstatSync(cursor);
|
|
8487
|
-
} catch (error) {
|
|
8488
|
-
if (!isMissing(error))
|
|
8489
|
-
throw unknown(`cannot inspect path ${cursor}`, error);
|
|
8490
|
-
const parent = path26.dirname(cursor);
|
|
8491
|
-
if (parent === cursor) {
|
|
8492
|
-
throw unknown(`cannot find an existing ancestor for ${input}`);
|
|
8493
|
-
}
|
|
8494
|
-
missing.unshift(path26.basename(cursor));
|
|
8495
|
-
cursor = parent;
|
|
8496
|
-
continue;
|
|
8497
|
-
}
|
|
8498
|
-
try {
|
|
8499
|
-
return path26.resolve(fs31.realpathSync.native(cursor), ...missing);
|
|
8500
|
-
} catch (error) {
|
|
8501
|
-
throw unknown(`cannot canonicalize existing ancestor ${cursor}`, error);
|
|
8502
|
-
}
|
|
8503
|
-
}
|
|
8504
|
-
}
|
|
8505
|
-
function currentUserComponent() {
|
|
8506
|
-
if (typeof process.getuid === "function") return `uid-${process.getuid()}`;
|
|
8507
|
-
return `user-${crypto.createHash("sha256").update(os3.userInfo().username).digest("hex").slice(0, 16)}`;
|
|
8508
|
-
}
|
|
8509
|
-
function defaultLockRootForCurrentUser(temporaryRoot = os3.tmpdir()) {
|
|
8510
|
-
return path26.join(
|
|
8511
|
-
temporaryRoot,
|
|
8512
|
-
`miaoda-preview-output-leases-v${OUTPUT_LEASE_PROTOCOL_VERSION}-${currentUserComponent()}`
|
|
8513
|
-
);
|
|
8514
|
-
}
|
|
8515
|
-
function validatePrivateDirectory(directory, label, expected) {
|
|
8516
|
-
let link;
|
|
8517
|
-
let stat;
|
|
8518
|
-
try {
|
|
8519
|
-
link = fs31.lstatSync(directory);
|
|
8520
|
-
if (link.isSymbolicLink() || !link.isDirectory()) {
|
|
8521
|
-
throw new Error("not a direct directory");
|
|
8522
|
-
}
|
|
8523
|
-
stat = fs31.statSync(directory);
|
|
8524
|
-
} catch (error) {
|
|
8525
|
-
throw unknown(`untrusted ${label} ${directory}`, error);
|
|
8526
|
-
}
|
|
8527
|
-
if (!stat.isDirectory() || stat.dev !== link.dev || stat.ino !== link.ino) {
|
|
8528
|
-
throw unknown(`untrusted ${label} identity ${directory}`);
|
|
8529
|
-
}
|
|
8530
|
-
if (expected && (stat.dev !== expected.device || stat.ino !== expected.inode)) {
|
|
8531
|
-
throw unknown(`${label} changed identity ${directory}`);
|
|
8532
|
-
}
|
|
8533
|
-
if (process.platform !== "win32") {
|
|
8534
|
-
if (typeof process.getuid !== "function" || stat.uid !== process.getuid()) {
|
|
8535
|
-
throw unknown(`unexpected owner for ${label} ${directory}`);
|
|
8536
|
-
}
|
|
8537
|
-
if ((stat.mode & 63) !== 0) {
|
|
8538
|
-
throw unknown(`insecure permissions for ${label} ${directory}`);
|
|
8539
|
-
}
|
|
8540
|
-
}
|
|
8541
|
-
return { device: stat.dev, inode: stat.ino };
|
|
8542
|
-
}
|
|
8543
|
-
function prepareTrustedLockRoot(lockRoot) {
|
|
8544
|
-
try {
|
|
8545
|
-
fs31.mkdirSync(lockRoot, { mode: 448 });
|
|
8546
|
-
} catch (error) {
|
|
8547
|
-
if (errnoCode(error) !== "EEXIST") {
|
|
8548
|
-
throw unknown(`cannot prepare lease storage ${lockRoot}`, error);
|
|
8549
|
-
}
|
|
8550
|
-
}
|
|
8551
|
-
return validatePrivateDirectory(lockRoot, "lease root");
|
|
8552
|
-
}
|
|
8553
|
-
function assertTrustedRoot(target) {
|
|
8554
|
-
validatePrivateDirectory(target.lockRoot, "lease root", {
|
|
8555
|
-
device: target.lockRootDevice,
|
|
8556
|
-
inode: target.lockRootInode
|
|
8557
|
-
});
|
|
8558
|
-
}
|
|
8559
|
-
function resolveOutputLeaseTarget(rootDir, outDir, lockRoot = defaultLockRootForCurrentUser()) {
|
|
8560
|
-
const canonicalRoot = canonicalizeWithoutCreating(rootDir);
|
|
8561
|
-
const absoluteOutDir = path26.isAbsolute(outDir) ? outDir : path26.resolve(canonicalRoot, outDir);
|
|
8562
|
-
const canonicalOutDir = canonicalizeWithoutCreating(absoluteOutDir);
|
|
8563
|
-
const key = crypto.createHash("sha256").update(`miaoda-preview-output-lease:v${OUTPUT_LEASE_PROTOCOL_VERSION}\0`).update(canonicalOutDir).digest("hex");
|
|
8564
|
-
const canonicalLockRoot = path26.resolve(lockRoot);
|
|
8565
|
-
const identity = prepareTrustedLockRoot(canonicalLockRoot);
|
|
8566
|
-
const lockDir = path26.join(canonicalLockRoot, `${key}.lease`);
|
|
8567
|
-
return {
|
|
8568
|
-
key,
|
|
8569
|
-
canonicalRoot,
|
|
8570
|
-
canonicalOutDir,
|
|
8571
|
-
lockRoot: canonicalLockRoot,
|
|
8572
|
-
lockRootDevice: identity.device,
|
|
8573
|
-
lockRootInode: identity.inode,
|
|
8574
|
-
lockDir,
|
|
8575
|
-
ownerFile: path26.join(lockDir, OWNER_FILE),
|
|
8576
|
-
transitionDir: path26.join(canonicalLockRoot, `${key}.transition`)
|
|
8577
|
-
};
|
|
8578
|
-
}
|
|
8579
|
-
function acquireTransitionGuard(target) {
|
|
8580
|
-
for (let attempt = 0; attempt < TRANSITION_RETRY_COUNT; attempt += 1) {
|
|
8581
|
-
assertTrustedRoot(target);
|
|
8582
|
-
try {
|
|
8583
|
-
fs31.mkdirSync(target.transitionDir, { mode: 448 });
|
|
8584
|
-
const identity = validatePrivateDirectory(
|
|
8585
|
-
target.transitionDir,
|
|
8586
|
-
"transition guard"
|
|
8587
|
-
);
|
|
8588
|
-
assertTrustedRoot(target);
|
|
8589
|
-
return {
|
|
8590
|
-
path: target.transitionDir,
|
|
8591
|
-
device: identity.device,
|
|
8592
|
-
inode: identity.inode,
|
|
8593
|
-
poisoned: false
|
|
8594
|
-
};
|
|
8595
|
-
} catch (error) {
|
|
8596
|
-
if (errnoCode(error) !== "EEXIST") {
|
|
8597
|
-
if (error instanceof OutputLeaseProtocolError) throw error;
|
|
8598
|
-
throw unknown(
|
|
8599
|
-
`cannot acquire transition guard for ${target.key}`,
|
|
8600
|
-
error
|
|
8601
|
-
);
|
|
8602
|
-
}
|
|
8603
|
-
try {
|
|
8604
|
-
validatePrivateDirectory(target.transitionDir, "transition guard");
|
|
8605
|
-
} catch (validationError) {
|
|
8606
|
-
if (!fs31.existsSync(target.transitionDir)) continue;
|
|
8607
|
-
throw validationError;
|
|
8608
|
-
}
|
|
8609
|
-
sleepSync(TRANSITION_RETRY_MS);
|
|
8610
|
-
}
|
|
8611
|
-
}
|
|
8612
|
-
throw unknown(
|
|
8613
|
-
`transition guard is unavailable for ${target.canonicalOutDir}`
|
|
8614
|
-
);
|
|
8615
|
-
}
|
|
8616
|
-
function assertTransitionGuard(target, guard) {
|
|
8617
|
-
assertTrustedRoot(target);
|
|
8618
|
-
validatePrivateDirectory(guard.path, "transition guard", guard);
|
|
8619
|
-
}
|
|
8620
|
-
function releaseTransitionGuard(target, guard) {
|
|
8621
|
-
assertTransitionGuard(target, guard);
|
|
8622
|
-
try {
|
|
8623
|
-
fs31.rmdirSync(guard.path);
|
|
8624
|
-
} catch (error) {
|
|
8625
|
-
throw unknown(`cannot release transition guard for ${target.key}`, error);
|
|
8626
|
-
}
|
|
8627
|
-
}
|
|
8628
|
-
function withTransitionGuard(target, operation) {
|
|
8629
|
-
const guard = acquireTransitionGuard(target);
|
|
8630
|
-
let result;
|
|
8631
|
-
let failure;
|
|
8632
|
-
try {
|
|
8633
|
-
result = operation(guard);
|
|
8634
|
-
} catch (error) {
|
|
8635
|
-
failure = error;
|
|
8636
|
-
}
|
|
8637
|
-
if (!guard.poisoned) {
|
|
8638
|
-
try {
|
|
8639
|
-
releaseTransitionGuard(target, guard);
|
|
8640
|
-
} catch (error) {
|
|
8641
|
-
failure = error;
|
|
8642
|
-
}
|
|
8643
|
-
}
|
|
8644
|
-
if (failure !== void 0) throw failure;
|
|
8645
|
-
return result;
|
|
8646
|
-
}
|
|
8647
|
-
function parseLinuxStat(contents) {
|
|
8648
|
-
const end = contents.lastIndexOf(")");
|
|
8649
|
-
if (end < 0) throw new Error("missing process command terminator");
|
|
8650
|
-
const fields = contents.slice(end + 2).trim().split(/\s+/);
|
|
8651
|
-
if (fields.length < 20) throw new Error("incomplete process stat");
|
|
8652
|
-
const parentPid = Number(fields[1]);
|
|
8653
|
-
const startTime = fields[19];
|
|
8654
|
-
if (!Number.isSafeInteger(parentPid) || parentPid < 0 || !/^\d+$/.test(startTime)) {
|
|
8655
|
-
throw new Error("invalid process stat");
|
|
8656
|
-
}
|
|
8657
|
-
return { parentPid, startIdentity: `linux-proc-start:${startTime}` };
|
|
8658
|
-
}
|
|
8659
|
-
function inspectProcess(pid) {
|
|
8660
|
-
if (!Number.isSafeInteger(pid) || pid <= 0) return { state: "unknown" };
|
|
8661
|
-
if (process.platform === "linux") {
|
|
8662
|
-
try {
|
|
8663
|
-
return {
|
|
8664
|
-
state: "alive",
|
|
8665
|
-
startIdentity: parseLinuxStat(
|
|
8666
|
-
fs31.readFileSync(`/proc/${pid}/stat`, "utf8")
|
|
8667
|
-
).startIdentity
|
|
8668
|
-
};
|
|
8669
|
-
} catch (error) {
|
|
8670
|
-
return isMissing(error) ? { state: "dead" } : { state: "unknown" };
|
|
8671
|
-
}
|
|
8672
|
-
}
|
|
8673
|
-
if (process.platform !== "win32") {
|
|
8674
|
-
try {
|
|
8675
|
-
const value = execFileSync2("ps", ["-o", "lstart=", "-p", String(pid)], {
|
|
8676
|
-
encoding: "utf8",
|
|
8677
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
8678
|
-
}).trim();
|
|
8679
|
-
return value ? { state: "alive", startIdentity: `posix-ps-start:${value}` } : { state: "dead" };
|
|
8680
|
-
} catch {
|
|
8681
|
-
try {
|
|
8682
|
-
process.kill(pid, 0);
|
|
8683
|
-
return { state: "unknown" };
|
|
8684
|
-
} catch (error) {
|
|
8685
|
-
return isMissing(error) ? { state: "dead" } : { state: "unknown" };
|
|
8686
|
-
}
|
|
8687
|
-
}
|
|
8688
|
-
}
|
|
8689
|
-
return { state: "unknown" };
|
|
8690
|
-
}
|
|
8691
|
-
function readParentPid(pid) {
|
|
8692
|
-
if (process.platform === "linux") {
|
|
8693
|
-
try {
|
|
8694
|
-
return parseLinuxStat(fs31.readFileSync(`/proc/${pid}/stat`, "utf8")).parentPid;
|
|
8695
|
-
} catch {
|
|
8696
|
-
return null;
|
|
8697
|
-
}
|
|
8698
|
-
}
|
|
8699
|
-
if (process.platform !== "win32") {
|
|
8700
|
-
try {
|
|
8701
|
-
const parent = Number(
|
|
8702
|
-
execFileSync2("ps", ["-o", "ppid=", "-p", String(pid)], {
|
|
8703
|
-
encoding: "utf8",
|
|
8704
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
8705
|
-
}).trim()
|
|
8706
|
-
);
|
|
8707
|
-
return Number.isSafeInteger(parent) && parent >= 0 ? parent : null;
|
|
8708
|
-
} catch {
|
|
8709
|
-
return null;
|
|
8710
|
-
}
|
|
8711
|
-
}
|
|
8712
|
-
return null;
|
|
8713
|
-
}
|
|
8714
|
-
function isProcessOrDescendant(pid, ancestorPid) {
|
|
8715
|
-
let cursor = pid;
|
|
8716
|
-
const seen = /* @__PURE__ */ new Set();
|
|
8717
|
-
for (let depth = 0; depth < 256; depth += 1) {
|
|
8718
|
-
if (cursor === ancestorPid) return true;
|
|
8719
|
-
if (cursor <= 1 || seen.has(cursor)) return false;
|
|
8720
|
-
seen.add(cursor);
|
|
8721
|
-
const parent = readParentPid(cursor);
|
|
8722
|
-
if (parent === null) return false;
|
|
8723
|
-
cursor = parent;
|
|
8724
|
-
}
|
|
8725
|
-
return false;
|
|
8726
|
-
}
|
|
8727
|
-
function validateOwner(value, target) {
|
|
8728
|
-
if (!value || typeof value !== "object") {
|
|
8729
|
-
throw unknown(`malformed owner for ${target.canonicalOutDir}`);
|
|
8730
|
-
}
|
|
8731
|
-
const owner = value;
|
|
8732
|
-
const valid = owner.version === OUTPUT_LEASE_PROTOCOL_VERSION && (owner.kind === "dev" || owner.kind === "production") && owner.key === target.key && typeof owner.canonicalRoot === "string" && path26.isAbsolute(owner.canonicalRoot) && owner.canonicalOutDir === target.canonicalOutDir && typeof owner.generation === "string" && TOKEN_PATTERN.test(owner.generation) && typeof owner.token === "string" && TOKEN_PATTERN.test(owner.token) && Number.isSafeInteger(owner.pid) && Number(owner.pid) > 0 && typeof owner.processStartIdentity === "string" && owner.processStartIdentity.length > 0 && Number.isFinite(owner.createdAt) && Number(owner.createdAt) > 0 && Number.isFinite(owner.heartbeatAt) && Number(owner.heartbeatAt) > 0;
|
|
8733
|
-
if (!valid) throw unknown(`malformed owner for ${target.canonicalOutDir}`);
|
|
8734
|
-
return owner;
|
|
8735
|
-
}
|
|
8736
|
-
function validateRegularPrivateFile(file, label) {
|
|
8737
|
-
try {
|
|
8738
|
-
const link = fs31.lstatSync(file);
|
|
8739
|
-
const stat = fs31.statSync(file);
|
|
8740
|
-
if (link.isSymbolicLink() || !link.isFile() || !stat.isFile() || link.dev !== stat.dev || link.ino !== stat.ino || stat.nlink !== 1) {
|
|
8741
|
-
throw new Error("not a direct single-link file");
|
|
8742
|
-
}
|
|
8743
|
-
if (process.platform !== "win32") {
|
|
8744
|
-
if (typeof process.getuid !== "function" || stat.uid !== process.getuid()) {
|
|
8745
|
-
throw new Error("unexpected file owner");
|
|
8746
|
-
}
|
|
8747
|
-
if ((stat.mode & 63) !== 0) throw new Error("insecure file mode");
|
|
8748
|
-
}
|
|
8749
|
-
} catch (error) {
|
|
8750
|
-
throw unknown(`untrusted ${label} ${file}`, error);
|
|
8751
|
-
}
|
|
8752
|
-
}
|
|
8753
|
-
function readOwnerFromDirectory(target, directory) {
|
|
8754
|
-
assertTrustedRoot(target);
|
|
8755
|
-
validatePrivateDirectory(directory, "lease generation");
|
|
8756
|
-
const ownerFile = path26.join(directory, OWNER_FILE);
|
|
8757
|
-
validateRegularPrivateFile(ownerFile, "owner record");
|
|
8758
|
-
try {
|
|
8759
|
-
return validateOwner(
|
|
8760
|
-
JSON.parse(fs31.readFileSync(ownerFile, "utf8")),
|
|
8761
|
-
target
|
|
8762
|
-
);
|
|
8763
|
-
} catch (error) {
|
|
8764
|
-
if (error instanceof OutputLeaseProtocolError) throw error;
|
|
8765
|
-
throw unknown(`cannot read owner for ${target.canonicalOutDir}`, error);
|
|
8766
|
-
}
|
|
8767
|
-
}
|
|
8768
|
-
function readOwner(target) {
|
|
8769
|
-
return readOwnerFromDirectory(target, target.lockDir);
|
|
8770
|
-
}
|
|
8771
|
-
function sameGeneration(owner, expected) {
|
|
8772
|
-
return owner.generation === expected.generation && owner.token === expected.token;
|
|
8773
|
-
}
|
|
8774
|
-
function writeOwnerAtomically(target, owner, guard) {
|
|
8775
|
-
assertTransitionGuard(target, guard);
|
|
8776
|
-
const temporary = path26.join(
|
|
8777
|
-
target.lockDir,
|
|
8778
|
-
`.owner-${process.pid}-${crypto.randomBytes(8).toString("hex")}.tmp`
|
|
8779
|
-
);
|
|
8780
|
-
try {
|
|
8781
|
-
fs31.writeFileSync(temporary, `${JSON.stringify(owner)}
|
|
8782
|
-
`, {
|
|
8783
|
-
encoding: "utf8",
|
|
8784
|
-
flag: "wx",
|
|
8785
|
-
mode: 384
|
|
8786
|
-
});
|
|
8787
|
-
validateRegularPrivateFile(temporary, "temporary owner record");
|
|
8788
|
-
assertTransitionGuard(target, guard);
|
|
8789
|
-
fs31.renameSync(temporary, target.ownerFile);
|
|
8790
|
-
} catch (error) {
|
|
8791
|
-
try {
|
|
8792
|
-
assertTransitionGuard(target, guard);
|
|
8793
|
-
if (fs31.existsSync(temporary)) {
|
|
8794
|
-
validateRegularPrivateFile(temporary, "temporary owner record");
|
|
8795
|
-
fs31.unlinkSync(temporary);
|
|
8796
|
-
}
|
|
8797
|
-
} catch {
|
|
8798
|
-
guard.poisoned = true;
|
|
8799
|
-
}
|
|
8800
|
-
throw unknown(`cannot update owner for ${target.canonicalOutDir}`, error);
|
|
8801
|
-
}
|
|
8802
|
-
}
|
|
8803
|
-
function createOwner(target, kind, ownerPid, now) {
|
|
8804
|
-
const inspection = inspectProcess(ownerPid);
|
|
8805
|
-
if (inspection.state !== "alive") {
|
|
8806
|
-
throw unknown(`cannot verify lease owner process ${ownerPid}`);
|
|
8807
|
-
}
|
|
8808
|
-
return {
|
|
8809
|
-
version: OUTPUT_LEASE_PROTOCOL_VERSION,
|
|
8810
|
-
kind,
|
|
8811
|
-
key: target.key,
|
|
8812
|
-
canonicalRoot: target.canonicalRoot,
|
|
8813
|
-
canonicalOutDir: target.canonicalOutDir,
|
|
8814
|
-
generation: crypto.randomBytes(32).toString("base64url"),
|
|
8815
|
-
token: crypto.randomBytes(32).toString("base64url"),
|
|
8816
|
-
pid: ownerPid,
|
|
8817
|
-
processStartIdentity: inspection.startIdentity,
|
|
8818
|
-
createdAt: now,
|
|
8819
|
-
heartbeatAt: now
|
|
8820
|
-
};
|
|
8821
|
-
}
|
|
8822
|
-
function serializeInheritedToken(owner) {
|
|
8823
|
-
return Buffer.from(
|
|
8824
|
-
JSON.stringify({
|
|
8825
|
-
version: OUTPUT_LEASE_PROTOCOL_VERSION,
|
|
8826
|
-
key: owner.key,
|
|
8827
|
-
generation: owner.generation,
|
|
8828
|
-
token: owner.token
|
|
8829
|
-
}),
|
|
8830
|
-
"utf8"
|
|
8831
|
-
).toString("base64url");
|
|
8832
|
-
}
|
|
8833
|
-
function parseInheritedToken(value) {
|
|
8834
|
-
let parsed;
|
|
8835
|
-
try {
|
|
8836
|
-
parsed = JSON.parse(
|
|
8837
|
-
Buffer.from(value, "base64url").toString("utf8")
|
|
8838
|
-
);
|
|
8839
|
-
} catch (error) {
|
|
8840
|
-
throw unknown("malformed inherited output lease token", error);
|
|
8841
|
-
}
|
|
8842
|
-
if (!parsed || typeof parsed !== "object") {
|
|
8843
|
-
throw unknown("malformed inherited output lease token");
|
|
8844
|
-
}
|
|
8845
|
-
const token = parsed;
|
|
8846
|
-
if (token.version !== OUTPUT_LEASE_PROTOCOL_VERSION || typeof token.key !== "string" || !KEY_PATTERN.test(token.key) || typeof token.generation !== "string" || !TOKEN_PATTERN.test(token.generation) || typeof token.token !== "string" || !TOKEN_PATTERN.test(token.token)) {
|
|
8847
|
-
throw unknown("malformed inherited output lease token");
|
|
8848
|
-
}
|
|
8849
|
-
return {
|
|
8850
|
-
version: token.version,
|
|
8851
|
-
key: token.key,
|
|
8852
|
-
generation: token.generation,
|
|
8853
|
-
token: token.token
|
|
8854
|
-
};
|
|
8855
|
-
}
|
|
8856
|
-
function verifyOwnerIsCurrent(owner) {
|
|
8857
|
-
const inspection = inspectProcess(owner.pid);
|
|
8858
|
-
return inspection.state === "alive" && inspection.startIdentity === owner.processStartIdentity;
|
|
8859
|
-
}
|
|
8860
|
-
function verifyInheritedGuarded(target, kind, encoded, guard) {
|
|
8861
|
-
const inherited = parseInheritedToken(encoded);
|
|
8862
|
-
if (inherited.key !== target.key) return null;
|
|
8863
|
-
assertTransitionGuard(target, guard);
|
|
8864
|
-
const owner = readOwner(target);
|
|
8865
|
-
if (owner.generation !== inherited.generation || owner.token !== inherited.token || owner.kind !== kind) {
|
|
8866
|
-
throw conflict(target, owner);
|
|
8867
|
-
}
|
|
8868
|
-
if (owner.canonicalRoot !== target.canonicalRoot) {
|
|
8869
|
-
throw unknown(
|
|
8870
|
-
`inherited owner root does not match ${target.canonicalRoot}`
|
|
8871
|
-
);
|
|
8872
|
-
}
|
|
8873
|
-
if (!verifyOwnerIsCurrent(owner)) {
|
|
8874
|
-
throw unknown(
|
|
8875
|
-
`inherited owner is not current for ${target.canonicalOutDir}`
|
|
8876
|
-
);
|
|
8877
|
-
}
|
|
8878
|
-
if (!isProcessOrDescendant(process.pid, owner.pid)) {
|
|
8879
|
-
throw unknown(
|
|
8880
|
-
`inherited token is not in the owner process tree for ${target.canonicalOutDir}`
|
|
8881
|
-
);
|
|
8882
|
-
}
|
|
8883
|
-
return owner;
|
|
8884
|
-
}
|
|
8885
|
-
function ownerState(owner, now, staleAfterMs) {
|
|
8886
|
-
const heartbeatAge = now - owner.heartbeatAt;
|
|
8887
|
-
if (!Number.isFinite(heartbeatAge) || heartbeatAge < -staleAfterMs) {
|
|
8888
|
-
return "unknown";
|
|
8889
|
-
}
|
|
8890
|
-
const inspection = inspectProcess(owner.pid);
|
|
8891
|
-
if (inspection.state === "alive" && inspection.startIdentity === owner.processStartIdentity) {
|
|
8892
|
-
return "active";
|
|
8893
|
-
}
|
|
8894
|
-
if (heartbeatAge <= staleAfterMs) return "unknown";
|
|
8895
|
-
if (inspection.state === "dead") return "stale";
|
|
8896
|
-
if (inspection.state === "alive" && inspection.startIdentity !== owner.processStartIdentity) {
|
|
8897
|
-
return "stale";
|
|
8898
|
-
}
|
|
8899
|
-
return "unknown";
|
|
8900
|
-
}
|
|
8901
|
-
function safeRemoveLeaseDirectory(target, directory, guard) {
|
|
8902
|
-
assertTransitionGuard(target, guard);
|
|
8903
|
-
if (path26.dirname(directory) !== target.lockRoot) {
|
|
8904
|
-
throw unknown("refusing to remove lease path outside trusted root");
|
|
8905
|
-
}
|
|
8906
|
-
validatePrivateDirectory(directory, "retired lease generation");
|
|
8907
|
-
for (const entry of fs31.readdirSync(directory)) {
|
|
8908
|
-
if (entry !== OWNER_FILE && !TEMP_OWNER_PATTERN.test(entry)) {
|
|
8909
|
-
throw unknown(`unexpected entry in retired lease generation ${entry}`);
|
|
8910
|
-
}
|
|
8911
|
-
const file = path26.join(directory, entry);
|
|
8912
|
-
validateRegularPrivateFile(file, "retired lease file");
|
|
8913
|
-
fs31.unlinkSync(file);
|
|
8914
|
-
}
|
|
8915
|
-
fs31.rmdirSync(directory);
|
|
8916
|
-
}
|
|
8917
|
-
function retireStaleGeneration(target, expected, guard) {
|
|
8918
|
-
assertTransitionGuard(target, guard);
|
|
8919
|
-
const current = readOwner(target);
|
|
8920
|
-
if (!sameGeneration(current, expected)) throw conflict(target, current);
|
|
8921
|
-
const retiredDir = path26.join(
|
|
8922
|
-
target.lockRoot,
|
|
8923
|
-
`${target.key}.stale-${expected.generation.slice(0, 12)}-${crypto.randomBytes(8).toString("hex")}`
|
|
8924
|
-
);
|
|
8925
|
-
try {
|
|
8926
|
-
fs31.renameSync(target.lockDir, retiredDir);
|
|
8927
|
-
guard.poisoned = true;
|
|
8928
|
-
const moved = readOwnerFromDirectory(target, retiredDir);
|
|
8929
|
-
if (!sameGeneration(moved, expected)) {
|
|
8930
|
-
throw unknown(`owner changed while retiring ${target.canonicalOutDir}`);
|
|
8931
|
-
}
|
|
8932
|
-
safeRemoveLeaseDirectory(target, retiredDir, guard);
|
|
8933
|
-
guard.poisoned = false;
|
|
8934
|
-
} catch (error) {
|
|
8935
|
-
if (error instanceof OutputLeaseProtocolError) throw error;
|
|
8936
|
-
throw unknown(
|
|
8937
|
-
`cannot atomically retire stale owner for ${target.canonicalOutDir}`,
|
|
8938
|
-
error
|
|
8939
|
-
);
|
|
8940
|
-
}
|
|
8941
|
-
}
|
|
8942
|
-
function releaseGenerationGuarded(target, expected, guard) {
|
|
8943
|
-
assertTransitionGuard(target, guard);
|
|
8944
|
-
if (!fs31.existsSync(target.lockDir)) return false;
|
|
8945
|
-
const current = readOwner(target);
|
|
8946
|
-
if (!sameGeneration(current, expected)) return false;
|
|
8947
|
-
const releasedDir = path26.join(
|
|
8948
|
-
target.lockRoot,
|
|
8949
|
-
`${target.key}.released-${expected.generation.slice(0, 12)}-${crypto.randomBytes(8).toString("hex")}`
|
|
8950
|
-
);
|
|
8951
|
-
try {
|
|
8952
|
-
assertTransitionGuard(target, guard);
|
|
8953
|
-
const exact = readOwner(target);
|
|
8954
|
-
if (!sameGeneration(exact, expected)) return false;
|
|
8955
|
-
fs31.renameSync(target.lockDir, releasedDir);
|
|
8956
|
-
guard.poisoned = true;
|
|
8957
|
-
const moved = readOwnerFromDirectory(target, releasedDir);
|
|
8958
|
-
if (!sameGeneration(moved, expected)) {
|
|
8959
|
-
throw unknown(`owner changed while releasing ${target.canonicalOutDir}`);
|
|
8960
|
-
}
|
|
8961
|
-
safeRemoveLeaseDirectory(target, releasedDir, guard);
|
|
8962
|
-
guard.poisoned = false;
|
|
8963
|
-
return true;
|
|
8964
|
-
} catch (error) {
|
|
8965
|
-
if (error instanceof OutputLeaseProtocolError) throw error;
|
|
8966
|
-
throw unknown(`cannot release owner for ${target.canonicalOutDir}`, error);
|
|
8967
|
-
}
|
|
8968
|
-
}
|
|
8969
|
-
function releaseByGeneration(target, expected) {
|
|
8970
|
-
return withTransitionGuard(
|
|
8971
|
-
target,
|
|
8972
|
-
(guard) => releaseGenerationGuarded(target, expected, guard)
|
|
8973
|
-
);
|
|
8974
|
-
}
|
|
8975
|
-
function refreshGenerationGuarded(target, expected, now, guard) {
|
|
8976
|
-
assertTransitionGuard(target, guard);
|
|
8977
|
-
const current = readOwner(target);
|
|
8978
|
-
if (!sameGeneration(current, expected)) {
|
|
8979
|
-
throw unknown(`owner changed while refreshing ${target.canonicalOutDir}`);
|
|
8980
|
-
}
|
|
8981
|
-
writeOwnerAtomically(target, { ...current, heartbeatAt: now }, guard);
|
|
8982
|
-
}
|
|
8983
|
-
function refreshByGeneration(target, expected, now) {
|
|
8984
|
-
withTransitionGuard(
|
|
8985
|
-
target,
|
|
8986
|
-
(guard) => refreshGenerationGuarded(target, expected, now, guard)
|
|
8987
|
-
);
|
|
8988
|
-
}
|
|
8989
|
-
function createLeaseHandle(target, owner, reentrant, options) {
|
|
8990
|
-
const now = options.now ?? Date.now;
|
|
8991
|
-
const manageLifetime = options.manageLifetime !== false && !reentrant;
|
|
8992
|
-
const heartbeatIntervalMs = options.heartbeatIntervalMs ?? DEFAULT_HEARTBEAT_INTERVAL_MS;
|
|
8993
|
-
const generation = {
|
|
8994
|
-
generation: owner.generation,
|
|
8995
|
-
token: owner.token
|
|
8996
|
-
};
|
|
8997
|
-
let released = false;
|
|
8998
|
-
let heartbeat;
|
|
8999
|
-
const signalHandlers = /* @__PURE__ */ new Map();
|
|
9000
|
-
const release = () => {
|
|
9001
|
-
if (released || reentrant) return false;
|
|
9002
|
-
released = true;
|
|
9003
|
-
if (heartbeat) clearInterval(heartbeat);
|
|
9004
|
-
process.removeListener("exit", onExit);
|
|
9005
|
-
for (const [signal, handler] of signalHandlers) {
|
|
9006
|
-
process.removeListener(signal, handler);
|
|
9007
|
-
}
|
|
9008
|
-
return releaseByGeneration(target, generation);
|
|
9009
|
-
};
|
|
9010
|
-
const onExit = () => {
|
|
9011
|
-
try {
|
|
9012
|
-
releaseByGeneration(target, generation);
|
|
9013
|
-
} catch {
|
|
9014
|
-
}
|
|
9015
|
-
};
|
|
9016
|
-
if (manageLifetime) {
|
|
9017
|
-
heartbeat = setInterval(() => {
|
|
9018
|
-
try {
|
|
9019
|
-
refreshByGeneration(target, generation, now());
|
|
9020
|
-
} catch {
|
|
9021
|
-
setImmediate(() => process.exit(1));
|
|
9022
|
-
}
|
|
9023
|
-
}, heartbeatIntervalMs);
|
|
9024
|
-
heartbeat.unref();
|
|
9025
|
-
process.once("exit", onExit);
|
|
9026
|
-
const signalExitCodes = {
|
|
9027
|
-
SIGHUP: 129,
|
|
9028
|
-
SIGINT: 130,
|
|
9029
|
-
SIGTERM: 143
|
|
9030
|
-
};
|
|
9031
|
-
for (const signal of Object.keys(signalExitCodes)) {
|
|
9032
|
-
const handler = () => {
|
|
9033
|
-
try {
|
|
9034
|
-
release();
|
|
9035
|
-
} finally {
|
|
9036
|
-
setImmediate(() => process.exit(signalExitCodes[signal] ?? 1));
|
|
9037
|
-
}
|
|
9038
|
-
};
|
|
9039
|
-
signalHandlers.set(signal, handler);
|
|
9040
|
-
process.once(signal, handler);
|
|
9041
|
-
}
|
|
9042
|
-
}
|
|
9043
|
-
return {
|
|
9044
|
-
target,
|
|
9045
|
-
owner,
|
|
9046
|
-
inheritedToken: serializeInheritedToken(owner),
|
|
9047
|
-
reentrant,
|
|
9048
|
-
refresh() {
|
|
9049
|
-
if (!reentrant && !released) {
|
|
9050
|
-
refreshByGeneration(target, generation, now());
|
|
9051
|
-
}
|
|
9052
|
-
},
|
|
9053
|
-
release
|
|
9054
|
-
};
|
|
9055
|
-
}
|
|
9056
|
-
function acquireOutputLease(options) {
|
|
9057
|
-
const now = options.now ?? Date.now;
|
|
9058
|
-
const staleAfterMs = options.staleAfterMs ?? DEFAULT_STALE_AFTER_MS;
|
|
9059
|
-
const env = options.env ?? process.env;
|
|
9060
|
-
const target = resolveOutputLeaseTarget(
|
|
9061
|
-
options.rootDir,
|
|
9062
|
-
options.outDir,
|
|
9063
|
-
options.lockRoot
|
|
9064
|
-
);
|
|
9065
|
-
return withTransitionGuard(target, (guard) => {
|
|
9066
|
-
const inheritedValue = env[OUTPUT_LEASE_ENV];
|
|
9067
|
-
if (inheritedValue) {
|
|
9068
|
-
const inheritedOwner = verifyInheritedGuarded(
|
|
9069
|
-
target,
|
|
9070
|
-
options.kind,
|
|
9071
|
-
inheritedValue,
|
|
9072
|
-
guard
|
|
9073
|
-
);
|
|
9074
|
-
if (inheritedOwner) {
|
|
9075
|
-
return createLeaseHandle(target, inheritedOwner, true, options);
|
|
9076
|
-
}
|
|
9077
|
-
}
|
|
9078
|
-
const ownerPid = options.ownerPid ?? process.pid;
|
|
9079
|
-
if (ownerPid !== process.pid) {
|
|
9080
|
-
if (options.manageLifetime !== false) {
|
|
9081
|
-
throw unknown("an external owner requires externally managed lifetime");
|
|
9082
|
-
}
|
|
9083
|
-
if (!isProcessOrDescendant(process.pid, ownerPid)) {
|
|
9084
|
-
throw unknown(`process ${ownerPid} is not an ancestor of the acquirer`);
|
|
9085
|
-
}
|
|
9086
|
-
}
|
|
9087
|
-
if (fs31.existsSync(target.lockDir)) {
|
|
9088
|
-
const existing = readOwner(target);
|
|
9089
|
-
const state = ownerState(existing, now(), staleAfterMs);
|
|
9090
|
-
if (state === "active") throw conflict(target, existing);
|
|
9091
|
-
if (state === "unknown") {
|
|
9092
|
-
throw unknown(`cannot prove owner state for ${target.canonicalOutDir}`);
|
|
9093
|
-
}
|
|
9094
|
-
retireStaleGeneration(target, existing, guard);
|
|
9095
|
-
}
|
|
9096
|
-
assertTransitionGuard(target, guard);
|
|
9097
|
-
try {
|
|
9098
|
-
fs31.mkdirSync(target.lockDir, { mode: 448 });
|
|
9099
|
-
validatePrivateDirectory(target.lockDir, "lease generation");
|
|
9100
|
-
const owner = createOwner(target, options.kind, ownerPid, now());
|
|
9101
|
-
fs31.writeFileSync(target.ownerFile, `${JSON.stringify(owner)}
|
|
9102
|
-
`, {
|
|
9103
|
-
encoding: "utf8",
|
|
9104
|
-
flag: "wx",
|
|
9105
|
-
mode: 384
|
|
9106
|
-
});
|
|
9107
|
-
validateRegularPrivateFile(target.ownerFile, "owner record");
|
|
9108
|
-
return createLeaseHandle(target, owner, false, options);
|
|
9109
|
-
} catch (error) {
|
|
9110
|
-
if (error instanceof OutputLeaseProtocolError) throw error;
|
|
9111
|
-
throw unknown(`cannot create owner for ${target.canonicalOutDir}`, error);
|
|
9112
|
-
}
|
|
9113
|
-
});
|
|
9114
|
-
}
|
|
9115
|
-
function targetAndEncoded(options) {
|
|
9116
|
-
const encoded = (options.env ?? process.env)[OUTPUT_LEASE_ENV];
|
|
9117
|
-
if (!encoded) throw unknown("inherited output lease token is missing");
|
|
9118
|
-
return {
|
|
9119
|
-
target: resolveOutputLeaseTarget(
|
|
9120
|
-
options.rootDir,
|
|
9121
|
-
options.outDir,
|
|
9122
|
-
options.lockRoot
|
|
9123
|
-
),
|
|
9124
|
-
encoded
|
|
9125
|
-
};
|
|
9126
|
-
}
|
|
9127
|
-
function verifyInheritedOutputLease(options) {
|
|
9128
|
-
const { target, encoded } = targetAndEncoded(options);
|
|
9129
|
-
return withTransitionGuard(target, (guard) => {
|
|
9130
|
-
const owner = verifyInheritedGuarded(target, options.kind, encoded, guard);
|
|
9131
|
-
if (!owner) {
|
|
9132
|
-
throw unknown("inherited token targets a different output directory");
|
|
9133
|
-
}
|
|
9134
|
-
return owner;
|
|
9135
|
-
});
|
|
9136
|
-
}
|
|
9137
|
-
function refreshInheritedOutputLease(options) {
|
|
9138
|
-
const { target, encoded } = targetAndEncoded(options);
|
|
9139
|
-
withTransitionGuard(target, (guard) => {
|
|
9140
|
-
const owner = verifyInheritedGuarded(target, options.kind, encoded, guard);
|
|
9141
|
-
if (!owner) {
|
|
9142
|
-
throw unknown("inherited token targets a different output directory");
|
|
9143
|
-
}
|
|
9144
|
-
refreshGenerationGuarded(
|
|
9145
|
-
target,
|
|
9146
|
-
{ generation: owner.generation, token: owner.token },
|
|
9147
|
-
(options.now ?? Date.now)(),
|
|
9148
|
-
guard
|
|
9149
|
-
);
|
|
9150
|
-
});
|
|
9151
|
-
}
|
|
9152
|
-
function poisonInheritedOutputLease(options) {
|
|
9153
|
-
const { target, encoded } = targetAndEncoded(options);
|
|
9154
|
-
withTransitionGuard(target, (guard) => {
|
|
9155
|
-
const owner = verifyInheritedGuarded(target, options.kind, encoded, guard);
|
|
9156
|
-
if (!owner) {
|
|
9157
|
-
throw unknown("inherited token targets a different output directory");
|
|
9158
|
-
}
|
|
9159
|
-
guard.poisoned = true;
|
|
9160
|
-
});
|
|
9161
|
-
}
|
|
9162
|
-
function releaseInheritedOutputLease(options) {
|
|
9163
|
-
const env = options.env ?? process.env;
|
|
9164
|
-
const encoded = env[OUTPUT_LEASE_ENV];
|
|
9165
|
-
if (!encoded) return false;
|
|
9166
|
-
const target = resolveOutputLeaseTarget(
|
|
9167
|
-
options.rootDir,
|
|
9168
|
-
options.outDir,
|
|
9169
|
-
options.lockRoot
|
|
9170
|
-
);
|
|
9171
|
-
return withTransitionGuard(target, (guard) => {
|
|
9172
|
-
const owner = verifyInheritedGuarded(target, options.kind, encoded, guard);
|
|
9173
|
-
if (!owner) return false;
|
|
9174
|
-
return releaseGenerationGuarded(
|
|
9175
|
-
target,
|
|
9176
|
-
{ generation: owner.generation, token: owner.token },
|
|
9177
|
-
guard
|
|
9178
|
-
);
|
|
9179
|
-
});
|
|
9180
|
-
}
|
|
9181
|
-
|
|
9182
|
-
// src/commands/output-lease/index.ts
|
|
9183
|
-
function leaseOptions(options) {
|
|
9184
|
-
return {
|
|
9185
|
-
rootDir: options.root,
|
|
9186
|
-
outDir: options.outDir,
|
|
9187
|
-
kind: "production"
|
|
9188
|
-
};
|
|
9189
|
-
}
|
|
9190
|
-
function parseOwnerPid(value) {
|
|
9191
|
-
const pid = Number(value);
|
|
9192
|
-
if (!Number.isSafeInteger(pid) || pid <= 0) {
|
|
9193
|
-
throw new Error("MIAODA_PREVIEW_STATE_UNKNOWN: invalid wrapper owner PID");
|
|
9194
|
-
}
|
|
9195
|
-
return pid;
|
|
9196
|
-
}
|
|
9197
|
-
function acquireWrapperOutputLease(options) {
|
|
9198
|
-
const lease = acquireOutputLease({
|
|
9199
|
-
...leaseOptions(options),
|
|
9200
|
-
ownerPid: parseOwnerPid(options.ownerPid),
|
|
9201
|
-
manageLifetime: false,
|
|
9202
|
-
// Only Vite children may reenter. A second wrapper must contend normally.
|
|
9203
|
-
env: {}
|
|
9204
|
-
});
|
|
9205
|
-
return lease.inheritedToken;
|
|
9206
|
-
}
|
|
9207
|
-
function verifyWrapperOutputLease(options) {
|
|
9208
|
-
verifyInheritedOutputLease(leaseOptions(options));
|
|
9209
|
-
}
|
|
9210
|
-
function releaseWrapperOutputLease(options) {
|
|
9211
|
-
return releaseInheritedOutputLease(leaseOptions(options));
|
|
9212
|
-
}
|
|
9213
|
-
function poisonWrapperOutputLease(options) {
|
|
9214
|
-
poisonInheritedOutputLease(leaseOptions(options));
|
|
9215
|
-
}
|
|
9216
|
-
function startWrapperOutputLeaseHeartbeat(options, intervalMs = 2e3) {
|
|
9217
|
-
refreshInheritedOutputLease(leaseOptions(options));
|
|
9218
|
-
const heartbeat = setInterval(() => {
|
|
9219
|
-
try {
|
|
9220
|
-
refreshInheritedOutputLease(leaseOptions(options));
|
|
9221
|
-
} catch (error) {
|
|
9222
|
-
console.error(error instanceof Error ? error.message : String(error));
|
|
9223
|
-
process.exit(1);
|
|
9224
|
-
}
|
|
9225
|
-
}, intervalMs);
|
|
9226
|
-
return heartbeat;
|
|
9227
|
-
}
|
|
9228
|
-
function addTargetOptions(command) {
|
|
9229
|
-
return command.requiredOption("--root <dir>", "Project root").requiredOption("--out-dir <dir>", "Managed output directory");
|
|
9230
|
-
}
|
|
9231
|
-
var acquireCommand = {
|
|
9232
|
-
name: "acquire",
|
|
9233
|
-
description: "Acquire a private production output lease for the build wrapper",
|
|
9234
|
-
register(program) {
|
|
9235
|
-
addTargetOptions(program.command(this.name)).requiredOption("--owner-pid <pid>", "PID of the wrapper shell").action((options) => {
|
|
9236
|
-
process.stdout.write(`${acquireWrapperOutputLease(options)}
|
|
9237
|
-
`);
|
|
9238
|
-
});
|
|
9239
|
-
}
|
|
9240
|
-
};
|
|
9241
|
-
var verifyCommand = {
|
|
9242
|
-
name: "verify",
|
|
9243
|
-
description: "Verify the inherited wrapper lease",
|
|
9244
|
-
register(program) {
|
|
9245
|
-
addTargetOptions(program.command(this.name)).action(
|
|
9246
|
-
(options) => {
|
|
9247
|
-
verifyWrapperOutputLease(options);
|
|
9248
|
-
}
|
|
9249
|
-
);
|
|
9250
|
-
}
|
|
9251
|
-
};
|
|
9252
|
-
var heartbeatCommand = {
|
|
9253
|
-
name: "heartbeat",
|
|
9254
|
-
description: "Maintain the inherited wrapper lease",
|
|
9255
|
-
register(program) {
|
|
9256
|
-
addTargetOptions(program.command(this.name)).action(
|
|
9257
|
-
(options) => {
|
|
9258
|
-
startWrapperOutputLeaseHeartbeat(options);
|
|
9259
|
-
}
|
|
9260
|
-
);
|
|
9261
|
-
}
|
|
9262
|
-
};
|
|
9263
|
-
var poisonCommand = {
|
|
9264
|
-
name: "poison",
|
|
9265
|
-
description: "Durably quarantine an inherited wrapper lease",
|
|
9266
|
-
register(program) {
|
|
9267
|
-
addTargetOptions(program.command(this.name)).action(
|
|
9268
|
-
(options) => {
|
|
9269
|
-
poisonWrapperOutputLease(options);
|
|
9270
|
-
}
|
|
9271
|
-
);
|
|
9272
|
-
}
|
|
9273
|
-
};
|
|
9274
|
-
var releaseCommand = {
|
|
9275
|
-
name: "release",
|
|
9276
|
-
description: "Release the inherited wrapper lease",
|
|
9277
|
-
register(program) {
|
|
9278
|
-
addTargetOptions(program.command(this.name)).action(
|
|
9279
|
-
(options) => {
|
|
9280
|
-
if (!releaseWrapperOutputLease(options)) {
|
|
9281
|
-
throw new Error(
|
|
9282
|
-
`${OUTPUT_LEASE_ENV}: the current token does not own this lease`
|
|
9283
|
-
);
|
|
9284
|
-
}
|
|
9285
|
-
}
|
|
9286
|
-
);
|
|
9287
|
-
}
|
|
9288
|
-
};
|
|
9289
|
-
var outputLeaseCommandGroup = {
|
|
9290
|
-
name: "output-lease",
|
|
9291
|
-
description: "Private build output ownership protocol",
|
|
9292
|
-
commands: [
|
|
9293
|
-
acquireCommand,
|
|
9294
|
-
verifyCommand,
|
|
9295
|
-
heartbeatCommand,
|
|
9296
|
-
poisonCommand,
|
|
9297
|
-
releaseCommand
|
|
9298
|
-
]
|
|
9299
|
-
};
|
|
9300
|
-
|
|
9301
8467
|
// src/commands/index.ts
|
|
9302
8468
|
var commands = [
|
|
9303
8469
|
genDbSchemaCommand,
|
|
@@ -9308,19 +8474,18 @@ var commands = [
|
|
|
9308
8474
|
componentCommandGroup,
|
|
9309
8475
|
migrationCommand,
|
|
9310
8476
|
readLogsCommand,
|
|
9311
|
-
buildCommandGroup
|
|
9312
|
-
outputLeaseCommandGroup
|
|
8477
|
+
buildCommandGroup
|
|
9313
8478
|
];
|
|
9314
8479
|
|
|
9315
8480
|
// src/index.ts
|
|
9316
8481
|
for (const filename of [".env.local", ".env"]) {
|
|
9317
|
-
const envPath =
|
|
9318
|
-
if (
|
|
8482
|
+
const envPath = path26.join(process.cwd(), filename);
|
|
8483
|
+
if (fs31.existsSync(envPath)) {
|
|
9319
8484
|
dotenvConfig({ path: envPath });
|
|
9320
8485
|
}
|
|
9321
8486
|
}
|
|
9322
|
-
var __dirname =
|
|
9323
|
-
var pkg = JSON.parse(
|
|
8487
|
+
var __dirname = path26.dirname(fileURLToPath5(import.meta.url));
|
|
8488
|
+
var pkg = JSON.parse(fs31.readFileSync(path26.join(__dirname, "../package.json"), "utf-8"));
|
|
9324
8489
|
var cli = new FullstackCLI(pkg.version);
|
|
9325
8490
|
cli.useAll(commands);
|
|
9326
8491
|
cli.run();
|