@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  // src/index.ts
2
- import fs32 from "fs";
3
- import path27 from "path";
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
- pluginInstance = plugin.create(capability.formValue || {});
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
- const jsonSchema = pluginInstance.getInputJsonSchema(manifestAction.key);
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(`Failed to get input schema for action: ${manifestAction.key}`);
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
- const jsonSchema = pluginInstance.getOutputJsonSchema(manifestAction.key, capability.formValue || {});
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(`Failed to get output schema for action: ${manifestAction.key}`);
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((resolve3) => {
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
- resolve3({
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
- resolve3({
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((resolve3) => {
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
- resolve3();
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 = path27.join(process.cwd(), filename);
9318
- if (fs32.existsSync(envPath)) {
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 = path27.dirname(fileURLToPath5(import.meta.url));
9323
- var pkg = JSON.parse(fs32.readFileSync(path27.join(__dirname, "../package.json"), "utf-8"));
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();