@lark-apaas/fullstack-cli 1.1.59-alpha.20260724074215 → 1.1.59-alpha.20260802184501
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 +612 -225
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
|
-
import
|
|
3
|
-
import
|
|
2
|
+
import fs38 from "fs";
|
|
3
|
+
import path34 from "path";
|
|
4
4
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
5
5
|
import { config as dotenvConfig } from "dotenv";
|
|
6
6
|
|
|
@@ -4921,7 +4921,7 @@ var PROMPT_PATTERNS = [
|
|
|
4921
4921
|
{ pattern: /proceed\?/i, answer: "y\n" }
|
|
4922
4922
|
];
|
|
4923
4923
|
async function executeShadcnAdd(registryItemPath) {
|
|
4924
|
-
return new Promise((
|
|
4924
|
+
return new Promise((resolve10) => {
|
|
4925
4925
|
let output = "";
|
|
4926
4926
|
const args = ["--yes", "shadcn@3.8.2", "add", registryItemPath];
|
|
4927
4927
|
const ptyProcess = pty.spawn("npx", args, {
|
|
@@ -4947,7 +4947,7 @@ async function executeShadcnAdd(registryItemPath) {
|
|
|
4947
4947
|
});
|
|
4948
4948
|
const timeoutId = setTimeout(() => {
|
|
4949
4949
|
ptyProcess.kill();
|
|
4950
|
-
|
|
4950
|
+
resolve10({
|
|
4951
4951
|
success: false,
|
|
4952
4952
|
files: [],
|
|
4953
4953
|
error: "\u6267\u884C\u8D85\u65F6"
|
|
@@ -4958,7 +4958,7 @@ async function executeShadcnAdd(registryItemPath) {
|
|
|
4958
4958
|
const success = exitCode === 0;
|
|
4959
4959
|
const filePaths = parseOutput(output);
|
|
4960
4960
|
const files = filePaths.map(toFileInfo);
|
|
4961
|
-
|
|
4961
|
+
resolve10({
|
|
4962
4962
|
success,
|
|
4963
4963
|
files,
|
|
4964
4964
|
error: success ? void 0 : output || `Process exited with code ${exitCode}`
|
|
@@ -4969,12 +4969,12 @@ async function executeShadcnAdd(registryItemPath) {
|
|
|
4969
4969
|
|
|
4970
4970
|
// src/commands/component/add.handler.ts
|
|
4971
4971
|
function runActionPluginInit() {
|
|
4972
|
-
return new Promise((
|
|
4972
|
+
return new Promise((resolve10) => {
|
|
4973
4973
|
execFile("fullstack-cli", ["action-plugin", "init"], { cwd: process.cwd(), stdio: "ignore" }, (error) => {
|
|
4974
4974
|
if (error) {
|
|
4975
4975
|
debug("action-plugin init \u5931\u8D25: %s", error.message);
|
|
4976
4976
|
}
|
|
4977
|
-
|
|
4977
|
+
resolve10();
|
|
4978
4978
|
});
|
|
4979
4979
|
});
|
|
4980
4980
|
}
|
|
@@ -8677,16 +8677,270 @@ async function buildPreviewServerArtifact(options) {
|
|
|
8677
8677
|
}
|
|
8678
8678
|
|
|
8679
8679
|
// src/commands/preview-artifact/producer.ts
|
|
8680
|
-
import * as
|
|
8681
|
-
import * as
|
|
8682
|
-
import * as
|
|
8683
|
-
import { spawnSync as
|
|
8680
|
+
import * as crypto4 from "crypto";
|
|
8681
|
+
import * as fs35 from "fs";
|
|
8682
|
+
import * as path31 from "path";
|
|
8683
|
+
import { spawnSync as spawnSync9 } from "child_process";
|
|
8684
8684
|
|
|
8685
8685
|
// src/commands/preview-artifact/generation.ts
|
|
8686
|
+
import * as crypto2 from "crypto";
|
|
8687
|
+
import * as fs31 from "fs";
|
|
8688
|
+
import * as path27 from "path";
|
|
8689
|
+
|
|
8690
|
+
// src/commands/preview-artifact/workspace-identity.ts
|
|
8686
8691
|
import * as crypto from "crypto";
|
|
8692
|
+
import { spawnSync as spawnSync7 } from "child_process";
|
|
8687
8693
|
import * as fs30 from "fs";
|
|
8694
|
+
import * as os4 from "os";
|
|
8688
8695
|
import * as path26 from "path";
|
|
8689
|
-
var
|
|
8696
|
+
var PREVIEW_ARTIFACT_WORKSPACE_MANIFEST_SCHEMA_VERSION = 1;
|
|
8697
|
+
var WORKSPACE_IDENTITY_DOMAIN = "workspace-content:v1\n";
|
|
8698
|
+
var WORKSPACE_BACKUP_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
|
|
8699
|
+
var GIT_OBJECT_ID_PATTERN = /^(?:[a-f0-9]{40}|[a-f0-9]{64})$/;
|
|
8700
|
+
var SOURCE_TREE_ID_PATTERN = /^git-tree-(sha1|sha256):([a-f0-9]{40}|[a-f0-9]{64})$/;
|
|
8701
|
+
var WORKSPACE_VERSION_PATTERN = /^sha256:[a-f0-9]{64}$/;
|
|
8702
|
+
function normalizeSourceTreeIncludePaths(values) {
|
|
8703
|
+
if (!values || values.length === 0) return [];
|
|
8704
|
+
const normalized = /* @__PURE__ */ new Set();
|
|
8705
|
+
for (const rawValue of values) {
|
|
8706
|
+
if (typeof rawValue !== "string" || rawValue.includes("\0") || rawValue.includes("\n") || rawValue.includes("\r") || rawValue.includes("\\")) {
|
|
8707
|
+
throw new Error(`invalid source tree include path: ${String(rawValue)}`);
|
|
8708
|
+
}
|
|
8709
|
+
const value = path26.posix.normalize(rawValue.trim());
|
|
8710
|
+
if (!value || value === ".." || value.startsWith("../") || path26.posix.isAbsolute(value)) {
|
|
8711
|
+
throw new Error(`invalid source tree include path: ${rawValue}`);
|
|
8712
|
+
}
|
|
8713
|
+
if (value === ".") return [];
|
|
8714
|
+
normalized.add(value.replace(/\/$/, ""));
|
|
8715
|
+
}
|
|
8716
|
+
return [...normalized].sort((left, right) => left.localeCompare(right));
|
|
8717
|
+
}
|
|
8718
|
+
function createPreviewArtifactSourceTreeValidation(enabled, includePaths) {
|
|
8719
|
+
return enabled ? {
|
|
8720
|
+
enabled: true,
|
|
8721
|
+
includePaths: normalizeSourceTreeIncludePaths(includePaths)
|
|
8722
|
+
} : { enabled: false, includePaths: [] };
|
|
8723
|
+
}
|
|
8724
|
+
function previewArtifactSourceTreeValidation(identity) {
|
|
8725
|
+
if (identity.sourceTreeValidation == null) {
|
|
8726
|
+
return identity.sourceTreeId != null || identity.workspaceVersion != null ? { enabled: true, includePaths: [] } : { enabled: false, includePaths: [] };
|
|
8727
|
+
}
|
|
8728
|
+
return identity.sourceTreeValidation;
|
|
8729
|
+
}
|
|
8730
|
+
function samePreviewArtifactSourceTreeValidation(left, right) {
|
|
8731
|
+
return left.enabled === right.enabled && left.includePaths.length === right.includePaths.length && left.includePaths.every(
|
|
8732
|
+
(value, index) => value === right.includePaths[index]
|
|
8733
|
+
);
|
|
8734
|
+
}
|
|
8735
|
+
function runGit(projectRoot, args, environment, allowFailure = false) {
|
|
8736
|
+
const result = spawnSync7("git", args, {
|
|
8737
|
+
cwd: projectRoot,
|
|
8738
|
+
env: { ...process.env, ...environment },
|
|
8739
|
+
encoding: "utf8",
|
|
8740
|
+
maxBuffer: 10 * 1024 * 1024
|
|
8741
|
+
});
|
|
8742
|
+
if (result.status === 0) return result.stdout.trim();
|
|
8743
|
+
if (allowFailure) return void 0;
|
|
8744
|
+
const diagnostic = `${result.stderr ?? ""}
|
|
8745
|
+
${result.stdout ?? ""}`.trim().slice(-8 * 1024);
|
|
8746
|
+
throw new Error(
|
|
8747
|
+
`git ${args.join(" ")} failed with exit ${result.status ?? "unknown"}${diagnostic ? `: ${diagnostic}` : ""}`
|
|
8748
|
+
);
|
|
8749
|
+
}
|
|
8750
|
+
function resolveGitPath(projectRoot, relativePath) {
|
|
8751
|
+
return path26.isAbsolute(relativePath) ? relativePath : path26.resolve(projectRoot, relativePath);
|
|
8752
|
+
}
|
|
8753
|
+
function writeJsonAtomic(filePath, value) {
|
|
8754
|
+
fs30.mkdirSync(path26.dirname(filePath), { recursive: true });
|
|
8755
|
+
const temporaryPath = `${filePath}.${process.pid}.${Date.now()}.tmp`;
|
|
8756
|
+
try {
|
|
8757
|
+
fs30.writeFileSync(temporaryPath, `${JSON.stringify(value, null, 2)}
|
|
8758
|
+
`, {
|
|
8759
|
+
mode: 384
|
|
8760
|
+
});
|
|
8761
|
+
fs30.renameSync(temporaryPath, filePath);
|
|
8762
|
+
} finally {
|
|
8763
|
+
fs30.rmSync(temporaryPath, { force: true });
|
|
8764
|
+
}
|
|
8765
|
+
}
|
|
8766
|
+
function gitObjectFormat(projectRoot, treeId) {
|
|
8767
|
+
const reported = runGit(
|
|
8768
|
+
projectRoot,
|
|
8769
|
+
["rev-parse", "--show-object-format"],
|
|
8770
|
+
void 0,
|
|
8771
|
+
true
|
|
8772
|
+
);
|
|
8773
|
+
if (reported === "sha1" || reported === "sha256") return reported;
|
|
8774
|
+
if (treeId.length === 40) return "sha1";
|
|
8775
|
+
if (treeId.length === 64) return "sha256";
|
|
8776
|
+
throw new Error(`unsupported Git tree object ID: ${treeId}`);
|
|
8777
|
+
}
|
|
8778
|
+
function workspaceVersionForSourceTreeId(sourceTreeId) {
|
|
8779
|
+
if (!SOURCE_TREE_ID_PATTERN.test(sourceTreeId)) {
|
|
8780
|
+
throw new Error(`invalid sourceTreeId: ${sourceTreeId}`);
|
|
8781
|
+
}
|
|
8782
|
+
return `sha256:${crypto.createHash("sha256").update(`${WORKSPACE_IDENTITY_DOMAIN}${sourceTreeId}`).digest("hex")}`;
|
|
8783
|
+
}
|
|
8784
|
+
function validatePreviewArtifactWorkspaceManifest(manifest) {
|
|
8785
|
+
const reasons = [];
|
|
8786
|
+
if (manifest.schemaVersion !== PREVIEW_ARTIFACT_WORKSPACE_MANIFEST_SCHEMA_VERSION) {
|
|
8787
|
+
reasons.push("workspace manifest schema version is incompatible");
|
|
8788
|
+
}
|
|
8789
|
+
reasons.push(...validatePreviewArtifactWorkspaceIdentity(manifest));
|
|
8790
|
+
if (typeof manifest.appId !== "string" || manifest.appId.length === 0 || manifest.appId.length > 256) {
|
|
8791
|
+
reasons.push("workspace application ID is invalid");
|
|
8792
|
+
}
|
|
8793
|
+
if (typeof manifest.createdAt !== "string" || !Number.isFinite(Date.parse(manifest.createdAt))) {
|
|
8794
|
+
reasons.push("workspace manifest creation time is invalid");
|
|
8795
|
+
}
|
|
8796
|
+
return reasons;
|
|
8797
|
+
}
|
|
8798
|
+
function validatePreviewArtifactWorkspaceIdentity(identity) {
|
|
8799
|
+
const reasons = [];
|
|
8800
|
+
if (!WORKSPACE_BACKUP_ID_PATTERN.test(identity.workspaceBackupId)) {
|
|
8801
|
+
reasons.push("workspace backup ID is invalid");
|
|
8802
|
+
}
|
|
8803
|
+
if (identity.commitId !== "UNBORN" && !GIT_OBJECT_ID_PATTERN.test(identity.commitId)) {
|
|
8804
|
+
reasons.push("workspace commit ID is invalid");
|
|
8805
|
+
}
|
|
8806
|
+
const validation = previewArtifactSourceTreeValidation(identity);
|
|
8807
|
+
let normalizedIncludePaths;
|
|
8808
|
+
try {
|
|
8809
|
+
normalizedIncludePaths = normalizeSourceTreeIncludePaths(
|
|
8810
|
+
validation.includePaths
|
|
8811
|
+
);
|
|
8812
|
+
} catch {
|
|
8813
|
+
reasons.push("workspace source tree include paths are invalid");
|
|
8814
|
+
}
|
|
8815
|
+
if (typeof validation.enabled !== "boolean" || !Array.isArray(validation.includePaths) || !normalizedIncludePaths || normalizedIncludePaths.length !== validation.includePaths.length || normalizedIncludePaths.some(
|
|
8816
|
+
(value, index) => value !== validation.includePaths[index]
|
|
8817
|
+
)) {
|
|
8818
|
+
reasons.push("workspace source tree validation policy is invalid");
|
|
8819
|
+
}
|
|
8820
|
+
if (!validation.enabled) {
|
|
8821
|
+
if (identity.sourceTreeId != null || identity.workspaceVersion != null) {
|
|
8822
|
+
reasons.push(
|
|
8823
|
+
"disabled source tree validation must not persist an identity"
|
|
8824
|
+
);
|
|
8825
|
+
}
|
|
8826
|
+
return reasons;
|
|
8827
|
+
}
|
|
8828
|
+
if (typeof identity.sourceTreeId !== "string" || !SOURCE_TREE_ID_PATTERN.test(identity.sourceTreeId)) {
|
|
8829
|
+
reasons.push("workspace source tree ID is invalid");
|
|
8830
|
+
}
|
|
8831
|
+
if (typeof identity.workspaceVersion !== "string" || !WORKSPACE_VERSION_PATTERN.test(identity.workspaceVersion)) {
|
|
8832
|
+
reasons.push("workspace version is invalid");
|
|
8833
|
+
} else if (typeof identity.sourceTreeId === "string") {
|
|
8834
|
+
try {
|
|
8835
|
+
if (workspaceVersionForSourceTreeId(identity.sourceTreeId) !== identity.workspaceVersion) {
|
|
8836
|
+
reasons.push("workspace version does not match source tree ID");
|
|
8837
|
+
}
|
|
8838
|
+
} catch {
|
|
8839
|
+
}
|
|
8840
|
+
}
|
|
8841
|
+
return reasons;
|
|
8842
|
+
}
|
|
8843
|
+
function readPreviewArtifactWorkspaceManifest(manifestFile) {
|
|
8844
|
+
const manifest = JSON.parse(
|
|
8845
|
+
fs30.readFileSync(path26.resolve(manifestFile), "utf8")
|
|
8846
|
+
);
|
|
8847
|
+
const reasons = validatePreviewArtifactWorkspaceManifest(manifest);
|
|
8848
|
+
if (reasons.length > 0) {
|
|
8849
|
+
throw new Error(reasons.join("; "));
|
|
8850
|
+
}
|
|
8851
|
+
return manifest;
|
|
8852
|
+
}
|
|
8853
|
+
function createPreviewArtifactWorkspaceManifest(options) {
|
|
8854
|
+
if (!WORKSPACE_BACKUP_ID_PATTERN.test(options.workspaceBackupId)) {
|
|
8855
|
+
throw new Error(
|
|
8856
|
+
`invalid workspace backup ID: ${options.workspaceBackupId}`
|
|
8857
|
+
);
|
|
8858
|
+
}
|
|
8859
|
+
if (!options.appId || options.appId.length > 256) {
|
|
8860
|
+
throw new Error(`invalid application ID: ${options.appId}`);
|
|
8861
|
+
}
|
|
8862
|
+
const projectRoot = fs30.realpathSync(path26.resolve(options.projectRoot));
|
|
8863
|
+
const repositoryRoot = runGit(projectRoot, ["rev-parse", "--show-toplevel"]);
|
|
8864
|
+
if (!repositoryRoot || fs30.realpathSync(repositoryRoot) !== projectRoot) {
|
|
8865
|
+
throw new Error("project root must be the Git repository root");
|
|
8866
|
+
}
|
|
8867
|
+
const sourceTreeValidation = createPreviewArtifactSourceTreeValidation(
|
|
8868
|
+
options.validateSourceTree,
|
|
8869
|
+
options.sourceTreeIncludePaths
|
|
8870
|
+
);
|
|
8871
|
+
let sourceTreeId;
|
|
8872
|
+
let workspaceVersion;
|
|
8873
|
+
if (sourceTreeValidation.enabled) {
|
|
8874
|
+
const temporaryRoot = fs30.mkdtempSync(
|
|
8875
|
+
path26.join(os4.tmpdir(), "preview-workspace-identity-")
|
|
8876
|
+
);
|
|
8877
|
+
try {
|
|
8878
|
+
const temporaryIndex = path26.join(temporaryRoot, "index");
|
|
8879
|
+
const temporaryObjects = path26.join(temporaryRoot, "objects");
|
|
8880
|
+
fs30.mkdirSync(temporaryObjects, { recursive: true });
|
|
8881
|
+
const gitObjectsPath = resolveGitPath(
|
|
8882
|
+
projectRoot,
|
|
8883
|
+
runGit(projectRoot, ["rev-parse", "--git-path", "objects"])
|
|
8884
|
+
);
|
|
8885
|
+
const isolatedGitEnvironment = {
|
|
8886
|
+
GIT_INDEX_FILE: temporaryIndex,
|
|
8887
|
+
GIT_OBJECT_DIRECTORY: temporaryObjects,
|
|
8888
|
+
GIT_ALTERNATE_OBJECT_DIRECTORIES: gitObjectsPath,
|
|
8889
|
+
GIT_OPTIONAL_LOCKS: "0"
|
|
8890
|
+
};
|
|
8891
|
+
runGit(projectRoot, ["read-tree", "--empty"], isolatedGitEnvironment);
|
|
8892
|
+
const includedPaths = sourceTreeValidation.includePaths.length === 0 ? ["."] : sourceTreeValidation.includePaths.filter(
|
|
8893
|
+
(includedPath) => fs30.existsSync(path26.join(projectRoot, includedPath))
|
|
8894
|
+
);
|
|
8895
|
+
if (includedPaths.length > 0) {
|
|
8896
|
+
runGit(
|
|
8897
|
+
projectRoot,
|
|
8898
|
+
[
|
|
8899
|
+
"add",
|
|
8900
|
+
"-A",
|
|
8901
|
+
"--",
|
|
8902
|
+
...includedPaths.map(
|
|
8903
|
+
(includedPath) => includedPath === "." ? "." : `:(top,literal)${includedPath}`
|
|
8904
|
+
)
|
|
8905
|
+
],
|
|
8906
|
+
isolatedGitEnvironment
|
|
8907
|
+
);
|
|
8908
|
+
}
|
|
8909
|
+
const treeId = runGit(
|
|
8910
|
+
projectRoot,
|
|
8911
|
+
["write-tree"],
|
|
8912
|
+
isolatedGitEnvironment
|
|
8913
|
+
);
|
|
8914
|
+
if (!treeId || !GIT_OBJECT_ID_PATTERN.test(treeId)) {
|
|
8915
|
+
throw new Error(
|
|
8916
|
+
`git write-tree returned an invalid object ID: ${treeId}`
|
|
8917
|
+
);
|
|
8918
|
+
}
|
|
8919
|
+
sourceTreeId = `git-tree-${gitObjectFormat(
|
|
8920
|
+
projectRoot,
|
|
8921
|
+
treeId
|
|
8922
|
+
)}:${treeId}`;
|
|
8923
|
+
workspaceVersion = workspaceVersionForSourceTreeId(sourceTreeId);
|
|
8924
|
+
} finally {
|
|
8925
|
+
fs30.rmSync(temporaryRoot, { recursive: true, force: true });
|
|
8926
|
+
}
|
|
8927
|
+
}
|
|
8928
|
+
const manifest = {
|
|
8929
|
+
schemaVersion: PREVIEW_ARTIFACT_WORKSPACE_MANIFEST_SCHEMA_VERSION,
|
|
8930
|
+
workspaceBackupId: options.workspaceBackupId,
|
|
8931
|
+
appId: options.appId,
|
|
8932
|
+
commitId: runGit(projectRoot, ["rev-parse", "HEAD"], void 0, true) ?? "UNBORN",
|
|
8933
|
+
sourceTreeValidation,
|
|
8934
|
+
...sourceTreeId == null ? {} : { sourceTreeId },
|
|
8935
|
+
...workspaceVersion == null ? {} : { workspaceVersion },
|
|
8936
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
8937
|
+
};
|
|
8938
|
+
writeJsonAtomic(path26.resolve(options.manifestFile), manifest);
|
|
8939
|
+
return manifest;
|
|
8940
|
+
}
|
|
8941
|
+
|
|
8942
|
+
// src/commands/preview-artifact/generation.ts
|
|
8943
|
+
var PREVIEW_ARTIFACT_SCHEMA_VERSION = 4;
|
|
8690
8944
|
var PREVIEW_ARTIFACT_PROTOCOL_VERSION = 3;
|
|
8691
8945
|
var MANIFEST_FILE = "manifest.json";
|
|
8692
8946
|
var PROBE_FILE = "probe.json";
|
|
@@ -8733,7 +8987,7 @@ function previewArtifactBuildEnvironmentHash(environment, appBasePath) {
|
|
|
8733
8987
|
([left], [right]) => left.localeCompare(right)
|
|
8734
8988
|
)
|
|
8735
8989
|
);
|
|
8736
|
-
return
|
|
8990
|
+
return crypto2.createHash("sha256").update(JSON.stringify(canonical)).digest("hex");
|
|
8737
8991
|
}
|
|
8738
8992
|
function parseNodeMajor(nodeVersion) {
|
|
8739
8993
|
const match = /^v?(\d+)(?:\.|$)/.exec(nodeVersion.trim());
|
|
@@ -8743,13 +8997,13 @@ function parseNodeMajor(nodeVersion) {
|
|
|
8743
8997
|
return Number(match[1]);
|
|
8744
8998
|
}
|
|
8745
8999
|
function sha256File(filePath) {
|
|
8746
|
-
const hash =
|
|
8747
|
-
hash.update(
|
|
9000
|
+
const hash = crypto2.createHash("sha256");
|
|
9001
|
+
hash.update(fs31.readFileSync(filePath));
|
|
8748
9002
|
return hash.digest("hex");
|
|
8749
9003
|
}
|
|
8750
9004
|
function fileIdentity(generationDir, relativePath) {
|
|
8751
|
-
const filePath =
|
|
8752
|
-
const stat =
|
|
9005
|
+
const filePath = path27.join(generationDir, relativePath);
|
|
9006
|
+
const stat = fs31.lstatSync(filePath);
|
|
8753
9007
|
if (!stat.isFile() || stat.isSymbolicLink()) {
|
|
8754
9008
|
throw new Error(`artifact file must be a regular file: ${relativePath}`);
|
|
8755
9009
|
}
|
|
@@ -8760,16 +9014,16 @@ function fileIdentity(generationDir, relativePath) {
|
|
|
8760
9014
|
};
|
|
8761
9015
|
}
|
|
8762
9016
|
function readJson2(filePath) {
|
|
8763
|
-
return JSON.parse(
|
|
9017
|
+
return JSON.parse(fs31.readFileSync(filePath, "utf8"));
|
|
8764
9018
|
}
|
|
8765
|
-
function
|
|
8766
|
-
|
|
9019
|
+
function writeJsonAtomic2(filePath, value) {
|
|
9020
|
+
fs31.mkdirSync(path27.dirname(filePath), { recursive: true });
|
|
8767
9021
|
const temporaryPath = `${filePath}.${process.pid}.${Date.now()}.tmp`;
|
|
8768
|
-
|
|
9022
|
+
fs31.writeFileSync(temporaryPath, `${JSON.stringify(value, null, 2)}
|
|
8769
9023
|
`, {
|
|
8770
9024
|
mode: 384
|
|
8771
9025
|
});
|
|
8772
|
-
|
|
9026
|
+
fs31.renameSync(temporaryPath, filePath);
|
|
8773
9027
|
}
|
|
8774
9028
|
function runtimeIdentity(nodeVersion, platform, arch) {
|
|
8775
9029
|
return { nodeMajor: parseNodeMajor(nodeVersion), platform, arch };
|
|
@@ -8778,12 +9032,23 @@ function sameRuntime(left, right) {
|
|
|
8778
9032
|
return left.nodeMajor === right.nodeMajor && left.platform === right.platform && left.arch === right.arch;
|
|
8779
9033
|
}
|
|
8780
9034
|
function createPreviewArtifactManifest(options) {
|
|
8781
|
-
const generationDir =
|
|
9035
|
+
const generationDir = path27.resolve(options.generationDir);
|
|
9036
|
+
const workspaceIdentityReasons = validatePreviewArtifactWorkspaceIdentity(
|
|
9037
|
+
options.workspaceManifest
|
|
9038
|
+
);
|
|
9039
|
+
if (workspaceIdentityReasons.length > 0) {
|
|
9040
|
+
throw new Error(workspaceIdentityReasons.join("; "));
|
|
9041
|
+
}
|
|
8782
9042
|
const manifest = {
|
|
8783
9043
|
schemaVersion: PREVIEW_ARTIFACT_SCHEMA_VERSION,
|
|
8784
9044
|
protocolVersion: PREVIEW_ARTIFACT_PROTOCOL_VERSION,
|
|
8785
9045
|
generationId: options.generationId,
|
|
8786
9046
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
9047
|
+
workspaceBackupId: options.workspaceManifest.workspaceBackupId,
|
|
9048
|
+
commitId: options.workspaceManifest.commitId,
|
|
9049
|
+
sourceTreeValidation: options.workspaceManifest.sourceTreeValidation,
|
|
9050
|
+
...options.workspaceManifest.sourceTreeId == null ? {} : { sourceTreeId: options.workspaceManifest.sourceTreeId },
|
|
9051
|
+
...options.workspaceManifest.workspaceVersion == null ? {} : { workspaceVersion: options.workspaceManifest.workspaceVersion },
|
|
8787
9052
|
buildEnvironmentSha256: options.buildEnvironmentSha256,
|
|
8788
9053
|
runtime: runtimeIdentity(
|
|
8789
9054
|
options.nodeVersion,
|
|
@@ -8798,14 +9063,14 @@ function createPreviewArtifactManifest(options) {
|
|
|
8798
9063
|
runtimeProbeStatus: "not-run",
|
|
8799
9064
|
consumable: false
|
|
8800
9065
|
};
|
|
8801
|
-
|
|
8802
|
-
|
|
9066
|
+
writeJsonAtomic2(path27.join(generationDir, MANIFEST_FILE), manifest);
|
|
9067
|
+
fs31.rmSync(path27.join(generationDir, PROBE_FILE), { force: true });
|
|
8803
9068
|
return manifest;
|
|
8804
9069
|
}
|
|
8805
9070
|
function recordPreviewArtifactProbe(options) {
|
|
8806
|
-
const generationDir =
|
|
9071
|
+
const generationDir = path27.resolve(options.generationDir);
|
|
8807
9072
|
const manifest = readJson2(
|
|
8808
|
-
|
|
9073
|
+
path27.join(generationDir, MANIFEST_FILE)
|
|
8809
9074
|
);
|
|
8810
9075
|
const probe = {
|
|
8811
9076
|
schemaVersion: PREVIEW_ARTIFACT_SCHEMA_VERSION,
|
|
@@ -8818,8 +9083,8 @@ function recordPreviewArtifactProbe(options) {
|
|
|
8818
9083
|
options.platform,
|
|
8819
9084
|
options.arch
|
|
8820
9085
|
),
|
|
8821
|
-
serverSha256: sha256File(
|
|
8822
|
-
clientSha256: sha256File(
|
|
9086
|
+
serverSha256: sha256File(path27.join(generationDir, SERVER_FILE)),
|
|
9087
|
+
clientSha256: sha256File(path27.join(generationDir, CLIENT_FILE)),
|
|
8823
9088
|
coverage: {
|
|
8824
9089
|
html: options.coverage.html,
|
|
8825
9090
|
businessApi: options.coverage.businessApi,
|
|
@@ -8827,20 +9092,20 @@ function recordPreviewArtifactProbe(options) {
|
|
|
8827
9092
|
actionPlugins: Array.from(new Set(options.coverage.actionPlugins)).sort()
|
|
8828
9093
|
}
|
|
8829
9094
|
};
|
|
8830
|
-
|
|
9095
|
+
writeJsonAtomic2(path27.join(generationDir, PROBE_FILE), probe);
|
|
8831
9096
|
return probe;
|
|
8832
9097
|
}
|
|
8833
9098
|
function promotePreviewArtifactGeneration(generationDirInput) {
|
|
8834
|
-
const generationDir =
|
|
9099
|
+
const generationDir = path27.resolve(generationDirInput);
|
|
8835
9100
|
const reasons = [];
|
|
8836
9101
|
let manifest;
|
|
8837
9102
|
let probe;
|
|
8838
9103
|
try {
|
|
8839
9104
|
manifest = readJson2(
|
|
8840
|
-
|
|
9105
|
+
path27.join(generationDir, MANIFEST_FILE)
|
|
8841
9106
|
);
|
|
8842
9107
|
probe = readJson2(
|
|
8843
|
-
|
|
9108
|
+
path27.join(generationDir, PROBE_FILE)
|
|
8844
9109
|
);
|
|
8845
9110
|
} catch (error) {
|
|
8846
9111
|
return {
|
|
@@ -8864,8 +9129,8 @@ function promotePreviewArtifactGeneration(generationDirInput) {
|
|
|
8864
9129
|
"probe runtime identity does not match build runtime identity"
|
|
8865
9130
|
);
|
|
8866
9131
|
}
|
|
8867
|
-
const currentServerHash = sha256File(
|
|
8868
|
-
const currentClientHash = sha256File(
|
|
9132
|
+
const currentServerHash = sha256File(path27.join(generationDir, SERVER_FILE));
|
|
9133
|
+
const currentClientHash = sha256File(path27.join(generationDir, CLIENT_FILE));
|
|
8869
9134
|
if (currentServerHash !== manifest.files.server.sha256 || currentServerHash !== probe.serverSha256) {
|
|
8870
9135
|
reasons.push("server.bundle.cjs hash does not match probe identity");
|
|
8871
9136
|
}
|
|
@@ -8898,16 +9163,16 @@ function promotePreviewArtifactGeneration(generationDirInput) {
|
|
|
8898
9163
|
runtimeProbeStatus: "passed",
|
|
8899
9164
|
consumable: true
|
|
8900
9165
|
};
|
|
8901
|
-
|
|
9166
|
+
writeJsonAtomic2(path27.join(generationDir, MANIFEST_FILE), promotedManifest);
|
|
8902
9167
|
return { promoted: true, manifest: promotedManifest };
|
|
8903
9168
|
}
|
|
8904
9169
|
|
|
8905
9170
|
// src/commands/preview-artifact/probe.ts
|
|
8906
|
-
import * as
|
|
9171
|
+
import * as fs32 from "fs";
|
|
8907
9172
|
import * as net from "net";
|
|
8908
|
-
import * as
|
|
8909
|
-
import * as
|
|
8910
|
-
import { spawn as spawn2, spawnSync as
|
|
9173
|
+
import * as os5 from "os";
|
|
9174
|
+
import * as path28 from "path";
|
|
9175
|
+
import { spawn as spawn2, spawnSync as spawnSync8 } from "child_process";
|
|
8911
9176
|
|
|
8912
9177
|
// src/commands/preview-artifact/readiness.ts
|
|
8913
9178
|
function isPreviewBusinessResponseReady(response) {
|
|
@@ -8917,10 +9182,10 @@ function isPreviewBusinessResponseReady(response) {
|
|
|
8917
9182
|
|
|
8918
9183
|
// src/commands/preview-artifact/probe.ts
|
|
8919
9184
|
function sleep(ms) {
|
|
8920
|
-
return new Promise((
|
|
9185
|
+
return new Promise((resolve10) => setTimeout(resolve10, ms));
|
|
8921
9186
|
}
|
|
8922
9187
|
async function reservePort() {
|
|
8923
|
-
return new Promise((
|
|
9188
|
+
return new Promise((resolve10, reject) => {
|
|
8924
9189
|
const server = net.createServer();
|
|
8925
9190
|
server.unref();
|
|
8926
9191
|
server.once("error", reject);
|
|
@@ -8932,13 +9197,13 @@ async function reservePort() {
|
|
|
8932
9197
|
}
|
|
8933
9198
|
server.close((error) => {
|
|
8934
9199
|
if (error) reject(error);
|
|
8935
|
-
else
|
|
9200
|
+
else resolve10(address.port);
|
|
8936
9201
|
});
|
|
8937
9202
|
});
|
|
8938
9203
|
});
|
|
8939
9204
|
}
|
|
8940
9205
|
function archiveEntries(archivePath) {
|
|
8941
|
-
const listing =
|
|
9206
|
+
const listing = spawnSync8("unzip", ["-Z1", archivePath], {
|
|
8942
9207
|
encoding: "utf8"
|
|
8943
9208
|
});
|
|
8944
9209
|
if (listing.status !== 0) {
|
|
@@ -8946,15 +9211,15 @@ function archiveEntries(archivePath) {
|
|
|
8946
9211
|
}
|
|
8947
9212
|
const entries = listing.stdout.split("\n").filter(Boolean);
|
|
8948
9213
|
for (const entry of entries) {
|
|
8949
|
-
const normalized =
|
|
8950
|
-
if (
|
|
9214
|
+
const normalized = path28.posix.normalize(entry.replaceAll("\\", "/"));
|
|
9215
|
+
if (path28.posix.isAbsolute(normalized) || normalized === ".." || normalized.startsWith("../")) {
|
|
8951
9216
|
throw new Error(`client archive contains unsafe path: ${entry}`);
|
|
8952
9217
|
}
|
|
8953
9218
|
}
|
|
8954
9219
|
return entries;
|
|
8955
9220
|
}
|
|
8956
9221
|
function rejectArchivedSymlinks(archivePath) {
|
|
8957
|
-
const listing =
|
|
9222
|
+
const listing = spawnSync8("unzip", ["-Z", "-l", archivePath], {
|
|
8958
9223
|
encoding: "utf8"
|
|
8959
9224
|
});
|
|
8960
9225
|
if (listing.status !== 0) {
|
|
@@ -8973,9 +9238,9 @@ function rejectSymlinks(root) {
|
|
|
8973
9238
|
while (pending.length > 0) {
|
|
8974
9239
|
const current = pending.pop();
|
|
8975
9240
|
if (!current) continue;
|
|
8976
|
-
for (const entry of
|
|
8977
|
-
const entryPath =
|
|
8978
|
-
const stat =
|
|
9241
|
+
for (const entry of fs32.readdirSync(current, { withFileTypes: true })) {
|
|
9242
|
+
const entryPath = path28.join(current, entry.name);
|
|
9243
|
+
const stat = fs32.lstatSync(entryPath);
|
|
8979
9244
|
if (stat.isSymbolicLink()) {
|
|
8980
9245
|
throw new Error(`client archive contains symlink: ${entry.name}`);
|
|
8981
9246
|
}
|
|
@@ -8988,8 +9253,8 @@ function inspectClientArchive(archivePath) {
|
|
|
8988
9253
|
rejectArchivedSymlinks(archivePath);
|
|
8989
9254
|
}
|
|
8990
9255
|
function isPathInside(parent, candidate) {
|
|
8991
|
-
const relative5 =
|
|
8992
|
-
return relative5 !== "" && relative5 !== ".." && !relative5.startsWith(`..${
|
|
9256
|
+
const relative5 = path28.relative(parent, candidate);
|
|
9257
|
+
return relative5 !== "" && relative5 !== ".." && !relative5.startsWith(`..${path28.sep}`) && !path28.isAbsolute(relative5);
|
|
8993
9258
|
}
|
|
8994
9259
|
function clientEntryAssetReferences(html, appBasePath) {
|
|
8995
9260
|
const local = /* @__PURE__ */ new Set();
|
|
@@ -9049,23 +9314,23 @@ function resolveClientEntryAsset(clientRoot, appBasePath, requestPath) {
|
|
|
9049
9314
|
const normalizedBase = appBasePath.endsWith("/") ? appBasePath : `${appBasePath}/`;
|
|
9050
9315
|
const relative5 = decoded.startsWith(normalizedBase) ? decoded.slice(normalizedBase.length) : decoded.replace(/^\/+/, "");
|
|
9051
9316
|
if (!relative5 || relative5 === "index.html") return void 0;
|
|
9052
|
-
const candidate =
|
|
9317
|
+
const candidate = path28.resolve(clientRoot, relative5);
|
|
9053
9318
|
if (!isPathInside(clientRoot, candidate)) return void 0;
|
|
9054
9319
|
try {
|
|
9055
|
-
const stat =
|
|
9320
|
+
const stat = fs32.lstatSync(candidate);
|
|
9056
9321
|
return stat.isFile() && !stat.isSymbolicLink() ? candidate : void 0;
|
|
9057
9322
|
} catch {
|
|
9058
9323
|
return void 0;
|
|
9059
9324
|
}
|
|
9060
9325
|
}
|
|
9061
9326
|
function extractClientArchive(archivePath, destination) {
|
|
9062
|
-
if (
|
|
9327
|
+
if (fs32.existsSync(destination)) {
|
|
9063
9328
|
throw new Error("client archive destination must not already exist");
|
|
9064
9329
|
}
|
|
9065
9330
|
inspectClientArchive(archivePath);
|
|
9066
|
-
|
|
9331
|
+
fs32.mkdirSync(destination, { recursive: true });
|
|
9067
9332
|
try {
|
|
9068
|
-
const extraction =
|
|
9333
|
+
const extraction = spawnSync8(
|
|
9069
9334
|
"unzip",
|
|
9070
9335
|
["-q", archivePath, "-d", destination],
|
|
9071
9336
|
{ encoding: "utf8" }
|
|
@@ -9077,17 +9342,17 @@ function extractClientArchive(archivePath, destination) {
|
|
|
9077
9342
|
}
|
|
9078
9343
|
rejectSymlinks(destination);
|
|
9079
9344
|
} catch (error) {
|
|
9080
|
-
|
|
9345
|
+
fs32.rmSync(destination, { recursive: true, force: true });
|
|
9081
9346
|
throw error;
|
|
9082
9347
|
}
|
|
9083
9348
|
}
|
|
9084
9349
|
async function extractInspectedClientArchive(archivePath, destination) {
|
|
9085
|
-
if (
|
|
9350
|
+
if (fs32.existsSync(destination)) {
|
|
9086
9351
|
throw new Error("client archive destination must not already exist");
|
|
9087
9352
|
}
|
|
9088
|
-
|
|
9353
|
+
fs32.mkdirSync(destination, { recursive: true });
|
|
9089
9354
|
try {
|
|
9090
|
-
await new Promise((
|
|
9355
|
+
await new Promise((resolve10, reject) => {
|
|
9091
9356
|
const extraction = spawn2(
|
|
9092
9357
|
"unzip",
|
|
9093
9358
|
["-q", archivePath, "-d", destination],
|
|
@@ -9100,7 +9365,7 @@ async function extractInspectedClientArchive(archivePath, destination) {
|
|
|
9100
9365
|
});
|
|
9101
9366
|
extraction.once("error", reject);
|
|
9102
9367
|
extraction.once("close", (code) => {
|
|
9103
|
-
if (code === 0)
|
|
9368
|
+
if (code === 0) resolve10();
|
|
9104
9369
|
else
|
|
9105
9370
|
reject(
|
|
9106
9371
|
new Error(
|
|
@@ -9111,7 +9376,7 @@ async function extractInspectedClientArchive(archivePath, destination) {
|
|
|
9111
9376
|
});
|
|
9112
9377
|
rejectSymlinks(destination);
|
|
9113
9378
|
} catch (error) {
|
|
9114
|
-
|
|
9379
|
+
fs32.rmSync(destination, { recursive: true, force: true });
|
|
9115
9380
|
throw error;
|
|
9116
9381
|
}
|
|
9117
9382
|
}
|
|
@@ -9141,9 +9406,9 @@ function stopProcessGroup(pid) {
|
|
|
9141
9406
|
}
|
|
9142
9407
|
}
|
|
9143
9408
|
async function runPreviewArtifactProbe(options) {
|
|
9144
|
-
const generationDir =
|
|
9145
|
-
const runtimeRoot =
|
|
9146
|
-
|
|
9409
|
+
const generationDir = path28.resolve(options.generationDir);
|
|
9410
|
+
const runtimeRoot = fs32.mkdtempSync(
|
|
9411
|
+
path28.join(os5.tmpdir(), "preview-artifact-probe-")
|
|
9147
9412
|
);
|
|
9148
9413
|
const coverage = {
|
|
9149
9414
|
html: false,
|
|
@@ -9152,19 +9417,19 @@ async function runPreviewArtifactProbe(options) {
|
|
|
9152
9417
|
actionPlugins: []
|
|
9153
9418
|
};
|
|
9154
9419
|
const reasons = [];
|
|
9155
|
-
const registryFile =
|
|
9420
|
+
const registryFile = path28.join(runtimeRoot, "plugin-registry.json");
|
|
9156
9421
|
let isolatedNodeModulesPresent = false;
|
|
9157
9422
|
let child;
|
|
9158
9423
|
try {
|
|
9159
|
-
const runtimeBundle =
|
|
9160
|
-
|
|
9161
|
-
|
|
9424
|
+
const runtimeBundle = path28.join(runtimeRoot, "server.bundle.cjs");
|
|
9425
|
+
fs32.copyFileSync(
|
|
9426
|
+
path28.join(generationDir, "server.bundle.cjs"),
|
|
9162
9427
|
runtimeBundle
|
|
9163
9428
|
);
|
|
9164
|
-
const clientRoot =
|
|
9165
|
-
extractClientArchive(
|
|
9166
|
-
isolatedNodeModulesPresent =
|
|
9167
|
-
|
|
9429
|
+
const clientRoot = path28.join(runtimeRoot, "dist", "client");
|
|
9430
|
+
extractClientArchive(path28.join(generationDir, "client.zip"), clientRoot);
|
|
9431
|
+
isolatedNodeModulesPresent = fs32.existsSync(
|
|
9432
|
+
path28.join(runtimeRoot, "node_modules")
|
|
9168
9433
|
);
|
|
9169
9434
|
if (isolatedNodeModulesPresent) {
|
|
9170
9435
|
reasons.push("isolated runtime unexpectedly contains node_modules");
|
|
@@ -9227,12 +9492,12 @@ async function runPreviewArtifactProbe(options) {
|
|
|
9227
9492
|
`business API returned ${apiResponse?.status ?? "unreachable"}`
|
|
9228
9493
|
);
|
|
9229
9494
|
}
|
|
9230
|
-
while (!
|
|
9495
|
+
while (!fs32.existsSync(registryFile) && Date.now() < timeoutAt) {
|
|
9231
9496
|
await sleep(25);
|
|
9232
9497
|
}
|
|
9233
|
-
if (
|
|
9498
|
+
if (fs32.existsSync(registryFile)) {
|
|
9234
9499
|
const parsed = JSON.parse(
|
|
9235
|
-
|
|
9500
|
+
fs32.readFileSync(registryFile, "utf8")
|
|
9236
9501
|
);
|
|
9237
9502
|
if (Array.isArray(parsed) && parsed.every((value) => typeof value === "string")) {
|
|
9238
9503
|
coverage.actionPlugins = parsed.filter((value) => typeof value === "string").sort();
|
|
@@ -9256,17 +9521,17 @@ async function runPreviewArtifactProbe(options) {
|
|
|
9256
9521
|
arch: process.arch,
|
|
9257
9522
|
coverage
|
|
9258
9523
|
});
|
|
9259
|
-
|
|
9524
|
+
fs32.rmSync(runtimeRoot, { recursive: true, force: true });
|
|
9260
9525
|
return { ...probe, isolatedNodeModulesPresent };
|
|
9261
9526
|
}
|
|
9262
9527
|
|
|
9263
9528
|
// src/commands/preview-artifact/environment.ts
|
|
9264
|
-
import * as
|
|
9265
|
-
import * as
|
|
9529
|
+
import * as fs33 from "fs";
|
|
9530
|
+
import * as path29 from "path";
|
|
9266
9531
|
var ENVIRONMENT_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
9267
9532
|
function readPreviewArtifactEnvironmentFile(filePath) {
|
|
9268
|
-
const resolved =
|
|
9269
|
-
const parsed = JSON.parse(
|
|
9533
|
+
const resolved = path29.resolve(filePath);
|
|
9534
|
+
const parsed = JSON.parse(fs33.readFileSync(resolved, "utf8"));
|
|
9270
9535
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
9271
9536
|
throw new Error("preview artifact environment file must contain an object");
|
|
9272
9537
|
}
|
|
@@ -9291,40 +9556,42 @@ function previewArtifactRuntimeEnvironment(environment) {
|
|
|
9291
9556
|
}
|
|
9292
9557
|
|
|
9293
9558
|
// src/commands/preview-artifact/store.ts
|
|
9294
|
-
import * as
|
|
9295
|
-
import * as
|
|
9296
|
-
import * as
|
|
9559
|
+
import * as crypto3 from "crypto";
|
|
9560
|
+
import * as fs34 from "fs";
|
|
9561
|
+
import * as path30 from "path";
|
|
9297
9562
|
var GENERATION_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
|
|
9563
|
+
var WORKSPACE_BACKUP_ID_PATTERN2 = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
|
|
9564
|
+
var WORKSPACE_VERSION_PATTERN2 = /^sha256:[a-f0-9]{64}$/;
|
|
9298
9565
|
function readCurrentPointer(storeRoot) {
|
|
9299
9566
|
try {
|
|
9300
9567
|
const pointer = readJson3(
|
|
9301
|
-
|
|
9568
|
+
path30.join(storeRoot, "current.json")
|
|
9302
9569
|
);
|
|
9303
|
-
return pointer.schemaVersion === PREVIEW_ARTIFACT_SCHEMA_VERSION && GENERATION_ID_PATTERN.test(pointer.generationId) ? pointer : void 0;
|
|
9570
|
+
return pointer.schemaVersion === PREVIEW_ARTIFACT_SCHEMA_VERSION && GENERATION_ID_PATTERN.test(pointer.generationId) && WORKSPACE_BACKUP_ID_PATTERN2.test(pointer.workspaceBackupId) && (pointer.workspaceVersion == null || WORKSPACE_VERSION_PATTERN2.test(pointer.workspaceVersion)) ? pointer : void 0;
|
|
9304
9571
|
} catch {
|
|
9305
9572
|
return void 0;
|
|
9306
9573
|
}
|
|
9307
9574
|
}
|
|
9308
9575
|
function readJson3(filePath) {
|
|
9309
|
-
return JSON.parse(
|
|
9576
|
+
return JSON.parse(fs34.readFileSync(filePath, "utf8"));
|
|
9310
9577
|
}
|
|
9311
|
-
function
|
|
9312
|
-
|
|
9578
|
+
function writeJsonAtomic3(filePath, value) {
|
|
9579
|
+
fs34.mkdirSync(path30.dirname(filePath), { recursive: true });
|
|
9313
9580
|
const temporaryPath = `${filePath}.${process.pid}.${Date.now()}.tmp`;
|
|
9314
|
-
|
|
9581
|
+
fs34.writeFileSync(temporaryPath, `${JSON.stringify(value, null, 2)}
|
|
9315
9582
|
`, {
|
|
9316
9583
|
mode: 384
|
|
9317
9584
|
});
|
|
9318
|
-
|
|
9585
|
+
fs34.renameSync(temporaryPath, filePath);
|
|
9319
9586
|
}
|
|
9320
9587
|
function pruneSupersededGenerations(generationsRoot, retainedGenerationIds) {
|
|
9321
9588
|
const retained = new Set(retainedGenerationIds);
|
|
9322
|
-
for (const entry of
|
|
9589
|
+
for (const entry of fs34.readdirSync(generationsRoot, {
|
|
9323
9590
|
withFileTypes: true
|
|
9324
9591
|
})) {
|
|
9325
9592
|
if (retained.has(entry.name)) continue;
|
|
9326
9593
|
try {
|
|
9327
|
-
|
|
9594
|
+
fs34.rmSync(path30.join(generationsRoot, entry.name), {
|
|
9328
9595
|
recursive: true,
|
|
9329
9596
|
force: true
|
|
9330
9597
|
});
|
|
@@ -9337,12 +9604,12 @@ function parseNodeMajor2(nodeVersion) {
|
|
|
9337
9604
|
return match ? Number(match[1]) : null;
|
|
9338
9605
|
}
|
|
9339
9606
|
function isPathInside2(parent, candidate) {
|
|
9340
|
-
const relative5 =
|
|
9341
|
-
return relative5 !== "" && !relative5.startsWith(`..${
|
|
9607
|
+
const relative5 = path30.relative(parent, candidate);
|
|
9608
|
+
return relative5 !== "" && !relative5.startsWith(`..${path30.sep}`) && relative5 !== ".." && !path30.isAbsolute(relative5);
|
|
9342
9609
|
}
|
|
9343
9610
|
function validateRegularDirectory(directoryPath, label) {
|
|
9344
9611
|
try {
|
|
9345
|
-
const stat =
|
|
9612
|
+
const stat = fs34.lstatSync(directoryPath);
|
|
9346
9613
|
if (!stat.isDirectory() || stat.isSymbolicLink()) {
|
|
9347
9614
|
return [`${label} must be a regular directory`];
|
|
9348
9615
|
}
|
|
@@ -9363,10 +9630,10 @@ function validateGeneration(generationDir, runtime, verifyPayloads = true) {
|
|
|
9363
9630
|
let probe;
|
|
9364
9631
|
try {
|
|
9365
9632
|
manifest = readJson3(
|
|
9366
|
-
|
|
9633
|
+
path30.join(generationDir, "manifest.json")
|
|
9367
9634
|
);
|
|
9368
9635
|
probe = readJson3(
|
|
9369
|
-
|
|
9636
|
+
path30.join(generationDir, "probe.json")
|
|
9370
9637
|
);
|
|
9371
9638
|
} catch (error) {
|
|
9372
9639
|
return {
|
|
@@ -9393,6 +9660,7 @@ function validateGeneration(generationDir, runtime, verifyPayloads = true) {
|
|
|
9393
9660
|
if (!/^[a-f0-9]{64}$/.test(manifest.buildEnvironmentSha256)) {
|
|
9394
9661
|
reasons.push("build environment identity is invalid");
|
|
9395
9662
|
}
|
|
9663
|
+
reasons.push(...validatePreviewArtifactWorkspaceIdentity(manifest));
|
|
9396
9664
|
for (const [label, identity, expectedPath] of [
|
|
9397
9665
|
["server", manifest.files?.server, "server.bundle.cjs"],
|
|
9398
9666
|
["client", manifest.files?.client, "client.zip"]
|
|
@@ -9402,8 +9670,8 @@ function validateGeneration(generationDir, runtime, verifyPayloads = true) {
|
|
|
9402
9670
|
}
|
|
9403
9671
|
}
|
|
9404
9672
|
if (verifyPayloads && manifest.files?.server && manifest.files?.client) {
|
|
9405
|
-
const serverPath =
|
|
9406
|
-
const clientPath =
|
|
9673
|
+
const serverPath = path30.join(generationDir, "server.bundle.cjs");
|
|
9674
|
+
const clientPath = path30.join(generationDir, "client.zip");
|
|
9407
9675
|
try {
|
|
9408
9676
|
if (sha256File(serverPath) !== manifest.files.server.sha256) {
|
|
9409
9677
|
reasons.push("server.bundle.cjs hash does not match manifest");
|
|
@@ -9426,10 +9694,10 @@ function validateGeneration(generationDir, runtime, verifyPayloads = true) {
|
|
|
9426
9694
|
return { manifest, reasons };
|
|
9427
9695
|
}
|
|
9428
9696
|
function publishPreviewArtifactGeneration(options) {
|
|
9429
|
-
const storeRoot =
|
|
9697
|
+
const storeRoot = path30.resolve(options.storeRoot);
|
|
9430
9698
|
const previousPointer = readCurrentPointer(storeRoot);
|
|
9431
|
-
const stagingRoot =
|
|
9432
|
-
const stagingDir =
|
|
9699
|
+
const stagingRoot = path30.join(storeRoot, ".staging");
|
|
9700
|
+
const stagingDir = path30.resolve(options.stagingDir);
|
|
9433
9701
|
if (!isPathInside2(stagingRoot, stagingDir)) {
|
|
9434
9702
|
return {
|
|
9435
9703
|
published: false,
|
|
@@ -9443,23 +9711,25 @@ function publishPreviewArtifactGeneration(options) {
|
|
|
9443
9711
|
return { published: false, reasons: validation.reasons };
|
|
9444
9712
|
}
|
|
9445
9713
|
const generationId = validation.manifest.generationId;
|
|
9446
|
-
const generationsRoot =
|
|
9447
|
-
const generationDir =
|
|
9448
|
-
|
|
9449
|
-
if (
|
|
9714
|
+
const generationsRoot = path30.join(storeRoot, "generations");
|
|
9715
|
+
const generationDir = path30.join(generationsRoot, generationId);
|
|
9716
|
+
fs34.mkdirSync(generationsRoot, { recursive: true });
|
|
9717
|
+
if (fs34.existsSync(generationDir)) {
|
|
9450
9718
|
return {
|
|
9451
9719
|
published: false,
|
|
9452
9720
|
reasons: [`generation already exists: ${generationId}`]
|
|
9453
9721
|
};
|
|
9454
9722
|
}
|
|
9455
9723
|
try {
|
|
9456
|
-
|
|
9724
|
+
fs34.renameSync(stagingDir, generationDir);
|
|
9457
9725
|
const pointer = {
|
|
9458
9726
|
schemaVersion: PREVIEW_ARTIFACT_SCHEMA_VERSION,
|
|
9459
9727
|
generationId,
|
|
9728
|
+
workspaceBackupId: validation.manifest.workspaceBackupId,
|
|
9729
|
+
...validation.manifest.workspaceVersion == null ? {} : { workspaceVersion: validation.manifest.workspaceVersion },
|
|
9460
9730
|
publishedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
9461
9731
|
};
|
|
9462
|
-
|
|
9732
|
+
writeJsonAtomic3(path30.join(storeRoot, "current.json"), pointer);
|
|
9463
9733
|
pruneSupersededGenerations(
|
|
9464
9734
|
generationsRoot,
|
|
9465
9735
|
[generationId, previousPointer?.generationId].filter(
|
|
@@ -9477,10 +9747,37 @@ function publishPreviewArtifactGeneration(options) {
|
|
|
9477
9747
|
}
|
|
9478
9748
|
}
|
|
9479
9749
|
function resolveCurrentPreviewArtifactMetadata(options) {
|
|
9480
|
-
|
|
9750
|
+
if (!options.workspaceManifest) {
|
|
9751
|
+
return {
|
|
9752
|
+
resolved: false,
|
|
9753
|
+
reasons: ["workspace manifest is unavailable"]
|
|
9754
|
+
};
|
|
9755
|
+
}
|
|
9756
|
+
const workspaceManifestReasons = validatePreviewArtifactWorkspaceManifest(
|
|
9757
|
+
options.workspaceManifest
|
|
9758
|
+
);
|
|
9759
|
+
if (workspaceManifestReasons.length > 0) {
|
|
9760
|
+
return {
|
|
9761
|
+
resolved: false,
|
|
9762
|
+
reasons: workspaceManifestReasons
|
|
9763
|
+
};
|
|
9764
|
+
}
|
|
9765
|
+
let requestedSourceTreeValidation;
|
|
9766
|
+
try {
|
|
9767
|
+
requestedSourceTreeValidation = createPreviewArtifactSourceTreeValidation(
|
|
9768
|
+
options.sourceTreeValidation?.enabled,
|
|
9769
|
+
options.sourceTreeValidation?.includePaths
|
|
9770
|
+
);
|
|
9771
|
+
} catch {
|
|
9772
|
+
return {
|
|
9773
|
+
resolved: false,
|
|
9774
|
+
reasons: ["requested source tree validation policy is invalid"]
|
|
9775
|
+
};
|
|
9776
|
+
}
|
|
9777
|
+
const storeRoot = path30.resolve(options.storeRoot);
|
|
9481
9778
|
let pointer;
|
|
9482
9779
|
try {
|
|
9483
|
-
pointer = readJson3(
|
|
9780
|
+
pointer = readJson3(path30.join(storeRoot, "current.json"));
|
|
9484
9781
|
} catch (error) {
|
|
9485
9782
|
return {
|
|
9486
9783
|
resolved: false,
|
|
@@ -9489,17 +9786,19 @@ function resolveCurrentPreviewArtifactMetadata(options) {
|
|
|
9489
9786
|
]
|
|
9490
9787
|
};
|
|
9491
9788
|
}
|
|
9492
|
-
if (pointer.schemaVersion !== PREVIEW_ARTIFACT_SCHEMA_VERSION || !GENERATION_ID_PATTERN.test(pointer.generationId)) {
|
|
9789
|
+
if (pointer.schemaVersion !== PREVIEW_ARTIFACT_SCHEMA_VERSION || !GENERATION_ID_PATTERN.test(pointer.generationId) || !WORKSPACE_BACKUP_ID_PATTERN2.test(pointer.workspaceBackupId) || pointer.workspaceVersion != null && !WORKSPACE_VERSION_PATTERN2.test(pointer.workspaceVersion)) {
|
|
9493
9790
|
return {
|
|
9494
9791
|
resolved: false,
|
|
9495
|
-
reasons: [
|
|
9792
|
+
reasons: [
|
|
9793
|
+
"current generation ID, workspace identity, or schema version is invalid"
|
|
9794
|
+
]
|
|
9496
9795
|
};
|
|
9497
9796
|
}
|
|
9498
9797
|
const nodeMajor = parseNodeMajor2(options.nodeVersion);
|
|
9499
9798
|
if (nodeMajor == null) {
|
|
9500
9799
|
return { resolved: false, reasons: ["runtime Node.js version is invalid"] };
|
|
9501
9800
|
}
|
|
9502
|
-
const generationDir =
|
|
9801
|
+
const generationDir = path30.join(
|
|
9503
9802
|
storeRoot,
|
|
9504
9803
|
"generations",
|
|
9505
9804
|
pointer.generationId
|
|
@@ -9516,12 +9815,38 @@ function resolveCurrentPreviewArtifactMetadata(options) {
|
|
|
9516
9815
|
if (!validation.manifest || validation.reasons.length > 0) {
|
|
9517
9816
|
return { resolved: false, reasons: validation.reasons };
|
|
9518
9817
|
}
|
|
9519
|
-
if (validation.manifest.generationId !== pointer.generationId) {
|
|
9818
|
+
if (validation.manifest.generationId !== pointer.generationId || validation.manifest.workspaceBackupId !== pointer.workspaceBackupId || validation.manifest.workspaceVersion !== pointer.workspaceVersion) {
|
|
9520
9819
|
return {
|
|
9521
9820
|
resolved: false,
|
|
9522
9821
|
reasons: ["current pointer does not match generation manifest"]
|
|
9523
9822
|
};
|
|
9524
9823
|
}
|
|
9824
|
+
if (requestedSourceTreeValidation.enabled) {
|
|
9825
|
+
const generationSourceTreeValidation = previewArtifactSourceTreeValidation(
|
|
9826
|
+
validation.manifest
|
|
9827
|
+
);
|
|
9828
|
+
const workspaceSourceTreeValidation = previewArtifactSourceTreeValidation(
|
|
9829
|
+
options.workspaceManifest
|
|
9830
|
+
);
|
|
9831
|
+
if (!samePreviewArtifactSourceTreeValidation(
|
|
9832
|
+
requestedSourceTreeValidation,
|
|
9833
|
+
generationSourceTreeValidation
|
|
9834
|
+
) || !samePreviewArtifactSourceTreeValidation(
|
|
9835
|
+
requestedSourceTreeValidation,
|
|
9836
|
+
workspaceSourceTreeValidation
|
|
9837
|
+
)) {
|
|
9838
|
+
return {
|
|
9839
|
+
resolved: false,
|
|
9840
|
+
reasons: ["workspace source tree validation policy mismatch"]
|
|
9841
|
+
};
|
|
9842
|
+
}
|
|
9843
|
+
if (options.workspaceManifest.workspaceVersion !== validation.manifest.workspaceVersion) {
|
|
9844
|
+
return {
|
|
9845
|
+
resolved: false,
|
|
9846
|
+
reasons: ["workspace version mismatch"]
|
|
9847
|
+
};
|
|
9848
|
+
}
|
|
9849
|
+
}
|
|
9525
9850
|
if (validation.manifest.buildEnvironmentSha256 !== options.buildEnvironmentSha256) {
|
|
9526
9851
|
return {
|
|
9527
9852
|
resolved: false,
|
|
@@ -9536,33 +9861,33 @@ function resolveCurrentPreviewArtifactMetadata(options) {
|
|
|
9536
9861
|
};
|
|
9537
9862
|
}
|
|
9538
9863
|
async function materializeVerifiedPreviewArtifactFile(options) {
|
|
9539
|
-
const source =
|
|
9540
|
-
const destination =
|
|
9864
|
+
const source = path30.resolve(options.source);
|
|
9865
|
+
const destination = path30.resolve(options.destination);
|
|
9541
9866
|
const temporary = `${destination}.${process.pid}.${Date.now()}.tmp`;
|
|
9542
|
-
|
|
9543
|
-
if (
|
|
9867
|
+
fs34.mkdirSync(path30.dirname(destination), { recursive: true });
|
|
9868
|
+
if (fs34.existsSync(destination)) {
|
|
9544
9869
|
throw new Error(
|
|
9545
9870
|
`materialization destination already exists: ${destination}`
|
|
9546
9871
|
);
|
|
9547
9872
|
}
|
|
9548
|
-
const noFollow =
|
|
9873
|
+
const noFollow = fs34.constants.O_NOFOLLOW ?? 0;
|
|
9549
9874
|
let sourceHandle;
|
|
9550
9875
|
let destinationHandle;
|
|
9551
9876
|
try {
|
|
9552
|
-
sourceHandle = await
|
|
9877
|
+
sourceHandle = await fs34.promises.open(
|
|
9553
9878
|
source,
|
|
9554
|
-
|
|
9879
|
+
fs34.constants.O_RDONLY | noFollow
|
|
9555
9880
|
);
|
|
9556
9881
|
const sourceStat = await sourceHandle.stat();
|
|
9557
9882
|
if (!sourceStat.isFile()) {
|
|
9558
9883
|
throw new Error(`${options.identity.path} is not a regular file`);
|
|
9559
9884
|
}
|
|
9560
|
-
destinationHandle = await
|
|
9885
|
+
destinationHandle = await fs34.promises.open(
|
|
9561
9886
|
temporary,
|
|
9562
|
-
|
|
9887
|
+
fs34.constants.O_WRONLY | fs34.constants.O_CREAT | fs34.constants.O_EXCL,
|
|
9563
9888
|
384
|
|
9564
9889
|
);
|
|
9565
|
-
const hash =
|
|
9890
|
+
const hash = crypto3.createHash("sha256");
|
|
9566
9891
|
const buffer = Buffer.allocUnsafe(256 * 1024);
|
|
9567
9892
|
let totalBytes = 0;
|
|
9568
9893
|
for (; ; ) {
|
|
@@ -9601,24 +9926,24 @@ async function materializeVerifiedPreviewArtifactFile(options) {
|
|
|
9601
9926
|
await destinationHandle.sync();
|
|
9602
9927
|
await destinationHandle.close();
|
|
9603
9928
|
destinationHandle = void 0;
|
|
9604
|
-
await
|
|
9929
|
+
await fs34.promises.rename(temporary, destination);
|
|
9605
9930
|
return { bytes: totalBytes, sha256 };
|
|
9606
9931
|
} finally {
|
|
9607
9932
|
await destinationHandle?.close().catch(() => void 0);
|
|
9608
9933
|
await sourceHandle?.close().catch(() => void 0);
|
|
9609
|
-
await
|
|
9934
|
+
await fs34.promises.rm(temporary, { force: true }).catch(() => void 0);
|
|
9610
9935
|
}
|
|
9611
9936
|
}
|
|
9612
9937
|
|
|
9613
9938
|
// src/commands/preview-artifact/producer.ts
|
|
9614
9939
|
function defaultGenerationId() {
|
|
9615
9940
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[-:.TZ]/g, "");
|
|
9616
|
-
return `${timestamp}-${
|
|
9941
|
+
return `${timestamp}-${crypto4.randomBytes(5).toString("hex")}`;
|
|
9617
9942
|
}
|
|
9618
9943
|
function defaultPreviewBundleBuildCommands(projectRootInput) {
|
|
9619
|
-
const projectRoot =
|
|
9944
|
+
const projectRoot = path31.resolve(projectRootInput);
|
|
9620
9945
|
const executableSuffix = process.platform === "win32" ? ".cmd" : "";
|
|
9621
|
-
const localBinary = (name) =>
|
|
9946
|
+
const localBinary = (name) => path31.join(
|
|
9622
9947
|
projectRoot,
|
|
9623
9948
|
"node_modules",
|
|
9624
9949
|
".bin",
|
|
@@ -9638,7 +9963,7 @@ function defaultPreviewBundleBuildCommands(projectRootInput) {
|
|
|
9638
9963
|
}
|
|
9639
9964
|
function runBuildCommands(projectRoot, buildCommands, environment) {
|
|
9640
9965
|
for (const buildCommand of buildCommands) {
|
|
9641
|
-
const result =
|
|
9966
|
+
const result = spawnSync9(buildCommand.command, buildCommand.args, {
|
|
9642
9967
|
cwd: projectRoot,
|
|
9643
9968
|
env: { ...process.env, ...environment },
|
|
9644
9969
|
encoding: "utf8",
|
|
@@ -9656,27 +9981,27 @@ ${diagnostic}` : ""}`
|
|
|
9656
9981
|
return [];
|
|
9657
9982
|
}
|
|
9658
9983
|
function createClientArchive(clientDir, archivePath) {
|
|
9659
|
-
if (!
|
|
9984
|
+
if (!fs35.existsSync(path31.join(clientDir, "index.html"))) {
|
|
9660
9985
|
return [`client build output is missing: ${clientDir}`];
|
|
9661
9986
|
}
|
|
9662
|
-
const result =
|
|
9987
|
+
const result = spawnSync9("zip", ["-qry", archivePath, "."], {
|
|
9663
9988
|
cwd: clientDir,
|
|
9664
9989
|
encoding: "utf8"
|
|
9665
9990
|
});
|
|
9666
|
-
if (result.status === 0 &&
|
|
9991
|
+
if (result.status === 0 && fs35.existsSync(archivePath)) return [];
|
|
9667
9992
|
return [`client archive failed with exit ${result.status ?? "unknown"}`];
|
|
9668
9993
|
}
|
|
9669
9994
|
function readActionPlugins2(metadataFile) {
|
|
9670
|
-
const metadata = JSON.parse(
|
|
9995
|
+
const metadata = JSON.parse(fs35.readFileSync(metadataFile, "utf8"));
|
|
9671
9996
|
if (!Array.isArray(metadata.actionPlugins)) return [];
|
|
9672
9997
|
return metadata.actionPlugins.filter((value) => typeof value === "string").sort();
|
|
9673
9998
|
}
|
|
9674
9999
|
async function producePreviewArtifactGeneration(options) {
|
|
9675
10000
|
const startedAt = Date.now();
|
|
9676
|
-
const projectRoot =
|
|
9677
|
-
const storeRoot =
|
|
10001
|
+
const projectRoot = path31.resolve(options.projectRoot);
|
|
10002
|
+
const storeRoot = path31.resolve(options.storeRoot);
|
|
9678
10003
|
const generationId = options.generationId ?? defaultGenerationId();
|
|
9679
|
-
const stagingDir =
|
|
10004
|
+
const stagingDir = path31.join(
|
|
9680
10005
|
storeRoot,
|
|
9681
10006
|
".staging",
|
|
9682
10007
|
`${generationId}.${process.pid}.${Date.now()}`
|
|
@@ -9688,7 +10013,7 @@ async function producePreviewArtifactGeneration(options) {
|
|
|
9688
10013
|
reasons,
|
|
9689
10014
|
...probe ? { probe } : {}
|
|
9690
10015
|
});
|
|
9691
|
-
|
|
10016
|
+
fs35.mkdirSync(stagingDir, { recursive: true });
|
|
9692
10017
|
try {
|
|
9693
10018
|
const buildEnvironment = previewArtifactBuildEnvironment(
|
|
9694
10019
|
options.environment,
|
|
@@ -9701,8 +10026,8 @@ async function producePreviewArtifactGeneration(options) {
|
|
|
9701
10026
|
buildEnvironment
|
|
9702
10027
|
);
|
|
9703
10028
|
if (buildReasons.length > 0) return failed(buildReasons);
|
|
9704
|
-
const serverOutfile =
|
|
9705
|
-
const serverMetadataFile =
|
|
10029
|
+
const serverOutfile = path31.join(stagingDir, "server.bundle.cjs");
|
|
10030
|
+
const serverMetadataFile = path31.join(stagingDir, "server-build.json");
|
|
9706
10031
|
const serverResult = await buildPreviewServerArtifact({
|
|
9707
10032
|
projectRoot,
|
|
9708
10033
|
entry: "dist/main.js",
|
|
@@ -9711,12 +10036,12 @@ async function producePreviewArtifactGeneration(options) {
|
|
|
9711
10036
|
});
|
|
9712
10037
|
if (!serverResult.built) return failed(serverResult.reasons);
|
|
9713
10038
|
const archiveReasons = createClientArchive(
|
|
9714
|
-
|
|
9715
|
-
|
|
10039
|
+
path31.join(projectRoot, "dist", "client"),
|
|
10040
|
+
path31.join(stagingDir, "client.zip")
|
|
9716
10041
|
);
|
|
9717
10042
|
if (archiveReasons.length > 0) return failed(archiveReasons);
|
|
9718
10043
|
const actionPlugins = readActionPlugins2(serverMetadataFile);
|
|
9719
|
-
|
|
10044
|
+
fs35.rmSync(serverMetadataFile, { force: true });
|
|
9720
10045
|
createPreviewArtifactManifest({
|
|
9721
10046
|
generationDir: stagingDir,
|
|
9722
10047
|
generationId,
|
|
@@ -9727,6 +10052,7 @@ async function producePreviewArtifactGeneration(options) {
|
|
|
9727
10052
|
options.environment,
|
|
9728
10053
|
options.appBasePath
|
|
9729
10054
|
),
|
|
10055
|
+
workspaceManifest: options.workspaceManifest,
|
|
9730
10056
|
actionPlugins
|
|
9731
10057
|
});
|
|
9732
10058
|
const probe = await runPreviewArtifactProbe({
|
|
@@ -9753,21 +10079,21 @@ async function producePreviewArtifactGeneration(options) {
|
|
|
9753
10079
|
} catch (error) {
|
|
9754
10080
|
return failed([error instanceof Error ? error.message : String(error)]);
|
|
9755
10081
|
} finally {
|
|
9756
|
-
|
|
10082
|
+
fs35.rmSync(stagingDir, { recursive: true, force: true });
|
|
9757
10083
|
}
|
|
9758
10084
|
}
|
|
9759
10085
|
|
|
9760
10086
|
// src/commands/preview-artifact/runtime.ts
|
|
9761
|
-
import * as
|
|
10087
|
+
import * as fs37 from "fs";
|
|
9762
10088
|
import * as net3 from "net";
|
|
9763
|
-
import * as
|
|
10089
|
+
import * as path33 from "path";
|
|
9764
10090
|
import { spawn as spawn3 } from "child_process";
|
|
9765
10091
|
|
|
9766
10092
|
// src/commands/preview-artifact/switcher.ts
|
|
9767
10093
|
import * as http from "http";
|
|
9768
10094
|
import * as net2 from "net";
|
|
9769
|
-
import * as
|
|
9770
|
-
import * as
|
|
10095
|
+
import * as fs36 from "fs";
|
|
10096
|
+
import * as path32 from "path";
|
|
9771
10097
|
function targetOrigin(target) {
|
|
9772
10098
|
return `http://${target.host}:${target.port}`;
|
|
9773
10099
|
}
|
|
@@ -9815,7 +10141,7 @@ function cachedAssetFile(request2, cachedAssetFiles) {
|
|
|
9815
10141
|
return cachedAssetFiles.get(pathname);
|
|
9816
10142
|
}
|
|
9817
10143
|
function contentType(filePath) {
|
|
9818
|
-
switch (
|
|
10144
|
+
switch (path32.extname(filePath).toLowerCase()) {
|
|
9819
10145
|
case ".js":
|
|
9820
10146
|
case ".mjs":
|
|
9821
10147
|
return "text/javascript; charset=utf-8";
|
|
@@ -9852,7 +10178,7 @@ function contentType(filePath) {
|
|
|
9852
10178
|
function serveCachedAsset(request2, response, filePath) {
|
|
9853
10179
|
let stat;
|
|
9854
10180
|
try {
|
|
9855
|
-
stat =
|
|
10181
|
+
stat = fs36.lstatSync(filePath);
|
|
9856
10182
|
} catch {
|
|
9857
10183
|
response.writeHead(404).end();
|
|
9858
10184
|
return;
|
|
@@ -9870,7 +10196,7 @@ function serveCachedAsset(request2, response, filePath) {
|
|
|
9870
10196
|
response.end();
|
|
9871
10197
|
return;
|
|
9872
10198
|
}
|
|
9873
|
-
const stream =
|
|
10199
|
+
const stream = fs36.createReadStream(filePath);
|
|
9874
10200
|
stream.on("error", () => response.destroy());
|
|
9875
10201
|
stream.pipe(response);
|
|
9876
10202
|
}
|
|
@@ -9917,8 +10243,8 @@ function createPreviewArtifactSwitcher(options) {
|
|
|
9917
10243
|
let closed = false;
|
|
9918
10244
|
let pollTimer;
|
|
9919
10245
|
let settleCutover;
|
|
9920
|
-
const cutover = new Promise((
|
|
9921
|
-
settleCutover =
|
|
10246
|
+
const cutover = new Promise((resolve10) => {
|
|
10247
|
+
settleCutover = resolve10;
|
|
9922
10248
|
});
|
|
9923
10249
|
const currentTarget = () => active === "cached" ? options.cachedTarget : options.freshTarget;
|
|
9924
10250
|
const server = http.createServer((request2, response) => {
|
|
@@ -9972,7 +10298,7 @@ function createPreviewArtifactSwitcher(options) {
|
|
|
9972
10298
|
},
|
|
9973
10299
|
cutover,
|
|
9974
10300
|
start() {
|
|
9975
|
-
return new Promise((
|
|
10301
|
+
return new Promise((resolve10, reject) => {
|
|
9976
10302
|
server.once("error", reject);
|
|
9977
10303
|
server.listen(options.listenPort, listenHost, () => {
|
|
9978
10304
|
const address = server.address();
|
|
@@ -9981,7 +10307,7 @@ function createPreviewArtifactSwitcher(options) {
|
|
|
9981
10307
|
return;
|
|
9982
10308
|
}
|
|
9983
10309
|
schedulePoll();
|
|
9984
|
-
|
|
10310
|
+
resolve10({
|
|
9985
10311
|
host: listenHost,
|
|
9986
10312
|
port: address.port,
|
|
9987
10313
|
origin: `http://${listenHost}:${address.port}`
|
|
@@ -10000,8 +10326,8 @@ function createPreviewArtifactSwitcher(options) {
|
|
|
10000
10326
|
});
|
|
10001
10327
|
settleCutover = void 0;
|
|
10002
10328
|
}
|
|
10003
|
-
return new Promise((
|
|
10004
|
-
server.close(() =>
|
|
10329
|
+
return new Promise((resolve10) => {
|
|
10330
|
+
server.close(() => resolve10());
|
|
10005
10331
|
server.closeAllConnections?.();
|
|
10006
10332
|
});
|
|
10007
10333
|
}
|
|
@@ -10026,10 +10352,10 @@ async function measureAsync(timings, phase, operation, detail) {
|
|
|
10026
10352
|
}
|
|
10027
10353
|
}
|
|
10028
10354
|
function sleep2(ms) {
|
|
10029
|
-
return new Promise((
|
|
10355
|
+
return new Promise((resolve10) => setTimeout(resolve10, ms));
|
|
10030
10356
|
}
|
|
10031
10357
|
async function reservePort2() {
|
|
10032
|
-
return new Promise((
|
|
10358
|
+
return new Promise((resolve10, reject) => {
|
|
10033
10359
|
const server = net3.createServer();
|
|
10034
10360
|
server.unref();
|
|
10035
10361
|
server.once("error", reject);
|
|
@@ -10041,7 +10367,7 @@ async function reservePort2() {
|
|
|
10041
10367
|
}
|
|
10042
10368
|
server.close((error) => {
|
|
10043
10369
|
if (error) reject(error);
|
|
10044
|
-
else
|
|
10370
|
+
else resolve10(address.port);
|
|
10045
10371
|
});
|
|
10046
10372
|
});
|
|
10047
10373
|
});
|
|
@@ -10059,12 +10385,12 @@ function stopProcessGroup2(child) {
|
|
|
10059
10385
|
}
|
|
10060
10386
|
}
|
|
10061
10387
|
function isPathInside3(parent, candidate) {
|
|
10062
|
-
const relative5 =
|
|
10063
|
-
return relative5 !== "" && relative5 !== ".." && !relative5.startsWith(`..${
|
|
10388
|
+
const relative5 = path33.relative(parent, candidate);
|
|
10389
|
+
return relative5 !== "" && relative5 !== ".." && !relative5.startsWith(`..${path33.sep}`) && !path33.isAbsolute(relative5);
|
|
10064
10390
|
}
|
|
10065
10391
|
function ensureRegularRuntimeRoot(runtimeRoot) {
|
|
10066
|
-
|
|
10067
|
-
const stat =
|
|
10392
|
+
fs37.mkdirSync(runtimeRoot, { recursive: true });
|
|
10393
|
+
const stat = fs37.lstatSync(runtimeRoot);
|
|
10068
10394
|
if (!stat.isDirectory() || stat.isSymbolicLink()) {
|
|
10069
10395
|
throw new Error("runtime root must be a regular directory");
|
|
10070
10396
|
}
|
|
@@ -10073,14 +10399,14 @@ function collectCachedAssetFiles(clientRoot, appBasePath) {
|
|
|
10073
10399
|
const files = /* @__PURE__ */ new Map();
|
|
10074
10400
|
const basePath = appBasePath.endsWith("/") ? appBasePath : `${appBasePath}/`;
|
|
10075
10401
|
const visit = (directory) => {
|
|
10076
|
-
for (const entry of
|
|
10077
|
-
const absolute =
|
|
10402
|
+
for (const entry of fs37.readdirSync(directory, { withFileTypes: true })) {
|
|
10403
|
+
const absolute = path33.join(directory, entry.name);
|
|
10078
10404
|
if (entry.isDirectory()) {
|
|
10079
10405
|
visit(absolute);
|
|
10080
10406
|
continue;
|
|
10081
10407
|
}
|
|
10082
10408
|
if (!entry.isFile() || entry.isSymbolicLink()) continue;
|
|
10083
|
-
const relative5 =
|
|
10409
|
+
const relative5 = path33.relative(clientRoot, absolute).split(path33.sep).join("/");
|
|
10084
10410
|
if (relative5 === "index.html") continue;
|
|
10085
10411
|
files.set(
|
|
10086
10412
|
new URL(`/${relative5}`, "http://preview.local").pathname,
|
|
@@ -10096,13 +10422,13 @@ function collectCachedAssetFiles(clientRoot, appBasePath) {
|
|
|
10096
10422
|
return files;
|
|
10097
10423
|
}
|
|
10098
10424
|
function writeMarkerAtomic(markerFile, marker) {
|
|
10099
|
-
|
|
10425
|
+
fs37.mkdirSync(path33.dirname(markerFile), { recursive: true });
|
|
10100
10426
|
const temporaryFile = `${markerFile}.${process.pid}.${Date.now()}.tmp`;
|
|
10101
|
-
|
|
10427
|
+
fs37.writeFileSync(temporaryFile, `${JSON.stringify(marker, null, 2)}
|
|
10102
10428
|
`, {
|
|
10103
10429
|
mode: 384
|
|
10104
10430
|
});
|
|
10105
|
-
|
|
10431
|
+
fs37.renameSync(temporaryFile, markerFile);
|
|
10106
10432
|
}
|
|
10107
10433
|
async function waitForCachedRuntime(origin, appBasePath, businessApiPath, timeoutMs, child) {
|
|
10108
10434
|
const timeoutAt = Date.now() + timeoutMs;
|
|
@@ -10145,12 +10471,14 @@ async function startPreviewArtifactRuntime(options) {
|
|
|
10145
10471
|
buildEnvironmentSha256: previewArtifactBuildEnvironmentHash(
|
|
10146
10472
|
options.environment,
|
|
10147
10473
|
options.appBasePath
|
|
10148
|
-
)
|
|
10474
|
+
),
|
|
10475
|
+
sourceTreeValidation: options.sourceTreeValidation,
|
|
10476
|
+
workspaceManifest: options.workspaceManifest
|
|
10149
10477
|
})
|
|
10150
10478
|
);
|
|
10151
10479
|
if (!resolved.resolved) return { started: false, reasons: resolved.reasons };
|
|
10152
|
-
const runtimeRoot =
|
|
10153
|
-
const markerFile =
|
|
10480
|
+
const runtimeRoot = path33.resolve(options.runtimeRoot);
|
|
10481
|
+
const markerFile = path33.resolve(options.markerFile);
|
|
10154
10482
|
let runtimeDir;
|
|
10155
10483
|
let cachedRuntime;
|
|
10156
10484
|
let switcher;
|
|
@@ -10160,58 +10488,22 @@ async function startPreviewArtifactRuntime(options) {
|
|
|
10160
10488
|
closed = true;
|
|
10161
10489
|
stopProcessGroup2(cachedRuntime);
|
|
10162
10490
|
await switcher?.close();
|
|
10163
|
-
|
|
10164
|
-
if (runtimeDir)
|
|
10491
|
+
fs37.rmSync(markerFile, { force: true });
|
|
10492
|
+
if (runtimeDir) fs37.rmSync(runtimeDir, { recursive: true, force: true });
|
|
10165
10493
|
};
|
|
10166
10494
|
try {
|
|
10167
10495
|
ensureRegularRuntimeRoot(runtimeRoot);
|
|
10168
10496
|
if (!isPathInside3(runtimeRoot, markerFile)) {
|
|
10169
10497
|
throw new Error("runtime marker must be inside the runtime root");
|
|
10170
10498
|
}
|
|
10171
|
-
runtimeDir =
|
|
10172
|
-
|
|
10499
|
+
runtimeDir = fs37.mkdtempSync(
|
|
10500
|
+
path33.join(runtimeRoot, `${resolved.generationId}-`)
|
|
10173
10501
|
);
|
|
10174
|
-
const runtimeBundle =
|
|
10175
|
-
const serverSource =
|
|
10176
|
-
const clientArchive =
|
|
10177
|
-
const clientSource =
|
|
10178
|
-
const clientRoot =
|
|
10179
|
-
const backendReady = (async () => {
|
|
10180
|
-
await measureAsync(
|
|
10181
|
-
timings,
|
|
10182
|
-
"server_materialize",
|
|
10183
|
-
() => materializeVerifiedPreviewArtifactFile({
|
|
10184
|
-
source: serverSource,
|
|
10185
|
-
destination: runtimeBundle,
|
|
10186
|
-
identity: resolved.manifest.files.server
|
|
10187
|
-
}),
|
|
10188
|
-
{ bytes: resolved.manifest.files.server.bytes }
|
|
10189
|
-
);
|
|
10190
|
-
const cachedServerPort2 = options.cachedServerPort === 0 ? await reservePort2() : options.cachedServerPort;
|
|
10191
|
-
cachedRuntime = spawn3(process.execPath, [runtimeBundle], {
|
|
10192
|
-
cwd: runtimeDir,
|
|
10193
|
-
detached: true,
|
|
10194
|
-
stdio: "ignore",
|
|
10195
|
-
env: {
|
|
10196
|
-
...process.env,
|
|
10197
|
-
...previewArtifactRuntimeEnvironment(options.environment),
|
|
10198
|
-
SERVER_PORT: String(cachedServerPort2)
|
|
10199
|
-
}
|
|
10200
|
-
});
|
|
10201
|
-
const cachedOrigin = `http://127.0.0.1:${cachedServerPort2}`;
|
|
10202
|
-
await measureAsync(
|
|
10203
|
-
timings,
|
|
10204
|
-
"backend_spawn_to_business_ready",
|
|
10205
|
-
() => waitForCachedRuntime(
|
|
10206
|
-
cachedOrigin,
|
|
10207
|
-
options.appBasePath,
|
|
10208
|
-
options.businessApiPath,
|
|
10209
|
-
options.startupTimeoutMs ?? 3e4,
|
|
10210
|
-
cachedRuntime
|
|
10211
|
-
)
|
|
10212
|
-
);
|
|
10213
|
-
return cachedServerPort2;
|
|
10214
|
-
})();
|
|
10502
|
+
const runtimeBundle = path33.join(runtimeDir, "server.bundle.cjs");
|
|
10503
|
+
const serverSource = path33.join(resolved.generationDir, "server.bundle.cjs");
|
|
10504
|
+
const clientArchive = path33.join(runtimeDir, "client.zip");
|
|
10505
|
+
const clientSource = path33.join(resolved.generationDir, "client.zip");
|
|
10506
|
+
const clientRoot = path33.join(runtimeDir, "dist", "client");
|
|
10215
10507
|
const assetsReady = (async () => {
|
|
10216
10508
|
await measureAsync(
|
|
10217
10509
|
timings,
|
|
@@ -10241,9 +10533,48 @@ async function startPreviewArtifactRuntime(options) {
|
|
|
10241
10533
|
timings[timings.length - 1].fileCount = new Set(
|
|
10242
10534
|
cachedAssetFiles2.values()
|
|
10243
10535
|
).size;
|
|
10244
|
-
|
|
10536
|
+
fs37.rmSync(clientArchive, { force: true });
|
|
10245
10537
|
return cachedAssetFiles2;
|
|
10246
10538
|
})();
|
|
10539
|
+
const backendReady = (async () => {
|
|
10540
|
+
await measureAsync(
|
|
10541
|
+
timings,
|
|
10542
|
+
"server_materialize",
|
|
10543
|
+
() => materializeVerifiedPreviewArtifactFile({
|
|
10544
|
+
source: serverSource,
|
|
10545
|
+
destination: runtimeBundle,
|
|
10546
|
+
identity: resolved.manifest.files.server
|
|
10547
|
+
}),
|
|
10548
|
+
{ bytes: resolved.manifest.files.server.bytes }
|
|
10549
|
+
);
|
|
10550
|
+
const cachedServerPort2 = options.cachedServerPort === 0 ? await reservePort2() : options.cachedServerPort;
|
|
10551
|
+
cachedRuntime = spawn3(process.execPath, [runtimeBundle], {
|
|
10552
|
+
cwd: runtimeDir,
|
|
10553
|
+
detached: true,
|
|
10554
|
+
stdio: "ignore",
|
|
10555
|
+
env: {
|
|
10556
|
+
...process.env,
|
|
10557
|
+
...previewArtifactRuntimeEnvironment(options.environment),
|
|
10558
|
+
SERVER_PORT: String(cachedServerPort2)
|
|
10559
|
+
}
|
|
10560
|
+
});
|
|
10561
|
+
const cachedOrigin = `http://127.0.0.1:${cachedServerPort2}`;
|
|
10562
|
+
await measureAsync(
|
|
10563
|
+
timings,
|
|
10564
|
+
"backend_spawn_to_business_ready",
|
|
10565
|
+
async () => {
|
|
10566
|
+
await assetsReady;
|
|
10567
|
+
await waitForCachedRuntime(
|
|
10568
|
+
cachedOrigin,
|
|
10569
|
+
options.appBasePath,
|
|
10570
|
+
options.businessApiPath,
|
|
10571
|
+
options.startupTimeoutMs ?? 3e4,
|
|
10572
|
+
cachedRuntime
|
|
10573
|
+
);
|
|
10574
|
+
}
|
|
10575
|
+
);
|
|
10576
|
+
return cachedServerPort2;
|
|
10577
|
+
})();
|
|
10247
10578
|
const [backendResult, assetsResult] = await Promise.allSettled([
|
|
10248
10579
|
backendReady,
|
|
10249
10580
|
assetsReady
|
|
@@ -10371,6 +10702,35 @@ var previewServerArtifactCommand = {
|
|
|
10371
10702
|
);
|
|
10372
10703
|
}
|
|
10373
10704
|
};
|
|
10705
|
+
var previewArtifactWorkspaceManifestCommand = {
|
|
10706
|
+
name: "preview-artifact-workspace-manifest",
|
|
10707
|
+
description: "Snapshot the Git-visible Workspace content identity for backup and cache validation",
|
|
10708
|
+
register(program) {
|
|
10709
|
+
program.command(this.name).description(this.description).option("--project-root <dir>", "Application project root", process.cwd()).requiredOption(
|
|
10710
|
+
"--manifest-file <file>",
|
|
10711
|
+
"Workspace Manifest sidecar output file"
|
|
10712
|
+
).requiredOption(
|
|
10713
|
+
"--workspace-backup-id <id>",
|
|
10714
|
+
"Physical Workspace backup identity"
|
|
10715
|
+
).requiredOption("--app-id <id>", "Application ID").option(
|
|
10716
|
+
"--validate-source-tree",
|
|
10717
|
+
"Calculate a sourceTreeId and bind cache consumption to it"
|
|
10718
|
+
).option(
|
|
10719
|
+
"--source-tree-include <path>",
|
|
10720
|
+
"Repository-relative file or directory included in sourceTreeId; repeatable",
|
|
10721
|
+
(value, previous) => [...previous, value],
|
|
10722
|
+
[]
|
|
10723
|
+
).action(
|
|
10724
|
+
(options) => {
|
|
10725
|
+
const manifest = createPreviewArtifactWorkspaceManifest({
|
|
10726
|
+
...options,
|
|
10727
|
+
sourceTreeIncludePaths: options.sourceTreeInclude
|
|
10728
|
+
});
|
|
10729
|
+
console.log(JSON.stringify(manifest));
|
|
10730
|
+
}
|
|
10731
|
+
);
|
|
10732
|
+
}
|
|
10733
|
+
};
|
|
10374
10734
|
var previewArtifactGenerationCommand = {
|
|
10375
10735
|
name: "preview-artifact-generation",
|
|
10376
10736
|
description: "Build, probe, promote, and atomically publish a Preview Artifact generation",
|
|
@@ -10381,12 +10741,18 @@ var previewArtifactGenerationCommand = {
|
|
|
10381
10741
|
).requiredOption(
|
|
10382
10742
|
"--environment-file <file>",
|
|
10383
10743
|
"JSON application environment passed only to build/probe children"
|
|
10744
|
+
).requiredOption(
|
|
10745
|
+
"--workspace-manifest-file <file>",
|
|
10746
|
+
"Workspace Manifest sidecar created at backup start"
|
|
10384
10747
|
).action(
|
|
10385
10748
|
async (options) => {
|
|
10386
10749
|
const result = await producePreviewArtifactGeneration({
|
|
10387
10750
|
...options,
|
|
10388
10751
|
environment: readPreviewArtifactEnvironmentFile(
|
|
10389
10752
|
options.environmentFile
|
|
10753
|
+
),
|
|
10754
|
+
workspaceManifest: readPreviewArtifactWorkspaceManifest(
|
|
10755
|
+
options.workspaceManifestFile
|
|
10390
10756
|
)
|
|
10391
10757
|
});
|
|
10392
10758
|
console.log(JSON.stringify(result));
|
|
@@ -10443,6 +10809,17 @@ var previewArtifactRuntimeCommand = {
|
|
|
10443
10809
|
).requiredOption(
|
|
10444
10810
|
"--environment-file <file>",
|
|
10445
10811
|
"JSON application environment passed only to cached application child"
|
|
10812
|
+
).requiredOption(
|
|
10813
|
+
"--workspace-manifest-file <file>",
|
|
10814
|
+
"Workspace Manifest sidecar restored with this backup"
|
|
10815
|
+
).option(
|
|
10816
|
+
"--validate-source-tree",
|
|
10817
|
+
"Reject cache when its sourceTreeId does not match the Workspace backup"
|
|
10818
|
+
).option(
|
|
10819
|
+
"--source-tree-include <path>",
|
|
10820
|
+
"Repository-relative sourceTreeId scope; repeatable",
|
|
10821
|
+
(value, previous) => [...previous, value],
|
|
10822
|
+
[]
|
|
10446
10823
|
).action(
|
|
10447
10824
|
async (options) => {
|
|
10448
10825
|
const environmentLoadStartedAt = Date.now();
|
|
@@ -10450,9 +10827,18 @@ var previewArtifactRuntimeCommand = {
|
|
|
10450
10827
|
options.environmentFile
|
|
10451
10828
|
);
|
|
10452
10829
|
const environmentLoadDurationMs = Date.now() - environmentLoadStartedAt;
|
|
10830
|
+
const workspaceManifest = readPreviewArtifactWorkspaceManifest(
|
|
10831
|
+
options.workspaceManifestFile
|
|
10832
|
+
);
|
|
10833
|
+
const sourceTreeValidation = createPreviewArtifactSourceTreeValidation(
|
|
10834
|
+
options.validateSourceTree,
|
|
10835
|
+
options.sourceTreeInclude
|
|
10836
|
+
);
|
|
10453
10837
|
const runtime = await startPreviewArtifactRuntime({
|
|
10454
10838
|
...options,
|
|
10455
10839
|
environment,
|
|
10840
|
+
workspaceManifest,
|
|
10841
|
+
sourceTreeValidation,
|
|
10456
10842
|
initialTimings: [
|
|
10457
10843
|
{
|
|
10458
10844
|
phase: "cli_bootstrap",
|
|
@@ -10498,6 +10884,7 @@ var buildCommandGroup = {
|
|
|
10498
10884
|
uploadStaticCommand,
|
|
10499
10885
|
preUploadStaticCommand,
|
|
10500
10886
|
previewServerArtifactCommand,
|
|
10887
|
+
previewArtifactWorkspaceManifestCommand,
|
|
10501
10888
|
previewArtifactGenerationCommand,
|
|
10502
10889
|
previewArtifactRuntimeCommand
|
|
10503
10890
|
]
|
|
@@ -10517,12 +10904,12 @@ var commands = [
|
|
|
10517
10904
|
];
|
|
10518
10905
|
|
|
10519
10906
|
// src/index.ts
|
|
10520
|
-
var envPath =
|
|
10521
|
-
if (
|
|
10907
|
+
var envPath = path34.join(process.cwd(), ".env");
|
|
10908
|
+
if (fs38.existsSync(envPath)) {
|
|
10522
10909
|
dotenvConfig({ path: envPath });
|
|
10523
10910
|
}
|
|
10524
|
-
var __dirname =
|
|
10525
|
-
var pkg = JSON.parse(
|
|
10911
|
+
var __dirname = path34.dirname(fileURLToPath5(import.meta.url));
|
|
10912
|
+
var pkg = JSON.parse(fs38.readFileSync(path34.join(__dirname, "../package.json"), "utf-8"));
|
|
10526
10913
|
var cli = new FullstackCLI(pkg.version);
|
|
10527
10914
|
cli.useAll(commands);
|
|
10528
10915
|
cli.run();
|