@lark-apaas/fullstack-cli 1.1.59-alpha.20260804110530 → 1.1.59-alpha.20260818101742
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 +214 -2867
- package/package.json +1 -4
- package/templates/.spark_project +2 -2
- package/templates/nest-cli.json +1 -5
- package/templates/scripts/dev-local.js +113 -0
- package/templates/scripts/dev.js +63 -414
- package/templates/scripts/dev.sh +23 -1
- package/templates/scripts/lib/__test__/preserve-dev-cache.test.ts +168 -0
- package/templates/scripts/lib/preserve-dev-cache.cjs +122 -0
- package/templates/scripts/lint.js +27 -15
- package/templates/scripts/prune-smart.js +41 -1
- package/templates/scripts/preview-startup-timing.cjs +0 -459
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
|
-
import
|
|
3
|
-
import
|
|
2
|
+
import fs29 from "fs";
|
|
3
|
+
import path25 from "path";
|
|
4
4
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
5
5
|
import { config as dotenvConfig } from "dotenv";
|
|
6
6
|
|
|
@@ -847,6 +847,117 @@ var tweakImportsTransform = {
|
|
|
847
847
|
}
|
|
848
848
|
};
|
|
849
849
|
|
|
850
|
+
// src/commands/db/gen-dbschema/transforms/ast/fix-opclass.ts
|
|
851
|
+
import { Node as Node9 } from "ts-morph";
|
|
852
|
+
var TYPE_TO_OPCLASS = {
|
|
853
|
+
uuid: "uuid_ops",
|
|
854
|
+
date: "date_ops",
|
|
855
|
+
varchar: "text_ops",
|
|
856
|
+
text: "text_ops",
|
|
857
|
+
boolean: "bool_ops",
|
|
858
|
+
integer: "int4_ops",
|
|
859
|
+
serial: "int4_ops",
|
|
860
|
+
numeric: "numeric_ops",
|
|
861
|
+
jsonb: "jsonb_ops",
|
|
862
|
+
customTimestamptz: "timestamptz_ops",
|
|
863
|
+
bigint: "int8_ops",
|
|
864
|
+
smallint: "int2_ops",
|
|
865
|
+
real: "float4_ops",
|
|
866
|
+
doublePrecision: "float8_ops"
|
|
867
|
+
};
|
|
868
|
+
var fixOpclassTransform = {
|
|
869
|
+
name: "fix-opclass",
|
|
870
|
+
transform(ctx) {
|
|
871
|
+
const { sourceFile } = ctx;
|
|
872
|
+
sourceFile.forEachDescendant((node) => {
|
|
873
|
+
if (!Node9.isCallExpression(node)) return;
|
|
874
|
+
const expr = node.getExpression();
|
|
875
|
+
if (!Node9.isIdentifier(expr)) return;
|
|
876
|
+
const fnName = expr.getText();
|
|
877
|
+
if (fnName !== "pgTable") return;
|
|
878
|
+
const args = node.getArguments();
|
|
879
|
+
if (args.length < 3) return;
|
|
880
|
+
const tableColumns = collectTableColumns(args[1]);
|
|
881
|
+
if (!tableColumns.size) return;
|
|
882
|
+
const indexFn = args[2];
|
|
883
|
+
if (!Node9.isArrowFunction(indexFn)) return;
|
|
884
|
+
fixOpclassInIndexes(indexFn, tableColumns);
|
|
885
|
+
});
|
|
886
|
+
}
|
|
887
|
+
};
|
|
888
|
+
function collectTableColumns(columnsArg) {
|
|
889
|
+
const columns = /* @__PURE__ */ new Map();
|
|
890
|
+
if (!Node9.isObjectLiteralExpression(columnsArg)) return columns;
|
|
891
|
+
for (const prop of columnsArg.getProperties()) {
|
|
892
|
+
if (!Node9.isPropertyAssignment(prop)) continue;
|
|
893
|
+
const colName = prop.getName();
|
|
894
|
+
const initializer = prop.getInitializer();
|
|
895
|
+
if (!initializer) continue;
|
|
896
|
+
const colType = extractColumnType(initializer);
|
|
897
|
+
if (colType) {
|
|
898
|
+
columns.set(colName, colType);
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
return columns;
|
|
902
|
+
}
|
|
903
|
+
function extractColumnType(node) {
|
|
904
|
+
const text = node.getText();
|
|
905
|
+
for (const typeName of Object.keys(TYPE_TO_OPCLASS)) {
|
|
906
|
+
const pattern = new RegExp(`\\b${typeName}\\s*\\(`);
|
|
907
|
+
if (pattern.test(text)) {
|
|
908
|
+
return typeName;
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
return void 0;
|
|
912
|
+
}
|
|
913
|
+
function fixOpclassInIndexes(indexFn, tableColumns) {
|
|
914
|
+
const paramName = getArrowParam(indexFn);
|
|
915
|
+
if (!paramName) return;
|
|
916
|
+
indexFn.forEachDescendant((node) => {
|
|
917
|
+
if (!Node9.isCallExpression(node)) return;
|
|
918
|
+
const expr = node.getExpression();
|
|
919
|
+
if (!Node9.isPropertyAccessExpression(expr)) return;
|
|
920
|
+
if (expr.getName() !== "op") return;
|
|
921
|
+
const opArgs = node.getArguments();
|
|
922
|
+
if (opArgs.length !== 1) return;
|
|
923
|
+
const currentOp = opArgs[0];
|
|
924
|
+
if (!Node9.isStringLiteral(currentOp)) return;
|
|
925
|
+
const colName = resolveColumnName(expr.getExpression(), paramName);
|
|
926
|
+
if (!colName) return;
|
|
927
|
+
const colType = tableColumns.get(colName);
|
|
928
|
+
if (!colType) return;
|
|
929
|
+
const correctOp = TYPE_TO_OPCLASS[colType];
|
|
930
|
+
if (!correctOp) return;
|
|
931
|
+
const currentValue = currentOp.getLiteralValue();
|
|
932
|
+
if (currentValue !== correctOp) {
|
|
933
|
+
currentOp.setLiteralValue(correctOp);
|
|
934
|
+
}
|
|
935
|
+
});
|
|
936
|
+
}
|
|
937
|
+
function getArrowParam(node) {
|
|
938
|
+
if (!Node9.isArrowFunction(node)) return void 0;
|
|
939
|
+
const params = node.getParameters();
|
|
940
|
+
if (params.length !== 1) return void 0;
|
|
941
|
+
return params[0].getName();
|
|
942
|
+
}
|
|
943
|
+
function resolveColumnName(node, tableParam) {
|
|
944
|
+
if (Node9.isPropertyAccessExpression(node)) {
|
|
945
|
+
const obj = node.getExpression();
|
|
946
|
+
const propName = node.getName();
|
|
947
|
+
if (Node9.isIdentifier(obj) && obj.getText() === tableParam) {
|
|
948
|
+
return propName;
|
|
949
|
+
}
|
|
950
|
+
return resolveColumnName(obj, tableParam);
|
|
951
|
+
}
|
|
952
|
+
if (Node9.isCallExpression(node)) {
|
|
953
|
+
const callExpr = node.getExpression();
|
|
954
|
+
if (Node9.isPropertyAccessExpression(callExpr)) {
|
|
955
|
+
return resolveColumnName(callExpr.getExpression(), tableParam);
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
return void 0;
|
|
959
|
+
}
|
|
960
|
+
|
|
850
961
|
// src/commands/db/gen-dbschema/transforms/ast/index.ts
|
|
851
962
|
var defaultTransforms = [
|
|
852
963
|
patchDefectsTransform,
|
|
@@ -865,6 +976,8 @@ var defaultTransforms = [
|
|
|
865
976
|
// #9 Replace .defaultNow()
|
|
866
977
|
removeSystemFieldsTransform,
|
|
867
978
|
// #10 Remove conflicting system fields
|
|
979
|
+
fixOpclassTransform,
|
|
980
|
+
// #11 Fix multi-column index opclass mapping
|
|
868
981
|
tweakImportsTransform
|
|
869
982
|
// #12 Adjust imports
|
|
870
983
|
];
|
|
@@ -1901,7 +2014,7 @@ export class ${className}Module {}
|
|
|
1901
2014
|
}
|
|
1902
2015
|
|
|
1903
2016
|
// src/commands/db/gen-nest-resource/schema-parser.ts
|
|
1904
|
-
import { Project as Project2, Node as
|
|
2017
|
+
import { Project as Project2, Node as Node10 } from "ts-morph";
|
|
1905
2018
|
var DrizzleSchemaParser = class {
|
|
1906
2019
|
constructor(projectOptions) {
|
|
1907
2020
|
this.project = new Project2(projectOptions);
|
|
@@ -1914,7 +2027,7 @@ var DrizzleSchemaParser = class {
|
|
|
1914
2027
|
const declarations = statement.getDeclarations();
|
|
1915
2028
|
for (const declaration of declarations) {
|
|
1916
2029
|
const initializer = declaration.getInitializer();
|
|
1917
|
-
if (initializer &&
|
|
2030
|
+
if (initializer && Node10.isCallExpression(initializer)) {
|
|
1918
2031
|
const expression = initializer.getExpression();
|
|
1919
2032
|
if (expression.getText() === "pgTable") {
|
|
1920
2033
|
const tableInfo = this.parsePgTable(
|
|
@@ -1937,13 +2050,13 @@ var DrizzleSchemaParser = class {
|
|
|
1937
2050
|
}
|
|
1938
2051
|
const tableName = args[0].getText().replace(/['"]/g, "");
|
|
1939
2052
|
const fieldsArg = args[1];
|
|
1940
|
-
if (!
|
|
2053
|
+
if (!Node10.isObjectLiteralExpression(fieldsArg)) {
|
|
1941
2054
|
return null;
|
|
1942
2055
|
}
|
|
1943
2056
|
const fields = [];
|
|
1944
2057
|
const properties = fieldsArg.getProperties();
|
|
1945
2058
|
for (const prop of properties) {
|
|
1946
|
-
if (
|
|
2059
|
+
if (Node10.isPropertyAssignment(prop)) {
|
|
1947
2060
|
const fieldName = prop.getName();
|
|
1948
2061
|
const initializer = prop.getInitializer();
|
|
1949
2062
|
const leadingComments = prop.getLeadingCommentRanges();
|
|
@@ -1951,7 +2064,7 @@ var DrizzleSchemaParser = class {
|
|
|
1951
2064
|
if (leadingComments.length > 0) {
|
|
1952
2065
|
comment = leadingComments.map((c) => c.getText()).join("\n").replace(/\/\//g, "").trim();
|
|
1953
2066
|
}
|
|
1954
|
-
if (initializer &&
|
|
2067
|
+
if (initializer && Node10.isCallExpression(initializer)) {
|
|
1955
2068
|
const fieldInfo = this.parseField(fieldName, initializer, comment);
|
|
1956
2069
|
fields.push(fieldInfo);
|
|
1957
2070
|
}
|
|
@@ -1983,10 +2096,10 @@ var DrizzleSchemaParser = class {
|
|
|
1983
2096
|
parseBaseType(callExpr, fieldInfo) {
|
|
1984
2097
|
let current = callExpr;
|
|
1985
2098
|
let baseCall = null;
|
|
1986
|
-
while (
|
|
2099
|
+
while (Node10.isCallExpression(current)) {
|
|
1987
2100
|
baseCall = current;
|
|
1988
2101
|
const expression2 = current.getExpression();
|
|
1989
|
-
if (
|
|
2102
|
+
if (Node10.isPropertyAccessExpression(expression2)) {
|
|
1990
2103
|
current = expression2.getExpression();
|
|
1991
2104
|
} else {
|
|
1992
2105
|
break;
|
|
@@ -1997,7 +2110,7 @@ var DrizzleSchemaParser = class {
|
|
|
1997
2110
|
}
|
|
1998
2111
|
const expression = baseCall.getExpression();
|
|
1999
2112
|
let typeName = "";
|
|
2000
|
-
if (
|
|
2113
|
+
if (Node10.isPropertyAccessExpression(expression)) {
|
|
2001
2114
|
typeName = expression.getName();
|
|
2002
2115
|
} else {
|
|
2003
2116
|
typeName = expression.getText();
|
|
@@ -2006,25 +2119,25 @@ var DrizzleSchemaParser = class {
|
|
|
2006
2119
|
const args = baseCall.getArguments();
|
|
2007
2120
|
if (args.length > 0) {
|
|
2008
2121
|
const firstArg = args[0];
|
|
2009
|
-
if (
|
|
2122
|
+
if (Node10.isStringLiteral(firstArg)) {
|
|
2010
2123
|
fieldInfo.columnName = firstArg.getLiteralText();
|
|
2011
|
-
} else if (
|
|
2124
|
+
} else if (Node10.isObjectLiteralExpression(firstArg)) {
|
|
2012
2125
|
this.parseTypeConfig(firstArg, fieldInfo);
|
|
2013
|
-
} else if (
|
|
2126
|
+
} else if (Node10.isArrayLiteralExpression(firstArg)) {
|
|
2014
2127
|
fieldInfo.enumValues = firstArg.getElements().map((el) => el.getText().replace(/['"]/g, ""));
|
|
2015
2128
|
}
|
|
2016
2129
|
}
|
|
2017
|
-
if (args.length > 1 &&
|
|
2130
|
+
if (args.length > 1 && Node10.isObjectLiteralExpression(args[1])) {
|
|
2018
2131
|
this.parseTypeConfig(args[1], fieldInfo);
|
|
2019
2132
|
}
|
|
2020
2133
|
}
|
|
2021
2134
|
parseTypeConfig(objLiteral, fieldInfo) {
|
|
2022
|
-
if (!
|
|
2135
|
+
if (!Node10.isObjectLiteralExpression(objLiteral)) {
|
|
2023
2136
|
return;
|
|
2024
2137
|
}
|
|
2025
2138
|
const properties = objLiteral.getProperties();
|
|
2026
2139
|
for (const prop of properties) {
|
|
2027
|
-
if (
|
|
2140
|
+
if (Node10.isPropertyAssignment(prop)) {
|
|
2028
2141
|
const propName = prop.getName();
|
|
2029
2142
|
const value = prop.getInitializer()?.getText();
|
|
2030
2143
|
switch (propName) {
|
|
@@ -2056,9 +2169,9 @@ var DrizzleSchemaParser = class {
|
|
|
2056
2169
|
}
|
|
2057
2170
|
parseCallChain(callExpr, fieldInfo) {
|
|
2058
2171
|
let current = callExpr;
|
|
2059
|
-
while (
|
|
2172
|
+
while (Node10.isCallExpression(current)) {
|
|
2060
2173
|
const expression = current.getExpression();
|
|
2061
|
-
if (
|
|
2174
|
+
if (Node10.isPropertyAccessExpression(expression)) {
|
|
2062
2175
|
const methodName = expression.getName();
|
|
2063
2176
|
const args = current.getArguments();
|
|
2064
2177
|
switch (methodName) {
|
|
@@ -2154,9 +2267,9 @@ async function parseAndGenerateNestResourceTemplate(options) {
|
|
|
2154
2267
|
var require2 = createRequire(import.meta.url);
|
|
2155
2268
|
async function run(options = {}) {
|
|
2156
2269
|
let exitCode = 0;
|
|
2157
|
-
const
|
|
2158
|
-
if (fs4.existsSync(
|
|
2159
|
-
loadEnv({ path:
|
|
2270
|
+
const envPath = path2.resolve(process.cwd(), ".env");
|
|
2271
|
+
if (fs4.existsSync(envPath)) {
|
|
2272
|
+
loadEnv({ path: envPath });
|
|
2160
2273
|
console.log("[gen-db-schema] \u2713 Loaded .env file");
|
|
2161
2274
|
}
|
|
2162
2275
|
const databaseUrl = process.env.SUDA_DATABASE_URL;
|
|
@@ -2455,6 +2568,12 @@ function buildDefaultRules(opts) {
|
|
|
2455
2568
|
to: ".gitignore",
|
|
2456
2569
|
line: ".agent/"
|
|
2457
2570
|
},
|
|
2571
|
+
// 7a. 确保 .gitignore 包含 .npm_cache 目录
|
|
2572
|
+
{
|
|
2573
|
+
type: "add-line",
|
|
2574
|
+
to: ".gitignore",
|
|
2575
|
+
line: ".npm_cache"
|
|
2576
|
+
},
|
|
2458
2577
|
// 8. 同步 .spark_project 配置文件(总是覆盖)
|
|
2459
2578
|
{
|
|
2460
2579
|
from: "templates/.spark_project",
|
|
@@ -2507,7 +2626,9 @@ var SYNC_PROFILES = {
|
|
|
2507
2626
|
"design-stack": defaultProfile,
|
|
2508
2627
|
"nestjs-react-fullstack": defaultProfile,
|
|
2509
2628
|
"vite-react": viteReactProfile,
|
|
2510
|
-
html: emptyProfile
|
|
2629
|
+
html: emptyProfile,
|
|
2630
|
+
// design-html 与 html 同语义:空 sync + 不激活 git hooks(彻底 no-op)
|
|
2631
|
+
"design-html": emptyProfile
|
|
2511
2632
|
};
|
|
2512
2633
|
function genSyncConfig(stack, opts = {}) {
|
|
2513
2634
|
const factory = stack && SYNC_PROFILES[stack] || defaultProfile;
|
|
@@ -4921,7 +5042,7 @@ var PROMPT_PATTERNS = [
|
|
|
4921
5042
|
{ pattern: /proceed\?/i, answer: "y\n" }
|
|
4922
5043
|
];
|
|
4923
5044
|
async function executeShadcnAdd(registryItemPath) {
|
|
4924
|
-
return new Promise((
|
|
5045
|
+
return new Promise((resolve2) => {
|
|
4925
5046
|
let output = "";
|
|
4926
5047
|
const args = ["--yes", "shadcn@3.8.2", "add", registryItemPath];
|
|
4927
5048
|
const ptyProcess = pty.spawn("npx", args, {
|
|
@@ -4947,7 +5068,7 @@ async function executeShadcnAdd(registryItemPath) {
|
|
|
4947
5068
|
});
|
|
4948
5069
|
const timeoutId = setTimeout(() => {
|
|
4949
5070
|
ptyProcess.kill();
|
|
4950
|
-
|
|
5071
|
+
resolve2({
|
|
4951
5072
|
success: false,
|
|
4952
5073
|
files: [],
|
|
4953
5074
|
error: "\u6267\u884C\u8D85\u65F6"
|
|
@@ -4958,7 +5079,7 @@ async function executeShadcnAdd(registryItemPath) {
|
|
|
4958
5079
|
const success = exitCode === 0;
|
|
4959
5080
|
const filePaths = parseOutput(output);
|
|
4960
5081
|
const files = filePaths.map(toFileInfo);
|
|
4961
|
-
|
|
5082
|
+
resolve2({
|
|
4962
5083
|
success,
|
|
4963
5084
|
files,
|
|
4964
5085
|
error: success ? void 0 : output || `Process exited with code ${exitCode}`
|
|
@@ -4969,12 +5090,12 @@ async function executeShadcnAdd(registryItemPath) {
|
|
|
4969
5090
|
|
|
4970
5091
|
// src/commands/component/add.handler.ts
|
|
4971
5092
|
function runActionPluginInit() {
|
|
4972
|
-
return new Promise((
|
|
5093
|
+
return new Promise((resolve2) => {
|
|
4973
5094
|
execFile("fullstack-cli", ["action-plugin", "init"], { cwd: process.cwd(), stdio: "ignore" }, (error) => {
|
|
4974
5095
|
if (error) {
|
|
4975
5096
|
debug("action-plugin init \u5931\u8D25: %s", error.message);
|
|
4976
5097
|
}
|
|
4977
|
-
|
|
5098
|
+
resolve2();
|
|
4978
5099
|
});
|
|
4979
5100
|
});
|
|
4980
5101
|
}
|
|
@@ -5641,17 +5762,17 @@ function analyzeImports(sourceFile) {
|
|
|
5641
5762
|
}
|
|
5642
5763
|
|
|
5643
5764
|
// src/commands/migration/versions/v001_capability/code-migrator/analyzers/call-site-analyzer.ts
|
|
5644
|
-
import { SyntaxKind as
|
|
5765
|
+
import { SyntaxKind as SyntaxKind5 } from "ts-morph";
|
|
5645
5766
|
function analyzeCallSites(sourceFile, imports) {
|
|
5646
5767
|
const callSites = [];
|
|
5647
5768
|
const importMap = /* @__PURE__ */ new Map();
|
|
5648
5769
|
for (const imp of imports) {
|
|
5649
5770
|
importMap.set(imp.importName, imp.capabilityId);
|
|
5650
5771
|
}
|
|
5651
|
-
const callExpressions = sourceFile.getDescendantsOfKind(
|
|
5772
|
+
const callExpressions = sourceFile.getDescendantsOfKind(SyntaxKind5.CallExpression);
|
|
5652
5773
|
for (const callExpr of callExpressions) {
|
|
5653
5774
|
const expression = callExpr.getExpression();
|
|
5654
|
-
if (expression.getKind() ===
|
|
5775
|
+
if (expression.getKind() === SyntaxKind5.Identifier) {
|
|
5655
5776
|
const functionName = expression.getText();
|
|
5656
5777
|
const capabilityId = importMap.get(functionName);
|
|
5657
5778
|
if (capabilityId) {
|
|
@@ -5664,11 +5785,11 @@ function analyzeCallSites(sourceFile, imports) {
|
|
|
5664
5785
|
text: callExpr.getText()
|
|
5665
5786
|
});
|
|
5666
5787
|
}
|
|
5667
|
-
} else if (expression.getKind() ===
|
|
5668
|
-
const propAccess = expression.asKind(
|
|
5788
|
+
} else if (expression.getKind() === SyntaxKind5.PropertyAccessExpression) {
|
|
5789
|
+
const propAccess = expression.asKind(SyntaxKind5.PropertyAccessExpression);
|
|
5669
5790
|
if (propAccess) {
|
|
5670
5791
|
const objectExpr = propAccess.getExpression();
|
|
5671
|
-
if (objectExpr.getKind() ===
|
|
5792
|
+
if (objectExpr.getKind() === SyntaxKind5.Identifier) {
|
|
5672
5793
|
const objectName = objectExpr.getText();
|
|
5673
5794
|
const capabilityId = importMap.get(objectName);
|
|
5674
5795
|
if (capabilityId) {
|
|
@@ -5897,7 +6018,7 @@ function addInjection(sourceFile) {
|
|
|
5897
6018
|
}
|
|
5898
6019
|
|
|
5899
6020
|
// src/commands/migration/versions/v001_capability/code-migrator/transformers/call-site-transformer.ts
|
|
5900
|
-
import { SyntaxKind as
|
|
6021
|
+
import { SyntaxKind as SyntaxKind6 } from "ts-morph";
|
|
5901
6022
|
var DEFAULT_ACTION_NAME = "run";
|
|
5902
6023
|
function generateNewCallText(capabilityId, actionName, args) {
|
|
5903
6024
|
const argsText = args.trim() || "{}";
|
|
@@ -5912,19 +6033,19 @@ function transformCallSites(sourceFile, imports) {
|
|
|
5912
6033
|
});
|
|
5913
6034
|
}
|
|
5914
6035
|
let replacedCount = 0;
|
|
5915
|
-
const callExpressions = sourceFile.getDescendantsOfKind(
|
|
6036
|
+
const callExpressions = sourceFile.getDescendantsOfKind(SyntaxKind6.CallExpression);
|
|
5916
6037
|
const sortedCalls = [...callExpressions].sort((a, b) => b.getStart() - a.getStart());
|
|
5917
6038
|
for (const callExpr of sortedCalls) {
|
|
5918
6039
|
const expression = callExpr.getExpression();
|
|
5919
6040
|
let importInfo;
|
|
5920
|
-
if (expression.getKind() ===
|
|
6041
|
+
if (expression.getKind() === SyntaxKind6.Identifier) {
|
|
5921
6042
|
const functionName = expression.getText();
|
|
5922
6043
|
importInfo = importMap.get(functionName);
|
|
5923
|
-
} else if (expression.getKind() ===
|
|
5924
|
-
const propAccess = expression.asKind(
|
|
6044
|
+
} else if (expression.getKind() === SyntaxKind6.PropertyAccessExpression) {
|
|
6045
|
+
const propAccess = expression.asKind(SyntaxKind6.PropertyAccessExpression);
|
|
5925
6046
|
if (propAccess) {
|
|
5926
6047
|
const objectExpr = propAccess.getExpression();
|
|
5927
|
-
if (objectExpr.getKind() ===
|
|
6048
|
+
if (objectExpr.getKind() === SyntaxKind6.Identifier) {
|
|
5928
6049
|
const objectName = objectExpr.getText();
|
|
5929
6050
|
importInfo = importMap.get(objectName);
|
|
5930
6051
|
}
|
|
@@ -7577,8 +7698,10 @@ function sanitizeStructuredLog(value) {
|
|
|
7577
7698
|
delete sanitized.pid;
|
|
7578
7699
|
return sanitized;
|
|
7579
7700
|
}
|
|
7701
|
+
var TRANSIENT_CONNECTION_ERROR_PATTERN = /ECONNREFUSED|ECONNRESET|ETIMEDOUT|ENETUNREACH|proxy error|\[Proxy\] (?:Error:\s*$|Error during|Connection error|Non-connection error|Headers already sent|Service (?:recovered|did not recover))/i;
|
|
7580
7702
|
function hasErrorInStdLines(lines) {
|
|
7581
|
-
const
|
|
7703
|
+
const filtered = lines.filter((line) => !TRANSIENT_CONNECTION_ERROR_PATTERN.test(line));
|
|
7704
|
+
const combined = filtered.join("\n");
|
|
7582
7705
|
if (!combined) return false;
|
|
7583
7706
|
const strong = [
|
|
7584
7707
|
/compiled with errors/i,
|
|
@@ -7597,7 +7720,7 @@ function hasErrorInStdLines(lines) {
|
|
|
7597
7720
|
/\b0\s+errors?\b/i,
|
|
7598
7721
|
/Server Error \d{3}/i
|
|
7599
7722
|
];
|
|
7600
|
-
return
|
|
7723
|
+
return filtered.some((line) => {
|
|
7601
7724
|
const text = line.trim();
|
|
7602
7725
|
if (!text) return false;
|
|
7603
7726
|
if (ignorePatterns.some((re) => re.test(text))) return false;
|
|
@@ -8098,2838 +8221,62 @@ async function preUploadStatic(options) {
|
|
|
8098
8221
|
}
|
|
8099
8222
|
}
|
|
8100
8223
|
|
|
8101
|
-
// src/commands/build/
|
|
8102
|
-
|
|
8103
|
-
|
|
8104
|
-
|
|
8105
|
-
|
|
8106
|
-
|
|
8107
|
-
|
|
8108
|
-
import { simple as walkSimple } from "acorn-walk";
|
|
8109
|
-
var STATIC_PLUGIN_REGISTRY_SYMBOL = "@lark-apaas/nestjs-capability/static-plugin-registry";
|
|
8110
|
-
var STATIC_PLUGIN_REGISTRY_PROTOCOL_VERSION = 1;
|
|
8111
|
-
var OPTIONAL_DEPENDENCY_STUBS = [
|
|
8112
|
-
"@nestjs/microservices",
|
|
8113
|
-
"@nestjs/microservices/microservices-module",
|
|
8114
|
-
"@nestjs/websockets/socket-module",
|
|
8115
|
-
"fsevents"
|
|
8116
|
-
];
|
|
8117
|
-
var BUILTIN_MODULES = /* @__PURE__ */ new Set([
|
|
8118
|
-
...builtinModules,
|
|
8119
|
-
...builtinModules.map((moduleName) => `node:${moduleName}`)
|
|
8120
|
-
]);
|
|
8121
|
-
function toAbsolutePath(projectRoot, candidate) {
|
|
8122
|
-
return path25.isAbsolute(candidate) ? candidate : path25.resolve(projectRoot, candidate);
|
|
8123
|
-
}
|
|
8124
|
-
function readJson(filePath) {
|
|
8125
|
-
return JSON.parse(fs29.readFileSync(filePath, "utf8"));
|
|
8126
|
-
}
|
|
8127
|
-
function findPackageRoot(entryPath) {
|
|
8128
|
-
let current = path25.dirname(entryPath);
|
|
8129
|
-
const filesystemRoot = path25.parse(current).root;
|
|
8130
|
-
while (current !== filesystemRoot) {
|
|
8131
|
-
if (fs29.existsSync(path25.join(current, "package.json"))) {
|
|
8132
|
-
return current;
|
|
8133
|
-
}
|
|
8134
|
-
current = path25.dirname(current);
|
|
8135
|
-
}
|
|
8136
|
-
return null;
|
|
8137
|
-
}
|
|
8138
|
-
function findNativeAddons(root) {
|
|
8139
|
-
const nativeAddons = [];
|
|
8140
|
-
const pending = [root];
|
|
8141
|
-
while (pending.length > 0) {
|
|
8142
|
-
const current = pending.pop();
|
|
8143
|
-
if (!current) {
|
|
8144
|
-
continue;
|
|
8145
|
-
}
|
|
8146
|
-
for (const entry of fs29.readdirSync(current, { withFileTypes: true })) {
|
|
8147
|
-
const entryPath = path25.join(current, entry.name);
|
|
8148
|
-
if (entry.isSymbolicLink()) {
|
|
8149
|
-
continue;
|
|
8150
|
-
}
|
|
8151
|
-
if (entry.isDirectory()) {
|
|
8152
|
-
pending.push(entryPath);
|
|
8153
|
-
} else if (entry.isFile() && entry.name.endsWith(".node")) {
|
|
8154
|
-
nativeAddons.push(entryPath);
|
|
8155
|
-
}
|
|
8156
|
-
}
|
|
8157
|
-
}
|
|
8158
|
-
return nativeAddons.sort();
|
|
8159
|
-
}
|
|
8160
|
-
function resolveActionPlugins(projectRoot, packageJson) {
|
|
8161
|
-
const configuredPlugins = packageJson.actionPlugins;
|
|
8162
|
-
if (!configuredPlugins || typeof configuredPlugins !== "object") {
|
|
8163
|
-
return { plugins: [], reasons: [] };
|
|
8164
|
-
}
|
|
8165
|
-
const projectRequire = createRequire3(path25.join(projectRoot, "package.json"));
|
|
8166
|
-
const plugins = [];
|
|
8167
|
-
const reasons = [];
|
|
8168
|
-
for (const name of Object.keys(configuredPlugins).sort()) {
|
|
8169
|
-
let entryPath;
|
|
8170
|
-
try {
|
|
8171
|
-
entryPath = projectRequire.resolve(name);
|
|
8172
|
-
} catch (error) {
|
|
8173
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
8174
|
-
reasons.push(`Action Plugin ${name} \u65E0\u6CD5\u89E3\u6790: ${message}`);
|
|
8175
|
-
continue;
|
|
8176
|
-
}
|
|
8177
|
-
const packageRoot = findPackageRoot(entryPath);
|
|
8178
|
-
if (!packageRoot) {
|
|
8179
|
-
reasons.push(`Action Plugin ${name} \u65E0\u6CD5\u5B9A\u4F4D package.json: ${entryPath}`);
|
|
8180
|
-
continue;
|
|
8181
|
-
}
|
|
8182
|
-
const nativeAddons = findNativeAddons(packageRoot);
|
|
8183
|
-
if (nativeAddons.length > 0) {
|
|
8184
|
-
const relativeAddons = nativeAddons.map(
|
|
8185
|
-
(filePath) => path25.relative(packageRoot, filePath)
|
|
8186
|
-
);
|
|
8187
|
-
reasons.push(
|
|
8188
|
-
`Action Plugin ${name} \u5305\u542B\u539F\u751F\u6269\u5C55\uFF0C\u4E0D\u80FD\u751F\u6210\u8DE8\u6C99\u7BB1\u5355 bundle: ${relativeAddons.join(", ")}`
|
|
8189
|
-
);
|
|
8190
|
-
continue;
|
|
8191
|
-
}
|
|
8192
|
-
const manifestPath = path25.join(packageRoot, "manifest.json");
|
|
8193
|
-
let manifest;
|
|
8194
|
-
if (fs29.existsSync(manifestPath)) {
|
|
8195
|
-
try {
|
|
8196
|
-
manifest = readJson(manifestPath);
|
|
8197
|
-
} catch (error) {
|
|
8198
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
8199
|
-
reasons.push(
|
|
8200
|
-
`Action Plugin ${name} \u7684 manifest.json \u65E0\u6CD5\u89E3\u6790: ${message}`
|
|
8201
|
-
);
|
|
8202
|
-
continue;
|
|
8203
|
-
}
|
|
8204
|
-
}
|
|
8205
|
-
plugins.push({ name, entryPath, manifest });
|
|
8206
|
-
}
|
|
8207
|
-
if (reasons.length === 0 && plugins.length > 0) {
|
|
8208
|
-
try {
|
|
8209
|
-
const capabilityRuntime = projectRequire(
|
|
8210
|
-
"@lark-apaas/nestjs-capability"
|
|
8211
|
-
);
|
|
8212
|
-
if (capabilityRuntime.STATIC_PLUGIN_REGISTRY_PROTOCOL_VERSION !== STATIC_PLUGIN_REGISTRY_PROTOCOL_VERSION) {
|
|
8213
|
-
reasons.push(
|
|
8214
|
-
`@lark-apaas/nestjs-capability \u672A\u63D0\u4F9B\u517C\u5BB9\u7684 STATIC_PLUGIN_REGISTRY_PROTOCOL_VERSION=${STATIC_PLUGIN_REGISTRY_PROTOCOL_VERSION}`
|
|
8215
|
-
);
|
|
8216
|
-
}
|
|
8217
|
-
} catch (error) {
|
|
8218
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
8219
|
-
reasons.push(
|
|
8220
|
-
`@lark-apaas/nestjs-capability \u65E0\u6CD5\u52A0\u8F7D\uFF0C\u7F3A\u5C11 STATIC_PLUGIN_REGISTRY_PROTOCOL_VERSION=${STATIC_PLUGIN_REGISTRY_PROTOCOL_VERSION}: ${message}`
|
|
8221
|
-
);
|
|
8222
|
-
}
|
|
8223
|
-
}
|
|
8224
|
-
return { plugins, reasons };
|
|
8225
|
-
}
|
|
8226
|
-
function generateBootstrap(entryPath, plugins) {
|
|
8227
|
-
const lines = [`'use strict';`, `const __staticPluginRegistry = new Map();`];
|
|
8228
|
-
plugins.forEach((plugin, index) => {
|
|
8229
|
-
const moduleVariable = `__staticPlugin${index}`;
|
|
8230
|
-
lines.push(
|
|
8231
|
-
`const ${moduleVariable} = require(${JSON.stringify(plugin.entryPath)});`
|
|
8232
|
-
);
|
|
8233
|
-
lines.push(
|
|
8234
|
-
`__staticPluginRegistry.set(${JSON.stringify(plugin.name)}, { module: ${moduleVariable}, manifest: ${JSON.stringify(plugin.manifest ?? null)} });`
|
|
8235
|
-
);
|
|
8236
|
-
});
|
|
8237
|
-
lines.push(
|
|
8238
|
-
`globalThis[Symbol.for(${JSON.stringify(STATIC_PLUGIN_REGISTRY_SYMBOL)})] = __staticPluginRegistry;`,
|
|
8239
|
-
`if (process.env.PREVIEW_ARTIFACT_PROBE_REGISTRY_FILE) {`,
|
|
8240
|
-
` require('node:fs').writeFileSync(process.env.PREVIEW_ARTIFACT_PROBE_REGISTRY_FILE, JSON.stringify(Array.from(__staticPluginRegistry.keys()).sort()));`,
|
|
8241
|
-
`}`,
|
|
8242
|
-
`require(${JSON.stringify(entryPath)});`,
|
|
8243
|
-
""
|
|
8244
|
-
);
|
|
8245
|
-
return lines.join("\n");
|
|
8246
|
-
}
|
|
8247
|
-
function classTransformerStorageResolver(projectRoot) {
|
|
8248
|
-
const projectRequire = createRequire3(path25.join(projectRoot, "package.json"));
|
|
8249
|
-
return {
|
|
8250
|
-
name: "class-transformer-storage-resolver",
|
|
8251
|
-
setup(esbuild) {
|
|
8252
|
-
esbuild.onResolve({ filter: /^class-transformer\/storage$/ }, () => {
|
|
8253
|
-
try {
|
|
8254
|
-
return {
|
|
8255
|
-
path: projectRequire.resolve("class-transformer/cjs/storage.js")
|
|
8256
|
-
};
|
|
8257
|
-
} catch {
|
|
8258
|
-
return void 0;
|
|
8259
|
-
}
|
|
8260
|
-
});
|
|
8261
|
-
}
|
|
8262
|
-
};
|
|
8263
|
-
}
|
|
8264
|
-
function projectUsesNestjsDevtools(projectRoot) {
|
|
8265
|
-
const distRoot = path25.join(projectRoot, "dist");
|
|
8266
|
-
if (!fs29.existsSync(distRoot)) return false;
|
|
8267
|
-
const pending = [distRoot];
|
|
8268
|
-
while (pending.length > 0) {
|
|
8269
|
-
const current = pending.pop();
|
|
8270
|
-
if (!current) continue;
|
|
8271
|
-
for (const entry of fs29.readdirSync(current, { withFileTypes: true })) {
|
|
8272
|
-
const entryPath = path25.join(current, entry.name);
|
|
8273
|
-
if (entry.isDirectory()) {
|
|
8274
|
-
pending.push(entryPath);
|
|
8275
|
-
} else if (entry.isFile() && /\.[cm]?js$/.test(entry.name)) {
|
|
8276
|
-
const source = fs29.readFileSync(entryPath, "utf8");
|
|
8277
|
-
if (source.includes("@lark-apaas/fullstack-nestjs-core") && /\bDevTools(?:V2)?Module\b/.test(source)) {
|
|
8278
|
-
return true;
|
|
8279
|
-
}
|
|
8280
|
-
}
|
|
8281
|
-
}
|
|
8282
|
-
}
|
|
8283
|
-
return false;
|
|
8284
|
-
}
|
|
8285
|
-
function previewNestjsRuntimeEntryPlugin(projectRoot) {
|
|
8286
|
-
const projectRequire = createRequire3(path25.join(projectRoot, "package.json"));
|
|
8287
|
-
let runtimeEntry;
|
|
8288
|
-
if (!projectUsesNestjsDevtools(projectRoot)) {
|
|
8289
|
-
try {
|
|
8290
|
-
runtimeEntry = projectRequire.resolve(
|
|
8291
|
-
"@lark-apaas/fullstack-nestjs-core/runtime"
|
|
8292
|
-
);
|
|
8293
|
-
} catch {
|
|
8294
|
-
runtimeEntry = void 0;
|
|
8295
|
-
}
|
|
8296
|
-
}
|
|
8297
|
-
let runtimeEntryUsed = false;
|
|
8298
|
-
return {
|
|
8299
|
-
used: () => runtimeEntryUsed,
|
|
8300
|
-
plugin: {
|
|
8301
|
-
name: "preview-nestjs-runtime-entry",
|
|
8302
|
-
setup(esbuild) {
|
|
8303
|
-
esbuild.onResolve(
|
|
8304
|
-
{ filter: /^@lark-apaas\/fullstack-nestjs-core$/ },
|
|
8305
|
-
() => {
|
|
8306
|
-
if (!runtimeEntry) return void 0;
|
|
8307
|
-
runtimeEntryUsed = true;
|
|
8308
|
-
return { path: runtimeEntry };
|
|
8309
|
-
}
|
|
8310
|
-
);
|
|
8311
|
-
}
|
|
8312
|
-
}
|
|
8313
|
-
};
|
|
8314
|
-
}
|
|
8315
|
-
function optionalDependencyStubPlugin() {
|
|
8316
|
-
const stubbedDependencies = /* @__PURE__ */ new Set();
|
|
8317
|
-
const escapedNames = OPTIONAL_DEPENDENCY_STUBS.map(
|
|
8318
|
-
(name) => name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")
|
|
8319
|
-
);
|
|
8320
|
-
const filter = new RegExp(`^(?:${escapedNames.join("|")})$`);
|
|
8321
|
-
return {
|
|
8322
|
-
stubbedDependencies,
|
|
8323
|
-
plugin: {
|
|
8324
|
-
name: "preview-optional-dependency-stubs",
|
|
8325
|
-
setup(esbuild) {
|
|
8326
|
-
esbuild.onResolve({ filter }, (args) => {
|
|
8327
|
-
stubbedDependencies.add(args.path);
|
|
8328
|
-
return {
|
|
8329
|
-
path: args.path,
|
|
8330
|
-
namespace: "preview-optional-dependency"
|
|
8331
|
-
};
|
|
8332
|
-
});
|
|
8333
|
-
esbuild.onLoad(
|
|
8334
|
-
{ filter: /.*/, namespace: "preview-optional-dependency" },
|
|
8335
|
-
(args) => ({
|
|
8336
|
-
contents: `
|
|
8337
|
-
const error = new Error(${JSON.stringify(
|
|
8338
|
-
`Optional dependency is unavailable in Preview Artifact: ${args.path}`
|
|
8339
|
-
)});
|
|
8340
|
-
error.code = 'MODULE_NOT_FOUND';
|
|
8341
|
-
throw error;
|
|
8342
|
-
`,
|
|
8343
|
-
loader: "js"
|
|
8344
|
-
})
|
|
8345
|
-
);
|
|
8346
|
-
}
|
|
8347
|
-
}
|
|
8348
|
-
};
|
|
8349
|
-
}
|
|
8350
|
-
function isStaticString(node) {
|
|
8351
|
-
if (!node) {
|
|
8352
|
-
return false;
|
|
8353
|
-
}
|
|
8354
|
-
if (node.type === "Literal") {
|
|
8355
|
-
return typeof node.value === "string";
|
|
8356
|
-
}
|
|
8357
|
-
return node.type === "TemplateLiteral" && node.expressions.length === 0;
|
|
8358
|
-
}
|
|
8359
|
-
function staticPropertyName(node) {
|
|
8360
|
-
if (node.type !== "MemberExpression") {
|
|
8361
|
-
return void 0;
|
|
8362
|
-
}
|
|
8363
|
-
if (!node.computed && node.property.type === "Identifier") {
|
|
8364
|
-
return node.property.name;
|
|
8365
|
-
}
|
|
8366
|
-
if (node.computed && node.property.type === "Literal" && typeof node.property.value === "string") {
|
|
8367
|
-
return node.property.value;
|
|
8368
|
-
}
|
|
8369
|
-
return void 0;
|
|
8370
|
-
}
|
|
8371
|
-
function auditBundleClosure(outfile, stubbedDependencies) {
|
|
8372
|
-
const ast = parse2(fs29.readFileSync(outfile, "utf8"), {
|
|
8373
|
-
ecmaVersion: "latest",
|
|
8374
|
-
sourceType: "script",
|
|
8375
|
-
allowHashBang: true
|
|
8376
|
-
});
|
|
8377
|
-
let dynamicRequireCount = 0;
|
|
8378
|
-
let dynamicImportCount = 0;
|
|
8379
|
-
let createRequireCount = 0;
|
|
8380
|
-
let requireResolveCount = 0;
|
|
8381
|
-
let runtimeFileReadCount = 0;
|
|
8382
|
-
const runtimeFileReads = /* @__PURE__ */ new Set([
|
|
8383
|
-
"access",
|
|
8384
|
-
"accessSync",
|
|
8385
|
-
"createReadStream",
|
|
8386
|
-
"open",
|
|
8387
|
-
"openSync",
|
|
8388
|
-
"readFile",
|
|
8389
|
-
"readFileSync",
|
|
8390
|
-
"readdir",
|
|
8391
|
-
"readdirSync",
|
|
8392
|
-
"stat",
|
|
8393
|
-
"statSync"
|
|
8394
|
-
]);
|
|
8395
|
-
walkSimple(ast, {
|
|
8396
|
-
CallExpression(node) {
|
|
8397
|
-
if (node.callee.type === "Identifier" && node.callee.name === "require" && !isStaticString(node.arguments[0])) {
|
|
8398
|
-
dynamicRequireCount += 1;
|
|
8399
|
-
}
|
|
8400
|
-
if (node.callee.type === "Identifier" && node.callee.name === "createRequire" || staticPropertyName(node.callee) === "createRequire") {
|
|
8401
|
-
createRequireCount += 1;
|
|
8402
|
-
}
|
|
8403
|
-
const propertyName = staticPropertyName(node.callee);
|
|
8404
|
-
if (propertyName === "resolve" && node.callee.type === "MemberExpression" && node.callee.object.type === "Identifier" && node.callee.object.name === "require") {
|
|
8405
|
-
requireResolveCount += 1;
|
|
8406
|
-
}
|
|
8407
|
-
if (propertyName && runtimeFileReads.has(propertyName)) {
|
|
8408
|
-
runtimeFileReadCount += 1;
|
|
8409
|
-
}
|
|
8410
|
-
},
|
|
8411
|
-
ImportExpression(node) {
|
|
8412
|
-
if (!isStaticString(node.source)) {
|
|
8413
|
-
dynamicImportCount += 1;
|
|
8414
|
-
}
|
|
8415
|
-
}
|
|
8416
|
-
});
|
|
8417
|
-
const risks = [];
|
|
8418
|
-
if (dynamicRequireCount > 0) {
|
|
8419
|
-
risks.push(`bundle retains ${dynamicRequireCount} dynamic require call(s)`);
|
|
8420
|
-
}
|
|
8421
|
-
if (dynamicImportCount > 0) {
|
|
8422
|
-
risks.push(`bundle retains ${dynamicImportCount} dynamic import call(s)`);
|
|
8423
|
-
}
|
|
8424
|
-
if (createRequireCount > 0 || requireResolveCount > 0) {
|
|
8425
|
-
risks.push(
|
|
8426
|
-
`bundle retains runtime module resolution (createRequire=${createRequireCount}, require.resolve=${requireResolveCount})`
|
|
8427
|
-
);
|
|
8428
|
-
}
|
|
8429
|
-
if (runtimeFileReadCount > 0) {
|
|
8430
|
-
risks.push(
|
|
8431
|
-
`bundle retains ${runtimeFileReadCount} runtime filesystem read call(s)`
|
|
8432
|
-
);
|
|
8433
|
-
}
|
|
8434
|
-
if (stubbedDependencies.length > 0) {
|
|
8435
|
-
risks.push(
|
|
8436
|
-
`bundle stubs optional dependencies: ${stubbedDependencies.join(", ")}`
|
|
8437
|
-
);
|
|
8438
|
-
}
|
|
8439
|
-
return risks;
|
|
8440
|
-
}
|
|
8441
|
-
function collectExternalImports(metafile, outfile) {
|
|
8442
|
-
const normalizedOutfile = path25.resolve(outfile);
|
|
8443
|
-
const output = Object.entries(metafile.outputs).find(
|
|
8444
|
-
([outputPath]) => path25.resolve(outputPath) === normalizedOutfile
|
|
8445
|
-
)?.[1];
|
|
8446
|
-
if (!output) {
|
|
8447
|
-
return [];
|
|
8448
|
-
}
|
|
8449
|
-
return Array.from(
|
|
8450
|
-
new Set(
|
|
8451
|
-
output.imports.filter((importRecord) => importRecord.external).map((importRecord) => importRecord.path).filter(
|
|
8452
|
-
(importPath) => !BUILTIN_MODULES.has(importPath) && !importPath.startsWith("node:")
|
|
8453
|
-
)
|
|
8454
|
-
)
|
|
8455
|
-
).sort();
|
|
8456
|
-
}
|
|
8457
|
-
function formatBuildError(error) {
|
|
8458
|
-
if (!error || typeof error !== "object" || !("errors" in error)) {
|
|
8459
|
-
return [error instanceof Error ? error.message : String(error)];
|
|
8460
|
-
}
|
|
8461
|
-
const errors = error.errors;
|
|
8462
|
-
if (!errors || errors.length === 0) {
|
|
8463
|
-
return [error instanceof Error ? error.message : String(error)];
|
|
8464
|
-
}
|
|
8465
|
-
return errors.map((item) => {
|
|
8466
|
-
const location = item.location?.file ? `${item.location.file}${item.location.line ? `:${item.location.line}` : ""}: ` : "";
|
|
8467
|
-
return `${location}${item.text ?? "unknown esbuild error"}`;
|
|
8468
|
-
});
|
|
8469
|
-
}
|
|
8470
|
-
function removePublishedArtifact(outfile, metadataFile) {
|
|
8471
|
-
fs29.rmSync(outfile, { force: true });
|
|
8472
|
-
fs29.rmSync(metadataFile, { force: true });
|
|
8473
|
-
}
|
|
8474
|
-
function bestEffortRemove(targetPath, options = {}) {
|
|
8475
|
-
if (!targetPath) {
|
|
8476
|
-
return;
|
|
8477
|
-
}
|
|
8478
|
-
try {
|
|
8479
|
-
fs29.rmSync(targetPath, { force: true, recursive: options.recursive });
|
|
8480
|
-
} catch {
|
|
8481
|
-
}
|
|
8482
|
-
}
|
|
8483
|
-
function outputPathConflicts(entryPath, packageJsonPath, outfile, metadataFile) {
|
|
8484
|
-
const reasons = [];
|
|
8485
|
-
if (outfile === metadataFile) {
|
|
8486
|
-
reasons.push("bundle \u4E0E metadata \u8F93\u51FA\u8DEF\u5F84\u4E0D\u80FD\u76F8\u540C");
|
|
8487
|
-
}
|
|
8488
|
-
const protectedInputs = /* @__PURE__ */ new Map([
|
|
8489
|
-
[entryPath, "Nest \u7F16\u8BD1\u5165\u53E3"],
|
|
8490
|
-
[packageJsonPath, "package.json"]
|
|
8491
|
-
]);
|
|
8492
|
-
for (const [outputPath, outputName] of [
|
|
8493
|
-
[outfile, "bundle"],
|
|
8494
|
-
[metadataFile, "metadata"]
|
|
8495
|
-
]) {
|
|
8496
|
-
const protectedName = protectedInputs.get(outputPath);
|
|
8497
|
-
if (protectedName) {
|
|
8498
|
-
reasons.push(
|
|
8499
|
-
`${outputName} \u8F93\u51FA\u8DEF\u5F84\u4E0D\u80FD\u8986\u76D6 ${protectedName}: ${outputPath}`
|
|
8500
|
-
);
|
|
8501
|
-
}
|
|
8502
|
-
}
|
|
8503
|
-
return reasons;
|
|
8504
|
-
}
|
|
8505
|
-
async function buildPreviewServerArtifact(options) {
|
|
8506
|
-
const startedAt = Date.now();
|
|
8507
|
-
const projectRoot = path25.resolve(options.projectRoot);
|
|
8508
|
-
const entryPath = toAbsolutePath(
|
|
8509
|
-
projectRoot,
|
|
8510
|
-
options.entry ?? "dist/main.js"
|
|
8511
|
-
);
|
|
8512
|
-
const outfile = toAbsolutePath(
|
|
8513
|
-
projectRoot,
|
|
8514
|
-
options.outfile ?? ".preview-artifact/server.bundle.cjs"
|
|
8515
|
-
);
|
|
8516
|
-
const metadataFile = toAbsolutePath(
|
|
8517
|
-
projectRoot,
|
|
8518
|
-
options.metadataFile ?? `${outfile}.meta.json`
|
|
8519
|
-
);
|
|
8520
|
-
const packageJsonPath = path25.join(projectRoot, "package.json");
|
|
8521
|
-
let pluginCount = 0;
|
|
8522
|
-
let outputCleared = false;
|
|
8523
|
-
let phase = "\u6821\u9A8C\u8F93\u5165";
|
|
8524
|
-
let tempRoot;
|
|
8525
|
-
let tempOutputRoot;
|
|
8526
|
-
let tempMetadataFile;
|
|
8527
|
-
const failed = (reasons) => ({
|
|
8528
|
-
built: false,
|
|
8529
|
-
elapsedMs: Date.now() - startedAt,
|
|
8530
|
-
pluginCount,
|
|
8531
|
-
reasons
|
|
8532
|
-
});
|
|
8533
|
-
const pathConflicts = outputPathConflicts(
|
|
8534
|
-
entryPath,
|
|
8535
|
-
packageJsonPath,
|
|
8536
|
-
outfile,
|
|
8537
|
-
metadataFile
|
|
8538
|
-
);
|
|
8539
|
-
if (pathConflicts.length > 0) {
|
|
8540
|
-
return failed(pathConflicts);
|
|
8541
|
-
}
|
|
8542
|
-
if (!fs29.existsSync(entryPath)) {
|
|
8543
|
-
return failed([`Nest \u7F16\u8BD1\u5165\u53E3\u4E0D\u5B58\u5728: ${entryPath}`]);
|
|
8544
|
-
}
|
|
8545
|
-
if (!fs29.existsSync(packageJsonPath)) {
|
|
8546
|
-
return failed([`package.json \u4E0D\u5B58\u5728: ${packageJsonPath}`]);
|
|
8547
|
-
}
|
|
8548
|
-
try {
|
|
8549
|
-
phase = `\u89E3\u6790 ${packageJsonPath}`;
|
|
8550
|
-
const packageJson = readJson(packageJsonPath);
|
|
8551
|
-
phase = "\u89E3\u6790 Action Plugin";
|
|
8552
|
-
const { plugins, reasons } = resolveActionPlugins(projectRoot, packageJson);
|
|
8553
|
-
pluginCount = plugins.length;
|
|
8554
|
-
phase = "\u6E05\u7406\u65E7 Preview Artifact";
|
|
8555
|
-
removePublishedArtifact(outfile, metadataFile);
|
|
8556
|
-
outputCleared = true;
|
|
8557
|
-
if (reasons.length > 0) {
|
|
8558
|
-
return failed(reasons);
|
|
8559
|
-
}
|
|
8560
|
-
phase = "\u521B\u5EFA Preview Artifact \u4E34\u65F6\u76EE\u5F55";
|
|
8561
|
-
fs29.mkdirSync(path25.dirname(outfile), { recursive: true });
|
|
8562
|
-
fs29.mkdirSync(path25.dirname(metadataFile), { recursive: true });
|
|
8563
|
-
tempRoot = fs29.mkdtempSync(
|
|
8564
|
-
path25.join(os3.tmpdir(), "preview-server-artifact-")
|
|
8565
|
-
);
|
|
8566
|
-
tempOutputRoot = fs29.mkdtempSync(
|
|
8567
|
-
path25.join(path25.dirname(outfile), ".preview-server-artifact-")
|
|
8568
|
-
);
|
|
8569
|
-
const bootstrapPath = path25.join(tempRoot, "bootstrap.cjs");
|
|
8570
|
-
const tempOutfile = path25.join(tempOutputRoot, path25.basename(outfile));
|
|
8571
|
-
tempMetadataFile = path25.join(
|
|
8572
|
-
path25.dirname(metadataFile),
|
|
8573
|
-
`.${path25.basename(metadataFile)}.${process.pid}.${Date.now()}.tmp`
|
|
8574
|
-
);
|
|
8575
|
-
fs29.writeFileSync(bootstrapPath, generateBootstrap(entryPath, plugins));
|
|
8576
|
-
const optionalDependencies = optionalDependencyStubPlugin();
|
|
8577
|
-
const nestjsRuntimeEntry = previewNestjsRuntimeEntryPlugin(projectRoot);
|
|
8578
|
-
const buildOptions = {
|
|
8579
|
-
absWorkingDir: projectRoot,
|
|
8580
|
-
entryPoints: [bootstrapPath],
|
|
8581
|
-
outfile: tempOutfile,
|
|
8582
|
-
bundle: true,
|
|
8583
|
-
platform: "node",
|
|
8584
|
-
format: "cjs",
|
|
8585
|
-
target: "node22",
|
|
8586
|
-
treeShaking: true,
|
|
8587
|
-
keepNames: true,
|
|
8588
|
-
legalComments: "none",
|
|
8589
|
-
sourcemap: false,
|
|
8590
|
-
metafile: true,
|
|
8591
|
-
logLevel: "silent",
|
|
8592
|
-
plugins: [
|
|
8593
|
-
nestjsRuntimeEntry.plugin,
|
|
8594
|
-
classTransformerStorageResolver(projectRoot),
|
|
8595
|
-
optionalDependencies.plugin
|
|
8596
|
-
]
|
|
8597
|
-
};
|
|
8598
|
-
phase = "\u6267\u884C esbuild";
|
|
8599
|
-
const buildResult2 = await build(buildOptions);
|
|
8600
|
-
if (!buildResult2.metafile) {
|
|
8601
|
-
throw new Error("esbuild \u672A\u8FD4\u56DE metafile");
|
|
8602
|
-
}
|
|
8603
|
-
const externalImports = collectExternalImports(
|
|
8604
|
-
buildResult2.metafile,
|
|
8605
|
-
tempOutfile
|
|
8606
|
-
);
|
|
8607
|
-
if (externalImports.length > 0) {
|
|
8608
|
-
return failed([`bundle \u6B8B\u7559\u8FD0\u884C\u65F6\u4F9D\u8D56: ${externalImports.join(", ")}`]);
|
|
8609
|
-
}
|
|
8610
|
-
phase = "\u5BA1\u8BA1 bundle \u8FD0\u884C\u65F6\u95ED\u5305";
|
|
8611
|
-
const stubbedOptionalDependencies = Array.from(
|
|
8612
|
-
optionalDependencies.stubbedDependencies
|
|
8613
|
-
).sort();
|
|
8614
|
-
const closureRisks = auditBundleClosure(
|
|
8615
|
-
tempOutfile,
|
|
8616
|
-
stubbedOptionalDependencies
|
|
8617
|
-
);
|
|
8618
|
-
const closureStatus = closureRisks.length === 0 ? "static-audit-clean" : "runtime-validation-required";
|
|
8619
|
-
const metadata = {
|
|
8620
|
-
version: 1,
|
|
8621
|
-
entry: entryPath,
|
|
8622
|
-
outfile,
|
|
8623
|
-
bundleBytes: fs29.statSync(tempOutfile).size,
|
|
8624
|
-
inputFiles: Object.keys(buildResult2.metafile.inputs).length,
|
|
8625
|
-
externalImports,
|
|
8626
|
-
actionPlugins: plugins.map((plugin) => plugin.name),
|
|
8627
|
-
closureStatus,
|
|
8628
|
-
closureRisks,
|
|
8629
|
-
stubbedOptionalDependencies,
|
|
8630
|
-
nestjsRuntimeEntryUsed: nestjsRuntimeEntry.used(),
|
|
8631
|
-
runtimeProbeStatus: "not-run",
|
|
8632
|
-
consumable: false,
|
|
8633
|
-
elapsedMs: Date.now() - startedAt
|
|
8634
|
-
};
|
|
8635
|
-
phase = "\u53D1\u5E03 bundle \u4E0E metadata";
|
|
8636
|
-
fs29.writeFileSync(
|
|
8637
|
-
tempMetadataFile,
|
|
8638
|
-
`${JSON.stringify(metadata, null, 2)}
|
|
8639
|
-
`
|
|
8640
|
-
);
|
|
8641
|
-
fs29.renameSync(tempOutfile, outfile);
|
|
8642
|
-
fs29.renameSync(tempMetadataFile, metadataFile);
|
|
8643
|
-
return {
|
|
8644
|
-
built: true,
|
|
8645
|
-
outfile,
|
|
8646
|
-
metadataFile,
|
|
8647
|
-
bundleBytes: metadata.bundleBytes,
|
|
8648
|
-
inputFiles: metadata.inputFiles,
|
|
8649
|
-
externalImports,
|
|
8650
|
-
pluginCount,
|
|
8651
|
-
closureStatus,
|
|
8652
|
-
closureRisks,
|
|
8653
|
-
stubbedOptionalDependencies,
|
|
8654
|
-
runtimeProbeStatus: "not-run",
|
|
8655
|
-
consumable: false,
|
|
8656
|
-
elapsedMs: metadata.elapsedMs
|
|
8657
|
-
};
|
|
8658
|
-
} catch (error) {
|
|
8659
|
-
const reasons = formatBuildError(error).map(
|
|
8660
|
-
(reason) => `${phase}: ${reason}`
|
|
8661
|
-
);
|
|
8662
|
-
if (outputCleared) {
|
|
8663
|
-
try {
|
|
8664
|
-
removePublishedArtifact(outfile, metadataFile);
|
|
8665
|
-
} catch (cleanupError) {
|
|
8666
|
-
reasons.push(
|
|
8667
|
-
`\u6E05\u7406\u5931\u8D25\u4EA7\u7269: ${cleanupError instanceof Error ? cleanupError.message : String(cleanupError)}`
|
|
8668
|
-
);
|
|
8669
|
-
}
|
|
8670
|
-
}
|
|
8671
|
-
return failed(reasons);
|
|
8672
|
-
} finally {
|
|
8673
|
-
bestEffortRemove(tempRoot, { recursive: true });
|
|
8674
|
-
bestEffortRemove(tempOutputRoot, { recursive: true });
|
|
8675
|
-
bestEffortRemove(tempMetadataFile);
|
|
8676
|
-
}
|
|
8677
|
-
}
|
|
8678
|
-
|
|
8679
|
-
// src/commands/preview-artifact/producer.ts
|
|
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
|
-
|
|
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
|
|
8691
|
-
import * as crypto from "crypto";
|
|
8692
|
-
import { spawnSync as spawnSync7 } from "child_process";
|
|
8693
|
-
import * as fs30 from "fs";
|
|
8694
|
-
import * as os4 from "os";
|
|
8695
|
-
import * as path26 from "path";
|
|
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
|
|
8224
|
+
// src/commands/build/index.ts
|
|
8225
|
+
var getTokenCommand = {
|
|
8226
|
+
name: "get-token",
|
|
8227
|
+
description: "Get artifact upload credential (STI token)",
|
|
8228
|
+
register(program) {
|
|
8229
|
+
program.command(this.name).description(this.description).requiredOption("--app-id <id>", "Application ID").requiredOption("--scene <scene>", "Build scene (pipeline, static)").option("--commit-id <id>", "Git commit ID (required for pipeline scene)").action(async (options) => {
|
|
8230
|
+
await getToken(options);
|
|
8760
8231
|
});
|
|
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
8232
|
}
|
|
8789
|
-
|
|
8790
|
-
|
|
8791
|
-
|
|
8233
|
+
};
|
|
8234
|
+
var uploadStaticCommand = {
|
|
8235
|
+
name: "upload-static",
|
|
8236
|
+
description: "Upload shared/static files to TOS",
|
|
8237
|
+
register(program) {
|
|
8238
|
+
program.command(this.name).description(this.description).requiredOption("--app-id <id>", "Application ID").option("--static-dir <dir>", "Static files directory", UPLOAD_STATIC_DEFAULTS.staticDir).option("--tosutil-path <path>", "Path to tosutil binary", UPLOAD_STATIC_DEFAULTS.tosutilPath).option("--endpoint <endpoint>", "TOS endpoint", UPLOAD_STATIC_DEFAULTS.endpoint).option("--region <region>", "TOS region", UPLOAD_STATIC_DEFAULTS.region).action(async (options) => {
|
|
8239
|
+
await uploadStatic(options);
|
|
8240
|
+
});
|
|
8792
8241
|
}
|
|
8793
|
-
|
|
8794
|
-
|
|
8242
|
+
};
|
|
8243
|
+
var preUploadStaticCommand = {
|
|
8244
|
+
name: "pre-upload-static",
|
|
8245
|
+
description: "Get TOS upload info and output as env vars for build.sh eval",
|
|
8246
|
+
register(program) {
|
|
8247
|
+
program.command(this.name).description(this.description).requiredOption("--app-id <id>", "Application ID").action(async (options) => {
|
|
8248
|
+
await preUploadStatic(options);
|
|
8249
|
+
});
|
|
8795
8250
|
}
|
|
8796
|
-
|
|
8797
|
-
|
|
8798
|
-
|
|
8799
|
-
|
|
8800
|
-
|
|
8801
|
-
|
|
8251
|
+
};
|
|
8252
|
+
var buildCommandGroup = {
|
|
8253
|
+
name: "build",
|
|
8254
|
+
description: "Build related commands",
|
|
8255
|
+
commands: [getTokenCommand, uploadStaticCommand, preUploadStaticCommand]
|
|
8256
|
+
};
|
|
8257
|
+
|
|
8258
|
+
// src/commands/index.ts
|
|
8259
|
+
var commands = [
|
|
8260
|
+
genDbSchemaCommand,
|
|
8261
|
+
syncCommand,
|
|
8262
|
+
upgradeCommand,
|
|
8263
|
+
actionPluginCommandGroup,
|
|
8264
|
+
capabilityCommandGroup,
|
|
8265
|
+
componentCommandGroup,
|
|
8266
|
+
migrationCommand,
|
|
8267
|
+
readLogsCommand,
|
|
8268
|
+
buildCommandGroup
|
|
8269
|
+
];
|
|
8270
|
+
|
|
8271
|
+
// src/index.ts
|
|
8272
|
+
for (const filename of [".env.local", ".env"]) {
|
|
8273
|
+
const envPath = path25.join(process.cwd(), filename);
|
|
8274
|
+
if (fs29.existsSync(envPath)) {
|
|
8275
|
+
dotenvConfig({ path: envPath });
|
|
8802
8276
|
}
|
|
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;
|
|
8944
|
-
var PREVIEW_ARTIFACT_PROTOCOL_VERSION = 3;
|
|
8945
|
-
var MANIFEST_FILE = "manifest.json";
|
|
8946
|
-
var PROBE_FILE = "probe.json";
|
|
8947
|
-
var SERVER_FILE = "server.bundle.cjs";
|
|
8948
|
-
var CLIENT_FILE = "client.zip";
|
|
8949
|
-
var RUNTIME_ONLY_ENVIRONMENT_KEYS = /* @__PURE__ */ new Set([
|
|
8950
|
-
"CLIENT_DEV_HOST",
|
|
8951
|
-
"CLIENT_DEV_PORT",
|
|
8952
|
-
"FORCE_AUTHN_ACCESS_KEY",
|
|
8953
|
-
"FORCE_AUTHN_ACCESS_SECRET",
|
|
8954
|
-
"FORCE_AUTHN_PREVIEW_SESSION_ID",
|
|
8955
|
-
"MIAODA_HMR_WS_TOKEN",
|
|
8956
|
-
"MIAODA_PREVIEW_RUN_ID",
|
|
8957
|
-
"PREVIEW_RUN_ID",
|
|
8958
|
-
"SANDBOX_COOKIE",
|
|
8959
|
-
"SANDBOX_ID",
|
|
8960
|
-
"SERVER_PORT",
|
|
8961
|
-
"preview_run_id"
|
|
8962
|
-
]);
|
|
8963
|
-
var RUNTIME_ONLY_ENVIRONMENT_PREFIXES = [
|
|
8964
|
-
"MIAODA_PREVIEW_PHASE_",
|
|
8965
|
-
"PREVIEW_ARTIFACT_"
|
|
8966
|
-
];
|
|
8967
|
-
function previewArtifactBuildEnvironment(environment, appBasePath) {
|
|
8968
|
-
const buildEnvironment = {};
|
|
8969
|
-
for (const [key, value] of Object.entries(environment ?? {})) {
|
|
8970
|
-
if (typeof value === "string" && !RUNTIME_ONLY_ENVIRONMENT_KEYS.has(key) && !RUNTIME_ONLY_ENVIRONMENT_PREFIXES.some((prefix) => key.startsWith(prefix))) {
|
|
8971
|
-
buildEnvironment[key] = value;
|
|
8972
|
-
}
|
|
8973
|
-
}
|
|
8974
|
-
buildEnvironment.ASSETS_CDN_PATH = appBasePath;
|
|
8975
|
-
buildEnvironment.CLIENT_BASE_PATH = appBasePath;
|
|
8976
|
-
buildEnvironment.MIAODA_BUILD_TARGET = "preview-artifact";
|
|
8977
|
-
buildEnvironment.NODE_ENV = "development";
|
|
8978
|
-
return buildEnvironment;
|
|
8979
|
-
}
|
|
8980
|
-
function previewArtifactBuildEnvironmentHash(environment, appBasePath) {
|
|
8981
|
-
const buildEnvironment = previewArtifactBuildEnvironment(
|
|
8982
|
-
environment,
|
|
8983
|
-
appBasePath
|
|
8984
|
-
);
|
|
8985
|
-
const canonical = Object.fromEntries(
|
|
8986
|
-
Object.entries(buildEnvironment).sort(
|
|
8987
|
-
([left], [right]) => left.localeCompare(right)
|
|
8988
|
-
)
|
|
8989
|
-
);
|
|
8990
|
-
return crypto2.createHash("sha256").update(JSON.stringify(canonical)).digest("hex");
|
|
8991
|
-
}
|
|
8992
|
-
function parseNodeMajor(nodeVersion) {
|
|
8993
|
-
const match = /^v?(\d+)(?:\.|$)/.exec(nodeVersion.trim());
|
|
8994
|
-
if (!match) {
|
|
8995
|
-
throw new Error(`invalid Node.js version: ${nodeVersion}`);
|
|
8996
|
-
}
|
|
8997
|
-
return Number(match[1]);
|
|
8998
|
-
}
|
|
8999
|
-
function sha256File(filePath) {
|
|
9000
|
-
const hash = crypto2.createHash("sha256");
|
|
9001
|
-
hash.update(fs31.readFileSync(filePath));
|
|
9002
|
-
return hash.digest("hex");
|
|
9003
|
-
}
|
|
9004
|
-
function fileIdentity(generationDir, relativePath) {
|
|
9005
|
-
const filePath = path27.join(generationDir, relativePath);
|
|
9006
|
-
const stat = fs31.lstatSync(filePath);
|
|
9007
|
-
if (!stat.isFile() || stat.isSymbolicLink()) {
|
|
9008
|
-
throw new Error(`artifact file must be a regular file: ${relativePath}`);
|
|
9009
|
-
}
|
|
9010
|
-
return {
|
|
9011
|
-
path: relativePath,
|
|
9012
|
-
bytes: stat.size,
|
|
9013
|
-
sha256: sha256File(filePath)
|
|
9014
|
-
};
|
|
9015
|
-
}
|
|
9016
|
-
function readJson2(filePath) {
|
|
9017
|
-
return JSON.parse(fs31.readFileSync(filePath, "utf8"));
|
|
9018
|
-
}
|
|
9019
|
-
function writeJsonAtomic2(filePath, value) {
|
|
9020
|
-
fs31.mkdirSync(path27.dirname(filePath), { recursive: true });
|
|
9021
|
-
const temporaryPath = `${filePath}.${process.pid}.${Date.now()}.tmp`;
|
|
9022
|
-
fs31.writeFileSync(temporaryPath, `${JSON.stringify(value, null, 2)}
|
|
9023
|
-
`, {
|
|
9024
|
-
mode: 384
|
|
9025
|
-
});
|
|
9026
|
-
fs31.renameSync(temporaryPath, filePath);
|
|
9027
|
-
}
|
|
9028
|
-
function runtimeIdentity(nodeVersion, platform, arch) {
|
|
9029
|
-
return { nodeMajor: parseNodeMajor(nodeVersion), platform, arch };
|
|
9030
|
-
}
|
|
9031
|
-
function sameRuntime(left, right) {
|
|
9032
|
-
return left.nodeMajor === right.nodeMajor && left.platform === right.platform && left.arch === right.arch;
|
|
9033
|
-
}
|
|
9034
|
-
function createPreviewArtifactManifest(options) {
|
|
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
|
-
}
|
|
9042
|
-
const manifest = {
|
|
9043
|
-
schemaVersion: PREVIEW_ARTIFACT_SCHEMA_VERSION,
|
|
9044
|
-
protocolVersion: PREVIEW_ARTIFACT_PROTOCOL_VERSION,
|
|
9045
|
-
generationId: options.generationId,
|
|
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 },
|
|
9052
|
-
buildEnvironmentSha256: options.buildEnvironmentSha256,
|
|
9053
|
-
runtime: runtimeIdentity(
|
|
9054
|
-
options.nodeVersion,
|
|
9055
|
-
options.platform,
|
|
9056
|
-
options.arch
|
|
9057
|
-
),
|
|
9058
|
-
files: {
|
|
9059
|
-
server: fileIdentity(generationDir, SERVER_FILE),
|
|
9060
|
-
client: fileIdentity(generationDir, CLIENT_FILE)
|
|
9061
|
-
},
|
|
9062
|
-
actionPlugins: Array.from(new Set(options.actionPlugins ?? [])).sort(),
|
|
9063
|
-
runtimeProbeStatus: "not-run",
|
|
9064
|
-
consumable: false
|
|
9065
|
-
};
|
|
9066
|
-
writeJsonAtomic2(path27.join(generationDir, MANIFEST_FILE), manifest);
|
|
9067
|
-
fs31.rmSync(path27.join(generationDir, PROBE_FILE), { force: true });
|
|
9068
|
-
return manifest;
|
|
9069
|
-
}
|
|
9070
|
-
function recordPreviewArtifactProbe(options) {
|
|
9071
|
-
const generationDir = path27.resolve(options.generationDir);
|
|
9072
|
-
const manifest = readJson2(
|
|
9073
|
-
path27.join(generationDir, MANIFEST_FILE)
|
|
9074
|
-
);
|
|
9075
|
-
const probe = {
|
|
9076
|
-
schemaVersion: PREVIEW_ARTIFACT_SCHEMA_VERSION,
|
|
9077
|
-
generationId: manifest.generationId,
|
|
9078
|
-
completedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
9079
|
-
success: options.success,
|
|
9080
|
-
reasons: options.reasons ?? [],
|
|
9081
|
-
runtime: runtimeIdentity(
|
|
9082
|
-
options.nodeVersion,
|
|
9083
|
-
options.platform,
|
|
9084
|
-
options.arch
|
|
9085
|
-
),
|
|
9086
|
-
serverSha256: sha256File(path27.join(generationDir, SERVER_FILE)),
|
|
9087
|
-
clientSha256: sha256File(path27.join(generationDir, CLIENT_FILE)),
|
|
9088
|
-
coverage: {
|
|
9089
|
-
html: options.coverage.html,
|
|
9090
|
-
businessApi: options.coverage.businessApi,
|
|
9091
|
-
clientAssets: Array.from(new Set(options.coverage.clientAssets)).sort(),
|
|
9092
|
-
actionPlugins: Array.from(new Set(options.coverage.actionPlugins)).sort()
|
|
9093
|
-
}
|
|
9094
|
-
};
|
|
9095
|
-
writeJsonAtomic2(path27.join(generationDir, PROBE_FILE), probe);
|
|
9096
|
-
return probe;
|
|
9097
|
-
}
|
|
9098
|
-
function promotePreviewArtifactGeneration(generationDirInput) {
|
|
9099
|
-
const generationDir = path27.resolve(generationDirInput);
|
|
9100
|
-
const reasons = [];
|
|
9101
|
-
let manifest;
|
|
9102
|
-
let probe;
|
|
9103
|
-
try {
|
|
9104
|
-
manifest = readJson2(
|
|
9105
|
-
path27.join(generationDir, MANIFEST_FILE)
|
|
9106
|
-
);
|
|
9107
|
-
probe = readJson2(
|
|
9108
|
-
path27.join(generationDir, PROBE_FILE)
|
|
9109
|
-
);
|
|
9110
|
-
} catch (error) {
|
|
9111
|
-
return {
|
|
9112
|
-
promoted: false,
|
|
9113
|
-
reasons: [
|
|
9114
|
-
`probe evidence missing or invalid: ${error instanceof Error ? error.message : String(error)}`
|
|
9115
|
-
]
|
|
9116
|
-
};
|
|
9117
|
-
}
|
|
9118
|
-
if (manifest.schemaVersion !== PREVIEW_ARTIFACT_SCHEMA_VERSION || probe.schemaVersion !== PREVIEW_ARTIFACT_SCHEMA_VERSION) {
|
|
9119
|
-
reasons.push("schema version mismatch");
|
|
9120
|
-
}
|
|
9121
|
-
if (probe.generationId !== manifest.generationId) {
|
|
9122
|
-
reasons.push("probe generation identity mismatch");
|
|
9123
|
-
}
|
|
9124
|
-
if (!probe.success) {
|
|
9125
|
-
reasons.push(`probe failed: ${probe.reasons.join("; ") || "unknown"}`);
|
|
9126
|
-
}
|
|
9127
|
-
if (!sameRuntime(manifest.runtime, probe.runtime)) {
|
|
9128
|
-
reasons.push(
|
|
9129
|
-
"probe runtime identity does not match build runtime identity"
|
|
9130
|
-
);
|
|
9131
|
-
}
|
|
9132
|
-
const currentServerHash = sha256File(path27.join(generationDir, SERVER_FILE));
|
|
9133
|
-
const currentClientHash = sha256File(path27.join(generationDir, CLIENT_FILE));
|
|
9134
|
-
if (currentServerHash !== manifest.files.server.sha256 || currentServerHash !== probe.serverSha256) {
|
|
9135
|
-
reasons.push("server.bundle.cjs hash does not match probe identity");
|
|
9136
|
-
}
|
|
9137
|
-
if (currentClientHash !== manifest.files.client.sha256 || currentClientHash !== probe.clientSha256) {
|
|
9138
|
-
reasons.push("client.zip hash does not match probe identity");
|
|
9139
|
-
}
|
|
9140
|
-
if (!probe.coverage.html) {
|
|
9141
|
-
reasons.push("probe did not verify HTML");
|
|
9142
|
-
}
|
|
9143
|
-
if (!probe.coverage.businessApi) {
|
|
9144
|
-
reasons.push("probe did not verify business API");
|
|
9145
|
-
}
|
|
9146
|
-
if (!Array.isArray(probe.coverage.clientAssets) || probe.coverage.clientAssets.length === 0) {
|
|
9147
|
-
reasons.push("probe did not verify client entry assets");
|
|
9148
|
-
}
|
|
9149
|
-
const verifiedPlugins = new Set(probe.coverage.actionPlugins);
|
|
9150
|
-
const missingPlugins = manifest.actionPlugins.filter(
|
|
9151
|
-
(plugin) => !verifiedPlugins.has(plugin)
|
|
9152
|
-
);
|
|
9153
|
-
if (missingPlugins.length > 0) {
|
|
9154
|
-
reasons.push(
|
|
9155
|
-
`probe did not verify Action Plugin: ${missingPlugins.join(", ")}`
|
|
9156
|
-
);
|
|
9157
|
-
}
|
|
9158
|
-
if (reasons.length > 0) {
|
|
9159
|
-
return { promoted: false, reasons };
|
|
9160
|
-
}
|
|
9161
|
-
const promotedManifest = {
|
|
9162
|
-
...manifest,
|
|
9163
|
-
runtimeProbeStatus: "passed",
|
|
9164
|
-
consumable: true
|
|
9165
|
-
};
|
|
9166
|
-
writeJsonAtomic2(path27.join(generationDir, MANIFEST_FILE), promotedManifest);
|
|
9167
|
-
return { promoted: true, manifest: promotedManifest };
|
|
9168
|
-
}
|
|
9169
|
-
|
|
9170
|
-
// src/commands/preview-artifact/probe.ts
|
|
9171
|
-
import * as fs32 from "fs";
|
|
9172
|
-
import * as net from "net";
|
|
9173
|
-
import * as os5 from "os";
|
|
9174
|
-
import * as path28 from "path";
|
|
9175
|
-
import { spawn as spawn2, spawnSync as spawnSync8 } from "child_process";
|
|
9176
|
-
|
|
9177
|
-
// src/commands/preview-artifact/readiness.ts
|
|
9178
|
-
function isPreviewBusinessResponseReady(response) {
|
|
9179
|
-
if (response.status === 401 || response.status === 403) return true;
|
|
9180
|
-
return response.status !== 404 && response.status < 500 && !response.headers.get("content-type")?.includes("text/html");
|
|
9181
|
-
}
|
|
9182
|
-
|
|
9183
|
-
// src/commands/preview-artifact/probe.ts
|
|
9184
|
-
function sleep(ms) {
|
|
9185
|
-
return new Promise((resolve10) => setTimeout(resolve10, ms));
|
|
9186
|
-
}
|
|
9187
|
-
async function reservePort() {
|
|
9188
|
-
return new Promise((resolve10, reject) => {
|
|
9189
|
-
const server = net.createServer();
|
|
9190
|
-
server.unref();
|
|
9191
|
-
server.once("error", reject);
|
|
9192
|
-
server.listen(0, "127.0.0.1", () => {
|
|
9193
|
-
const address = server.address();
|
|
9194
|
-
if (!address || typeof address === "string") {
|
|
9195
|
-
server.close(() => reject(new Error("unable to reserve probe port")));
|
|
9196
|
-
return;
|
|
9197
|
-
}
|
|
9198
|
-
server.close((error) => {
|
|
9199
|
-
if (error) reject(error);
|
|
9200
|
-
else resolve10(address.port);
|
|
9201
|
-
});
|
|
9202
|
-
});
|
|
9203
|
-
});
|
|
9204
|
-
}
|
|
9205
|
-
function archiveEntries(archivePath) {
|
|
9206
|
-
const listing = spawnSync8("unzip", ["-Z1", archivePath], {
|
|
9207
|
-
encoding: "utf8"
|
|
9208
|
-
});
|
|
9209
|
-
if (listing.status !== 0) {
|
|
9210
|
-
throw new Error(`client archive listing failed: ${listing.stderr.trim()}`);
|
|
9211
|
-
}
|
|
9212
|
-
const entries = listing.stdout.split("\n").filter(Boolean);
|
|
9213
|
-
for (const entry of entries) {
|
|
9214
|
-
const normalized = path28.posix.normalize(entry.replaceAll("\\", "/"));
|
|
9215
|
-
if (path28.posix.isAbsolute(normalized) || normalized === ".." || normalized.startsWith("../")) {
|
|
9216
|
-
throw new Error(`client archive contains unsafe path: ${entry}`);
|
|
9217
|
-
}
|
|
9218
|
-
}
|
|
9219
|
-
return entries;
|
|
9220
|
-
}
|
|
9221
|
-
function rejectArchivedSymlinks(archivePath) {
|
|
9222
|
-
const listing = spawnSync8("unzip", ["-Z", "-l", archivePath], {
|
|
9223
|
-
encoding: "utf8"
|
|
9224
|
-
});
|
|
9225
|
-
if (listing.status !== 0) {
|
|
9226
|
-
throw new Error(
|
|
9227
|
-
`client archive metadata listing failed: ${listing.stderr.trim()}`
|
|
9228
|
-
);
|
|
9229
|
-
}
|
|
9230
|
-
for (const line of listing.stdout.split("\n")) {
|
|
9231
|
-
if (/^l[rwxstST-]{9}\s/.test(line.trimStart())) {
|
|
9232
|
-
throw new Error("client archive contains symlink");
|
|
9233
|
-
}
|
|
9234
|
-
}
|
|
9235
|
-
}
|
|
9236
|
-
function rejectSymlinks(root) {
|
|
9237
|
-
const pending = [root];
|
|
9238
|
-
while (pending.length > 0) {
|
|
9239
|
-
const current = pending.pop();
|
|
9240
|
-
if (!current) continue;
|
|
9241
|
-
for (const entry of fs32.readdirSync(current, { withFileTypes: true })) {
|
|
9242
|
-
const entryPath = path28.join(current, entry.name);
|
|
9243
|
-
const stat = fs32.lstatSync(entryPath);
|
|
9244
|
-
if (stat.isSymbolicLink()) {
|
|
9245
|
-
throw new Error(`client archive contains symlink: ${entry.name}`);
|
|
9246
|
-
}
|
|
9247
|
-
if (stat.isDirectory()) pending.push(entryPath);
|
|
9248
|
-
}
|
|
9249
|
-
}
|
|
9250
|
-
}
|
|
9251
|
-
function inspectClientArchive(archivePath) {
|
|
9252
|
-
archiveEntries(archivePath);
|
|
9253
|
-
rejectArchivedSymlinks(archivePath);
|
|
9254
|
-
}
|
|
9255
|
-
function isPathInside(parent, candidate) {
|
|
9256
|
-
const relative5 = path28.relative(parent, candidate);
|
|
9257
|
-
return relative5 !== "" && relative5 !== ".." && !relative5.startsWith(`..${path28.sep}`) && !path28.isAbsolute(relative5);
|
|
9258
|
-
}
|
|
9259
|
-
function clientEntryAssetReferences(html, appBasePath) {
|
|
9260
|
-
const local = /* @__PURE__ */ new Set();
|
|
9261
|
-
const localExecutable = /* @__PURE__ */ new Set();
|
|
9262
|
-
const unsupportedExternal = /* @__PURE__ */ new Set();
|
|
9263
|
-
const tags = /<(script|link)\b[^>]*>/gi;
|
|
9264
|
-
const readAttribute = (tag, name) => new RegExp(`\\b${name}=["']([^"']+)["']`, "i").exec(tag)?.[1];
|
|
9265
|
-
const base = new URL(appBasePath, "http://preview.local");
|
|
9266
|
-
for (const match of html.matchAll(tags)) {
|
|
9267
|
-
const tagName = match[1]?.toLowerCase();
|
|
9268
|
-
const tag = match[0];
|
|
9269
|
-
let linkRel = [];
|
|
9270
|
-
if (tagName === "link") {
|
|
9271
|
-
linkRel = readAttribute(tag, "rel")?.toLowerCase().split(/\s+/) ?? [];
|
|
9272
|
-
if (!linkRel.some(
|
|
9273
|
-
(value2) => [
|
|
9274
|
-
"stylesheet",
|
|
9275
|
-
"modulepreload",
|
|
9276
|
-
"preload",
|
|
9277
|
-
"icon",
|
|
9278
|
-
"manifest"
|
|
9279
|
-
].includes(value2)
|
|
9280
|
-
)) {
|
|
9281
|
-
continue;
|
|
9282
|
-
}
|
|
9283
|
-
}
|
|
9284
|
-
const value = readAttribute(
|
|
9285
|
-
tag,
|
|
9286
|
-
tagName === "script" ? "src" : "href"
|
|
9287
|
-
)?.trim();
|
|
9288
|
-
if (!value || value.startsWith("data:") || value.startsWith("#")) continue;
|
|
9289
|
-
const resolved = new URL(value, base);
|
|
9290
|
-
if (resolved.origin !== base.origin) {
|
|
9291
|
-
if (tagName === "script" || linkRel.some(
|
|
9292
|
-
(value2) => ["stylesheet", "modulepreload", "preload"].includes(value2)
|
|
9293
|
-
)) {
|
|
9294
|
-
unsupportedExternal.add(resolved.toString());
|
|
9295
|
-
}
|
|
9296
|
-
continue;
|
|
9297
|
-
}
|
|
9298
|
-
local.add(resolved.pathname);
|
|
9299
|
-
if (tagName === "script") localExecutable.add(resolved.pathname);
|
|
9300
|
-
}
|
|
9301
|
-
return {
|
|
9302
|
-
local: Array.from(local).sort(),
|
|
9303
|
-
localExecutable: Array.from(localExecutable).sort(),
|
|
9304
|
-
unsupportedExternal: Array.from(unsupportedExternal).sort()
|
|
9305
|
-
};
|
|
9306
|
-
}
|
|
9307
|
-
function resolveClientEntryAsset(clientRoot, appBasePath, requestPath) {
|
|
9308
|
-
let decoded;
|
|
9309
|
-
try {
|
|
9310
|
-
decoded = decodeURIComponent(requestPath);
|
|
9311
|
-
} catch {
|
|
9312
|
-
return void 0;
|
|
9313
|
-
}
|
|
9314
|
-
const normalizedBase = appBasePath.endsWith("/") ? appBasePath : `${appBasePath}/`;
|
|
9315
|
-
const relative5 = decoded.startsWith(normalizedBase) ? decoded.slice(normalizedBase.length) : decoded.replace(/^\/+/, "");
|
|
9316
|
-
if (!relative5 || relative5 === "index.html") return void 0;
|
|
9317
|
-
const candidate = path28.resolve(clientRoot, relative5);
|
|
9318
|
-
if (!isPathInside(clientRoot, candidate)) return void 0;
|
|
9319
|
-
try {
|
|
9320
|
-
const stat = fs32.lstatSync(candidate);
|
|
9321
|
-
return stat.isFile() && !stat.isSymbolicLink() ? candidate : void 0;
|
|
9322
|
-
} catch {
|
|
9323
|
-
return void 0;
|
|
9324
|
-
}
|
|
9325
|
-
}
|
|
9326
|
-
function extractClientArchive(archivePath, destination) {
|
|
9327
|
-
if (fs32.existsSync(destination)) {
|
|
9328
|
-
throw new Error("client archive destination must not already exist");
|
|
9329
|
-
}
|
|
9330
|
-
inspectClientArchive(archivePath);
|
|
9331
|
-
fs32.mkdirSync(destination, { recursive: true });
|
|
9332
|
-
try {
|
|
9333
|
-
const extraction = spawnSync8(
|
|
9334
|
-
"unzip",
|
|
9335
|
-
["-q", archivePath, "-d", destination],
|
|
9336
|
-
{ encoding: "utf8" }
|
|
9337
|
-
);
|
|
9338
|
-
if (extraction.status !== 0) {
|
|
9339
|
-
throw new Error(
|
|
9340
|
-
`client archive extraction failed: ${extraction.stderr.trim()}`
|
|
9341
|
-
);
|
|
9342
|
-
}
|
|
9343
|
-
rejectSymlinks(destination);
|
|
9344
|
-
} catch (error) {
|
|
9345
|
-
fs32.rmSync(destination, { recursive: true, force: true });
|
|
9346
|
-
throw error;
|
|
9347
|
-
}
|
|
9348
|
-
}
|
|
9349
|
-
async function extractInspectedClientArchive(archivePath, destination) {
|
|
9350
|
-
if (fs32.existsSync(destination)) {
|
|
9351
|
-
throw new Error("client archive destination must not already exist");
|
|
9352
|
-
}
|
|
9353
|
-
fs32.mkdirSync(destination, { recursive: true });
|
|
9354
|
-
try {
|
|
9355
|
-
await new Promise((resolve10, reject) => {
|
|
9356
|
-
const extraction = spawn2(
|
|
9357
|
-
"unzip",
|
|
9358
|
-
["-q", archivePath, "-d", destination],
|
|
9359
|
-
{ stdio: ["ignore", "ignore", "pipe"] }
|
|
9360
|
-
);
|
|
9361
|
-
let stderr = "";
|
|
9362
|
-
extraction.stderr?.setEncoding("utf8");
|
|
9363
|
-
extraction.stderr?.on("data", (chunk) => {
|
|
9364
|
-
stderr = `${stderr}${String(chunk)}`.slice(-8 * 1024);
|
|
9365
|
-
});
|
|
9366
|
-
extraction.once("error", reject);
|
|
9367
|
-
extraction.once("close", (code) => {
|
|
9368
|
-
if (code === 0) resolve10();
|
|
9369
|
-
else
|
|
9370
|
-
reject(
|
|
9371
|
-
new Error(
|
|
9372
|
-
`client archive extraction failed: ${stderr.trim() || `exit ${code ?? "unknown"}`}`
|
|
9373
|
-
)
|
|
9374
|
-
);
|
|
9375
|
-
});
|
|
9376
|
-
});
|
|
9377
|
-
rejectSymlinks(destination);
|
|
9378
|
-
} catch (error) {
|
|
9379
|
-
fs32.rmSync(destination, { recursive: true, force: true });
|
|
9380
|
-
throw error;
|
|
9381
|
-
}
|
|
9382
|
-
}
|
|
9383
|
-
async function fetchUntil(url, timeoutAt) {
|
|
9384
|
-
while (Date.now() < timeoutAt) {
|
|
9385
|
-
try {
|
|
9386
|
-
return await fetch(url, {
|
|
9387
|
-
redirect: "manual",
|
|
9388
|
-
signal: AbortSignal.timeout(1e3)
|
|
9389
|
-
});
|
|
9390
|
-
} catch {
|
|
9391
|
-
await sleep(50);
|
|
9392
|
-
}
|
|
9393
|
-
}
|
|
9394
|
-
return null;
|
|
9395
|
-
}
|
|
9396
|
-
function stopProcessGroup(pid) {
|
|
9397
|
-
if (!pid) return;
|
|
9398
|
-
try {
|
|
9399
|
-
process.kill(-pid, "SIGTERM");
|
|
9400
|
-
} catch {
|
|
9401
|
-
try {
|
|
9402
|
-
process.kill(pid, "SIGTERM");
|
|
9403
|
-
} catch {
|
|
9404
|
-
return;
|
|
9405
|
-
}
|
|
9406
|
-
}
|
|
9407
|
-
}
|
|
9408
|
-
async function runPreviewArtifactProbe(options) {
|
|
9409
|
-
const generationDir = path28.resolve(options.generationDir);
|
|
9410
|
-
const runtimeRoot = fs32.mkdtempSync(
|
|
9411
|
-
path28.join(os5.tmpdir(), "preview-artifact-probe-")
|
|
9412
|
-
);
|
|
9413
|
-
const coverage = {
|
|
9414
|
-
html: false,
|
|
9415
|
-
businessApi: false,
|
|
9416
|
-
clientAssets: [],
|
|
9417
|
-
actionPlugins: []
|
|
9418
|
-
};
|
|
9419
|
-
const reasons = [];
|
|
9420
|
-
const registryFile = path28.join(runtimeRoot, "plugin-registry.json");
|
|
9421
|
-
let isolatedNodeModulesPresent = false;
|
|
9422
|
-
let child;
|
|
9423
|
-
try {
|
|
9424
|
-
const runtimeBundle = path28.join(runtimeRoot, "server.bundle.cjs");
|
|
9425
|
-
fs32.copyFileSync(
|
|
9426
|
-
path28.join(generationDir, "server.bundle.cjs"),
|
|
9427
|
-
runtimeBundle
|
|
9428
|
-
);
|
|
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")
|
|
9433
|
-
);
|
|
9434
|
-
if (isolatedNodeModulesPresent) {
|
|
9435
|
-
reasons.push("isolated runtime unexpectedly contains node_modules");
|
|
9436
|
-
}
|
|
9437
|
-
const port = await reservePort();
|
|
9438
|
-
const timeoutAt = Date.now() + (options.timeoutMs ?? 12e4);
|
|
9439
|
-
child = spawn2(process.execPath, [runtimeBundle], {
|
|
9440
|
-
cwd: runtimeRoot,
|
|
9441
|
-
detached: true,
|
|
9442
|
-
stdio: "ignore",
|
|
9443
|
-
env: {
|
|
9444
|
-
...process.env,
|
|
9445
|
-
...options.environment,
|
|
9446
|
-
SERVER_PORT: String(port),
|
|
9447
|
-
PREVIEW_ARTIFACT_PROBE_REGISTRY_FILE: registryFile
|
|
9448
|
-
}
|
|
9449
|
-
});
|
|
9450
|
-
const origin = `http://127.0.0.1:${port}`;
|
|
9451
|
-
const htmlResponse = await fetchUntil(
|
|
9452
|
-
new URL(options.appBasePath, origin).toString(),
|
|
9453
|
-
timeoutAt
|
|
9454
|
-
);
|
|
9455
|
-
coverage.html = htmlResponse?.status === 200;
|
|
9456
|
-
if (!coverage.html) {
|
|
9457
|
-
reasons.push(
|
|
9458
|
-
`HTML probe failed with status ${htmlResponse?.status ?? "unreachable"}`
|
|
9459
|
-
);
|
|
9460
|
-
}
|
|
9461
|
-
if (coverage.html && htmlResponse) {
|
|
9462
|
-
const html = await htmlResponse.text();
|
|
9463
|
-
const entryReferences = clientEntryAssetReferences(
|
|
9464
|
-
html,
|
|
9465
|
-
options.appBasePath
|
|
9466
|
-
);
|
|
9467
|
-
const entryAssets = entryReferences.local;
|
|
9468
|
-
coverage.clientAssets = entryReferences.localExecutable;
|
|
9469
|
-
if (entryReferences.localExecutable.length === 0) {
|
|
9470
|
-
reasons.push("client HTML has no local executable entry asset");
|
|
9471
|
-
for (const externalAsset of entryReferences.unsupportedExternal) {
|
|
9472
|
-
reasons.push(
|
|
9473
|
-
`external client entry asset is unsupported: ${externalAsset}`
|
|
9474
|
-
);
|
|
9475
|
-
}
|
|
9476
|
-
}
|
|
9477
|
-
for (const entryAsset of entryAssets) {
|
|
9478
|
-
if (!resolveClientEntryAsset(clientRoot, options.appBasePath, entryAsset)) {
|
|
9479
|
-
reasons.push(`client entry asset is unavailable: ${entryAsset}`);
|
|
9480
|
-
}
|
|
9481
|
-
}
|
|
9482
|
-
}
|
|
9483
|
-
const apiResponse = await fetchUntil(
|
|
9484
|
-
new URL(options.businessApiPath, origin).toString(),
|
|
9485
|
-
timeoutAt
|
|
9486
|
-
);
|
|
9487
|
-
coverage.businessApi = Boolean(
|
|
9488
|
-
apiResponse && isPreviewBusinessResponseReady(apiResponse)
|
|
9489
|
-
);
|
|
9490
|
-
if (!coverage.businessApi) {
|
|
9491
|
-
reasons.push(
|
|
9492
|
-
`business API returned ${apiResponse?.status ?? "unreachable"}`
|
|
9493
|
-
);
|
|
9494
|
-
}
|
|
9495
|
-
while (!fs32.existsSync(registryFile) && Date.now() < timeoutAt) {
|
|
9496
|
-
await sleep(25);
|
|
9497
|
-
}
|
|
9498
|
-
if (fs32.existsSync(registryFile)) {
|
|
9499
|
-
const parsed = JSON.parse(
|
|
9500
|
-
fs32.readFileSync(registryFile, "utf8")
|
|
9501
|
-
);
|
|
9502
|
-
if (Array.isArray(parsed) && parsed.every((value) => typeof value === "string")) {
|
|
9503
|
-
coverage.actionPlugins = parsed.filter((value) => typeof value === "string").sort();
|
|
9504
|
-
} else {
|
|
9505
|
-
reasons.push("Action Plugin registry evidence is invalid");
|
|
9506
|
-
}
|
|
9507
|
-
} else {
|
|
9508
|
-
reasons.push("Action Plugin registry evidence is unavailable");
|
|
9509
|
-
}
|
|
9510
|
-
} catch (error) {
|
|
9511
|
-
reasons.push(error instanceof Error ? error.message : String(error));
|
|
9512
|
-
} finally {
|
|
9513
|
-
stopProcessGroup(child?.pid);
|
|
9514
|
-
}
|
|
9515
|
-
const probe = recordPreviewArtifactProbe({
|
|
9516
|
-
generationDir,
|
|
9517
|
-
success: reasons.length === 0,
|
|
9518
|
-
reasons,
|
|
9519
|
-
nodeVersion: process.version,
|
|
9520
|
-
platform: process.platform,
|
|
9521
|
-
arch: process.arch,
|
|
9522
|
-
coverage
|
|
9523
|
-
});
|
|
9524
|
-
fs32.rmSync(runtimeRoot, { recursive: true, force: true });
|
|
9525
|
-
return { ...probe, isolatedNodeModulesPresent };
|
|
9526
|
-
}
|
|
9527
|
-
|
|
9528
|
-
// src/commands/preview-artifact/environment.ts
|
|
9529
|
-
import * as fs33 from "fs";
|
|
9530
|
-
import * as path29 from "path";
|
|
9531
|
-
var ENVIRONMENT_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
9532
|
-
function readPreviewArtifactEnvironmentFile(filePath) {
|
|
9533
|
-
const resolved = path29.resolve(filePath);
|
|
9534
|
-
const parsed = JSON.parse(fs33.readFileSync(resolved, "utf8"));
|
|
9535
|
-
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
9536
|
-
throw new Error("preview artifact environment file must contain an object");
|
|
9537
|
-
}
|
|
9538
|
-
const environment = {};
|
|
9539
|
-
for (const [key, value] of Object.entries(parsed)) {
|
|
9540
|
-
if (!ENVIRONMENT_NAME.test(key) || typeof value !== "string") {
|
|
9541
|
-
throw new Error(`invalid environment variable in ${resolved}: ${key}`);
|
|
9542
|
-
}
|
|
9543
|
-
environment[key] = value;
|
|
9544
|
-
}
|
|
9545
|
-
return environment;
|
|
9546
|
-
}
|
|
9547
|
-
function previewArtifactRuntimeEnvironment(environment) {
|
|
9548
|
-
return {
|
|
9549
|
-
...environment,
|
|
9550
|
-
NODE_ENV: "development",
|
|
9551
|
-
// Probe and switcher connect through 127.0.0.1. Linux may resolve
|
|
9552
|
-
// `localhost` to IPv6-only ::1, so the isolated Nest process must bind to
|
|
9553
|
-
// the same explicit private endpoint instead of inheriting app defaults.
|
|
9554
|
-
SERVER_HOST: "127.0.0.1"
|
|
9555
|
-
};
|
|
9556
|
-
}
|
|
9557
|
-
|
|
9558
|
-
// src/commands/preview-artifact/store.ts
|
|
9559
|
-
import * as crypto3 from "crypto";
|
|
9560
|
-
import * as fs34 from "fs";
|
|
9561
|
-
import * as path30 from "path";
|
|
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}$/;
|
|
9565
|
-
function readCurrentPointer(storeRoot) {
|
|
9566
|
-
try {
|
|
9567
|
-
const pointer = readJson3(
|
|
9568
|
-
path30.join(storeRoot, "current.json")
|
|
9569
|
-
);
|
|
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;
|
|
9571
|
-
} catch {
|
|
9572
|
-
return void 0;
|
|
9573
|
-
}
|
|
9574
|
-
}
|
|
9575
|
-
function readJson3(filePath) {
|
|
9576
|
-
return JSON.parse(fs34.readFileSync(filePath, "utf8"));
|
|
9577
|
-
}
|
|
9578
|
-
function writeJsonAtomic3(filePath, value) {
|
|
9579
|
-
fs34.mkdirSync(path30.dirname(filePath), { recursive: true });
|
|
9580
|
-
const temporaryPath = `${filePath}.${process.pid}.${Date.now()}.tmp`;
|
|
9581
|
-
fs34.writeFileSync(temporaryPath, `${JSON.stringify(value, null, 2)}
|
|
9582
|
-
`, {
|
|
9583
|
-
mode: 384
|
|
9584
|
-
});
|
|
9585
|
-
fs34.renameSync(temporaryPath, filePath);
|
|
9586
|
-
}
|
|
9587
|
-
function pruneSupersededGenerations(generationsRoot, retainedGenerationIds) {
|
|
9588
|
-
const retained = new Set(retainedGenerationIds);
|
|
9589
|
-
for (const entry of fs34.readdirSync(generationsRoot, {
|
|
9590
|
-
withFileTypes: true
|
|
9591
|
-
})) {
|
|
9592
|
-
if (retained.has(entry.name)) continue;
|
|
9593
|
-
try {
|
|
9594
|
-
fs34.rmSync(path30.join(generationsRoot, entry.name), {
|
|
9595
|
-
recursive: true,
|
|
9596
|
-
force: true
|
|
9597
|
-
});
|
|
9598
|
-
} catch {
|
|
9599
|
-
}
|
|
9600
|
-
}
|
|
9601
|
-
}
|
|
9602
|
-
function parseNodeMajor2(nodeVersion) {
|
|
9603
|
-
const match = /^v?(\d+)(?:\.|$)/.exec(nodeVersion.trim());
|
|
9604
|
-
return match ? Number(match[1]) : null;
|
|
9605
|
-
}
|
|
9606
|
-
function isPathInside2(parent, candidate) {
|
|
9607
|
-
const relative5 = path30.relative(parent, candidate);
|
|
9608
|
-
return relative5 !== "" && !relative5.startsWith(`..${path30.sep}`) && relative5 !== ".." && !path30.isAbsolute(relative5);
|
|
9609
|
-
}
|
|
9610
|
-
function validateRegularDirectory(directoryPath, label) {
|
|
9611
|
-
try {
|
|
9612
|
-
const stat = fs34.lstatSync(directoryPath);
|
|
9613
|
-
if (!stat.isDirectory() || stat.isSymbolicLink()) {
|
|
9614
|
-
return [`${label} must be a regular directory`];
|
|
9615
|
-
}
|
|
9616
|
-
return [];
|
|
9617
|
-
} catch (error) {
|
|
9618
|
-
return [
|
|
9619
|
-
`${label} is unavailable: ${error instanceof Error ? error.message : String(error)}`
|
|
9620
|
-
];
|
|
9621
|
-
}
|
|
9622
|
-
}
|
|
9623
|
-
function validateGeneration(generationDir, runtime, verifyPayloads = true) {
|
|
9624
|
-
const reasons = validateRegularDirectory(
|
|
9625
|
-
generationDir,
|
|
9626
|
-
"generation directory"
|
|
9627
|
-
);
|
|
9628
|
-
if (reasons.length > 0) return { reasons };
|
|
9629
|
-
let manifest;
|
|
9630
|
-
let probe;
|
|
9631
|
-
try {
|
|
9632
|
-
manifest = readJson3(
|
|
9633
|
-
path30.join(generationDir, "manifest.json")
|
|
9634
|
-
);
|
|
9635
|
-
probe = readJson3(
|
|
9636
|
-
path30.join(generationDir, "probe.json")
|
|
9637
|
-
);
|
|
9638
|
-
} catch (error) {
|
|
9639
|
-
return {
|
|
9640
|
-
reasons: [
|
|
9641
|
-
`generation metadata is invalid: ${error instanceof Error ? error.message : String(error)}`
|
|
9642
|
-
]
|
|
9643
|
-
};
|
|
9644
|
-
}
|
|
9645
|
-
if (manifest.schemaVersion !== PREVIEW_ARTIFACT_SCHEMA_VERSION || manifest.protocolVersion !== PREVIEW_ARTIFACT_PROTOCOL_VERSION || probe.schemaVersion !== PREVIEW_ARTIFACT_SCHEMA_VERSION) {
|
|
9646
|
-
reasons.push("generation schema version is incompatible");
|
|
9647
|
-
}
|
|
9648
|
-
if (!GENERATION_ID_PATTERN.test(manifest.generationId) || probe.generationId !== manifest.generationId) {
|
|
9649
|
-
reasons.push("generation ID is invalid or inconsistent");
|
|
9650
|
-
}
|
|
9651
|
-
if (!manifest.consumable || manifest.runtimeProbeStatus !== "passed") {
|
|
9652
|
-
reasons.push("generation is not consumable");
|
|
9653
|
-
}
|
|
9654
|
-
if (!probe.success) {
|
|
9655
|
-
reasons.push("generation runtime probe did not pass");
|
|
9656
|
-
}
|
|
9657
|
-
if (!Array.isArray(probe.coverage?.clientAssets) || probe.coverage.clientAssets.length === 0) {
|
|
9658
|
-
reasons.push("generation client asset evidence is invalid");
|
|
9659
|
-
}
|
|
9660
|
-
if (!/^[a-f0-9]{64}$/.test(manifest.buildEnvironmentSha256)) {
|
|
9661
|
-
reasons.push("build environment identity is invalid");
|
|
9662
|
-
}
|
|
9663
|
-
reasons.push(...validatePreviewArtifactWorkspaceIdentity(manifest));
|
|
9664
|
-
for (const [label, identity, expectedPath] of [
|
|
9665
|
-
["server", manifest.files?.server, "server.bundle.cjs"],
|
|
9666
|
-
["client", manifest.files?.client, "client.zip"]
|
|
9667
|
-
]) {
|
|
9668
|
-
if (!identity || identity.path !== expectedPath || !Number.isSafeInteger(identity.bytes) || identity.bytes < 0 || !/^[a-f0-9]{64}$/.test(identity.sha256)) {
|
|
9669
|
-
reasons.push(`${label} file identity is invalid`);
|
|
9670
|
-
}
|
|
9671
|
-
}
|
|
9672
|
-
if (verifyPayloads && manifest.files?.server && manifest.files?.client) {
|
|
9673
|
-
const serverPath = path30.join(generationDir, "server.bundle.cjs");
|
|
9674
|
-
const clientPath = path30.join(generationDir, "client.zip");
|
|
9675
|
-
try {
|
|
9676
|
-
if (sha256File(serverPath) !== manifest.files.server.sha256) {
|
|
9677
|
-
reasons.push("server.bundle.cjs hash does not match manifest");
|
|
9678
|
-
}
|
|
9679
|
-
if (sha256File(clientPath) !== manifest.files.client.sha256) {
|
|
9680
|
-
reasons.push("client.zip hash does not match manifest");
|
|
9681
|
-
}
|
|
9682
|
-
} catch (error) {
|
|
9683
|
-
reasons.push(
|
|
9684
|
-
`generation files are unavailable: ${error instanceof Error ? error.message : String(error)}`
|
|
9685
|
-
);
|
|
9686
|
-
}
|
|
9687
|
-
}
|
|
9688
|
-
if (probe.serverSha256 !== manifest.files.server.sha256 || probe.clientSha256 !== manifest.files.client.sha256) {
|
|
9689
|
-
reasons.push("probe file identity does not match manifest");
|
|
9690
|
-
}
|
|
9691
|
-
if (runtime && (runtime.nodeMajor !== manifest.runtime.nodeMajor || runtime.platform !== manifest.runtime.platform || runtime.arch !== manifest.runtime.arch)) {
|
|
9692
|
-
reasons.push("runtime identity is incompatible with generation");
|
|
9693
|
-
}
|
|
9694
|
-
return { manifest, reasons };
|
|
9695
|
-
}
|
|
9696
|
-
function publishPreviewArtifactGeneration(options) {
|
|
9697
|
-
const storeRoot = path30.resolve(options.storeRoot);
|
|
9698
|
-
const previousPointer = readCurrentPointer(storeRoot);
|
|
9699
|
-
const stagingRoot = path30.join(storeRoot, ".staging");
|
|
9700
|
-
const stagingDir = path30.resolve(options.stagingDir);
|
|
9701
|
-
if (!isPathInside2(stagingRoot, stagingDir)) {
|
|
9702
|
-
return {
|
|
9703
|
-
published: false,
|
|
9704
|
-
reasons: [
|
|
9705
|
-
"staging directory must be inside the store .staging directory"
|
|
9706
|
-
]
|
|
9707
|
-
};
|
|
9708
|
-
}
|
|
9709
|
-
const validation = validateGeneration(stagingDir);
|
|
9710
|
-
if (!validation.manifest || validation.reasons.length > 0) {
|
|
9711
|
-
return { published: false, reasons: validation.reasons };
|
|
9712
|
-
}
|
|
9713
|
-
const generationId = validation.manifest.generationId;
|
|
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)) {
|
|
9718
|
-
return {
|
|
9719
|
-
published: false,
|
|
9720
|
-
reasons: [`generation already exists: ${generationId}`]
|
|
9721
|
-
};
|
|
9722
|
-
}
|
|
9723
|
-
try {
|
|
9724
|
-
fs34.renameSync(stagingDir, generationDir);
|
|
9725
|
-
const pointer = {
|
|
9726
|
-
schemaVersion: PREVIEW_ARTIFACT_SCHEMA_VERSION,
|
|
9727
|
-
generationId,
|
|
9728
|
-
workspaceBackupId: validation.manifest.workspaceBackupId,
|
|
9729
|
-
...validation.manifest.workspaceVersion == null ? {} : { workspaceVersion: validation.manifest.workspaceVersion },
|
|
9730
|
-
publishedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
9731
|
-
};
|
|
9732
|
-
writeJsonAtomic3(path30.join(storeRoot, "current.json"), pointer);
|
|
9733
|
-
pruneSupersededGenerations(
|
|
9734
|
-
generationsRoot,
|
|
9735
|
-
[generationId, previousPointer?.generationId].filter(
|
|
9736
|
-
(value) => Boolean(value)
|
|
9737
|
-
)
|
|
9738
|
-
);
|
|
9739
|
-
return { published: true, generationId, generationDir };
|
|
9740
|
-
} catch (error) {
|
|
9741
|
-
return {
|
|
9742
|
-
published: false,
|
|
9743
|
-
reasons: [
|
|
9744
|
-
`generation publication failed: ${error instanceof Error ? error.message : String(error)}`
|
|
9745
|
-
]
|
|
9746
|
-
};
|
|
9747
|
-
}
|
|
9748
|
-
}
|
|
9749
|
-
function resolveCurrentPreviewArtifactMetadata(options) {
|
|
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);
|
|
9778
|
-
let pointer;
|
|
9779
|
-
try {
|
|
9780
|
-
pointer = readJson3(path30.join(storeRoot, "current.json"));
|
|
9781
|
-
} catch (error) {
|
|
9782
|
-
return {
|
|
9783
|
-
resolved: false,
|
|
9784
|
-
reasons: [
|
|
9785
|
-
`current pointer is unavailable: ${error instanceof Error ? error.message : String(error)}`
|
|
9786
|
-
]
|
|
9787
|
-
};
|
|
9788
|
-
}
|
|
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)) {
|
|
9790
|
-
return {
|
|
9791
|
-
resolved: false,
|
|
9792
|
-
reasons: [
|
|
9793
|
-
"current generation ID, workspace identity, or schema version is invalid"
|
|
9794
|
-
]
|
|
9795
|
-
};
|
|
9796
|
-
}
|
|
9797
|
-
const nodeMajor = parseNodeMajor2(options.nodeVersion);
|
|
9798
|
-
if (nodeMajor == null) {
|
|
9799
|
-
return { resolved: false, reasons: ["runtime Node.js version is invalid"] };
|
|
9800
|
-
}
|
|
9801
|
-
const generationDir = path30.join(
|
|
9802
|
-
storeRoot,
|
|
9803
|
-
"generations",
|
|
9804
|
-
pointer.generationId
|
|
9805
|
-
);
|
|
9806
|
-
const validation = validateGeneration(
|
|
9807
|
-
generationDir,
|
|
9808
|
-
{
|
|
9809
|
-
nodeMajor,
|
|
9810
|
-
platform: options.platform,
|
|
9811
|
-
arch: options.arch
|
|
9812
|
-
},
|
|
9813
|
-
false
|
|
9814
|
-
);
|
|
9815
|
-
if (!validation.manifest || validation.reasons.length > 0) {
|
|
9816
|
-
return { resolved: false, reasons: validation.reasons };
|
|
9817
|
-
}
|
|
9818
|
-
if (validation.manifest.generationId !== pointer.generationId || validation.manifest.workspaceBackupId !== pointer.workspaceBackupId || validation.manifest.workspaceVersion !== pointer.workspaceVersion) {
|
|
9819
|
-
return {
|
|
9820
|
-
resolved: false,
|
|
9821
|
-
reasons: ["current pointer does not match generation manifest"]
|
|
9822
|
-
};
|
|
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
|
-
}
|
|
9850
|
-
if (validation.manifest.buildEnvironmentSha256 !== options.buildEnvironmentSha256) {
|
|
9851
|
-
return {
|
|
9852
|
-
resolved: false,
|
|
9853
|
-
reasons: ["build environment is incompatible with generation"]
|
|
9854
|
-
};
|
|
9855
|
-
}
|
|
9856
|
-
return {
|
|
9857
|
-
resolved: true,
|
|
9858
|
-
generationId: pointer.generationId,
|
|
9859
|
-
generationDir,
|
|
9860
|
-
manifest: validation.manifest
|
|
9861
|
-
};
|
|
9862
|
-
}
|
|
9863
|
-
async function materializeVerifiedPreviewArtifactFile(options) {
|
|
9864
|
-
const source = path30.resolve(options.source);
|
|
9865
|
-
const destination = path30.resolve(options.destination);
|
|
9866
|
-
const temporary = `${destination}.${process.pid}.${Date.now()}.tmp`;
|
|
9867
|
-
fs34.mkdirSync(path30.dirname(destination), { recursive: true });
|
|
9868
|
-
if (fs34.existsSync(destination)) {
|
|
9869
|
-
throw new Error(
|
|
9870
|
-
`materialization destination already exists: ${destination}`
|
|
9871
|
-
);
|
|
9872
|
-
}
|
|
9873
|
-
const noFollow = fs34.constants.O_NOFOLLOW ?? 0;
|
|
9874
|
-
let sourceHandle;
|
|
9875
|
-
let destinationHandle;
|
|
9876
|
-
try {
|
|
9877
|
-
sourceHandle = await fs34.promises.open(
|
|
9878
|
-
source,
|
|
9879
|
-
fs34.constants.O_RDONLY | noFollow
|
|
9880
|
-
);
|
|
9881
|
-
const sourceStat = await sourceHandle.stat();
|
|
9882
|
-
if (!sourceStat.isFile()) {
|
|
9883
|
-
throw new Error(`${options.identity.path} is not a regular file`);
|
|
9884
|
-
}
|
|
9885
|
-
destinationHandle = await fs34.promises.open(
|
|
9886
|
-
temporary,
|
|
9887
|
-
fs34.constants.O_WRONLY | fs34.constants.O_CREAT | fs34.constants.O_EXCL,
|
|
9888
|
-
384
|
|
9889
|
-
);
|
|
9890
|
-
const hash = crypto3.createHash("sha256");
|
|
9891
|
-
const buffer = Buffer.allocUnsafe(256 * 1024);
|
|
9892
|
-
let totalBytes = 0;
|
|
9893
|
-
for (; ; ) {
|
|
9894
|
-
const { bytesRead } = await sourceHandle.read(
|
|
9895
|
-
buffer,
|
|
9896
|
-
0,
|
|
9897
|
-
buffer.length,
|
|
9898
|
-
null
|
|
9899
|
-
);
|
|
9900
|
-
if (bytesRead === 0) break;
|
|
9901
|
-
hash.update(buffer.subarray(0, bytesRead));
|
|
9902
|
-
let written = 0;
|
|
9903
|
-
while (written < bytesRead) {
|
|
9904
|
-
const result = await destinationHandle.write(
|
|
9905
|
-
buffer,
|
|
9906
|
-
written,
|
|
9907
|
-
bytesRead - written,
|
|
9908
|
-
null
|
|
9909
|
-
);
|
|
9910
|
-
if (result.bytesWritten <= 0) {
|
|
9911
|
-
throw new Error(
|
|
9912
|
-
`${options.identity.path} materialization short write`
|
|
9913
|
-
);
|
|
9914
|
-
}
|
|
9915
|
-
written += result.bytesWritten;
|
|
9916
|
-
}
|
|
9917
|
-
totalBytes += bytesRead;
|
|
9918
|
-
}
|
|
9919
|
-
const sha256 = hash.digest("hex");
|
|
9920
|
-
if (sha256 !== options.identity.sha256) {
|
|
9921
|
-
throw new Error(`${options.identity.path} hash does not match manifest`);
|
|
9922
|
-
}
|
|
9923
|
-
if (totalBytes !== options.identity.bytes) {
|
|
9924
|
-
throw new Error(`${options.identity.path} bytes do not match manifest`);
|
|
9925
|
-
}
|
|
9926
|
-
await destinationHandle.sync();
|
|
9927
|
-
await destinationHandle.close();
|
|
9928
|
-
destinationHandle = void 0;
|
|
9929
|
-
await fs34.promises.rename(temporary, destination);
|
|
9930
|
-
return { bytes: totalBytes, sha256 };
|
|
9931
|
-
} finally {
|
|
9932
|
-
await destinationHandle?.close().catch(() => void 0);
|
|
9933
|
-
await sourceHandle?.close().catch(() => void 0);
|
|
9934
|
-
await fs34.promises.rm(temporary, { force: true }).catch(() => void 0);
|
|
9935
|
-
}
|
|
9936
|
-
}
|
|
9937
|
-
|
|
9938
|
-
// src/commands/preview-artifact/producer.ts
|
|
9939
|
-
function defaultGenerationId() {
|
|
9940
|
-
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[-:.TZ]/g, "");
|
|
9941
|
-
return `${timestamp}-${crypto4.randomBytes(5).toString("hex")}`;
|
|
9942
|
-
}
|
|
9943
|
-
function defaultPreviewBundleBuildCommands(projectRootInput) {
|
|
9944
|
-
const projectRoot = path31.resolve(projectRootInput);
|
|
9945
|
-
const executableSuffix = process.platform === "win32" ? ".cmd" : "";
|
|
9946
|
-
const localBinary = (name) => path31.join(
|
|
9947
|
-
projectRoot,
|
|
9948
|
-
"node_modules",
|
|
9949
|
-
".bin",
|
|
9950
|
-
`${name}${executableSuffix}`
|
|
9951
|
-
);
|
|
9952
|
-
const nodeBuildCommand = (label, binary, args) => process.platform === "win32" ? { label, command: binary, args } : { label, command: process.execPath, args: [binary, ...args] };
|
|
9953
|
-
return [
|
|
9954
|
-
nodeBuildCommand("server", localBinary("nest"), ["build"]),
|
|
9955
|
-
nodeBuildCommand("client", localBinary("vite"), [
|
|
9956
|
-
"build",
|
|
9957
|
-
"--mode",
|
|
9958
|
-
"development",
|
|
9959
|
-
"--config",
|
|
9960
|
-
"vite.config.ts"
|
|
9961
|
-
])
|
|
9962
|
-
];
|
|
9963
|
-
}
|
|
9964
|
-
function runBuildCommands(projectRoot, buildCommands, environment) {
|
|
9965
|
-
for (const buildCommand of buildCommands) {
|
|
9966
|
-
const result = spawnSync9(buildCommand.command, buildCommand.args, {
|
|
9967
|
-
cwd: projectRoot,
|
|
9968
|
-
env: { ...process.env, ...environment },
|
|
9969
|
-
encoding: "utf8",
|
|
9970
|
-
maxBuffer: 10 * 1024 * 1024
|
|
9971
|
-
});
|
|
9972
|
-
if (result.status !== 0) {
|
|
9973
|
-
const diagnostic = `${result.stdout ?? ""}
|
|
9974
|
-
${result.stderr ?? ""}`.trim().slice(-8 * 1024);
|
|
9975
|
-
return [
|
|
9976
|
-
`preview bundle ${buildCommand.label} build failed with exit ${result.status ?? "unknown"}${result.error ? `: ${result.error.message}` : ""}${diagnostic ? `
|
|
9977
|
-
${diagnostic}` : ""}`
|
|
9978
|
-
];
|
|
9979
|
-
}
|
|
9980
|
-
}
|
|
9981
|
-
return [];
|
|
9982
|
-
}
|
|
9983
|
-
function createClientArchive(clientDir, archivePath) {
|
|
9984
|
-
if (!fs35.existsSync(path31.join(clientDir, "index.html"))) {
|
|
9985
|
-
return [`client build output is missing: ${clientDir}`];
|
|
9986
|
-
}
|
|
9987
|
-
const result = spawnSync9("zip", ["-qry", archivePath, "."], {
|
|
9988
|
-
cwd: clientDir,
|
|
9989
|
-
encoding: "utf8"
|
|
9990
|
-
});
|
|
9991
|
-
if (result.status === 0 && fs35.existsSync(archivePath)) return [];
|
|
9992
|
-
return [`client archive failed with exit ${result.status ?? "unknown"}`];
|
|
9993
|
-
}
|
|
9994
|
-
function readActionPlugins2(metadataFile) {
|
|
9995
|
-
const metadata = JSON.parse(fs35.readFileSync(metadataFile, "utf8"));
|
|
9996
|
-
if (!Array.isArray(metadata.actionPlugins)) return [];
|
|
9997
|
-
return metadata.actionPlugins.filter((value) => typeof value === "string").sort();
|
|
9998
|
-
}
|
|
9999
|
-
async function producePreviewArtifactGeneration(options) {
|
|
10000
|
-
const startedAt = Date.now();
|
|
10001
|
-
const projectRoot = path31.resolve(options.projectRoot);
|
|
10002
|
-
const storeRoot = path31.resolve(options.storeRoot);
|
|
10003
|
-
const generationId = options.generationId ?? defaultGenerationId();
|
|
10004
|
-
const stagingDir = path31.join(
|
|
10005
|
-
storeRoot,
|
|
10006
|
-
".staging",
|
|
10007
|
-
`${generationId}.${process.pid}.${Date.now()}`
|
|
10008
|
-
);
|
|
10009
|
-
const failed = (reasons, probe) => ({
|
|
10010
|
-
published: false,
|
|
10011
|
-
generationId,
|
|
10012
|
-
elapsedMs: Date.now() - startedAt,
|
|
10013
|
-
reasons,
|
|
10014
|
-
...probe ? { probe } : {}
|
|
10015
|
-
});
|
|
10016
|
-
fs35.mkdirSync(stagingDir, { recursive: true });
|
|
10017
|
-
try {
|
|
10018
|
-
const buildEnvironment = previewArtifactBuildEnvironment(
|
|
10019
|
-
options.environment,
|
|
10020
|
-
options.appBasePath
|
|
10021
|
-
);
|
|
10022
|
-
const buildCommands = options.buildCommands ?? (options.buildCommand ? [{ label: "custom", ...options.buildCommand }] : defaultPreviewBundleBuildCommands(projectRoot));
|
|
10023
|
-
const buildReasons = runBuildCommands(
|
|
10024
|
-
projectRoot,
|
|
10025
|
-
buildCommands,
|
|
10026
|
-
buildEnvironment
|
|
10027
|
-
);
|
|
10028
|
-
if (buildReasons.length > 0) return failed(buildReasons);
|
|
10029
|
-
const serverOutfile = path31.join(stagingDir, "server.bundle.cjs");
|
|
10030
|
-
const serverMetadataFile = path31.join(stagingDir, "server-build.json");
|
|
10031
|
-
const serverResult = await buildPreviewServerArtifact({
|
|
10032
|
-
projectRoot,
|
|
10033
|
-
entry: "dist/main.js",
|
|
10034
|
-
outfile: serverOutfile,
|
|
10035
|
-
metadataFile: serverMetadataFile
|
|
10036
|
-
});
|
|
10037
|
-
if (!serverResult.built) return failed(serverResult.reasons);
|
|
10038
|
-
const archiveReasons = createClientArchive(
|
|
10039
|
-
path31.join(projectRoot, "dist", "client"),
|
|
10040
|
-
path31.join(stagingDir, "client.zip")
|
|
10041
|
-
);
|
|
10042
|
-
if (archiveReasons.length > 0) return failed(archiveReasons);
|
|
10043
|
-
const actionPlugins = readActionPlugins2(serverMetadataFile);
|
|
10044
|
-
fs35.rmSync(serverMetadataFile, { force: true });
|
|
10045
|
-
createPreviewArtifactManifest({
|
|
10046
|
-
generationDir: stagingDir,
|
|
10047
|
-
generationId,
|
|
10048
|
-
nodeVersion: process.version,
|
|
10049
|
-
platform: process.platform,
|
|
10050
|
-
arch: process.arch,
|
|
10051
|
-
buildEnvironmentSha256: previewArtifactBuildEnvironmentHash(
|
|
10052
|
-
options.environment,
|
|
10053
|
-
options.appBasePath
|
|
10054
|
-
),
|
|
10055
|
-
workspaceManifest: options.workspaceManifest,
|
|
10056
|
-
actionPlugins
|
|
10057
|
-
});
|
|
10058
|
-
const probe = await runPreviewArtifactProbe({
|
|
10059
|
-
generationDir: stagingDir,
|
|
10060
|
-
appBasePath: options.appBasePath,
|
|
10061
|
-
businessApiPath: options.businessApiPath,
|
|
10062
|
-
environment: previewArtifactRuntimeEnvironment(options.environment)
|
|
10063
|
-
});
|
|
10064
|
-
if (!probe.success) return failed(probe.reasons, probe);
|
|
10065
|
-
const promotion = promotePreviewArtifactGeneration(stagingDir);
|
|
10066
|
-
if (!promotion.promoted) return failed(promotion.reasons, probe);
|
|
10067
|
-
const publication = publishPreviewArtifactGeneration({
|
|
10068
|
-
storeRoot,
|
|
10069
|
-
stagingDir
|
|
10070
|
-
});
|
|
10071
|
-
if (!publication.published) return failed(publication.reasons, probe);
|
|
10072
|
-
return {
|
|
10073
|
-
published: true,
|
|
10074
|
-
generationId,
|
|
10075
|
-
generationDir: publication.generationDir,
|
|
10076
|
-
elapsedMs: Date.now() - startedAt,
|
|
10077
|
-
probe
|
|
10078
|
-
};
|
|
10079
|
-
} catch (error) {
|
|
10080
|
-
return failed([error instanceof Error ? error.message : String(error)]);
|
|
10081
|
-
} finally {
|
|
10082
|
-
fs35.rmSync(stagingDir, { recursive: true, force: true });
|
|
10083
|
-
}
|
|
10084
|
-
}
|
|
10085
|
-
|
|
10086
|
-
// src/commands/preview-artifact/runtime.ts
|
|
10087
|
-
import * as fs37 from "fs";
|
|
10088
|
-
import * as net3 from "net";
|
|
10089
|
-
import * as path33 from "path";
|
|
10090
|
-
import { spawn as spawn3 } from "child_process";
|
|
10091
|
-
|
|
10092
|
-
// src/commands/preview-artifact/switcher.ts
|
|
10093
|
-
import * as http from "http";
|
|
10094
|
-
import * as net2 from "net";
|
|
10095
|
-
import * as fs36 from "fs";
|
|
10096
|
-
import * as path32 from "path";
|
|
10097
|
-
function targetOrigin(target) {
|
|
10098
|
-
return `http://${target.host}:${target.port}`;
|
|
10099
|
-
}
|
|
10100
|
-
function proxyHttpRequest(target, request2, response, fallback) {
|
|
10101
|
-
const upstream = http.request(
|
|
10102
|
-
{
|
|
10103
|
-
host: target.host,
|
|
10104
|
-
port: target.port,
|
|
10105
|
-
method: request2.method,
|
|
10106
|
-
path: request2.url,
|
|
10107
|
-
headers: request2.headers
|
|
10108
|
-
},
|
|
10109
|
-
(upstreamResponse) => {
|
|
10110
|
-
const upstreamContentType = upstreamResponse.headers["content-type"];
|
|
10111
|
-
const isHtmlFallback = typeof upstreamContentType === "string" && upstreamContentType.toLowerCase().includes("text/html");
|
|
10112
|
-
if (fallback && (upstreamResponse.statusCode === 404 || isHtmlFallback)) {
|
|
10113
|
-
upstreamResponse.resume();
|
|
10114
|
-
fallback();
|
|
10115
|
-
return;
|
|
10116
|
-
}
|
|
10117
|
-
response.writeHead(
|
|
10118
|
-
upstreamResponse.statusCode ?? 502,
|
|
10119
|
-
upstreamResponse.headers
|
|
10120
|
-
);
|
|
10121
|
-
upstreamResponse.pipe(response);
|
|
10122
|
-
}
|
|
10123
|
-
);
|
|
10124
|
-
upstream.on("error", (error) => {
|
|
10125
|
-
if (!response.headersSent) {
|
|
10126
|
-
response.writeHead(502, { "content-type": "application/json" });
|
|
10127
|
-
}
|
|
10128
|
-
response.end(
|
|
10129
|
-
JSON.stringify({
|
|
10130
|
-
ready: false,
|
|
10131
|
-
error: error.code ?? "upstream"
|
|
10132
|
-
})
|
|
10133
|
-
);
|
|
10134
|
-
});
|
|
10135
|
-
request2.pipe(upstream);
|
|
10136
|
-
}
|
|
10137
|
-
function cachedAssetFile(request2, cachedAssetFiles) {
|
|
10138
|
-
if (request2.method !== "GET" && request2.method !== "HEAD") return void 0;
|
|
10139
|
-
if (!cachedAssetFiles) return void 0;
|
|
10140
|
-
const pathname = new URL(request2.url ?? "/", "http://preview.local").pathname;
|
|
10141
|
-
return cachedAssetFiles.get(pathname);
|
|
10142
|
-
}
|
|
10143
|
-
function contentType(filePath) {
|
|
10144
|
-
switch (path32.extname(filePath).toLowerCase()) {
|
|
10145
|
-
case ".js":
|
|
10146
|
-
case ".mjs":
|
|
10147
|
-
return "text/javascript; charset=utf-8";
|
|
10148
|
-
case ".css":
|
|
10149
|
-
return "text/css; charset=utf-8";
|
|
10150
|
-
case ".json":
|
|
10151
|
-
case ".map":
|
|
10152
|
-
return "application/json; charset=utf-8";
|
|
10153
|
-
case ".svg":
|
|
10154
|
-
return "image/svg+xml";
|
|
10155
|
-
case ".png":
|
|
10156
|
-
return "image/png";
|
|
10157
|
-
case ".jpg":
|
|
10158
|
-
case ".jpeg":
|
|
10159
|
-
return "image/jpeg";
|
|
10160
|
-
case ".gif":
|
|
10161
|
-
return "image/gif";
|
|
10162
|
-
case ".webp":
|
|
10163
|
-
return "image/webp";
|
|
10164
|
-
case ".ico":
|
|
10165
|
-
return "image/x-icon";
|
|
10166
|
-
case ".woff":
|
|
10167
|
-
return "font/woff";
|
|
10168
|
-
case ".woff2":
|
|
10169
|
-
return "font/woff2";
|
|
10170
|
-
case ".ttf":
|
|
10171
|
-
return "font/ttf";
|
|
10172
|
-
case ".wasm":
|
|
10173
|
-
return "application/wasm";
|
|
10174
|
-
default:
|
|
10175
|
-
return "application/octet-stream";
|
|
10176
|
-
}
|
|
10177
|
-
}
|
|
10178
|
-
function serveCachedAsset(request2, response, filePath) {
|
|
10179
|
-
let stat;
|
|
10180
|
-
try {
|
|
10181
|
-
stat = fs36.lstatSync(filePath);
|
|
10182
|
-
} catch {
|
|
10183
|
-
response.writeHead(404).end();
|
|
10184
|
-
return;
|
|
10185
|
-
}
|
|
10186
|
-
if (!stat.isFile() || stat.isSymbolicLink()) {
|
|
10187
|
-
response.writeHead(404).end();
|
|
10188
|
-
return;
|
|
10189
|
-
}
|
|
10190
|
-
response.writeHead(200, {
|
|
10191
|
-
"content-type": contentType(filePath),
|
|
10192
|
-
"content-length": stat.size,
|
|
10193
|
-
"cache-control": "no-store"
|
|
10194
|
-
});
|
|
10195
|
-
if (request2.method === "HEAD") {
|
|
10196
|
-
response.end();
|
|
10197
|
-
return;
|
|
10198
|
-
}
|
|
10199
|
-
const stream = fs36.createReadStream(filePath);
|
|
10200
|
-
stream.on("error", () => response.destroy());
|
|
10201
|
-
stream.pipe(response);
|
|
10202
|
-
}
|
|
10203
|
-
function proxyUpgrade(target, request2, socket, head) {
|
|
10204
|
-
const upstream = net2.connect(target.port, target.host, () => {
|
|
10205
|
-
const headers = Object.entries(request2.headers).flatMap(([name, value]) => {
|
|
10206
|
-
if (Array.isArray(value)) return value.map((item) => `${name}: ${item}`);
|
|
10207
|
-
return value == null ? [] : [`${name}: ${value}`];
|
|
10208
|
-
}).join("\r\n");
|
|
10209
|
-
upstream.write(
|
|
10210
|
-
`${request2.method ?? "GET"} ${request2.url ?? "/"} HTTP/${request2.httpVersion}\r
|
|
10211
|
-
${headers}\r
|
|
10212
|
-
\r
|
|
10213
|
-
`
|
|
10214
|
-
);
|
|
10215
|
-
if (head.length > 0) upstream.write(head);
|
|
10216
|
-
socket.pipe(upstream).pipe(socket);
|
|
10217
|
-
});
|
|
10218
|
-
upstream.on("error", () => socket.destroy());
|
|
10219
|
-
socket.on("error", () => upstream.destroy());
|
|
10220
|
-
}
|
|
10221
|
-
function targetAcceptsConnection(target, timeoutMs = 1e3) {
|
|
10222
|
-
return new Promise((resolve10) => {
|
|
10223
|
-
const socket = net2.connect(target.port, target.host);
|
|
10224
|
-
let settled = false;
|
|
10225
|
-
const finish = (ready) => {
|
|
10226
|
-
if (settled) return;
|
|
10227
|
-
settled = true;
|
|
10228
|
-
socket.destroy();
|
|
10229
|
-
resolve10(ready);
|
|
10230
|
-
};
|
|
10231
|
-
socket.setTimeout(timeoutMs);
|
|
10232
|
-
socket.once("connect", () => finish(true));
|
|
10233
|
-
socket.once("timeout", () => finish(false));
|
|
10234
|
-
socket.once("error", () => finish(false));
|
|
10235
|
-
});
|
|
10236
|
-
}
|
|
10237
|
-
async function freshTargetReady(clientTarget, serverTarget, healthPath) {
|
|
10238
|
-
try {
|
|
10239
|
-
const [clientReady, serverReady] = await Promise.all([
|
|
10240
|
-
(async () => {
|
|
10241
|
-
const response = await fetch(
|
|
10242
|
-
new URL(healthPath, targetOrigin(clientTarget)),
|
|
10243
|
-
{ signal: AbortSignal.timeout(1e3) }
|
|
10244
|
-
);
|
|
10245
|
-
if (response.status !== 200) return false;
|
|
10246
|
-
const body = await response.json();
|
|
10247
|
-
return body.ready === true;
|
|
10248
|
-
})(),
|
|
10249
|
-
targetAcceptsConnection(serverTarget)
|
|
10250
|
-
]);
|
|
10251
|
-
return clientReady && serverReady;
|
|
10252
|
-
} catch {
|
|
10253
|
-
return false;
|
|
10254
|
-
}
|
|
10255
|
-
}
|
|
10256
|
-
function createPreviewArtifactSwitcher(options) {
|
|
10257
|
-
const listenHost = options.listenHost ?? "127.0.0.1";
|
|
10258
|
-
const healthPath = options.freshHealthPath ?? "/dev/health";
|
|
10259
|
-
const pollIntervalMs = options.pollIntervalMs ?? 100;
|
|
10260
|
-
let active = "cached";
|
|
10261
|
-
let closed = false;
|
|
10262
|
-
let pollTimer;
|
|
10263
|
-
let settleCutover;
|
|
10264
|
-
const cutover = new Promise((resolve10) => {
|
|
10265
|
-
settleCutover = resolve10;
|
|
10266
|
-
});
|
|
10267
|
-
const currentTarget = () => active === "cached" ? options.cachedTarget : options.freshTarget;
|
|
10268
|
-
const server = http.createServer((request2, response) => {
|
|
10269
|
-
if (request2.url?.split("?")[0] === "/dev/health") {
|
|
10270
|
-
response.writeHead(200, { "content-type": "application/json" });
|
|
10271
|
-
response.end(
|
|
10272
|
-
JSON.stringify({
|
|
10273
|
-
schemaVersion: 1,
|
|
10274
|
-
ready: active === "fresh",
|
|
10275
|
-
previewReady: true,
|
|
10276
|
-
active,
|
|
10277
|
-
generationId: options.generationId
|
|
10278
|
-
})
|
|
10279
|
-
);
|
|
10280
|
-
return;
|
|
10281
|
-
}
|
|
10282
|
-
const assetFile = cachedAssetFile(request2, options.cachedAssetFiles);
|
|
10283
|
-
if (active === "cached" && assetFile) {
|
|
10284
|
-
serveCachedAsset(request2, response, assetFile);
|
|
10285
|
-
return;
|
|
10286
|
-
}
|
|
10287
|
-
const target = currentTarget();
|
|
10288
|
-
const fallback = active === "fresh" && assetFile ? () => serveCachedAsset(request2, response, assetFile) : void 0;
|
|
10289
|
-
proxyHttpRequest(target, request2, response, fallback);
|
|
10290
|
-
});
|
|
10291
|
-
server.on("upgrade", (request2, socket, head) => {
|
|
10292
|
-
proxyUpgrade(currentTarget(), request2, socket, head);
|
|
10293
|
-
});
|
|
10294
|
-
const schedulePoll = () => {
|
|
10295
|
-
if (closed || active === "fresh") return;
|
|
10296
|
-
pollTimer = setTimeout(async () => {
|
|
10297
|
-
if (await freshTargetReady(
|
|
10298
|
-
options.freshTarget,
|
|
10299
|
-
options.freshServerTarget,
|
|
10300
|
-
healthPath
|
|
10301
|
-
)) {
|
|
10302
|
-
active = "fresh";
|
|
10303
|
-
settleCutover?.({
|
|
10304
|
-
switched: true,
|
|
10305
|
-
active,
|
|
10306
|
-
at: (/* @__PURE__ */ new Date()).toISOString()
|
|
10307
|
-
});
|
|
10308
|
-
settleCutover = void 0;
|
|
10309
|
-
return;
|
|
10310
|
-
}
|
|
10311
|
-
schedulePoll();
|
|
10312
|
-
}, pollIntervalMs);
|
|
10313
|
-
pollTimer.unref();
|
|
10314
|
-
};
|
|
10315
|
-
return {
|
|
10316
|
-
get active() {
|
|
10317
|
-
return active;
|
|
10318
|
-
},
|
|
10319
|
-
cutover,
|
|
10320
|
-
start() {
|
|
10321
|
-
return new Promise((resolve10, reject) => {
|
|
10322
|
-
server.once("error", reject);
|
|
10323
|
-
server.listen(options.listenPort, listenHost, () => {
|
|
10324
|
-
const address = server.address();
|
|
10325
|
-
if (!address || typeof address === "string") {
|
|
10326
|
-
reject(new Error("switcher did not expose a TCP address"));
|
|
10327
|
-
return;
|
|
10328
|
-
}
|
|
10329
|
-
schedulePoll();
|
|
10330
|
-
resolve10({
|
|
10331
|
-
host: listenHost,
|
|
10332
|
-
port: address.port,
|
|
10333
|
-
origin: `http://${listenHost}:${address.port}`
|
|
10334
|
-
});
|
|
10335
|
-
});
|
|
10336
|
-
});
|
|
10337
|
-
},
|
|
10338
|
-
close() {
|
|
10339
|
-
closed = true;
|
|
10340
|
-
if (pollTimer) clearTimeout(pollTimer);
|
|
10341
|
-
if (settleCutover) {
|
|
10342
|
-
settleCutover({
|
|
10343
|
-
switched: false,
|
|
10344
|
-
active,
|
|
10345
|
-
at: (/* @__PURE__ */ new Date()).toISOString()
|
|
10346
|
-
});
|
|
10347
|
-
settleCutover = void 0;
|
|
10348
|
-
}
|
|
10349
|
-
return new Promise((resolve10) => {
|
|
10350
|
-
server.close(() => resolve10());
|
|
10351
|
-
server.closeAllConnections?.();
|
|
10352
|
-
});
|
|
10353
|
-
}
|
|
10354
|
-
};
|
|
10355
|
-
}
|
|
10356
|
-
|
|
10357
|
-
// src/commands/preview-artifact/runtime.ts
|
|
10358
|
-
function measure(timings, phase, operation, detail) {
|
|
10359
|
-
const startedAt = Date.now();
|
|
10360
|
-
try {
|
|
10361
|
-
return operation();
|
|
10362
|
-
} finally {
|
|
10363
|
-
timings.push({ phase, durationMs: Date.now() - startedAt, ...detail });
|
|
10364
|
-
}
|
|
10365
|
-
}
|
|
10366
|
-
async function measureAsync(timings, phase, operation, detail) {
|
|
10367
|
-
const startedAt = Date.now();
|
|
10368
|
-
try {
|
|
10369
|
-
return await operation();
|
|
10370
|
-
} finally {
|
|
10371
|
-
timings.push({ phase, durationMs: Date.now() - startedAt, ...detail });
|
|
10372
|
-
}
|
|
10373
|
-
}
|
|
10374
|
-
function sleep2(ms) {
|
|
10375
|
-
return new Promise((resolve10) => setTimeout(resolve10, ms));
|
|
10376
|
-
}
|
|
10377
|
-
async function reservePort2() {
|
|
10378
|
-
return new Promise((resolve10, reject) => {
|
|
10379
|
-
const server = net3.createServer();
|
|
10380
|
-
server.unref();
|
|
10381
|
-
server.once("error", reject);
|
|
10382
|
-
server.listen(0, "127.0.0.1", () => {
|
|
10383
|
-
const address = server.address();
|
|
10384
|
-
if (!address || typeof address === "string") {
|
|
10385
|
-
server.close(() => reject(new Error("unable to reserve runtime port")));
|
|
10386
|
-
return;
|
|
10387
|
-
}
|
|
10388
|
-
server.close((error) => {
|
|
10389
|
-
if (error) reject(error);
|
|
10390
|
-
else resolve10(address.port);
|
|
10391
|
-
});
|
|
10392
|
-
});
|
|
10393
|
-
});
|
|
10394
|
-
}
|
|
10395
|
-
function stopProcessGroup2(child) {
|
|
10396
|
-
if (!child?.pid) return;
|
|
10397
|
-
try {
|
|
10398
|
-
process.kill(-child.pid, "SIGTERM");
|
|
10399
|
-
} catch {
|
|
10400
|
-
try {
|
|
10401
|
-
child.kill("SIGTERM");
|
|
10402
|
-
} catch {
|
|
10403
|
-
return;
|
|
10404
|
-
}
|
|
10405
|
-
}
|
|
10406
|
-
}
|
|
10407
|
-
function isPathInside3(parent, candidate) {
|
|
10408
|
-
const relative5 = path33.relative(parent, candidate);
|
|
10409
|
-
return relative5 !== "" && relative5 !== ".." && !relative5.startsWith(`..${path33.sep}`) && !path33.isAbsolute(relative5);
|
|
10410
|
-
}
|
|
10411
|
-
function ensureRegularRuntimeRoot(runtimeRoot) {
|
|
10412
|
-
fs37.mkdirSync(runtimeRoot, { recursive: true });
|
|
10413
|
-
const stat = fs37.lstatSync(runtimeRoot);
|
|
10414
|
-
if (!stat.isDirectory() || stat.isSymbolicLink()) {
|
|
10415
|
-
throw new Error("runtime root must be a regular directory");
|
|
10416
|
-
}
|
|
10417
|
-
}
|
|
10418
|
-
function collectCachedAssetFiles(clientRoot, appBasePath) {
|
|
10419
|
-
const files = /* @__PURE__ */ new Map();
|
|
10420
|
-
const basePath = appBasePath.endsWith("/") ? appBasePath : `${appBasePath}/`;
|
|
10421
|
-
const visit = (directory) => {
|
|
10422
|
-
for (const entry of fs37.readdirSync(directory, { withFileTypes: true })) {
|
|
10423
|
-
const absolute = path33.join(directory, entry.name);
|
|
10424
|
-
if (entry.isDirectory()) {
|
|
10425
|
-
visit(absolute);
|
|
10426
|
-
continue;
|
|
10427
|
-
}
|
|
10428
|
-
if (!entry.isFile() || entry.isSymbolicLink()) continue;
|
|
10429
|
-
const relative5 = path33.relative(clientRoot, absolute).split(path33.sep).join("/");
|
|
10430
|
-
if (relative5 === "index.html") continue;
|
|
10431
|
-
files.set(
|
|
10432
|
-
new URL(`/${relative5}`, "http://preview.local").pathname,
|
|
10433
|
-
absolute
|
|
10434
|
-
);
|
|
10435
|
-
files.set(
|
|
10436
|
-
new URL(relative5, `http://preview.local${basePath}`).pathname,
|
|
10437
|
-
absolute
|
|
10438
|
-
);
|
|
10439
|
-
}
|
|
10440
|
-
};
|
|
10441
|
-
visit(clientRoot);
|
|
10442
|
-
return files;
|
|
10443
|
-
}
|
|
10444
|
-
function writeMarkerAtomic(markerFile, marker) {
|
|
10445
|
-
fs37.mkdirSync(path33.dirname(markerFile), { recursive: true });
|
|
10446
|
-
const temporaryFile = `${markerFile}.${process.pid}.${Date.now()}.tmp`;
|
|
10447
|
-
fs37.writeFileSync(temporaryFile, `${JSON.stringify(marker, null, 2)}
|
|
10448
|
-
`, {
|
|
10449
|
-
mode: 384
|
|
10450
|
-
});
|
|
10451
|
-
fs37.renameSync(temporaryFile, markerFile);
|
|
10452
|
-
}
|
|
10453
|
-
async function waitForCachedRuntime(origin, appBasePath, businessApiPath, timeoutMs, child) {
|
|
10454
|
-
const timeoutAt = Date.now() + timeoutMs;
|
|
10455
|
-
while (Date.now() < timeoutAt) {
|
|
10456
|
-
if (child.exitCode != null || child.signalCode != null) {
|
|
10457
|
-
throw new Error(
|
|
10458
|
-
`cached runtime exited before readiness: ${child.exitCode ?? child.signalCode}`
|
|
10459
|
-
);
|
|
10460
|
-
}
|
|
10461
|
-
try {
|
|
10462
|
-
const response = await fetch(new URL(appBasePath, origin), {
|
|
10463
|
-
redirect: "manual",
|
|
10464
|
-
signal: AbortSignal.timeout(1e3)
|
|
10465
|
-
});
|
|
10466
|
-
if (response.status === 200) {
|
|
10467
|
-
const businessResponse = await fetch(new URL(businessApiPath, origin), {
|
|
10468
|
-
redirect: "manual",
|
|
10469
|
-
signal: AbortSignal.timeout(1e3)
|
|
10470
|
-
});
|
|
10471
|
-
if (isPreviewBusinessResponseReady(businessResponse)) {
|
|
10472
|
-
return;
|
|
10473
|
-
}
|
|
10474
|
-
}
|
|
10475
|
-
} catch {
|
|
10476
|
-
}
|
|
10477
|
-
await sleep2(50);
|
|
10478
|
-
}
|
|
10479
|
-
throw new Error("cached runtime readiness timed out");
|
|
10480
|
-
}
|
|
10481
|
-
async function startPreviewArtifactRuntime(options) {
|
|
10482
|
-
const timings = [...options.initialTimings ?? []];
|
|
10483
|
-
const resolved = measure(
|
|
10484
|
-
timings,
|
|
10485
|
-
"metadata_validate",
|
|
10486
|
-
() => resolveCurrentPreviewArtifactMetadata({
|
|
10487
|
-
storeRoot: options.storeRoot,
|
|
10488
|
-
nodeVersion: process.version,
|
|
10489
|
-
platform: process.platform,
|
|
10490
|
-
arch: process.arch,
|
|
10491
|
-
buildEnvironmentSha256: previewArtifactBuildEnvironmentHash(
|
|
10492
|
-
options.environment,
|
|
10493
|
-
options.appBasePath
|
|
10494
|
-
),
|
|
10495
|
-
sourceTreeValidation: options.sourceTreeValidation,
|
|
10496
|
-
workspaceManifest: options.workspaceManifest
|
|
10497
|
-
})
|
|
10498
|
-
);
|
|
10499
|
-
if (!resolved.resolved) return { started: false, reasons: resolved.reasons };
|
|
10500
|
-
const runtimeRoot = path33.resolve(options.runtimeRoot);
|
|
10501
|
-
const markerFile = path33.resolve(options.markerFile);
|
|
10502
|
-
let runtimeDir;
|
|
10503
|
-
let cachedRuntime;
|
|
10504
|
-
let switcher;
|
|
10505
|
-
let closed = false;
|
|
10506
|
-
const cleanup = async () => {
|
|
10507
|
-
if (closed) return;
|
|
10508
|
-
closed = true;
|
|
10509
|
-
stopProcessGroup2(cachedRuntime);
|
|
10510
|
-
await switcher?.close();
|
|
10511
|
-
fs37.rmSync(markerFile, { force: true });
|
|
10512
|
-
if (runtimeDir) fs37.rmSync(runtimeDir, { recursive: true, force: true });
|
|
10513
|
-
};
|
|
10514
|
-
try {
|
|
10515
|
-
ensureRegularRuntimeRoot(runtimeRoot);
|
|
10516
|
-
if (!isPathInside3(runtimeRoot, markerFile)) {
|
|
10517
|
-
throw new Error("runtime marker must be inside the runtime root");
|
|
10518
|
-
}
|
|
10519
|
-
runtimeDir = fs37.mkdtempSync(
|
|
10520
|
-
path33.join(runtimeRoot, `${resolved.generationId}-`)
|
|
10521
|
-
);
|
|
10522
|
-
const runtimeBundle = path33.join(runtimeDir, "server.bundle.cjs");
|
|
10523
|
-
const serverSource = path33.join(resolved.generationDir, "server.bundle.cjs");
|
|
10524
|
-
const clientArchive = path33.join(runtimeDir, "client.zip");
|
|
10525
|
-
const clientSource = path33.join(resolved.generationDir, "client.zip");
|
|
10526
|
-
const clientRoot = path33.join(runtimeDir, "dist", "client");
|
|
10527
|
-
const assetsReady = (async () => {
|
|
10528
|
-
await measureAsync(
|
|
10529
|
-
timings,
|
|
10530
|
-
"client_materialize",
|
|
10531
|
-
() => materializeVerifiedPreviewArtifactFile({
|
|
10532
|
-
source: clientSource,
|
|
10533
|
-
destination: clientArchive,
|
|
10534
|
-
identity: resolved.manifest.files.client
|
|
10535
|
-
}),
|
|
10536
|
-
{ bytes: resolved.manifest.files.client.bytes }
|
|
10537
|
-
);
|
|
10538
|
-
measure(
|
|
10539
|
-
timings,
|
|
10540
|
-
"client_archive_inspect",
|
|
10541
|
-
() => inspectClientArchive(clientArchive)
|
|
10542
|
-
);
|
|
10543
|
-
await measureAsync(
|
|
10544
|
-
timings,
|
|
10545
|
-
"client_extract",
|
|
10546
|
-
() => extractInspectedClientArchive(clientArchive, clientRoot)
|
|
10547
|
-
);
|
|
10548
|
-
const cachedAssetFiles2 = measure(
|
|
10549
|
-
timings,
|
|
10550
|
-
"asset_index",
|
|
10551
|
-
() => collectCachedAssetFiles(clientRoot, options.appBasePath)
|
|
10552
|
-
);
|
|
10553
|
-
timings[timings.length - 1].fileCount = new Set(
|
|
10554
|
-
cachedAssetFiles2.values()
|
|
10555
|
-
).size;
|
|
10556
|
-
fs37.rmSync(clientArchive, { force: true });
|
|
10557
|
-
return cachedAssetFiles2;
|
|
10558
|
-
})();
|
|
10559
|
-
const backendReady = (async () => {
|
|
10560
|
-
await measureAsync(
|
|
10561
|
-
timings,
|
|
10562
|
-
"server_materialize",
|
|
10563
|
-
() => materializeVerifiedPreviewArtifactFile({
|
|
10564
|
-
source: serverSource,
|
|
10565
|
-
destination: runtimeBundle,
|
|
10566
|
-
identity: resolved.manifest.files.server
|
|
10567
|
-
}),
|
|
10568
|
-
{ bytes: resolved.manifest.files.server.bytes }
|
|
10569
|
-
);
|
|
10570
|
-
const cachedServerPort2 = options.cachedServerPort === 0 ? await reservePort2() : options.cachedServerPort;
|
|
10571
|
-
cachedRuntime = spawn3(process.execPath, [runtimeBundle], {
|
|
10572
|
-
cwd: runtimeDir,
|
|
10573
|
-
detached: true,
|
|
10574
|
-
stdio: "ignore",
|
|
10575
|
-
env: {
|
|
10576
|
-
...process.env,
|
|
10577
|
-
...previewArtifactRuntimeEnvironment(options.environment),
|
|
10578
|
-
SERVER_PORT: String(cachedServerPort2)
|
|
10579
|
-
}
|
|
10580
|
-
});
|
|
10581
|
-
const cachedOrigin = `http://127.0.0.1:${cachedServerPort2}`;
|
|
10582
|
-
await measureAsync(
|
|
10583
|
-
timings,
|
|
10584
|
-
"backend_spawn_to_business_ready",
|
|
10585
|
-
async () => {
|
|
10586
|
-
await assetsReady;
|
|
10587
|
-
await waitForCachedRuntime(
|
|
10588
|
-
cachedOrigin,
|
|
10589
|
-
options.appBasePath,
|
|
10590
|
-
options.businessApiPath,
|
|
10591
|
-
options.startupTimeoutMs ?? 3e4,
|
|
10592
|
-
cachedRuntime
|
|
10593
|
-
);
|
|
10594
|
-
}
|
|
10595
|
-
);
|
|
10596
|
-
return cachedServerPort2;
|
|
10597
|
-
})();
|
|
10598
|
-
const [backendResult, assetsResult] = await Promise.allSettled([
|
|
10599
|
-
backendReady,
|
|
10600
|
-
assetsReady
|
|
10601
|
-
]);
|
|
10602
|
-
if (assetsResult.status === "rejected") throw assetsResult.reason;
|
|
10603
|
-
if (backendResult.status === "rejected") throw backendResult.reason;
|
|
10604
|
-
const cachedServerPort = backendResult.value;
|
|
10605
|
-
const cachedAssetFiles = assetsResult.value;
|
|
10606
|
-
switcher = createPreviewArtifactSwitcher({
|
|
10607
|
-
listenHost: options.listenHost,
|
|
10608
|
-
listenPort: options.listenPort,
|
|
10609
|
-
generationId: resolved.generationId,
|
|
10610
|
-
cachedTarget: { host: "127.0.0.1", port: cachedServerPort },
|
|
10611
|
-
freshTarget: { host: "127.0.0.1", port: options.freshClientPort },
|
|
10612
|
-
freshServerTarget: { host: "127.0.0.1", port: options.freshServerPort },
|
|
10613
|
-
cachedAssetFiles,
|
|
10614
|
-
pollIntervalMs: options.pollIntervalMs
|
|
10615
|
-
});
|
|
10616
|
-
const address = await measureAsync(
|
|
10617
|
-
timings,
|
|
10618
|
-
"switcher_listen",
|
|
10619
|
-
() => switcher.start()
|
|
10620
|
-
);
|
|
10621
|
-
const runtimeMarker = {
|
|
10622
|
-
schemaVersion: 1,
|
|
10623
|
-
status: "cached-serving",
|
|
10624
|
-
generationId: resolved.generationId,
|
|
10625
|
-
publicPort: address.port,
|
|
10626
|
-
cachedServerPort,
|
|
10627
|
-
freshServerPort: options.freshServerPort,
|
|
10628
|
-
freshClientPort: options.freshClientPort,
|
|
10629
|
-
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10630
|
-
timings
|
|
10631
|
-
};
|
|
10632
|
-
measure(
|
|
10633
|
-
timings,
|
|
10634
|
-
"marker_write",
|
|
10635
|
-
() => writeMarkerAtomic(markerFile, runtimeMarker)
|
|
10636
|
-
);
|
|
10637
|
-
writeMarkerAtomic(markerFile, runtimeMarker);
|
|
10638
|
-
const cutover = switcher.cutover.then(async (result) => {
|
|
10639
|
-
if (result.switched) {
|
|
10640
|
-
runtimeMarker.status = "fresh-serving";
|
|
10641
|
-
runtimeMarker.cutoverAt = result.at;
|
|
10642
|
-
writeMarkerAtomic(markerFile, runtimeMarker);
|
|
10643
|
-
}
|
|
10644
|
-
return result;
|
|
10645
|
-
});
|
|
10646
|
-
return {
|
|
10647
|
-
started: true,
|
|
10648
|
-
generationId: resolved.generationId,
|
|
10649
|
-
origin: address.origin,
|
|
10650
|
-
port: address.port,
|
|
10651
|
-
runtimeDir,
|
|
10652
|
-
cutover,
|
|
10653
|
-
close: cleanup
|
|
10654
|
-
};
|
|
10655
|
-
} catch (error) {
|
|
10656
|
-
await cleanup();
|
|
10657
|
-
return {
|
|
10658
|
-
started: false,
|
|
10659
|
-
reasons: [error instanceof Error ? error.message : String(error)]
|
|
10660
|
-
};
|
|
10661
|
-
}
|
|
10662
|
-
}
|
|
10663
|
-
|
|
10664
|
-
// src/commands/build/index.ts
|
|
10665
|
-
var getTokenCommand = {
|
|
10666
|
-
name: "get-token",
|
|
10667
|
-
description: "Get artifact upload credential (STI token)",
|
|
10668
|
-
register(program) {
|
|
10669
|
-
program.command(this.name).description(this.description).requiredOption("--app-id <id>", "Application ID").requiredOption("--scene <scene>", "Build scene (pipeline, static)").option("--commit-id <id>", "Git commit ID (required for pipeline scene)").action(
|
|
10670
|
-
async (options) => {
|
|
10671
|
-
await getToken(options);
|
|
10672
|
-
}
|
|
10673
|
-
);
|
|
10674
|
-
}
|
|
10675
|
-
};
|
|
10676
|
-
var uploadStaticCommand = {
|
|
10677
|
-
name: "upload-static",
|
|
10678
|
-
description: "Upload shared/static files to TOS",
|
|
10679
|
-
register(program) {
|
|
10680
|
-
program.command(this.name).description(this.description).requiredOption("--app-id <id>", "Application ID").option(
|
|
10681
|
-
"--static-dir <dir>",
|
|
10682
|
-
"Static files directory",
|
|
10683
|
-
UPLOAD_STATIC_DEFAULTS.staticDir
|
|
10684
|
-
).option(
|
|
10685
|
-
"--tosutil-path <path>",
|
|
10686
|
-
"Path to tosutil binary",
|
|
10687
|
-
UPLOAD_STATIC_DEFAULTS.tosutilPath
|
|
10688
|
-
).option(
|
|
10689
|
-
"--endpoint <endpoint>",
|
|
10690
|
-
"TOS endpoint",
|
|
10691
|
-
UPLOAD_STATIC_DEFAULTS.endpoint
|
|
10692
|
-
).option("--region <region>", "TOS region", UPLOAD_STATIC_DEFAULTS.region).action(async (options) => {
|
|
10693
|
-
await uploadStatic(options);
|
|
10694
|
-
});
|
|
10695
|
-
}
|
|
10696
|
-
};
|
|
10697
|
-
var preUploadStaticCommand = {
|
|
10698
|
-
name: "pre-upload-static",
|
|
10699
|
-
description: "Get TOS upload info and output as env vars for build.sh eval",
|
|
10700
|
-
register(program) {
|
|
10701
|
-
program.command(this.name).description(this.description).requiredOption("--app-id <id>", "Application ID").action(async (options) => {
|
|
10702
|
-
await preUploadStatic(options);
|
|
10703
|
-
});
|
|
10704
|
-
}
|
|
10705
|
-
};
|
|
10706
|
-
var previewServerArtifactCommand = {
|
|
10707
|
-
name: "preview-server-artifact",
|
|
10708
|
-
description: "Build and audit a single-file Nest preview server artifact",
|
|
10709
|
-
register(program) {
|
|
10710
|
-
program.command(this.name).description(this.description).option("--project-root <dir>", "Application project root", process.cwd()).option("--entry <file>", "Compiled Nest entry", "dist/main.js").option(
|
|
10711
|
-
"--outfile <file>",
|
|
10712
|
-
"Output bundle",
|
|
10713
|
-
".preview-artifact/server.bundle.cjs"
|
|
10714
|
-
).option("--metadata-file <file>", "Output build metadata file").action(
|
|
10715
|
-
async (options) => {
|
|
10716
|
-
const result = await buildPreviewServerArtifact(options);
|
|
10717
|
-
console.log(JSON.stringify(result));
|
|
10718
|
-
if (!result.built) {
|
|
10719
|
-
process.exitCode = 2;
|
|
10720
|
-
}
|
|
10721
|
-
}
|
|
10722
|
-
);
|
|
10723
|
-
}
|
|
10724
|
-
};
|
|
10725
|
-
var previewArtifactWorkspaceManifestCommand = {
|
|
10726
|
-
name: "preview-artifact-workspace-manifest",
|
|
10727
|
-
description: "Snapshot the Git-visible Workspace content identity for backup and cache validation",
|
|
10728
|
-
register(program) {
|
|
10729
|
-
program.command(this.name).description(this.description).option("--project-root <dir>", "Application project root", process.cwd()).requiredOption(
|
|
10730
|
-
"--manifest-file <file>",
|
|
10731
|
-
"Workspace Manifest sidecar output file"
|
|
10732
|
-
).requiredOption(
|
|
10733
|
-
"--workspace-backup-id <id>",
|
|
10734
|
-
"Physical Workspace backup identity"
|
|
10735
|
-
).requiredOption("--app-id <id>", "Application ID").option(
|
|
10736
|
-
"--validate-source-tree",
|
|
10737
|
-
"Calculate a sourceTreeId and bind cache consumption to it"
|
|
10738
|
-
).option(
|
|
10739
|
-
"--source-tree-include <path>",
|
|
10740
|
-
"Repository-relative file or directory included in sourceTreeId; repeatable",
|
|
10741
|
-
(value, previous) => [...previous, value],
|
|
10742
|
-
[]
|
|
10743
|
-
).action(
|
|
10744
|
-
(options) => {
|
|
10745
|
-
const manifest = createPreviewArtifactWorkspaceManifest({
|
|
10746
|
-
...options,
|
|
10747
|
-
sourceTreeIncludePaths: options.sourceTreeInclude
|
|
10748
|
-
});
|
|
10749
|
-
console.log(JSON.stringify(manifest));
|
|
10750
|
-
}
|
|
10751
|
-
);
|
|
10752
|
-
}
|
|
10753
|
-
};
|
|
10754
|
-
var previewArtifactGenerationCommand = {
|
|
10755
|
-
name: "preview-artifact-generation",
|
|
10756
|
-
description: "Build, probe, promote, and atomically publish a Preview Artifact generation",
|
|
10757
|
-
register(program) {
|
|
10758
|
-
program.command(this.name).description(this.description).option("--project-root <dir>", "Application project root", process.cwd()).requiredOption("--store-root <dir>", "Immutable generation store root").option("--generation-id <id>", "Explicit immutable generation ID").requiredOption("--app-base-path <path>", "Application HTML probe path").requiredOption(
|
|
10759
|
-
"--business-api-path <path>",
|
|
10760
|
-
"Business API probe path that must not return 404/5xx"
|
|
10761
|
-
).requiredOption(
|
|
10762
|
-
"--environment-file <file>",
|
|
10763
|
-
"JSON application environment passed only to build/probe children"
|
|
10764
|
-
).requiredOption(
|
|
10765
|
-
"--workspace-manifest-file <file>",
|
|
10766
|
-
"Workspace Manifest sidecar created at backup start"
|
|
10767
|
-
).action(
|
|
10768
|
-
async (options) => {
|
|
10769
|
-
const result = await producePreviewArtifactGeneration({
|
|
10770
|
-
...options,
|
|
10771
|
-
environment: readPreviewArtifactEnvironmentFile(
|
|
10772
|
-
options.environmentFile
|
|
10773
|
-
),
|
|
10774
|
-
workspaceManifest: readPreviewArtifactWorkspaceManifest(
|
|
10775
|
-
options.workspaceManifestFile
|
|
10776
|
-
)
|
|
10777
|
-
});
|
|
10778
|
-
console.log(JSON.stringify(result));
|
|
10779
|
-
if (!result.published) process.exitCode = 2;
|
|
10780
|
-
}
|
|
10781
|
-
);
|
|
10782
|
-
}
|
|
10783
|
-
};
|
|
10784
|
-
function parseTcpPort(value) {
|
|
10785
|
-
const port = Number(value);
|
|
10786
|
-
if (!Number.isInteger(port) || port < 0 || port > 65535) {
|
|
10787
|
-
throw new Error(`invalid TCP port: ${value}`);
|
|
10788
|
-
}
|
|
10789
|
-
return port;
|
|
10790
|
-
}
|
|
10791
|
-
var previewArtifactRuntimeCommand = {
|
|
10792
|
-
name: "preview-artifact-runtime",
|
|
10793
|
-
description: "Serve a verified Preview Artifact and switch to the fresh dev service",
|
|
10794
|
-
register(program) {
|
|
10795
|
-
program.command(this.name).description(this.description).requiredOption("--store-root <dir>", "Immutable generation store root").option(
|
|
10796
|
-
"--runtime-root <dir>",
|
|
10797
|
-
"Ephemeral runtime root",
|
|
10798
|
-
"/tmp/preview-artifact"
|
|
10799
|
-
).option(
|
|
10800
|
-
"--marker-file <file>",
|
|
10801
|
-
"Atomic runtime marker file",
|
|
10802
|
-
"/tmp/preview-artifact/runtime.json"
|
|
10803
|
-
).option(
|
|
10804
|
-
"--listen-port <port>",
|
|
10805
|
-
"Stable public preview port",
|
|
10806
|
-
parseTcpPort,
|
|
10807
|
-
8001
|
|
10808
|
-
).option(
|
|
10809
|
-
"--cached-server-port <port>",
|
|
10810
|
-
"Isolated cached Nest server port, 0 selects a free port",
|
|
10811
|
-
parseTcpPort,
|
|
10812
|
-
0
|
|
10813
|
-
).option(
|
|
10814
|
-
"--fresh-server-port <port>",
|
|
10815
|
-
"Fresh Nest shadow port recorded for dev startup",
|
|
10816
|
-
parseTcpPort,
|
|
10817
|
-
3001
|
|
10818
|
-
).option(
|
|
10819
|
-
"--fresh-client-port <port>",
|
|
10820
|
-
"Fresh Vite shadow port used as the cutover target",
|
|
10821
|
-
parseTcpPort,
|
|
10822
|
-
8002
|
|
10823
|
-
).requiredOption(
|
|
10824
|
-
"--app-base-path <path>",
|
|
10825
|
-
"Application HTML readiness path"
|
|
10826
|
-
).requiredOption(
|
|
10827
|
-
"--business-api-path <path>",
|
|
10828
|
-
"Business API path revalidated with the current runtime environment"
|
|
10829
|
-
).requiredOption(
|
|
10830
|
-
"--environment-file <file>",
|
|
10831
|
-
"JSON application environment passed only to cached application child"
|
|
10832
|
-
).requiredOption(
|
|
10833
|
-
"--workspace-manifest-file <file>",
|
|
10834
|
-
"Workspace Manifest sidecar restored with this backup"
|
|
10835
|
-
).option(
|
|
10836
|
-
"--validate-source-tree",
|
|
10837
|
-
"Reject cache when its sourceTreeId does not match the Workspace backup"
|
|
10838
|
-
).option(
|
|
10839
|
-
"--source-tree-include <path>",
|
|
10840
|
-
"Repository-relative sourceTreeId scope; repeatable",
|
|
10841
|
-
(value, previous) => [...previous, value],
|
|
10842
|
-
[]
|
|
10843
|
-
).action(
|
|
10844
|
-
async (options) => {
|
|
10845
|
-
const environmentLoadStartedAt = Date.now();
|
|
10846
|
-
const environment = readPreviewArtifactEnvironmentFile(
|
|
10847
|
-
options.environmentFile
|
|
10848
|
-
);
|
|
10849
|
-
const environmentLoadDurationMs = Date.now() - environmentLoadStartedAt;
|
|
10850
|
-
const workspaceManifest = readPreviewArtifactWorkspaceManifest(
|
|
10851
|
-
options.workspaceManifestFile
|
|
10852
|
-
);
|
|
10853
|
-
const sourceTreeValidation = createPreviewArtifactSourceTreeValidation(
|
|
10854
|
-
options.validateSourceTree,
|
|
10855
|
-
options.sourceTreeInclude
|
|
10856
|
-
);
|
|
10857
|
-
const runtime = await startPreviewArtifactRuntime({
|
|
10858
|
-
...options,
|
|
10859
|
-
environment,
|
|
10860
|
-
workspaceManifest,
|
|
10861
|
-
sourceTreeValidation,
|
|
10862
|
-
initialTimings: [
|
|
10863
|
-
{
|
|
10864
|
-
phase: "cli_bootstrap",
|
|
10865
|
-
durationMs: Math.max(0, Math.round(process.uptime() * 1e3))
|
|
10866
|
-
},
|
|
10867
|
-
{
|
|
10868
|
-
phase: "environment_load",
|
|
10869
|
-
durationMs: environmentLoadDurationMs
|
|
10870
|
-
}
|
|
10871
|
-
]
|
|
10872
|
-
});
|
|
10873
|
-
if (!runtime.started) {
|
|
10874
|
-
console.log(JSON.stringify(runtime));
|
|
10875
|
-
process.exitCode = 2;
|
|
10876
|
-
return;
|
|
10877
|
-
}
|
|
10878
|
-
console.log(
|
|
10879
|
-
JSON.stringify({
|
|
10880
|
-
started: true,
|
|
10881
|
-
generationId: runtime.generationId,
|
|
10882
|
-
origin: runtime.origin,
|
|
10883
|
-
runtimeDir: runtime.runtimeDir
|
|
10884
|
-
})
|
|
10885
|
-
);
|
|
10886
|
-
void runtime.cutover.then((result) => {
|
|
10887
|
-
console.log(JSON.stringify({ event: "fresh-cutover", ...result }));
|
|
10888
|
-
});
|
|
10889
|
-
const shutdown = async () => {
|
|
10890
|
-
await runtime.close();
|
|
10891
|
-
process.exit(0);
|
|
10892
|
-
};
|
|
10893
|
-
process.once("SIGTERM", () => void shutdown());
|
|
10894
|
-
process.once("SIGINT", () => void shutdown());
|
|
10895
|
-
}
|
|
10896
|
-
);
|
|
10897
|
-
}
|
|
10898
|
-
};
|
|
10899
|
-
var buildCommandGroup = {
|
|
10900
|
-
name: "build",
|
|
10901
|
-
description: "Build related commands",
|
|
10902
|
-
commands: [
|
|
10903
|
-
getTokenCommand,
|
|
10904
|
-
uploadStaticCommand,
|
|
10905
|
-
preUploadStaticCommand,
|
|
10906
|
-
previewServerArtifactCommand,
|
|
10907
|
-
previewArtifactWorkspaceManifestCommand,
|
|
10908
|
-
previewArtifactGenerationCommand,
|
|
10909
|
-
previewArtifactRuntimeCommand
|
|
10910
|
-
]
|
|
10911
|
-
};
|
|
10912
|
-
|
|
10913
|
-
// src/commands/index.ts
|
|
10914
|
-
var commands = [
|
|
10915
|
-
genDbSchemaCommand,
|
|
10916
|
-
syncCommand,
|
|
10917
|
-
upgradeCommand,
|
|
10918
|
-
actionPluginCommandGroup,
|
|
10919
|
-
capabilityCommandGroup,
|
|
10920
|
-
componentCommandGroup,
|
|
10921
|
-
migrationCommand,
|
|
10922
|
-
readLogsCommand,
|
|
10923
|
-
buildCommandGroup
|
|
10924
|
-
];
|
|
10925
|
-
|
|
10926
|
-
// src/index.ts
|
|
10927
|
-
var envPath = path34.join(process.cwd(), ".env");
|
|
10928
|
-
if (fs38.existsSync(envPath)) {
|
|
10929
|
-
dotenvConfig({ path: envPath });
|
|
10930
8277
|
}
|
|
10931
|
-
var __dirname =
|
|
10932
|
-
var pkg = JSON.parse(
|
|
8278
|
+
var __dirname = path25.dirname(fileURLToPath5(import.meta.url));
|
|
8279
|
+
var pkg = JSON.parse(fs29.readFileSync(path25.join(__dirname, "../package.json"), "utf-8"));
|
|
10933
8280
|
var cli = new FullstackCLI(pkg.version);
|
|
10934
8281
|
cli.useAll(commands);
|
|
10935
8282
|
cli.run();
|